diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/render/intern | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'source/blender/render/intern')
16 files changed, 10451 insertions, 9558 deletions
diff --git a/source/blender/render/intern/include/initrender.h b/source/blender/render/intern/include/initrender.h index 1c290084fbc..99282a8e703 100644 --- a/source/blender/render/intern/include/initrender.h +++ b/source/blender/render/intern/include/initrender.h @@ -21,7 +21,6 @@ * \ingroup render */ - #ifndef __INITRENDER_H__ #define __INITRENDER_H__ diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index d7636a150ef..24356194f5d 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -24,13 +24,13 @@ #ifndef __RENDER_RESULT_H__ #define __RENDER_RESULT_H__ -#define PASS_VECTOR_MAX 10000.0f +#define PASS_VECTOR_MAX 10000.0f -#define RR_USE_MEM 0 -#define RR_USE_EXR 1 +#define RR_USE_MEM 0 +#define RR_USE_EXR 1 -#define RR_ALL_LAYERS NULL -#define RR_ALL_VIEWS NULL +#define RR_ALL_LAYERS NULL +#define RR_ALL_VIEWS NULL struct ColorManagedDisplaySettings; struct ColorManagedViewSettings; @@ -47,9 +47,14 @@ struct rcti; /* New */ struct RenderResult *render_result_new(struct Render *re, - struct rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname); + struct rcti *partrct, + int crop, + int savebuffers, + const char *layername, + const char *viewname); -struct RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); +struct RenderResult *render_result_new_from_exr( + void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); void render_result_view_new(struct RenderResult *rr, const char *viewname); void render_result_views_new(struct RenderResult *rr, struct RenderData *rd); @@ -61,7 +66,12 @@ void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart); /* Add Passes */ void render_result_clone_passes(struct Render *re, struct RenderResult *rr, const char *viewname); -void render_result_add_pass(struct RenderResult *rr, const char *name, int channels, const char *chan_id, const char *layername, const char *viewname); +void render_result_add_pass(struct RenderResult *rr, + const char *name, + int channels, + const char *chan_id, + const char *layername, + const char *viewname); /* Free */ @@ -80,12 +90,23 @@ void render_result_exr_file_begin(struct Render *re, struct RenderEngine *engine void render_result_exr_file_end(struct Render *re, struct RenderEngine *engine); /* render pass wrapper for gpencil */ -struct RenderPass *gp_add_pass(struct RenderResult *rr, struct RenderLayer *rl, int channels, const char *name, const char *viewname); - -void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); - -void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); -int render_result_exr_file_read_path(struct RenderResult *rr, struct RenderLayer *rl_single, const char *filepath); +struct RenderPass *gp_add_pass(struct RenderResult *rr, + struct RenderLayer *rl, + int channels, + const char *name, + const char *viewname); + +void render_result_exr_file_merge(struct RenderResult *rr, + struct RenderResult *rrpart, + const char *viewname); + +void render_result_exr_file_path(struct Scene *scene, + const char *layname, + int sample, + char *filepath); +int render_result_exr_file_read_path(struct RenderResult *rr, + struct RenderLayer *rl_single, + const char *filepath); /* EXR cache */ @@ -94,40 +115,42 @@ bool render_result_exr_file_cache_read(struct Render *re); /* Combined Pixel Rect */ -struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd, const int view_id); +struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, + struct RenderData *rd, + const int view_id); void render_result_rect_fill_zero(struct RenderResult *rr, const int view_id); void render_result_rect_get_pixels(struct RenderResult *rr, - unsigned int *rect, int rectx, int recty, - const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings, - const int view_id); + unsigned int *rect, + int rectx, + int recty, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, + const int view_id); void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src); void render_result_views_shallowdelete(struct RenderResult *rr); bool render_result_has_views(struct RenderResult *rr); -#define FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re_, iter_) \ -{ \ - int nr_; \ - ViewLayer *iter_; \ - for (nr_ = 0, iter_ = (re_)->view_layers.first; \ - iter_ != NULL; \ - iter_ = iter_->next, nr_++) \ - { \ - if (!G.background && (re_)->r.scemode & R_SINGLE_LAYER) { \ - if (nr_ != re->active_view_layer) { \ - continue; \ - } \ - } \ - else { \ - if ((iter_->flag & VIEW_LAYER_RENDER) == 0) { \ - continue; \ - } \ - } - -#define FOREACH_VIEW_LAYER_TO_RENDER_END \ - } \ -} ((void)0) +#define FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re_, iter_) \ + { \ + int nr_; \ + ViewLayer *iter_; \ + for (nr_ = 0, iter_ = (re_)->view_layers.first; iter_ != NULL; iter_ = iter_->next, nr_++) { \ + if (!G.background && (re_)->r.scemode & R_SINGLE_LAYER) { \ + if (nr_ != re->active_view_layer) { \ + continue; \ + } \ + } \ + else { \ + if ((iter_->flag & VIEW_LAYER_RENDER) == 0) { \ + continue; \ + } \ + } + +#define FOREACH_VIEW_LAYER_TO_RENDER_END \ + } \ + } \ + ((void)0) #endif /* __RENDER_RESULT_H__ */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index a4533c6cdec..69e2ea991dd 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -21,7 +21,6 @@ * \ingroup render */ - #ifndef __RENDER_TYPES_H__ #define __RENDER_TYPES_H__ @@ -45,118 +44,118 @@ struct ReportList; /* this is handed over to threaded hiding/passes/shading engine */ typedef struct RenderPart { - struct RenderPart *next, *prev; + struct RenderPart *next, *prev; - RenderResult *result; /* result of part rendering */ - ListBase fullresult; /* optional full sample buffers */ + RenderResult *result; /* result of part rendering */ + ListBase fullresult; /* optional full sample buffers */ - rcti disprect; /* part coordinates within total picture */ - int rectx, recty; /* the size */ - int nr; /* nr is partnr */ - short status; + rcti disprect; /* part coordinates within total picture */ + int rectx, recty; /* the size */ + int nr; /* nr is partnr */ + short status; } RenderPart; enum { - PART_STATUS_NONE = 0, - PART_STATUS_IN_PROGRESS = 1, - PART_STATUS_RENDERED = 2, - PART_STATUS_MERGED = 3, + PART_STATUS_NONE = 0, + PART_STATUS_IN_PROGRESS = 1, + PART_STATUS_RENDERED = 2, + PART_STATUS_MERGED = 3, }; /* controls state of render, everything that's read-only during render stage */ struct Render { - struct Render *next, *prev; - char name[RE_MAXNAME]; - int slot; - - /* state settings */ - short flag, ok, result_ok; - - /* result of rendering */ - RenderResult *result; - /* if render with single-layer option, other rendered layers are stored here */ - RenderResult *pushedresult; - /* a list of RenderResults, for fullsample */ - ListBase fullresult; - /* read/write mutex, all internal code that writes to re->result must use a - * write lock, all external code must use a read lock. internal code is assumed - * to not conflict with writes, so no lock used for that */ - ThreadRWMutex resultmutex; - - /* window size, display rect, viewplane */ - int winx, winy; /* buffer width and height with percentage applied - * without border & crop. convert to long before multiplying together to avoid overflow. */ - rcti disprect; /* part within winx winy */ - rctf viewplane; /* mapped on winx winy */ - - /* final picture width and height (within disprect) */ - int rectx, recty; - - /* real maximum size of parts after correction for minimum - * partx*xparts can be larger than rectx, in that case last part is smaller */ - int partx, party; - - /* Camera transform, only used by Freestyle. */ - float winmat[4][4]; - - /* clippping */ - float clip_start; - float clip_end; - - /* main, scene, and its full copy of renderdata and world */ - struct Main *main; - Scene *scene; - RenderData r; - ListBase view_layers; - int active_view_layer; - struct Object *camera_override; - - ThreadRWMutex partsmutex; - ListBase parts; - - /* render engine */ - struct RenderEngine *engine; + struct Render *next, *prev; + char name[RE_MAXNAME]; + int slot; + + /* state settings */ + short flag, ok, result_ok; + + /* result of rendering */ + RenderResult *result; + /* if render with single-layer option, other rendered layers are stored here */ + RenderResult *pushedresult; + /* a list of RenderResults, for fullsample */ + ListBase fullresult; + /* read/write mutex, all internal code that writes to re->result must use a + * write lock, all external code must use a read lock. internal code is assumed + * to not conflict with writes, so no lock used for that */ + ThreadRWMutex resultmutex; + + /* window size, display rect, viewplane */ + int winx, winy; /* buffer width and height with percentage applied + * without border & crop. convert to long before multiplying together to avoid overflow. */ + rcti disprect; /* part within winx winy */ + rctf viewplane; /* mapped on winx winy */ + + /* final picture width and height (within disprect) */ + int rectx, recty; + + /* real maximum size of parts after correction for minimum + * partx*xparts can be larger than rectx, in that case last part is smaller */ + int partx, party; + + /* Camera transform, only used by Freestyle. */ + float winmat[4][4]; + + /* clippping */ + float clip_start; + float clip_end; + + /* main, scene, and its full copy of renderdata and world */ + struct Main *main; + Scene *scene; + RenderData r; + ListBase view_layers; + int active_view_layer; + struct Object *camera_override; + + ThreadRWMutex partsmutex; + ListBase parts; + + /* render engine */ + struct RenderEngine *engine; #ifdef WITH_FREESTYLE - struct Main *freestyle_bmain; - ListBase freestyle_renders; + struct Main *freestyle_bmain; + ListBase freestyle_renders; #endif - /* callbacks */ - void (*display_init)(void *handle, RenderResult *rr); - void *dih; - void (*display_clear)(void *handle, RenderResult *rr); - void *dch; - void (*display_update)(void *handle, RenderResult *rr, volatile rcti *rect); - void *duh; - void (*current_scene_update)(void *handle, struct Scene *scene); - void *suh; + /* callbacks */ + void (*display_init)(void *handle, RenderResult *rr); + void *dih; + void (*display_clear)(void *handle, RenderResult *rr); + void *dch; + void (*display_update)(void *handle, RenderResult *rr, volatile rcti *rect); + void *duh; + void (*current_scene_update)(void *handle, struct Scene *scene); + void *suh; - void (*stats_draw)(void *handle, RenderStats *ri); - void *sdh; - void (*progress)(void *handle, float i); - void *prh; + void (*stats_draw)(void *handle, RenderStats *ri); + void *sdh; + void (*progress)(void *handle, float i); + void *prh; - void (*draw_lock)(void *handle, int i); - void *dlh; - int (*test_break)(void *handle); - void *tbh; + void (*draw_lock)(void *handle, int i); + void *dlh; + int (*test_break)(void *handle); + void *tbh; - RenderStats i; + RenderStats i; - struct ReportList *reports; + struct ReportList *reports; - void **movie_ctx_arr; - char viewname[MAX_NAME]; + void **movie_ctx_arr; + char viewname[MAX_NAME]; - /* TODO replace by a whole draw manager. */ - void *gl_context; - void *gpu_context; + /* TODO replace by a whole draw manager. */ + void *gl_context; + void *gpu_context; }; /* **************** defines ********************* */ /* R.flag */ -#define R_ANIMATION 1 +#define R_ANIMATION 1 #endif /* __RENDER_TYPES_H__ */ diff --git a/source/blender/render/intern/include/renderpipeline.h b/source/blender/render/intern/include/renderpipeline.h index 7479de8b707..12b231ef55f 100644 --- a/source/blender/render/intern/include/renderpipeline.h +++ b/source/blender/render/intern/include/renderpipeline.h @@ -21,7 +21,6 @@ * \ingroup render */ - #ifndef __RENDERPIPELINE_H__ #define __RENDERPIPELINE_H__ @@ -32,7 +31,9 @@ struct RenderLayer; struct RenderResult; struct RenderLayer *render_get_active_layer(struct Render *re, struct RenderResult *rr); -void render_update_anim_renderdata(struct Render *re, struct RenderData *rd, struct ListBase *render_layers); +void render_update_anim_renderdata(struct Render *re, + struct RenderData *rd, + struct ListBase *render_layers); void render_copy_renderdata(struct RenderData *to, struct RenderData *from); #endif /* __RENDERPIPELINE_H__ */ diff --git a/source/blender/render/intern/include/texture.h b/source/blender/render/intern/include/texture.h index 7c86f700652..d783263c8b8 100644 --- a/source/blender/render/intern/include/texture.h +++ b/source/blender/render/intern/include/texture.h @@ -21,39 +21,44 @@ * \ingroup render */ - #ifndef __TEXTURE_H__ #define __TEXTURE_H__ -#define BRICONT \ - texres->tin= (texres->tin-0.5f) * tex->contrast+tex->bright-0.5f; \ - if (!(tex->flag & TEX_NO_CLAMP)) { \ - if (texres->tin < 0.0f) texres->tin= 0.0f; \ - else if (texres->tin > 1.0f) texres->tin= 1.0f; \ - } \ +#define BRICONT \ + texres->tin = (texres->tin - 0.5f) * tex->contrast + tex->bright - 0.5f; \ + if (!(tex->flag & TEX_NO_CLAMP)) { \ + if (texres->tin < 0.0f) \ + texres->tin = 0.0f; \ + else if (texres->tin > 1.0f) \ + texres->tin = 1.0f; \ + } -#define BRICONTRGB \ - texres->tr= tex->rfac*((texres->tr-0.5f)*tex->contrast+tex->bright-0.5f); \ - texres->tg= tex->gfac*((texres->tg-0.5f)*tex->contrast+tex->bright-0.5f); \ - texres->tb= tex->bfac*((texres->tb-0.5f)*tex->contrast+tex->bright-0.5f); \ - if (!(tex->flag & TEX_NO_CLAMP)) { \ - if (texres->tr < 0.0f) texres->tr= 0.0f; \ - if (texres->tg < 0.0f) texres->tg= 0.0f; \ - if (texres->tb < 0.0f) texres->tb= 0.0f; \ - } \ - if (tex->saturation != 1.0f) { \ - float _hsv[3]; \ - rgb_to_hsv(texres->tr, texres->tg, texres->tb, \ - _hsv, _hsv+1, _hsv+2); \ - _hsv[1] *= tex->saturation; \ - hsv_to_rgb(_hsv[0], _hsv[1], _hsv[2], \ - &texres->tr, &texres->tg, &texres->tb); \ - if ((tex->saturation > 1.0f) && !(tex->flag & TEX_NO_CLAMP)) { \ - if (texres->tr < 0.0f) texres->tr= 0.0f; \ - if (texres->tg < 0.0f) texres->tg= 0.0f; \ - if (texres->tb < 0.0f) texres->tb= 0.0f; \ - } \ - } \ +#define BRICONTRGB \ + texres->tr = tex->rfac * ((texres->tr - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + texres->tg = tex->gfac * ((texres->tg - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + texres->tb = tex->bfac * ((texres->tb - 0.5f) * tex->contrast + tex->bright - 0.5f); \ + if (!(tex->flag & TEX_NO_CLAMP)) { \ + if (texres->tr < 0.0f) \ + texres->tr = 0.0f; \ + if (texres->tg < 0.0f) \ + texres->tg = 0.0f; \ + if (texres->tb < 0.0f) \ + texres->tb = 0.0f; \ + } \ + if (tex->saturation != 1.0f) { \ + float _hsv[3]; \ + rgb_to_hsv(texres->tr, texres->tg, texres->tb, _hsv, _hsv + 1, _hsv + 2); \ + _hsv[1] *= tex->saturation; \ + hsv_to_rgb(_hsv[0], _hsv[1], _hsv[2], &texres->tr, &texres->tg, &texres->tb); \ + if ((tex->saturation > 1.0f) && !(tex->flag & TEX_NO_CLAMP)) { \ + if (texres->tr < 0.0f) \ + texres->tr = 0.0f; \ + if (texres->tg < 0.0f) \ + texres->tg = 0.0f; \ + if (texres->tb < 0.0f) \ + texres->tb = 0.0f; \ + } \ + } struct ImBuf; struct Image; @@ -63,8 +68,28 @@ struct TexResult; /* imagetexture.h */ -int imagewraposa(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], const float dxt[2], const float dyt[2], struct TexResult *texres, struct ImagePool *pool, const bool skip_load_image); -int imagewrap(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], struct TexResult *texres, struct ImagePool *pool, const bool skip_load_image); -void image_sample(struct Image *ima, float fx, float fy, float dx, float dy, float result[4], struct ImagePool *pool); +int imagewraposa(struct Tex *tex, + struct Image *ima, + struct ImBuf *ibuf, + const float texvec[3], + const float dxt[2], + const float dyt[2], + struct TexResult *texres, + struct ImagePool *pool, + const bool skip_load_image); +int imagewrap(struct Tex *tex, + struct Image *ima, + struct ImBuf *ibuf, + const float texvec[3], + struct TexResult *texres, + struct ImagePool *pool, + const bool skip_load_image); +void image_sample(struct Image *ima, + float fx, + float fy, + float dx, + float dy, + float result[4], + struct ImagePool *pool); #endif /* __TEXTURE_H__ */ diff --git a/source/blender/render/intern/include/zbuf.h b/source/blender/render/intern/include/zbuf.h index b5f4fffb0a9..bc6addd6c5f 100644 --- a/source/blender/render/intern/include/zbuf.h +++ b/source/blender/render/intern/include/zbuf.h @@ -18,23 +18,26 @@ * \ingroup render */ - #ifndef __ZBUF_H__ #define __ZBUF_H__ /* span fill in method, is also used to localize data for zbuffering */ typedef struct ZSpan { - int rectx, recty; /* range for clipping */ + int rectx, recty; /* range for clipping */ - int miny1, maxy1, miny2, maxy2; /* actual filled in range */ - const float *minp1, *maxp1, *minp2, *maxp2; /* vertex pointers detect min/max range in */ - float *span1, *span2; + int miny1, maxy1, miny2, maxy2; /* actual filled in range */ + const float *minp1, *maxp1, *minp2, *maxp2; /* vertex pointers detect min/max range in */ + float *span1, *span2; } ZSpan; void zbuf_alloc_span(struct ZSpan *zspan, int rectx, int recty); void zbuf_free_span(struct ZSpan *zspan); -void zspan_scanconvert(struct ZSpan *zpan, void *handle, float *v1, float *v2, float *v3, - void (*func)(void *, int, int, float, float) ); +void zspan_scanconvert(struct ZSpan *zpan, + void *handle, + float *v1, + float *v2, + float *v3, + void (*func)(void *, int, int, float, float)); #endif diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c index 5e63f76ca80..bc0c73fd11f 100644 --- a/source/blender/render/intern/source/bake_api.c +++ b/source/blender/render/intern/source/bake_api.c @@ -86,611 +86,660 @@ #include "zbuf.h" typedef struct BakeDataZSpan { - BakePixel *pixel_array; - int primitive_id; - BakeImage *bk_image; - ZSpan *zspan; - float du_dx, du_dy; - float dv_dx, dv_dy; + BakePixel *pixel_array; + int primitive_id; + BakeImage *bk_image; + ZSpan *zspan; + float du_dx, du_dy; + float dv_dx, dv_dy; } BakeDataZSpan; /** * struct wrapping up tangent space data */ typedef struct TSpace { - float tangent[3]; - float sign; + float tangent[3]; + float sign; } TSpace; typedef struct TriTessFace { - const MVert *mverts[3]; - const TSpace *tspace[3]; - float normal[3]; /* for flat faces */ - bool is_smooth; + const MVert *mverts[3]; + const TSpace *tspace[3]; + float normal[3]; /* for flat faces */ + bool is_smooth; } TriTessFace; static void store_bake_pixel(void *handle, int x, int y, float u, float v) { - BakeDataZSpan *bd = (BakeDataZSpan *)handle; - BakePixel *pixel; + BakeDataZSpan *bd = (BakeDataZSpan *)handle; + BakePixel *pixel; - const int width = bd->bk_image->width; - const size_t offset = bd->bk_image->offset; - const int i = offset + y * width + x; + const int width = bd->bk_image->width; + const size_t offset = bd->bk_image->offset; + const int i = offset + y * width + x; - pixel = &bd->pixel_array[i]; - pixel->primitive_id = bd->primitive_id; + pixel = &bd->pixel_array[i]; + pixel->primitive_id = bd->primitive_id; - /* At this point object_id is always 0, since this function runs for the - * lowpoly mesh only. The object_id lookup indices are set afterwards. */ + /* At this point object_id is always 0, since this function runs for the + * lowpoly mesh only. The object_id lookup indices are set afterwards. */ - copy_v2_fl2(pixel->uv, u, v); + copy_v2_fl2(pixel->uv, u, v); - pixel->du_dx = bd->du_dx; - pixel->du_dy = bd->du_dy; - pixel->dv_dx = bd->dv_dx; - pixel->dv_dy = bd->dv_dy; - pixel->object_id = 0; + pixel->du_dx = bd->du_dx; + pixel->du_dy = bd->du_dy; + pixel->dv_dx = bd->dv_dx; + pixel->dv_dy = bd->dv_dy; + pixel->object_id = 0; } void RE_bake_mask_fill(const BakePixel pixel_array[], const size_t num_pixels, char *mask) { - size_t i; - if (!mask) - return; - - /* only extend to pixels outside the mask area */ - for (i = 0; i < num_pixels; i++) { - if (pixel_array[i].primitive_id != -1) { - mask[i] = FILTER_MASK_USED; - } - } + size_t i; + if (!mask) + return; + + /* only extend to pixels outside the mask area */ + for (i = 0; i < num_pixels; i++) { + if (pixel_array[i].primitive_id != -1) { + mask[i] = FILTER_MASK_USED; + } + } } void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin) { - /* margin */ - IMB_filter_extend(ibuf, mask, margin); + /* margin */ + IMB_filter_extend(ibuf, mask, margin); - if (ibuf->planes != R_IMF_PLANES_RGBA) - /* clear alpha added by filtering */ - IMB_rectfill_alpha(ibuf, 1.0f); + if (ibuf->planes != R_IMF_PLANES_RGBA) + /* clear alpha added by filtering */ + IMB_rectfill_alpha(ibuf, 1.0f); } - /** * This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index. * The returned normal is actually the direction from the same barycentric coordinate in the cage to the base mesh * The returned coordinate is the point in the cage mesh */ -static void calc_point_from_barycentric_cage( - TriTessFace *triangles_low, TriTessFace *triangles_cage, - float mat_low[4][4], float mat_cage[4][4], - int primitive_id, float u, float v, - float r_co[3], float r_dir[3]) +static void calc_point_from_barycentric_cage(TriTessFace *triangles_low, + TriTessFace *triangles_cage, + float mat_low[4][4], + float mat_cage[4][4], + int primitive_id, + float u, + float v, + float r_co[3], + float r_dir[3]) { - float data[2][3][3]; - float coord[2][3]; - float dir[3]; - int i; + float data[2][3][3]; + float coord[2][3]; + float dir[3]; + int i; - TriTessFace *triangle[2]; + TriTessFace *triangle[2]; - triangle[0] = &triangles_low[primitive_id]; - triangle[1] = &triangles_cage[primitive_id]; + triangle[0] = &triangles_low[primitive_id]; + triangle[1] = &triangles_cage[primitive_id]; - for (i = 0; i < 2; i++) { - copy_v3_v3(data[i][0], triangle[i]->mverts[0]->co); - copy_v3_v3(data[i][1], triangle[i]->mverts[1]->co); - copy_v3_v3(data[i][2], triangle[i]->mverts[2]->co); - interp_barycentric_tri_v3(data[i], u, v, coord[i]); - } + for (i = 0; i < 2; i++) { + copy_v3_v3(data[i][0], triangle[i]->mverts[0]->co); + copy_v3_v3(data[i][1], triangle[i]->mverts[1]->co); + copy_v3_v3(data[i][2], triangle[i]->mverts[2]->co); + interp_barycentric_tri_v3(data[i], u, v, coord[i]); + } - /* convert from local to world space */ - mul_m4_v3(mat_low, coord[0]); - mul_m4_v3(mat_cage, coord[1]); + /* convert from local to world space */ + mul_m4_v3(mat_low, coord[0]); + mul_m4_v3(mat_cage, coord[1]); - sub_v3_v3v3(dir, coord[0], coord[1]); - normalize_v3(dir); + sub_v3_v3v3(dir, coord[0], coord[1]); + normalize_v3(dir); - copy_v3_v3(r_co, coord[1]); - copy_v3_v3(r_dir, dir); + copy_v3_v3(r_co, coord[1]); + copy_v3_v3(r_dir, dir); } /** * This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index. * The returned coordinate is extruded along the normal by cage_extrusion */ -static void calc_point_from_barycentric_extrusion( - TriTessFace *triangles, - float mat[4][4], float imat[4][4], - int primitive_id, float u, float v, - float cage_extrusion, - float r_co[3], float r_dir[3], - const bool is_cage) +static void calc_point_from_barycentric_extrusion(TriTessFace *triangles, + float mat[4][4], + float imat[4][4], + int primitive_id, + float u, + float v, + float cage_extrusion, + float r_co[3], + float r_dir[3], + const bool is_cage) { - float data[3][3]; - float coord[3]; - float dir[3]; - float cage[3]; - bool is_smooth; - - TriTessFace *triangle = &triangles[primitive_id]; - is_smooth = triangle->is_smooth || is_cage; - - copy_v3_v3(data[0], triangle->mverts[0]->co); - copy_v3_v3(data[1], triangle->mverts[1]->co); - copy_v3_v3(data[2], triangle->mverts[2]->co); - - interp_barycentric_tri_v3(data, u, v, coord); - - if (is_smooth) { - normal_short_to_float_v3(data[0], triangle->mverts[0]->no); - normal_short_to_float_v3(data[1], triangle->mverts[1]->no); - normal_short_to_float_v3(data[2], triangle->mverts[2]->no); - - interp_barycentric_tri_v3(data, u, v, dir); - normalize_v3(dir); - } - else { - copy_v3_v3(dir, triangle->normal); - } - - mul_v3_v3fl(cage, dir, cage_extrusion); - add_v3_v3(coord, cage); - - normalize_v3(dir); - negate_v3(dir); - - /* convert from local to world space */ - mul_m4_v3(mat, coord); - mul_transposed_mat3_m4_v3(imat, dir); - normalize_v3(dir); - - copy_v3_v3(r_co, coord); - copy_v3_v3(r_dir, dir); + float data[3][3]; + float coord[3]; + float dir[3]; + float cage[3]; + bool is_smooth; + + TriTessFace *triangle = &triangles[primitive_id]; + is_smooth = triangle->is_smooth || is_cage; + + copy_v3_v3(data[0], triangle->mverts[0]->co); + copy_v3_v3(data[1], triangle->mverts[1]->co); + copy_v3_v3(data[2], triangle->mverts[2]->co); + + interp_barycentric_tri_v3(data, u, v, coord); + + if (is_smooth) { + normal_short_to_float_v3(data[0], triangle->mverts[0]->no); + normal_short_to_float_v3(data[1], triangle->mverts[1]->no); + normal_short_to_float_v3(data[2], triangle->mverts[2]->no); + + interp_barycentric_tri_v3(data, u, v, dir); + normalize_v3(dir); + } + else { + copy_v3_v3(dir, triangle->normal); + } + + mul_v3_v3fl(cage, dir, cage_extrusion); + add_v3_v3(coord, cage); + + normalize_v3(dir); + negate_v3(dir); + + /* convert from local to world space */ + mul_m4_v3(mat, coord); + mul_transposed_mat3_m4_v3(imat, dir); + normalize_v3(dir); + + copy_v3_v3(r_co, coord); + copy_v3_v3(r_dir, dir); } -static void barycentric_differentials_from_position( - const float co[3], const float v1[3], const float v2[3], const float v3[3], - const float dxco[3], const float dyco[3], const float facenor[3], const bool differentials, - float *u, float *v, float *dx_u, float *dx_v, float *dy_u, float *dy_v) +static void barycentric_differentials_from_position(const float co[3], + const float v1[3], + const float v2[3], + const float v3[3], + const float dxco[3], + const float dyco[3], + const float facenor[3], + const bool differentials, + float *u, + float *v, + float *dx_u, + float *dx_v, + float *dy_u, + float *dy_v) { - /* find most stable axis to project */ - int axis1, axis2; - axis_dominant_v3(&axis1, &axis2, facenor); - - /* compute u,v and derivatives */ - float t00 = v3[axis1] - v1[axis1]; - float t01 = v3[axis2] - v1[axis2]; - float t10 = v3[axis1] - v2[axis1]; - float t11 = v3[axis2] - v2[axis2]; - - float detsh = (t00 * t11 - t10 * t01); - detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f; - t00 *= detsh; t01 *= detsh; - t10 *= detsh; t11 *= detsh; - - *u = (v3[axis1] - co[axis1]) * t11 - (v3[axis2] - co[axis2]) * t10; - *v = (v3[axis2] - co[axis2]) * t00 - (v3[axis1] - co[axis1]) * t01; - if (differentials) { - *dx_u = dxco[axis1] * t11 - dxco[axis2] * t10; - *dx_v = dxco[axis2] * t00 - dxco[axis1] * t01; - *dy_u = dyco[axis1] * t11 - dyco[axis2] * t10; - *dy_v = dyco[axis2] * t00 - dyco[axis1] * t01; - } + /* find most stable axis to project */ + int axis1, axis2; + axis_dominant_v3(&axis1, &axis2, facenor); + + /* compute u,v and derivatives */ + float t00 = v3[axis1] - v1[axis1]; + float t01 = v3[axis2] - v1[axis2]; + float t10 = v3[axis1] - v2[axis1]; + float t11 = v3[axis2] - v2[axis2]; + + float detsh = (t00 * t11 - t10 * t01); + detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f; + t00 *= detsh; + t01 *= detsh; + t10 *= detsh; + t11 *= detsh; + + *u = (v3[axis1] - co[axis1]) * t11 - (v3[axis2] - co[axis2]) * t10; + *v = (v3[axis2] - co[axis2]) * t00 - (v3[axis1] - co[axis1]) * t01; + if (differentials) { + *dx_u = dxco[axis1] * t11 - dxco[axis2] * t10; + *dx_v = dxco[axis2] * t00 - dxco[axis1] * t01; + *dy_u = dyco[axis1] * t11 - dyco[axis2] * t10; + *dy_v = dyco[axis2] * t00 - dyco[axis1] * t01; + } } /** * This function populates pixel_array and returns TRUE if things are correct */ -static bool cast_ray_highpoly( - BVHTreeFromMesh *treeData, TriTessFace *triangle_low, TriTessFace *triangles[], - BakePixel *pixel_array_low, BakePixel *pixel_array, float mat_low[4][4], BakeHighPolyData *highpoly, - const float co[3], const float dir[3], const int pixel_id, const int tot_highpoly) +static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, + TriTessFace *triangle_low, + TriTessFace *triangles[], + BakePixel *pixel_array_low, + BakePixel *pixel_array, + float mat_low[4][4], + BakeHighPolyData *highpoly, + const float co[3], + const float dir[3], + const int pixel_id, + const int tot_highpoly) { - int i; - int hit_mesh = -1; - float hit_distance = FLT_MAX; - - BVHTreeRayHit *hits; - hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays"); - - for (i = 0; i < tot_highpoly; i++) { - float co_high[3], dir_high[3]; - - hits[i].index = -1; - /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ - hits[i].dist = BVH_RAYCAST_DIST_MAX; - - /* transform the ray from the world space to the highpoly space */ - mul_v3_m4v3(co_high, highpoly[i].imat, co); - - /* rotates */ - mul_v3_mat3_m4v3(dir_high, highpoly[i].imat, dir); - normalize_v3(dir_high); - - /* cast ray */ - if (treeData[i].tree) { - BLI_bvhtree_ray_cast(treeData[i].tree, co_high, dir_high, 0.0f, &hits[i], treeData[i].raycast_callback, &treeData[i]); - } - - if (hits[i].index != -1) { - float distance; - float hit_world[3]; - - /* distance comparison in world space */ - mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); - distance = len_squared_v3v3(hit_world, co); - - if (distance < hit_distance) { - hit_mesh = i; - hit_distance = distance; - } - } - } - - if (hit_mesh != -1) { - int primitive_id_high = hits[hit_mesh].index; - TriTessFace *triangle_high = &triangles[hit_mesh][primitive_id_high]; - BakePixel *pixel_low = &pixel_array_low[pixel_id]; - BakePixel *pixel_high = &pixel_array[pixel_id]; - - pixel_high->primitive_id = primitive_id_high; - pixel_high->object_id = hit_mesh; - - /* ray direction in high poly object space */ - float dir_high[3]; - mul_v3_mat3_m4v3(dir_high, highpoly[hit_mesh].imat, dir); - normalize_v3(dir_high); - - /* compute position differentials on low poly object */ - float duco_low[3], dvco_low[3], dxco[3], dyco[3]; - sub_v3_v3v3(duco_low, triangle_low->mverts[0]->co, triangle_low->mverts[2]->co); - sub_v3_v3v3(dvco_low, triangle_low->mverts[1]->co, triangle_low->mverts[2]->co); - - mul_v3_v3fl(dxco, duco_low, pixel_low->du_dx); - madd_v3_v3fl(dxco, dvco_low, pixel_low->dv_dx); - mul_v3_v3fl(dyco, duco_low, pixel_low->du_dy); - madd_v3_v3fl(dyco, dvco_low, pixel_low->dv_dy); - - /* transform from low poly to high poly object space */ - mul_mat3_m4_v3(mat_low, dxco); - mul_mat3_m4_v3(mat_low, dyco); - mul_mat3_m4_v3(highpoly[hit_mesh].imat, dxco); - mul_mat3_m4_v3(highpoly[hit_mesh].imat, dyco); - - /* transfer position differentials */ - float tmp[3]; - mul_v3_v3fl(tmp, dir_high, 1.0f / dot_v3v3(dir_high, triangle_high->normal)); - madd_v3_v3fl(dxco, tmp, -dot_v3v3(dxco, triangle_high->normal)); - madd_v3_v3fl(dyco, tmp, -dot_v3v3(dyco, triangle_high->normal)); - - /* compute barycentric differentials from position differentials */ - barycentric_differentials_from_position( - hits[hit_mesh].co, triangle_high->mverts[0]->co, - triangle_high->mverts[1]->co, triangle_high->mverts[2]->co, - dxco, dyco, triangle_high->normal, true, - &pixel_high->uv[0], &pixel_high->uv[1], - &pixel_high->du_dx, &pixel_high->dv_dx, - &pixel_high->du_dy, &pixel_high->dv_dy); - - /* verify we have valid uvs */ - BLI_assert(pixel_high->uv[0] >= -1e-3f && - pixel_high->uv[1] >= -1e-3f && - pixel_high->uv[0] + pixel_high->uv[1] <= 1.0f + 1e-3f); - } - else { - pixel_array[pixel_id].primitive_id = -1; - pixel_array[pixel_id].object_id = -1; - } - - MEM_freeN(hits); - return hit_mesh != -1; + int i; + int hit_mesh = -1; + float hit_distance = FLT_MAX; + + BVHTreeRayHit *hits; + hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays"); + + for (i = 0; i < tot_highpoly; i++) { + float co_high[3], dir_high[3]; + + hits[i].index = -1; + /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + hits[i].dist = BVH_RAYCAST_DIST_MAX; + + /* transform the ray from the world space to the highpoly space */ + mul_v3_m4v3(co_high, highpoly[i].imat, co); + + /* rotates */ + mul_v3_mat3_m4v3(dir_high, highpoly[i].imat, dir); + normalize_v3(dir_high); + + /* cast ray */ + if (treeData[i].tree) { + BLI_bvhtree_ray_cast(treeData[i].tree, + co_high, + dir_high, + 0.0f, + &hits[i], + treeData[i].raycast_callback, + &treeData[i]); + } + + if (hits[i].index != -1) { + float distance; + float hit_world[3]; + + /* distance comparison in world space */ + mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); + distance = len_squared_v3v3(hit_world, co); + + if (distance < hit_distance) { + hit_mesh = i; + hit_distance = distance; + } + } + } + + if (hit_mesh != -1) { + int primitive_id_high = hits[hit_mesh].index; + TriTessFace *triangle_high = &triangles[hit_mesh][primitive_id_high]; + BakePixel *pixel_low = &pixel_array_low[pixel_id]; + BakePixel *pixel_high = &pixel_array[pixel_id]; + + pixel_high->primitive_id = primitive_id_high; + pixel_high->object_id = hit_mesh; + + /* ray direction in high poly object space */ + float dir_high[3]; + mul_v3_mat3_m4v3(dir_high, highpoly[hit_mesh].imat, dir); + normalize_v3(dir_high); + + /* compute position differentials on low poly object */ + float duco_low[3], dvco_low[3], dxco[3], dyco[3]; + sub_v3_v3v3(duco_low, triangle_low->mverts[0]->co, triangle_low->mverts[2]->co); + sub_v3_v3v3(dvco_low, triangle_low->mverts[1]->co, triangle_low->mverts[2]->co); + + mul_v3_v3fl(dxco, duco_low, pixel_low->du_dx); + madd_v3_v3fl(dxco, dvco_low, pixel_low->dv_dx); + mul_v3_v3fl(dyco, duco_low, pixel_low->du_dy); + madd_v3_v3fl(dyco, dvco_low, pixel_low->dv_dy); + + /* transform from low poly to high poly object space */ + mul_mat3_m4_v3(mat_low, dxco); + mul_mat3_m4_v3(mat_low, dyco); + mul_mat3_m4_v3(highpoly[hit_mesh].imat, dxco); + mul_mat3_m4_v3(highpoly[hit_mesh].imat, dyco); + + /* transfer position differentials */ + float tmp[3]; + mul_v3_v3fl(tmp, dir_high, 1.0f / dot_v3v3(dir_high, triangle_high->normal)); + madd_v3_v3fl(dxco, tmp, -dot_v3v3(dxco, triangle_high->normal)); + madd_v3_v3fl(dyco, tmp, -dot_v3v3(dyco, triangle_high->normal)); + + /* compute barycentric differentials from position differentials */ + barycentric_differentials_from_position(hits[hit_mesh].co, + triangle_high->mverts[0]->co, + triangle_high->mverts[1]->co, + triangle_high->mverts[2]->co, + dxco, + dyco, + triangle_high->normal, + true, + &pixel_high->uv[0], + &pixel_high->uv[1], + &pixel_high->du_dx, + &pixel_high->dv_dx, + &pixel_high->du_dy, + &pixel_high->dv_dy); + + /* verify we have valid uvs */ + BLI_assert(pixel_high->uv[0] >= -1e-3f && pixel_high->uv[1] >= -1e-3f && + pixel_high->uv[0] + pixel_high->uv[1] <= 1.0f + 1e-3f); + } + else { + pixel_array[pixel_id].primitive_id = -1; + pixel_array[pixel_id].object_id = -1; + } + + MEM_freeN(hits); + return hit_mesh != -1; } /** * This function populates an array of verts for the triangles of a mesh * Tangent and Normals are also stored */ -static TriTessFace *mesh_calc_tri_tessface( - Mesh *me, bool tangent, Mesh *me_eval) +static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval) { - int i; - MVert *mvert; - TSpace *tspace; - - const int tottri = poly_to_tri_count(me->totpoly, me->totloop); - MLoopTri *looptri; - TriTessFace *triangles; - - /* calculate normal for each polygon only once */ - unsigned int mpoly_prev = UINT_MAX; - float no[3]; - - mvert = CustomData_get_layer(&me->vdata, CD_MVERT); - looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); - triangles = MEM_mallocN(sizeof(TriTessFace) * tottri, __func__); - - if (tangent) { - BKE_mesh_ensure_normals_for_display(me_eval); - BKE_mesh_calc_loop_tangents(me_eval, true, NULL, 0); - - tspace = CustomData_get_layer(&me_eval->ldata, CD_TANGENT); - BLI_assert(tspace); - } - - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, - me->mvert, - me->totloop, me->totpoly, - looptri); - - const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); - const bool calculate_normal = precomputed_normals ? false : true; - - for (i = 0; i < tottri; i++) { - const MLoopTri *lt = &looptri[i]; - const MPoly *mp = &me->mpoly[lt->poly]; - - triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v]; - triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v]; - triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v]; - triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0; - - if (tangent) { - triangles[i].tspace[0] = &tspace[lt->tri[0]]; - triangles[i].tspace[1] = &tspace[lt->tri[1]]; - triangles[i].tspace[2] = &tspace[lt->tri[2]]; - } - - if (calculate_normal) { - if (lt->poly != mpoly_prev) { - BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, no); - mpoly_prev = lt->poly; - } - copy_v3_v3(triangles[i].normal, no); - } - else { - copy_v3_v3(triangles[i].normal, &precomputed_normals[lt->poly]); - } - } - - MEM_freeN(looptri); - - return triangles; + int i; + MVert *mvert; + TSpace *tspace; + + const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + MLoopTri *looptri; + TriTessFace *triangles; + + /* calculate normal for each polygon only once */ + unsigned int mpoly_prev = UINT_MAX; + float no[3]; + + mvert = CustomData_get_layer(&me->vdata, CD_MVERT); + looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); + triangles = MEM_mallocN(sizeof(TriTessFace) * tottri, __func__); + + if (tangent) { + BKE_mesh_ensure_normals_for_display(me_eval); + BKE_mesh_calc_loop_tangents(me_eval, true, NULL, 0); + + tspace = CustomData_get_layer(&me_eval->ldata, CD_TANGENT); + BLI_assert(tspace); + } + + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + + const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); + const bool calculate_normal = precomputed_normals ? false : true; + + for (i = 0; i < tottri; i++) { + const MLoopTri *lt = &looptri[i]; + const MPoly *mp = &me->mpoly[lt->poly]; + + triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v]; + triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v]; + triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v]; + triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0; + + if (tangent) { + triangles[i].tspace[0] = &tspace[lt->tri[0]]; + triangles[i].tspace[1] = &tspace[lt->tri[1]]; + triangles[i].tspace[2] = &tspace[lt->tri[2]]; + } + + if (calculate_normal) { + if (lt->poly != mpoly_prev) { + BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, no); + mpoly_prev = lt->poly; + } + copy_v3_v3(triangles[i].normal, no); + } + else { + copy_v3_v3(triangles[i].normal, &precomputed_normals[lt->poly]); + } + } + + MEM_freeN(looptri); + + return triangles; } -bool RE_bake_pixels_populate_from_objects( - struct Mesh *me_low, BakePixel pixel_array_from[], BakePixel pixel_array_to[], - BakeHighPolyData highpoly[], const int tot_highpoly, const size_t num_pixels, const bool is_custom_cage, - const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage) +bool RE_bake_pixels_populate_from_objects(struct Mesh *me_low, + BakePixel pixel_array_from[], + BakePixel pixel_array_to[], + BakeHighPolyData highpoly[], + const int tot_highpoly, + const size_t num_pixels, + const bool is_custom_cage, + const float cage_extrusion, + float mat_low[4][4], + float mat_cage[4][4], + struct Mesh *me_cage) { - size_t i; - int primitive_id; - float u, v; - float imat_low[4][4]; - bool is_cage = me_cage != NULL; - bool result = true; - - Mesh *me_eval_low = NULL; - Mesh **me_highpoly; - BVHTreeFromMesh *treeData; - - /* Note: all coordinates are in local space */ - TriTessFace *tris_low = NULL; - TriTessFace *tris_cage = NULL; - TriTessFace **tris_high; - - /* assume all lowpoly tessfaces can be quads */ - tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array"); - - /* assume all highpoly tessfaces are triangles */ - me_highpoly = MEM_mallocN(sizeof(Mesh *) * tot_highpoly, "Highpoly Derived Meshes"); - treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees"); - - if (!is_cage) { - me_eval_low = BKE_mesh_copy_for_eval(me_low, false); - tris_low = mesh_calc_tri_tessface(me_low, true, me_eval_low); - } - else if (is_custom_cage) { - tris_low = mesh_calc_tri_tessface(me_low, false, NULL); - tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); - } - else { - tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); - } - - invert_m4_m4(imat_low, mat_low); - - for (i = 0; i < tot_highpoly; i++) { - tris_high[i] = mesh_calc_tri_tessface(highpoly[i].me, false, NULL); - - me_highpoly[i] = highpoly[i].me; - BKE_mesh_runtime_looptri_ensure(me_highpoly[i]); - - if (me_highpoly[i]->runtime.looptris.len != 0) { - /* Create a bvh-tree for each highpoly object */ - BKE_bvhtree_from_mesh_get(&treeData[i], me_highpoly[i], BVHTREE_FROM_LOOPTRI, 2); - - if (treeData[i].tree == NULL) { - printf("Baking: out of memory while creating BHVTree for object \"%s\"\n", highpoly[i].ob->id.name + 2); - result = false; - goto cleanup; - } - } - } - - for (i = 0; i < num_pixels; i++) { - float co[3]; - float dir[3]; - TriTessFace *tri_low; - - primitive_id = pixel_array_from[i].primitive_id; - - if (primitive_id == -1) { - pixel_array_to[i].primitive_id = -1; - continue; - } - - u = pixel_array_from[i].uv[0]; - v = pixel_array_from[i].uv[1]; - - /* calculate from low poly mesh cage */ - if (is_custom_cage) { - calc_point_from_barycentric_cage(tris_low, tris_cage, mat_low, mat_cage, primitive_id, u, v, co, dir); - tri_low = &tris_cage[primitive_id]; - } - else if (is_cage) { - calc_point_from_barycentric_extrusion(tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true); - tri_low = &tris_cage[primitive_id]; - } - else { - calc_point_from_barycentric_extrusion(tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false); - tri_low = &tris_low[primitive_id]; - } - - /* cast ray */ - if (!cast_ray_highpoly(treeData, tri_low, tris_high, - pixel_array_from, pixel_array_to, mat_low, - highpoly, co, dir, i, tot_highpoly)) - { - /* if it fails mask out the original pixel array */ - pixel_array_from[i].primitive_id = -1; - } - } - - - /* garbage collection */ + size_t i; + int primitive_id; + float u, v; + float imat_low[4][4]; + bool is_cage = me_cage != NULL; + bool result = true; + + Mesh *me_eval_low = NULL; + Mesh **me_highpoly; + BVHTreeFromMesh *treeData; + + /* Note: all coordinates are in local space */ + TriTessFace *tris_low = NULL; + TriTessFace *tris_cage = NULL; + TriTessFace **tris_high; + + /* assume all lowpoly tessfaces can be quads */ + tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array"); + + /* assume all highpoly tessfaces are triangles */ + me_highpoly = MEM_mallocN(sizeof(Mesh *) * tot_highpoly, "Highpoly Derived Meshes"); + treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees"); + + if (!is_cage) { + me_eval_low = BKE_mesh_copy_for_eval(me_low, false); + tris_low = mesh_calc_tri_tessface(me_low, true, me_eval_low); + } + else if (is_custom_cage) { + tris_low = mesh_calc_tri_tessface(me_low, false, NULL); + tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); + } + else { + tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); + } + + invert_m4_m4(imat_low, mat_low); + + for (i = 0; i < tot_highpoly; i++) { + tris_high[i] = mesh_calc_tri_tessface(highpoly[i].me, false, NULL); + + me_highpoly[i] = highpoly[i].me; + BKE_mesh_runtime_looptri_ensure(me_highpoly[i]); + + if (me_highpoly[i]->runtime.looptris.len != 0) { + /* Create a bvh-tree for each highpoly object */ + BKE_bvhtree_from_mesh_get(&treeData[i], me_highpoly[i], BVHTREE_FROM_LOOPTRI, 2); + + if (treeData[i].tree == NULL) { + printf("Baking: out of memory while creating BHVTree for object \"%s\"\n", + highpoly[i].ob->id.name + 2); + result = false; + goto cleanup; + } + } + } + + for (i = 0; i < num_pixels; i++) { + float co[3]; + float dir[3]; + TriTessFace *tri_low; + + primitive_id = pixel_array_from[i].primitive_id; + + if (primitive_id == -1) { + pixel_array_to[i].primitive_id = -1; + continue; + } + + u = pixel_array_from[i].uv[0]; + v = pixel_array_from[i].uv[1]; + + /* calculate from low poly mesh cage */ + if (is_custom_cage) { + calc_point_from_barycentric_cage( + tris_low, tris_cage, mat_low, mat_cage, primitive_id, u, v, co, dir); + tri_low = &tris_cage[primitive_id]; + } + else if (is_cage) { + calc_point_from_barycentric_extrusion( + tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true); + tri_low = &tris_cage[primitive_id]; + } + else { + calc_point_from_barycentric_extrusion( + tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false); + tri_low = &tris_low[primitive_id]; + } + + /* cast ray */ + if (!cast_ray_highpoly(treeData, + tri_low, + tris_high, + pixel_array_from, + pixel_array_to, + mat_low, + highpoly, + co, + dir, + i, + tot_highpoly)) { + /* if it fails mask out the original pixel array */ + pixel_array_from[i].primitive_id = -1; + } + } + + /* garbage collection */ cleanup: - for (i = 0; i < tot_highpoly; i++) { - free_bvhtree_from_mesh(&treeData[i]); - - if (tris_high[i]) { - MEM_freeN(tris_high[i]); - } - } - - MEM_freeN(tris_high); - MEM_freeN(treeData); - MEM_freeN(me_highpoly); - - if (me_eval_low) { - BKE_id_free(NULL, me_eval_low); - } - if (tris_low) { - MEM_freeN(tris_low); - } - if (tris_cage) { - MEM_freeN(tris_cage); - } - - return result; + for (i = 0; i < tot_highpoly; i++) { + free_bvhtree_from_mesh(&treeData[i]); + + if (tris_high[i]) { + MEM_freeN(tris_high[i]); + } + } + + MEM_freeN(tris_high); + MEM_freeN(treeData); + MEM_freeN(me_highpoly); + + if (me_eval_low) { + BKE_id_free(NULL, me_eval_low); + } + if (tris_low) { + MEM_freeN(tris_low); + } + if (tris_cage) { + MEM_freeN(tris_cage); + } + + return result; } -static void bake_differentials(BakeDataZSpan *bd, const float *uv1, const float *uv2, const float *uv3) +static void bake_differentials(BakeDataZSpan *bd, + const float *uv1, + const float *uv2, + const float *uv3) { - float A; + float A; - /* assumes dPdu = P1 - P3 and dPdv = P2 - P3 */ - A = (uv2[0] - uv1[0]) * (uv3[1] - uv1[1]) - (uv3[0] - uv1[0]) * (uv2[1] - uv1[1]); + /* assumes dPdu = P1 - P3 and dPdv = P2 - P3 */ + A = (uv2[0] - uv1[0]) * (uv3[1] - uv1[1]) - (uv3[0] - uv1[0]) * (uv2[1] - uv1[1]); - if (fabsf(A) > FLT_EPSILON) { - A = 0.5f / A; + if (fabsf(A) > FLT_EPSILON) { + A = 0.5f / A; - bd->du_dx = (uv2[1] - uv3[1]) * A; - bd->dv_dx = (uv3[1] - uv1[1]) * A; + bd->du_dx = (uv2[1] - uv3[1]) * A; + bd->dv_dx = (uv3[1] - uv1[1]) * A; - bd->du_dy = (uv3[0] - uv2[0]) * A; - bd->dv_dy = (uv1[0] - uv3[0]) * A; - } - else { - bd->du_dx = bd->du_dy = 0.0f; - bd->dv_dx = bd->dv_dy = 0.0f; - } + bd->du_dy = (uv3[0] - uv2[0]) * A; + bd->dv_dy = (uv1[0] - uv3[0]) * A; + } + else { + bd->du_dx = bd->du_dy = 0.0f; + bd->dv_dx = bd->dv_dy = 0.0f; + } } -void RE_bake_pixels_populate( - Mesh *me, BakePixel pixel_array[], - const size_t num_pixels, const BakeImages *bake_images, const char *uv_layer) +void RE_bake_pixels_populate(Mesh *me, + BakePixel pixel_array[], + const size_t num_pixels, + const BakeImages *bake_images, + const char *uv_layer) { - BakeDataZSpan bd; - size_t i; - int a, p_id; - - const MLoopUV *mloopuv; - const int tottri = poly_to_tri_count(me->totpoly, me->totloop); - MLoopTri *looptri; - - if ((uv_layer == NULL) || (uv_layer[0] == '\0')) { - mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); - } - else { - int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer); - mloopuv = CustomData_get_layer_n(&me->ldata, CD_MTFACE, uv_id); - } - - if (mloopuv == NULL) - return; - - - bd.pixel_array = pixel_array; - bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan"); - - /* initialize all pixel arrays so we know which ones are 'blank' */ - for (i = 0; i < num_pixels; i++) { - pixel_array[i].primitive_id = -1; - pixel_array[i].object_id = 0; - } - - for (i = 0; i < bake_images->size; i++) { - zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height); - } - - looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); - - BKE_mesh_recalc_looptri( - me->mloop, me->mpoly, - me->mvert, - me->totloop, me->totpoly, - looptri); - - p_id = -1; - for (i = 0; i < tottri; i++) { - const MLoopTri *lt = &looptri[i]; - const MPoly *mp = &me->mpoly[lt->poly]; - float vec[3][2]; - int mat_nr = mp->mat_nr; - int image_id = bake_images->lookup[mat_nr]; - - if (image_id < 0) { - continue; - } - - bd.bk_image = &bake_images->data[image_id]; - bd.primitive_id = ++p_id; - - for (a = 0; a < 3; a++) { - const float *uv = mloopuv[lt->tri[a]].uv; - - /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests - * where a pixel gets in between 2 faces or the middle of a quad, - * camera aligned quads also have this problem but they are less common. - * Add a small offset to the UVs, fixes bug #18685 - Campbell */ - vec[a][0] = uv[0] * (float)bd.bk_image->width - (0.5f + 0.001f); - vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f); - } - - bake_differentials(&bd, vec[0], vec[1], vec[2]); - zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); - } - - for (i = 0; i < bake_images->size; i++) { - zbuf_free_span(&bd.zspan[i]); - } - - MEM_freeN(looptri); - MEM_freeN(bd.zspan); + BakeDataZSpan bd; + size_t i; + int a, p_id; + + const MLoopUV *mloopuv; + const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + MLoopTri *looptri; + + if ((uv_layer == NULL) || (uv_layer[0] == '\0')) { + mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); + } + else { + int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer); + mloopuv = CustomData_get_layer_n(&me->ldata, CD_MTFACE, uv_id); + } + + if (mloopuv == NULL) + return; + + bd.pixel_array = pixel_array; + bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan"); + + /* initialize all pixel arrays so we know which ones are 'blank' */ + for (i = 0; i < num_pixels; i++) { + pixel_array[i].primitive_id = -1; + pixel_array[i].object_id = 0; + } + + for (i = 0; i < bake_images->size; i++) { + zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height); + } + + looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); + + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + + p_id = -1; + for (i = 0; i < tottri; i++) { + const MLoopTri *lt = &looptri[i]; + const MPoly *mp = &me->mpoly[lt->poly]; + float vec[3][2]; + int mat_nr = mp->mat_nr; + int image_id = bake_images->lookup[mat_nr]; + + if (image_id < 0) { + continue; + } + + bd.bk_image = &bake_images->data[image_id]; + bd.primitive_id = ++p_id; + + for (a = 0; a < 3; a++) { + const float *uv = mloopuv[lt->tri[a]].uv; + + /* Note, workaround for pixel aligned UVs which are common and can screw up our intersection tests + * where a pixel gets in between 2 faces or the middle of a quad, + * camera aligned quads also have this problem but they are less common. + * Add a small offset to the UVs, fixes bug #18685 - Campbell */ + vec[a][0] = uv[0] * (float)bd.bk_image->width - (0.5f + 0.001f); + vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f); + } + + bake_differentials(&bd, vec[0], vec[1], vec[2]); + zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); + } + + for (i = 0; i < bake_images->size; i++) { + zbuf_free_span(&bd.zspan[i]); + } + + MEM_freeN(looptri); + MEM_freeN(bd.zspan); } /* ******************** NORMALS ************************ */ @@ -701,277 +750,284 @@ void RE_bake_pixels_populate( */ static void normal_uncompress(float out[3], const float in[3]) { - int i; - for (i = 0; i < 3; i++) - out[i] = 2.0f * in[i] - 1.0f; + int i; + for (i = 0; i < 3; i++) + out[i] = 2.0f * in[i] - 1.0f; } -static void normal_compress(float out[3], const float in[3], const eBakeNormalSwizzle normal_swizzle[3]) +static void normal_compress(float out[3], + const float in[3], + const eBakeNormalSwizzle normal_swizzle[3]) { - const int swizzle_index[6] = { - 0, /* R_BAKE_POSX */ - 1, /* R_BAKE_POSY */ - 2, /* R_BAKE_POSZ */ - 0, /* R_BAKE_NEGX */ - 1, /* R_BAKE_NEGY */ - 2, /* R_BAKE_NEGZ */ - }; - const float swizzle_sign[6] = { - +1.0f, /* R_BAKE_POSX */ - +1.0f, /* R_BAKE_POSY */ - +1.0f, /* R_BAKE_POSZ */ - -1.0f, /* R_BAKE_NEGX */ - -1.0f, /* R_BAKE_NEGY */ - -1.0f, /* R_BAKE_NEGZ */ - }; - - int i; - - for (i = 0; i < 3; i++) { - int index; - float sign; - - sign = swizzle_sign[normal_swizzle[i]]; - index = swizzle_index[normal_swizzle[i]]; - - /* - * There is a small 1e-5f bias for precision issues. otherwise - * we randomly get 127 or 128 for neutral colors in tangent maps. - * we choose 128 because it is the convention flat color. * - */ - - out[i] = sign * in[index] / 2.0f + 0.5f + 1e-5f; - } + const int swizzle_index[6] = { + 0, /* R_BAKE_POSX */ + 1, /* R_BAKE_POSY */ + 2, /* R_BAKE_POSZ */ + 0, /* R_BAKE_NEGX */ + 1, /* R_BAKE_NEGY */ + 2, /* R_BAKE_NEGZ */ + }; + const float swizzle_sign[6] = { + +1.0f, /* R_BAKE_POSX */ + +1.0f, /* R_BAKE_POSY */ + +1.0f, /* R_BAKE_POSZ */ + -1.0f, /* R_BAKE_NEGX */ + -1.0f, /* R_BAKE_NEGY */ + -1.0f, /* R_BAKE_NEGZ */ + }; + + int i; + + for (i = 0; i < 3; i++) { + int index; + float sign; + + sign = swizzle_sign[normal_swizzle[i]]; + index = swizzle_index[normal_swizzle[i]]; + + /* + * There is a small 1e-5f bias for precision issues. otherwise + * we randomly get 127 or 128 for neutral colors in tangent maps. + * we choose 128 because it is the convention flat color. * + */ + + out[i] = sign * in[index] / 2.0f + 0.5f + 1e-5f; + } } /** * This function converts an object space normal map to a tangent space normal map for a given low poly mesh */ -void RE_bake_normal_world_to_tangent( - const BakePixel pixel_array[], const size_t num_pixels, const int depth, - float result[], Mesh *me, const eBakeNormalSwizzle normal_swizzle[3], - float mat[4][4]) +void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + Mesh *me, + const eBakeNormalSwizzle normal_swizzle[3], + float mat[4][4]) { - size_t i; + size_t i; - TriTessFace *triangles; + TriTessFace *triangles; - Mesh *me_eval = BKE_mesh_copy_for_eval(me, false); + Mesh *me_eval = BKE_mesh_copy_for_eval(me, false); - triangles = mesh_calc_tri_tessface(me, true, me_eval); + triangles = mesh_calc_tri_tessface(me, true, me_eval); - BLI_assert(num_pixels >= 3); + BLI_assert(num_pixels >= 3); - for (i = 0; i < num_pixels; i++) { - TriTessFace *triangle; - float tangents[3][3]; - float normals[3][3]; - float signs[3]; - int j; + for (i = 0; i < num_pixels; i++) { + TriTessFace *triangle; + float tangents[3][3]; + float normals[3][3]; + float signs[3]; + int j; - float tangent[3]; - float normal[3]; - float binormal[3]; - float sign; - float u, v, w; + float tangent[3]; + float normal[3]; + float binormal[3]; + float sign; + float u, v, w; - float tsm[3][3]; /* tangent space matrix */ - float itsm[3][3]; + float tsm[3][3]; /* tangent space matrix */ + float itsm[3][3]; - size_t offset; - float nor[3]; /* texture normal */ + size_t offset; + float nor[3]; /* texture normal */ - bool is_smooth; + bool is_smooth; - int primitive_id = pixel_array[i].primitive_id; + int primitive_id = pixel_array[i].primitive_id; - offset = i * depth; + offset = i * depth; - if (primitive_id == -1) { - if (depth == 4) - copy_v4_fl4(&result[offset], 0.5f, 0.5f, 1.0f, 1.0f); - else - copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f); - continue; - } + if (primitive_id == -1) { + if (depth == 4) + copy_v4_fl4(&result[offset], 0.5f, 0.5f, 1.0f, 1.0f); + else + copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f); + continue; + } - triangle = &triangles[primitive_id]; - is_smooth = triangle->is_smooth; + triangle = &triangles[primitive_id]; + is_smooth = triangle->is_smooth; - for (j = 0; j < 3; j++) { - const TSpace *ts; + for (j = 0; j < 3; j++) { + const TSpace *ts; - if (is_smooth) - normal_short_to_float_v3(normals[j], triangle->mverts[j]->no); - else - normal[j] = triangle->normal[j]; + if (is_smooth) + normal_short_to_float_v3(normals[j], triangle->mverts[j]->no); + else + normal[j] = triangle->normal[j]; - ts = triangle->tspace[j]; - copy_v3_v3(tangents[j], ts->tangent); - signs[j] = ts->sign; - } + ts = triangle->tspace[j]; + copy_v3_v3(tangents[j], ts->tangent); + signs[j] = ts->sign; + } - u = pixel_array[i].uv[0]; - v = pixel_array[i].uv[1]; - w = 1.0f - u - v; + u = pixel_array[i].uv[0]; + v = pixel_array[i].uv[1]; + w = 1.0f - u - v; - /* normal */ - if (is_smooth) - interp_barycentric_tri_v3(normals, u, v, normal); + /* normal */ + if (is_smooth) + interp_barycentric_tri_v3(normals, u, v, normal); - /* tangent */ - interp_barycentric_tri_v3(tangents, u, v, tangent); + /* tangent */ + interp_barycentric_tri_v3(tangents, u, v, tangent); - /* sign */ - /* The sign is the same at all face vertices for any non degenerate face. - * Just in case we clamp the interpolated value though. */ - sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f; + /* sign */ + /* The sign is the same at all face vertices for any non degenerate face. + * Just in case we clamp the interpolated value though. */ + sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f; - /* binormal */ - /* B = sign * cross(N, T) */ - cross_v3_v3v3(binormal, normal, tangent); - mul_v3_fl(binormal, sign); + /* binormal */ + /* B = sign * cross(N, T) */ + cross_v3_v3v3(binormal, normal, tangent); + mul_v3_fl(binormal, sign); - /* populate tangent space matrix */ - copy_v3_v3(tsm[0], tangent); - copy_v3_v3(tsm[1], binormal); - copy_v3_v3(tsm[2], normal); + /* populate tangent space matrix */ + copy_v3_v3(tsm[0], tangent); + copy_v3_v3(tsm[1], binormal); + copy_v3_v3(tsm[2], normal); - /* texture values */ - normal_uncompress(nor, &result[offset]); + /* texture values */ + normal_uncompress(nor, &result[offset]); - /* converts from world space to local space */ - mul_transposed_mat3_m4_v3(mat, nor); + /* converts from world space to local space */ + mul_transposed_mat3_m4_v3(mat, nor); - invert_m3_m3(itsm, tsm); - mul_m3_v3(itsm, nor); - normalize_v3(nor); + invert_m3_m3(itsm, tsm); + mul_m3_v3(itsm, nor); + normalize_v3(nor); - /* save back the values */ - normal_compress(&result[offset], nor, normal_swizzle); - } + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } - /* garbage collection */ - MEM_freeN(triangles); + /* garbage collection */ + MEM_freeN(triangles); - if (me_eval) { - BKE_id_free(NULL, me_eval); - } + if (me_eval) { + BKE_id_free(NULL, me_eval); + } } -void RE_bake_normal_world_to_object( - const BakePixel pixel_array[], const size_t num_pixels, const int depth, - float result[], struct Object *ob, const eBakeNormalSwizzle normal_swizzle[3]) +void RE_bake_normal_world_to_object(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + struct Object *ob, + const eBakeNormalSwizzle normal_swizzle[3]) { - size_t i; - float iobmat[4][4]; + size_t i; + float iobmat[4][4]; - invert_m4_m4(iobmat, ob->obmat); + invert_m4_m4(iobmat, ob->obmat); - for (i = 0; i < num_pixels; i++) { - size_t offset; - float nor[3]; + for (i = 0; i < num_pixels; i++) { + size_t offset; + float nor[3]; - if (pixel_array[i].primitive_id == -1) - continue; + if (pixel_array[i].primitive_id == -1) + continue; - offset = i * depth; - normal_uncompress(nor, &result[offset]); + offset = i * depth; + normal_uncompress(nor, &result[offset]); - /* rotates only without translation */ - mul_mat3_m4_v3(iobmat, nor); - normalize_v3(nor); + /* rotates only without translation */ + mul_mat3_m4_v3(iobmat, nor); + normalize_v3(nor); - /* save back the values */ - normal_compress(&result[offset], nor, normal_swizzle); - } + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } } -void RE_bake_normal_world_to_world( - const BakePixel pixel_array[], const size_t num_pixels, const int depth, - float result[], const eBakeNormalSwizzle normal_swizzle[3]) +void RE_bake_normal_world_to_world(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + const eBakeNormalSwizzle normal_swizzle[3]) { - size_t i; + size_t i; - for (i = 0; i < num_pixels; i++) { - size_t offset; - float nor[3]; + for (i = 0; i < num_pixels; i++) { + size_t offset; + float nor[3]; - if (pixel_array[i].primitive_id == -1) - continue; + if (pixel_array[i].primitive_id == -1) + continue; - offset = i * depth; - normal_uncompress(nor, &result[offset]); + offset = i * depth; + normal_uncompress(nor, &result[offset]); - /* save back the values */ - normal_compress(&result[offset], nor, normal_swizzle); - } + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } } void RE_bake_ibuf_clear(Image *image, const bool is_tangent) { - ImBuf *ibuf; - void *lock; + ImBuf *ibuf; + void *lock; - const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f}; - const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f}; + const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f}; + const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f}; - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - BLI_assert(ibuf); + ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + BLI_assert(ibuf); - if (is_tangent) - IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid); - else - IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); + if (is_tangent) + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid); + else + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); - BKE_image_release_ibuf(image, ibuf, lock); + BKE_image_release_ibuf(image, ibuf, lock); } /* ************************************************************* */ int RE_pass_depth(const eScenePassType pass_type) { - /* IMB_buffer_byte_from_float assumes 4 channels - * making it work for now - XXX */ - return 4; - - switch (pass_type) { - case SCE_PASS_Z: - case SCE_PASS_AO: - case SCE_PASS_MIST: - { - return 1; - } - case SCE_PASS_UV: - { - return 2; - } - case SCE_PASS_COMBINED: - case SCE_PASS_SHADOW: - case SCE_PASS_NORMAL: - case SCE_PASS_VECTOR: - case SCE_PASS_INDEXOB: /* XXX double check */ - case SCE_PASS_RAYHITS: /* XXX double check */ - case SCE_PASS_EMIT: - case SCE_PASS_ENVIRONMENT: - case SCE_PASS_INDEXMA: - case SCE_PASS_DIFFUSE_DIRECT: - case SCE_PASS_DIFFUSE_INDIRECT: - case SCE_PASS_DIFFUSE_COLOR: - case SCE_PASS_GLOSSY_DIRECT: - case SCE_PASS_GLOSSY_INDIRECT: - case SCE_PASS_GLOSSY_COLOR: - case SCE_PASS_TRANSM_DIRECT: - case SCE_PASS_TRANSM_INDIRECT: - case SCE_PASS_TRANSM_COLOR: - case SCE_PASS_SUBSURFACE_DIRECT: - case SCE_PASS_SUBSURFACE_INDIRECT: - case SCE_PASS_SUBSURFACE_COLOR: - default: - { - return 3; - } - } + /* IMB_buffer_byte_from_float assumes 4 channels + * making it work for now - XXX */ + return 4; + + switch (pass_type) { + case SCE_PASS_Z: + case SCE_PASS_AO: + case SCE_PASS_MIST: { + return 1; + } + case SCE_PASS_UV: { + return 2; + } + case SCE_PASS_COMBINED: + case SCE_PASS_SHADOW: + case SCE_PASS_NORMAL: + case SCE_PASS_VECTOR: + case SCE_PASS_INDEXOB: /* XXX double check */ + case SCE_PASS_RAYHITS: /* XXX double check */ + case SCE_PASS_EMIT: + case SCE_PASS_ENVIRONMENT: + case SCE_PASS_INDEXMA: + case SCE_PASS_DIFFUSE_DIRECT: + case SCE_PASS_DIFFUSE_INDIRECT: + case SCE_PASS_DIFFUSE_COLOR: + case SCE_PASS_GLOSSY_DIRECT: + case SCE_PASS_GLOSSY_INDIRECT: + case SCE_PASS_GLOSSY_COLOR: + case SCE_PASS_TRANSM_DIRECT: + case SCE_PASS_TRANSM_INDIRECT: + case SCE_PASS_TRANSM_COLOR: + case SCE_PASS_SUBSURFACE_DIRECT: + case SCE_PASS_SUBSURFACE_INDIRECT: + case SCE_PASS_SUBSURFACE_COLOR: + default: { + return 3; + } + } } diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index a9bf80a6aa0..c4eef235b5d 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -50,7 +50,7 @@ #include "RNA_access.h" #ifdef WITH_PYTHON -#include "BPY_extern.h" +# include "BPY_extern.h" #endif #include "RE_engine.h" @@ -70,766 +70,785 @@ ListBase R_engines = {NULL, NULL}; void RE_engines_init(void) { - DRW_engines_register(); + DRW_engines_register(); } void RE_engines_exit(void) { - RenderEngineType *type, *next; + RenderEngineType *type, *next; - DRW_engines_free(); + DRW_engines_free(); - for (type = R_engines.first; type; type = next) { - next = type->next; + for (type = R_engines.first; type; type = next) { + next = type->next; - BLI_remlink(&R_engines, type); + BLI_remlink(&R_engines, type); - if (!(type->flag & RE_INTERNAL)) { - if (type->ext.free) - type->ext.free(type->ext.data); + if (!(type->flag & RE_INTERNAL)) { + if (type->ext.free) + type->ext.free(type->ext.data); - MEM_freeN(type); - } - } + MEM_freeN(type); + } + } } void RE_engines_register(RenderEngineType *render_type) { - if (render_type->draw_engine) { - DRW_engine_register(render_type->draw_engine); - } - BLI_addtail(&R_engines, render_type); + if (render_type->draw_engine) { + DRW_engine_register(render_type->draw_engine); + } + BLI_addtail(&R_engines, render_type); } RenderEngineType *RE_engines_find(const char *idname) { - RenderEngineType *type; + RenderEngineType *type; - type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)); - if (!type) - type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)); + type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)); + if (!type) + type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)); - return type; + return type; } bool RE_engine_is_external(Render *re) { - return (re->engine && re->engine->type && re->engine->type->render); + return (re->engine && re->engine->type && re->engine->type->render); } bool RE_engine_is_opengl(RenderEngineType *render_type) { - /* TODO refine? Can we have ogl render engine without ogl render pipeline? */ - return (render_type->draw_engine != NULL) && - DRW_engine_render_support(render_type->draw_engine); + /* TODO refine? Can we have ogl render engine without ogl render pipeline? */ + return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); } /* Create, Free */ RenderEngine *RE_engine_create(RenderEngineType *type) { - return RE_engine_create_ex(type, false); + return RE_engine_create_ex(type, false); } RenderEngine *RE_engine_create_ex(RenderEngineType *type, bool use_for_viewport) { - RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine"); - engine->type = type; + RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine"); + engine->type = type; - if (use_for_viewport) { - engine->flag |= RE_ENGINE_USED_FOR_VIEWPORT; + if (use_for_viewport) { + engine->flag |= RE_ENGINE_USED_FOR_VIEWPORT; - BLI_threaded_malloc_begin(); - } + BLI_threaded_malloc_begin(); + } - BLI_mutex_init(&engine->update_render_passes_mutex); + BLI_mutex_init(&engine->update_render_passes_mutex); - return engine; + return engine; } void RE_engine_free(RenderEngine *engine) { #ifdef WITH_PYTHON - if (engine->py_instance) { - BPY_DECREF_RNA_INVALIDATE(engine->py_instance); - } + if (engine->py_instance) { + BPY_DECREF_RNA_INVALIDATE(engine->py_instance); + } #endif - if (engine->flag & RE_ENGINE_USED_FOR_VIEWPORT) { - BLI_threaded_malloc_end(); - } + if (engine->flag & RE_ENGINE_USED_FOR_VIEWPORT) { + BLI_threaded_malloc_end(); + } - BLI_mutex_end(&engine->update_render_passes_mutex); + BLI_mutex_end(&engine->update_render_passes_mutex); - MEM_freeN(engine); + MEM_freeN(engine); } /* Render Results */ static RenderPart *get_part_from_result(Render *re, RenderResult *result) { - RenderPart *pa; + RenderPart *pa; - for (pa = re->parts.first; pa; pa = pa->next) { - if (result->tilerect.xmin == pa->disprect.xmin - re->disprect.xmin && - result->tilerect.ymin == pa->disprect.ymin - re->disprect.ymin && - result->tilerect.xmax == pa->disprect.xmax - re->disprect.xmin && - result->tilerect.ymax == pa->disprect.ymax - re->disprect.ymin) - { - return pa; - } - } + for (pa = re->parts.first; pa; pa = pa->next) { + if (result->tilerect.xmin == pa->disprect.xmin - re->disprect.xmin && + result->tilerect.ymin == pa->disprect.ymin - re->disprect.ymin && + result->tilerect.xmax == pa->disprect.xmax - re->disprect.xmin && + result->tilerect.ymax == pa->disprect.ymax - re->disprect.ymin) { + return pa; + } + } - return NULL; + return NULL; } RenderResult *RE_engine_begin_result( - RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) + RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { - Render *re = engine->re; - RenderResult *result; - rcti disprect; + Render *re = engine->re; + RenderResult *result; + rcti disprect; - /* ensure the coordinates are within the right limits */ - CLAMP(x, 0, re->result->rectx); - CLAMP(y, 0, re->result->recty); - CLAMP(w, 0, re->result->rectx); - CLAMP(h, 0, re->result->recty); + /* ensure the coordinates are within the right limits */ + CLAMP(x, 0, re->result->rectx); + CLAMP(y, 0, re->result->recty); + CLAMP(w, 0, re->result->rectx); + CLAMP(h, 0, re->result->recty); - if (x + w > re->result->rectx) - w = re->result->rectx - x; - if (y + h > re->result->recty) - h = re->result->recty - y; + if (x + w > re->result->rectx) + w = re->result->rectx - x; + if (y + h > re->result->recty) + h = re->result->recty - y; - /* allocate a render result */ - disprect.xmin = x; - disprect.xmax = x + w; - disprect.ymin = y; - disprect.ymax = y + h; + /* allocate a render result */ + disprect.xmin = x; + disprect.xmax = x + w; + disprect.ymin = y; + disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); - /* todo: make this thread safe */ + /* todo: make this thread safe */ - /* can be NULL if we CLAMP the width or height to 0 */ - if (result) { - render_result_clone_passes(re, result, viewname); + /* can be NULL if we CLAMP the width or height to 0 */ + if (result) { + render_result_clone_passes(re, result, viewname); - RenderPart *pa; + RenderPart *pa; - /* Copy EXR tile settings, so pipeline knows whether this is a result - * for Save Buffers enabled rendering. - */ - result->do_exr_tile = re->result->do_exr_tile; + /* Copy EXR tile settings, so pipeline knows whether this is a result + * for Save Buffers enabled rendering. + */ + result->do_exr_tile = re->result->do_exr_tile; - BLI_addtail(&engine->fullresult, result); + BLI_addtail(&engine->fullresult, result); - result->tilerect.xmin += re->disprect.xmin; - result->tilerect.xmax += re->disprect.xmin; - result->tilerect.ymin += re->disprect.ymin; - result->tilerect.ymax += re->disprect.ymin; + result->tilerect.xmin += re->disprect.xmin; + result->tilerect.xmax += re->disprect.xmin; + result->tilerect.ymin += re->disprect.ymin; + result->tilerect.ymax += re->disprect.ymin; - pa = get_part_from_result(re, result); + pa = get_part_from_result(re, result); - if (pa) - pa->status = PART_STATUS_IN_PROGRESS; - } + if (pa) + pa->status = PART_STATUS_IN_PROGRESS; + } - return result; + return result; } void RE_engine_update_result(RenderEngine *engine, RenderResult *result) { - Render *re = engine->re; + Render *re = engine->re; - if (result) { - result->renlay = result->layers.first; /* weak, draws first layer always */ - re->display_update(re->duh, result, NULL); - } + if (result) { + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } } -void RE_engine_add_pass(RenderEngine *engine, const char *name, int channels, const char *chan_id, const char *layername) +void RE_engine_add_pass(RenderEngine *engine, + const char *name, + int channels, + const char *chan_id, + const char *layername) { - Render *re = engine->re; + Render *re = engine->re; - if (!re || !re->result) { - return; - } + if (!re || !re->result) { + return; + } - render_result_add_pass(re->result, name, channels, chan_id, layername, NULL); + render_result_add_pass(re->result, name, channels, chan_id, layername, NULL); } -void RE_engine_end_result(RenderEngine *engine, RenderResult *result, bool cancel, bool highlight, bool merge_results) +void RE_engine_end_result( + RenderEngine *engine, RenderResult *result, bool cancel, bool highlight, bool merge_results) { - Render *re = engine->re; + Render *re = engine->re; - if (!result) { - return; - } + if (!result) { + return; + } - /* merge. on break, don't merge in result for preview renders, looks nicer */ - if (!highlight) { - /* for exr tile render, detect tiles that are done */ - RenderPart *pa = get_part_from_result(re, result); + /* merge. on break, don't merge in result for preview renders, looks nicer */ + if (!highlight) { + /* for exr tile render, detect tiles that are done */ + RenderPart *pa = get_part_from_result(re, result); - if (pa) { - pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; - } - else if (re->result->do_exr_tile) { - /* if written result does not match any tile and we are using save - * buffers, we are going to get openexr save errors */ - fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); - } - } + if (pa) { + pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; + } + else if (re->result->do_exr_tile) { + /* if written result does not match any tile and we are using save + * buffers, we are going to get openexr save errors */ + fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); + } + } - if (!cancel || merge_results) { - if (re->result->do_exr_tile) { - if (!cancel && merge_results) { - render_result_exr_file_merge(re->result, result, re->viewname); - } - } - else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) - render_result_merge(re->result, result); + if (!cancel || merge_results) { + if (re->result->do_exr_tile) { + if (!cancel && merge_results) { + render_result_exr_file_merge(re->result, result, re->viewname); + } + } + else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) + render_result_merge(re->result, result); - /* draw */ - if (!re->test_break(re->tbh)) { - result->renlay = result->layers.first; /* weak, draws first layer always */ - re->display_update(re->duh, result, NULL); - } - } + /* draw */ + if (!re->test_break(re->tbh)) { + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } + } - /* free */ - BLI_remlink(&engine->fullresult, result); - render_result_free(result); + /* free */ + BLI_remlink(&engine->fullresult, result); + render_result_free(result); } RenderResult *RE_engine_get_result(RenderEngine *engine) { - return engine->re->result; + return engine->re->result; } /* Cancel */ bool RE_engine_test_break(RenderEngine *engine) { - Render *re = engine->re; + Render *re = engine->re; - if (re) - return re->test_break(re->tbh); + if (re) + return re->test_break(re->tbh); - return 0; + return 0; } /* Statistics */ void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) { - Render *re = engine->re; + Render *re = engine->re; - /* stats draw callback */ - if (re) { - re->i.statstr = stats; - re->i.infostr = info; - re->stats_draw(re->sdh, &re->i); - re->i.infostr = NULL; - re->i.statstr = NULL; - } + /* stats draw callback */ + if (re) { + re->i.statstr = stats; + re->i.infostr = info; + re->stats_draw(re->sdh, &re->i); + re->i.infostr = NULL; + re->i.statstr = NULL; + } - /* set engine text */ - engine->text[0] = '\0'; + /* set engine text */ + engine->text[0] = '\0'; - if (stats && stats[0] && info && info[0]) - BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info); - else if (info && info[0]) - BLI_strncpy(engine->text, info, sizeof(engine->text)); - else if (stats && stats[0]) - BLI_strncpy(engine->text, stats, sizeof(engine->text)); + if (stats && stats[0] && info && info[0]) + BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info); + else if (info && info[0]) + BLI_strncpy(engine->text, info, sizeof(engine->text)); + else if (stats && stats[0]) + BLI_strncpy(engine->text, stats, sizeof(engine->text)); } void RE_engine_update_progress(RenderEngine *engine, float progress) { - Render *re = engine->re; + Render *re = engine->re; - if (re) { - CLAMP(progress, 0.0f, 1.0f); - re->progress(re->prh, progress); - } + if (re) { + CLAMP(progress, 0.0f, 1.0f); + re->progress(re->prh, progress); + } } void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak) { - Render *re = engine->re; + Render *re = engine->re; - if (re) { - re->i.mem_used = mem_used; - re->i.mem_peak = mem_peak; - } + if (re) { + re->i.mem_used = mem_used; + re->i.mem_peak = mem_peak; + } } void RE_engine_report(RenderEngine *engine, int type, const char *msg) { - Render *re = engine->re; + Render *re = engine->re; - if (re) - BKE_report(engine->re->reports, type, msg); - else if (engine->reports) - BKE_report(engine->reports, type, msg); + if (re) + BKE_report(engine->re->reports, type, msg); + else if (engine->reports) + BKE_report(engine->reports, type, msg); } void RE_engine_set_error_message(RenderEngine *engine, const char *msg) { - Render *re = engine->re; - if (re != NULL) { - RenderResult *rr = RE_AcquireResultRead(re); - if (rr) { - if (rr->error != NULL) { - MEM_freeN(rr->error); - } - rr->error = BLI_strdup(msg); - } - RE_ReleaseResult(re); - } + Render *re = engine->re; + if (re != NULL) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + if (rr->error != NULL) { + MEM_freeN(rr->error); + } + rr->error = BLI_strdup(msg); + } + RE_ReleaseResult(re); + } } const char *RE_engine_active_view_get(RenderEngine *engine) { - Render *re = engine->re; - return RE_GetActiveRenderView(re); + Render *re = engine->re; + return RE_GetActiveRenderView(re); } void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) { - Render *re = engine->re; - RE_SetActiveRenderView(re, viewname); + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); } float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo) { - Render *re = engine->re; + Render *re = engine->re; - /* when using spherical stereo, get camera shift without multiview, leaving stereo to be handled by the engine */ - if (use_spherical_stereo) - re = NULL; + /* when using spherical stereo, get camera shift without multiview, leaving stereo to be handled by the engine */ + if (use_spherical_stereo) + re = NULL; - return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); + return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); } -void RE_engine_get_camera_model_matrix( - RenderEngine *engine, Object *camera, bool use_spherical_stereo, float *r_modelmat) +void RE_engine_get_camera_model_matrix(RenderEngine *engine, + Object *camera, + bool use_spherical_stereo, + float *r_modelmat) { - Render *re = engine->re; + Render *re = engine->re; - /* when using spherical stereo, get model matrix without multiview, leaving stereo to be handled by the engine */ - if (use_spherical_stereo) - re = NULL; + /* when using spherical stereo, get model matrix without multiview, leaving stereo to be handled by the engine */ + if (use_spherical_stereo) + re = NULL; - BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat); + BKE_camera_multiview_model_matrix( + re ? &re->r : NULL, camera, re->viewname, (float(*)[4])r_modelmat); } bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) { - Render *re = engine->re; - return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0; + Render *re = engine->re; + return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0; } rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) { - static rcti tiles_static[BLENDER_MAX_THREADS]; - const int allocation_step = BLENDER_MAX_THREADS; - RenderPart *pa; - int total_tiles = 0; - rcti *tiles = tiles_static; - int allocation_size = BLENDER_MAX_THREADS; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); - - *r_needs_free = false; - - if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { - *r_total_tiles = 0; - BLI_rw_mutex_unlock(&re->partsmutex); - return NULL; - } - - for (pa = re->parts.first; pa; pa = pa->next) { - if (pa->status == PART_STATUS_IN_PROGRESS) { - if (total_tiles >= allocation_size) { - /* Just in case we're using crazy network rendering with more - * workers than BLENDER_MAX_THREADS. - */ - allocation_size += allocation_step; - if (tiles == tiles_static) { - /* Can not realloc yet, tiles are pointing to a - * stack memory. - */ - tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles"); - } - else { - tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti)); - } - *r_needs_free = true; - } - tiles[total_tiles] = pa->disprect; - - total_tiles++; - } - } - BLI_rw_mutex_unlock(&re->partsmutex); - *r_total_tiles = total_tiles; - return tiles; + static rcti tiles_static[BLENDER_MAX_THREADS]; + const int allocation_step = BLENDER_MAX_THREADS; + RenderPart *pa; + int total_tiles = 0; + rcti *tiles = tiles_static; + int allocation_size = BLENDER_MAX_THREADS; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); + + *r_needs_free = false; + + if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { + *r_total_tiles = 0; + BLI_rw_mutex_unlock(&re->partsmutex); + return NULL; + } + + for (pa = re->parts.first; pa; pa = pa->next) { + if (pa->status == PART_STATUS_IN_PROGRESS) { + if (total_tiles >= allocation_size) { + /* Just in case we're using crazy network rendering with more + * workers than BLENDER_MAX_THREADS. + */ + allocation_size += allocation_step; + if (tiles == tiles_static) { + /* Can not realloc yet, tiles are pointing to a + * stack memory. + */ + tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles"); + } + else { + tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti)); + } + *r_needs_free = true; + } + tiles[total_tiles] = pa->disprect; + + total_tiles++; + } + } + BLI_rw_mutex_unlock(&re->partsmutex); + *r_total_tiles = total_tiles; + return tiles; } RenderData *RE_engine_get_render_data(Render *re) { - return &re->r; + return &re->r; } /* Depsgraph */ static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) { - Main *bmain = engine->re->main; - Scene *scene = engine->re->scene; + Main *bmain = engine->re->main; + Scene *scene = engine->re->scene; - engine->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); - DEG_debug_name_set(engine->depsgraph, "RENDER"); + engine->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); + DEG_debug_name_set(engine->depsgraph, "RENDER"); - BKE_scene_graph_update_for_newframe(engine->depsgraph, bmain); + BKE_scene_graph_update_for_newframe(engine->depsgraph, bmain); } static void engine_depsgraph_free(RenderEngine *engine) { - DEG_graph_free(engine->depsgraph); + DEG_graph_free(engine->depsgraph); - engine->depsgraph = NULL; + engine->depsgraph = NULL; } void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe) { - if (!engine->depsgraph) { - return; - } + if (!engine->depsgraph) { + return; + } #ifdef WITH_PYTHON - BPy_BEGIN_ALLOW_THREADS; + BPy_BEGIN_ALLOW_THREADS; #endif - Render *re = engine->re; - double cfra = (double)frame + (double)subframe; + Render *re = engine->re; + double cfra = (double)frame + (double)subframe; - CLAMP(cfra, MINAFRAME, MAXFRAME); - BKE_scene_frame_set(re->scene, cfra); - BKE_scene_graph_update_for_newframe(engine->depsgraph, re->main); + CLAMP(cfra, MINAFRAME, MAXFRAME); + BKE_scene_frame_set(re->scene, cfra); + BKE_scene_graph_update_for_newframe(engine->depsgraph, re->main); - BKE_scene_camera_switch_update(re->scene); + BKE_scene_camera_switch_update(re->scene); #ifdef WITH_PYTHON - BPy_END_ALLOW_THREADS; + BPy_END_ALLOW_THREADS; #endif } /* Bake */ void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene) { - re->scene = scene; - re->main = bmain; - render_copy_renderdata(&re->r, &scene->r); + re->scene = scene; + re->main = bmain; + render_copy_renderdata(&re->r, &scene->r); } bool RE_bake_has_engine(Render *re) { - RenderEngineType *type = RE_engines_find(re->r.engine); - return (type->bake != NULL); + RenderEngineType *type = RE_engines_find(re->r.engine); + return (type->bake != NULL); } -bool RE_bake_engine( - Render *re, Depsgraph *depsgraph, Object *object, - const int object_id, const BakePixel pixel_array[], - const size_t num_pixels, const int depth, - const eScenePassType pass_type, const int pass_filter, - float result[]) +bool RE_bake_engine(Render *re, + Depsgraph *depsgraph, + Object *object, + const int object_id, + const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + const eScenePassType pass_type, + const int pass_filter, + float result[]) { - RenderEngineType *type = RE_engines_find(re->r.engine); - RenderEngine *engine; - bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + RenderEngineType *type = RE_engines_find(re->r.engine); + RenderEngine *engine; + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; - /* set render info */ - re->i.cfra = re->scene->r.cfra; - BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2); - re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0; + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2); + re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0; - /* render */ - engine = re->engine; + /* render */ + engine = re->engine; - if (!engine) { - engine = RE_engine_create(type); - re->engine = engine; - } + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } - engine->flag |= RE_ENGINE_RENDERING; + engine->flag |= RE_ENGINE_RENDERING; - /* TODO: actually link to a parent which shouldn't happen */ - engine->re = re; + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; - engine->resolution_x = re->winx; - engine->resolution_y = re->winy; + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; - RE_parts_init(re); - engine->tile_x = re->r.tilex; - engine->tile_y = re->r.tiley; + RE_parts_init(re); + engine->tile_x = re->r.tilex; + engine->tile_y = re->r.tiley; - if (type->bake) { - engine->depsgraph = depsgraph; + if (type->bake) { + engine->depsgraph = depsgraph; - /* update is only called so we create the engine.session */ - if (type->update) - type->update(engine, re->main, engine->depsgraph); + /* update is only called so we create the engine.session */ + if (type->update) + type->update(engine, re->main, engine->depsgraph); - type->bake(engine, - engine->depsgraph, - object, - pass_type, - pass_filter, - object_id, - pixel_array, - num_pixels, - depth, - result); + type->bake(engine, + engine->depsgraph, + object, + pass_type, + pass_filter, + object_id, + pixel_array, + num_pixels, + depth, + result); - engine->depsgraph = NULL; - } + engine->depsgraph = NULL; + } - engine->tile_x = 0; - engine->tile_y = 0; - engine->flag &= ~RE_ENGINE_RENDERING; + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - /* re->engine becomes zero if user changed active render engine during render */ - if (!persistent_data || !re->engine) { - RE_engine_free(engine); - re->engine = NULL; - } + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) - G.is_break = true; + if (BKE_reports_contain(re->reports, RPT_ERROR)) + G.is_break = true; - return true; + return true; } /* Render */ int RE_engine_render(Render *re, int do_all) { - RenderEngineType *type = RE_engines_find(re->r.engine); - RenderEngine *engine; - bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; - - /* verify if we can render */ - if (!type->render) - return 0; - if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW)) - return 0; - if (do_all && !(type->flag & RE_USE_POSTPROCESS)) - return 0; - if (!do_all && (type->flag & RE_USE_POSTPROCESS)) - return 0; - - /* Lock drawing in UI during data phase. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 1); - } - - /* update animation here so any render layer animation is applied before - * creating the render result */ - if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { - render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); - } - - /* create render result */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { - int savebuffers = RR_USE_MEM; - - if (re->result) - render_result_free(re->result); - - if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) - savebuffers = RR_USE_EXR; - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); - } - BLI_rw_mutex_unlock(&re->resultmutex); - - if (re->result == NULL) { - /* Clear UI drawing locks. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - /* Too small image is handled earlier, here it could only happen if - * there was no sufficient memory to allocate all passes. - */ - BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory"); - G.is_break = true; - return 1; - } - - /* set render info */ - re->i.cfra = re->scene->r.cfra; - BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); - re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0; - - /* render */ - engine = re->engine; - - if (!engine) { - engine = RE_engine_create(type); - re->engine = engine; - } - - engine->flag |= RE_ENGINE_RENDERING; - - /* TODO: actually link to a parent which shouldn't happen */ - engine->re = re; - - if (re->flag & R_ANIMATION) - engine->flag |= RE_ENGINE_ANIMATION; - if (re->r.scemode & R_BUTS_PREVIEW) - engine->flag |= RE_ENGINE_PREVIEW; - engine->camera_override = re->camera_override; - - engine->resolution_x = re->winx; - engine->resolution_y = re->winy; - - RE_parts_init(re); - engine->tile_x = re->partx; - engine->tile_y = re->party; - - if (re->result->do_exr_tile) - render_result_exr_file_begin(re, engine); - - /* Clear UI drawing locks. */ - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - - if (type->render) { - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re, view_layer_iter) - { - if (re->draw_lock) { - re->draw_lock(re->dlh, 1); - } - - ViewLayer *view_layer = BLI_findstring(&re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)); - engine_depsgraph_init(engine, view_layer); - - if (type->update) { - type->update(engine, re->main, engine->depsgraph); - } - - if (re->draw_lock) { - re->draw_lock(re->dlh, 0); - } - - type->render(engine, engine->depsgraph); - - /* Grease pencil render over previous render result. - * - * NOTE: External engine might have been requested to free its - * dependency graph, which is only allowed if there is no grease - * pencil (pipeline is taking care of that). - */ - if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) { - DRW_render_gpencil(engine, engine->depsgraph); - } - - engine_depsgraph_free(engine); - - if (RE_engine_test_break(engine)) { - break; - } - } - FOREACH_VIEW_LAYER_TO_RENDER_END; - } - - engine->tile_x = 0; - engine->tile_y = 0; - engine->flag &= ~RE_ENGINE_RENDERING; - - render_result_free_list(&engine->fullresult, engine->fullresult.first); - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - - if (re->result->do_exr_tile) { - render_result_exr_file_end(re, engine); - } - - /* re->engine becomes zero if user changed active render engine during render */ - if (!persistent_data || !re->engine) { - RE_engine_free(engine); - re->engine = NULL; - } - - if (re->r.scemode & R_EXR_CACHE_FILE) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_cache_write(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } + RenderEngineType *type = RE_engines_find(re->r.engine); + RenderEngine *engine; + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + + /* verify if we can render */ + if (!type->render) + return 0; + if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW)) + return 0; + if (do_all && !(type->flag & RE_USE_POSTPROCESS)) + return 0; + if (!do_all && (type->flag & RE_USE_POSTPROCESS)) + return 0; + + /* Lock drawing in UI during data phase. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + /* update animation here so any render layer animation is applied before + * creating the render result */ + if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { + render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); + } + + /* create render result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { + int savebuffers = RR_USE_MEM; + + if (re->result) + render_result_free(re->result); + + if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) + savebuffers = RR_USE_EXR; + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + } + BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result == NULL) { + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + /* Too small image is handled earlier, here it could only happen if + * there was no sufficient memory to allocate all passes. + */ + BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory"); + G.is_break = true; + return 1; + } + + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); + re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0; + + /* render */ + engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + + engine->flag |= RE_ENGINE_RENDERING; + + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; + + if (re->flag & R_ANIMATION) + engine->flag |= RE_ENGINE_ANIMATION; + if (re->r.scemode & R_BUTS_PREVIEW) + engine->flag |= RE_ENGINE_PREVIEW; + engine->camera_override = re->camera_override; + + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; + + RE_parts_init(re); + engine->tile_x = re->partx; + engine->tile_y = re->party; + + if (re->result->do_exr_tile) + render_result_exr_file_begin(re, engine); + + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + if (type->render) { + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + ViewLayer *view_layer = BLI_findstring( + &re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)); + engine_depsgraph_init(engine, view_layer); + + if (type->update) { + type->update(engine, re->main, engine->depsgraph); + } + + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + type->render(engine, engine->depsgraph); + + /* Grease pencil render over previous render result. + * + * NOTE: External engine might have been requested to free its + * dependency graph, which is only allowed if there is no grease + * pencil (pipeline is taking care of that). + */ + if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) { + DRW_render_gpencil(engine, engine->depsgraph); + } + + engine_depsgraph_free(engine); + + if (RE_engine_test_break(engine)) { + break; + } + } + FOREACH_VIEW_LAYER_TO_RENDER_END; + } + + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; + + render_result_free_list(&engine->fullresult, engine->fullresult.first); + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + + if (re->result->do_exr_tile) { + render_result_exr_file_end(re, engine); + } + + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) - G.is_break = true; + if (BKE_reports_contain(re->reports, RPT_ERROR)) + G.is_break = true; #ifdef WITH_FREESTYLE - if (re->r.mode & R_EDGE_FRS) - RE_RenderFreestyleExternal(re); + if (re->r.mode & R_EDGE_FRS) + RE_RenderFreestyleExternal(re); #endif - return 1; + return 1; } -void RE_engine_update_render_passes(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer, - update_render_passes_cb_t callback, void *callback_data) +void RE_engine_update_render_passes(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + update_render_passes_cb_t callback, + void *callback_data) { - if (!(scene && view_layer && engine && callback && engine->type->update_render_passes)) { - return; - } + if (!(scene && view_layer && engine && callback && engine->type->update_render_passes)) { + return; + } - BLI_mutex_lock(&engine->update_render_passes_mutex); + BLI_mutex_lock(&engine->update_render_passes_mutex); - engine->update_render_passes_cb = callback; - engine->update_render_passes_data = callback_data; - engine->type->update_render_passes(engine, scene, view_layer); - engine->update_render_passes_cb = NULL; - engine->update_render_passes_data = NULL; + engine->update_render_passes_cb = callback; + engine->update_render_passes_data = callback_data; + engine->type->update_render_passes(engine, scene, view_layer); + engine->update_render_passes_cb = NULL; + engine->update_render_passes_data = NULL; - BLI_mutex_unlock(&engine->update_render_passes_mutex); + BLI_mutex_unlock(&engine->update_render_passes_mutex); } -void RE_engine_register_pass(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer, - const char *name, int channels, const char *chanid, int type) +void RE_engine_register_pass(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + const char *name, + int channels, + const char *chanid, + int type) { - if (!(scene && view_layer && engine && engine->update_render_passes_cb)) { - return; - } + if (!(scene && view_layer && engine && engine->update_render_passes_cb)) { + return; + } - engine->update_render_passes_cb(engine->update_render_passes_data, scene, view_layer, name, channels, chanid, type); + engine->update_render_passes_cb( + engine->update_render_passes_data, scene, view_layer, name, channels, chanid, type); } void RE_engine_free_blender_memory(RenderEngine *engine) { - /* Weak way to save memory, but not crash grease pencil. - * - * TODO(sergey): Find better solution for this. - * TODO(sergey): Try to find solution which does not involve looping over - * all the objects. - */ - if (DRW_render_check_grease_pencil(engine->depsgraph)) { - return; - } - DEG_graph_free(engine->depsgraph); - engine->depsgraph = NULL; + /* Weak way to save memory, but not crash grease pencil. + * + * TODO(sergey): Find better solution for this. + * TODO(sergey): Try to find solution which does not involve looping over + * all the objects. + */ + if (DRW_render_check_grease_pencil(engine->depsgraph)) { + return; + } + DEG_graph_free(engine->depsgraph); + engine->depsgraph = NULL; } diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index e67a07e791e..a9b06edf558 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -27,9 +27,9 @@ #include <math.h> #include <float.h> #ifndef WIN32 -#include <unistd.h> +# include <unistd.h> #else -#include <io.h> +# include <io.h> #endif #include "IMB_imbuf_types.h" @@ -52,585 +52,634 @@ #include "render_types.h" #include "texture.h" -static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, TexResult *texres, const short imaprepeat, const short imapextend); +static void boxsample(ImBuf *ibuf, + float minx, + float miny, + float maxx, + float maxy, + TexResult *texres, + const short imaprepeat, + const short imapextend); /* *********** IMAGEWRAPPING ****************** */ - /* x and y have to be checked for image size */ static void ibuf_get_color(float col[4], struct ImBuf *ibuf, int x, int y) { - int ofs = y * ibuf->x + x; - - if (ibuf->rect_float) { - if (ibuf->channels == 4) { - const float *fp = ibuf->rect_float + 4 * ofs; - copy_v4_v4(col, fp); - } - else if (ibuf->channels == 3) { - const float *fp = ibuf->rect_float + 3 * ofs; - copy_v3_v3(col, fp); - col[3] = 1.0f; - } - else { - const float *fp = ibuf->rect_float + ofs; - col[0] = col[1] = col[2] = col[3] = *fp; - } - } - else { - const char *rect = (char *)(ibuf->rect + ofs); - - col[0] = ((float)rect[0]) * (1.0f / 255.0f); - col[1] = ((float)rect[1]) * (1.0f / 255.0f); - col[2] = ((float)rect[2]) * (1.0f / 255.0f); - col[3] = ((float)rect[3]) * (1.0f / 255.0f); - - /* bytes are internally straight, however render pipeline seems to expect premul */ - col[0] *= col[3]; - col[1] *= col[3]; - col[2] *= col[3]; - } + int ofs = y * ibuf->x + x; + + if (ibuf->rect_float) { + if (ibuf->channels == 4) { + const float *fp = ibuf->rect_float + 4 * ofs; + copy_v4_v4(col, fp); + } + else if (ibuf->channels == 3) { + const float *fp = ibuf->rect_float + 3 * ofs; + copy_v3_v3(col, fp); + col[3] = 1.0f; + } + else { + const float *fp = ibuf->rect_float + ofs; + col[0] = col[1] = col[2] = col[3] = *fp; + } + } + else { + const char *rect = (char *)(ibuf->rect + ofs); + + col[0] = ((float)rect[0]) * (1.0f / 255.0f); + col[1] = ((float)rect[1]) * (1.0f / 255.0f); + col[2] = ((float)rect[2]) * (1.0f / 255.0f); + col[3] = ((float)rect[3]) * (1.0f / 255.0f); + + /* bytes are internally straight, however render pipeline seems to expect premul */ + col[0] *= col[3]; + col[1] *= col[3]; + col[2] *= col[3]; + } } -int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) +int imagewrap(Tex *tex, + Image *ima, + ImBuf *ibuf, + const float texvec[3], + TexResult *texres, + struct ImagePool *pool, + const bool skip_load_image) { - float fx, fy, val1, val2, val3; - int x, y, retval; - int xi, yi; /* original values */ - - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; - - /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; - - /* quick tests */ - if (ibuf == NULL && ima == NULL) - return retval; - if (ima) { - - /* hack for icon render */ - if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) - return retval; - - ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); - - ima->flag |= IMA_USED_FOR_RENDER; - } - if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - - /* setup mapping */ - if (tex->imaflag & TEX_IMAROT) { - fy = texvec[0]; - fx = texvec[1]; - } - else { - fx = texvec[0]; - fy = texvec[1]; - } - - if (tex->extend == TEX_CHECKER) { - int xs, ys; - - xs = (int)floor(fx); - ys = (int)floor(fy); - fx -= xs; - fy -= ys; - - if ( (tex->flag & TEX_CHECKER_ODD) == 0) { - if ((xs + ys) & 1) { - /* pass */ - } - else { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - if ( (tex->flag & TEX_CHECKER_EVEN) == 0) { - if ((xs + ys) & 1) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - /* scale around center, (0.5, 0.5) */ - if (tex->checkerdist < 1.0f) { - fx = (fx - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; - fy = (fy - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; - } - } - - x = xi = (int)floorf(fx * ibuf->x); - y = yi = (int)floorf(fy * ibuf->y); - - if (tex->extend == TEX_CLIPCUBE) { - if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y || texvec[2] < -1.0f || texvec[2] > 1.0f) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { - if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else { - if (tex->extend == TEX_EXTEND) { - if (x >= ibuf->x) x = ibuf->x - 1; - else if (x < 0) x = 0; - } - else { - x = x % ibuf->x; - if (x < 0) x += ibuf->x; - } - if (tex->extend == TEX_EXTEND) { - if (y >= ibuf->y) y = ibuf->y - 1; - else if (y < 0) y = 0; - } - else { - y = y % ibuf->y; - if (y < 0) y += ibuf->y; - } - } - - /* keep this before interpolation [#29761] */ - if (ima) { - if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { - if ((tex->imaflag & TEX_CALCALPHA) == 0) { - texres->talpha = true; - } - } - } - - /* interpolate */ - if (tex->imaflag & TEX_INTERPOL) { - float filterx, filtery; - filterx = (0.5f * tex->filtersize) / ibuf->x; - filtery = (0.5f * tex->filtersize) / ibuf->y; - - /* important that this value is wrapped [#27782] - * this applies the modifications made by the checks above, - * back to the floating point values */ - fx -= (float)(xi - x) / (float)ibuf->x; - fy -= (float)(yi - y) / (float)ibuf->y; - - boxsample(ibuf, fx - filterx, fy - filtery, fx + filterx, fy + filtery, texres, (tex->extend == TEX_REPEAT), (tex->extend == TEX_EXTEND)); - } - else { /* no filtering */ - ibuf_get_color(&texres->tr, ibuf, x, y); - } - - if (texres->nor) { - if (tex->imaflag & TEX_NORMALMAP) { - /* qdn: normal from color - * The invert of the red channel is to make - * the normal map compliant with the outside world. - * It needs to be done because in Blender - * the normal used in the renderer points inward. It is generated - * this way in calc_vertexnormals(). Should this ever change - * this negate must be removed. */ - texres->nor[0] = -2.f * (texres->tr - 0.5f); - texres->nor[1] = 2.f * (texres->tg - 0.5f); - texres->nor[2] = 2.f * (texres->tb - 0.5f); - } - else { - /* bump: take three samples */ - val1 = texres->tr + texres->tg + texres->tb; - - if (x < ibuf->x - 1) { - float col[4]; - ibuf_get_color(col, ibuf, x + 1, y); - val2 = (col[0] + col[1] + col[2]); - } - else { - val2 = val1; - } - - if (y < ibuf->y - 1) { - float col[4]; - ibuf_get_color(col, ibuf, x, y + 1); - val3 = (col[0] + col[1] + col[2]); - } - else { - val3 = val1; - } - - /* do not mix up x and y here! */ - texres->nor[0] = (val1 - val2); - texres->nor[1] = (val1 - val3); - } - } - - if (texres->talpha) { - texres->tin = texres->ta; - } - else if (tex->imaflag & TEX_CALCALPHA) { - texres->ta = texres->tin = max_fff(texres->tr, texres->tg, texres->tb); - } - else { - texres->ta = texres->tin = 1.0; - } - - if (tex->flag & TEX_NEGALPHA) { - texres->ta = 1.0f - texres->ta; - } - - /* de-premul, this is being premulled in shade_input_do_shade() - * do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - fx = 1.0f / texres->ta; - texres->tr *= fx; - texres->tg *= fx; - texres->tb *= fx; - } - - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - - BRICONTRGB; - - return retval; + float fx, fy, val1, val2, val3; + int x, y, retval; + int xi, yi; /* original values */ + + texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; + + /* we need to set retval OK, otherwise texture code generates normals itself... */ + retval = texres->nor ? 3 : 1; + + /* quick tests */ + if (ibuf == NULL && ima == NULL) + return retval; + if (ima) { + + /* hack for icon render */ + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) + return retval; + + ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); + + ima->flag |= IMA_USED_FOR_RENDER; + } + if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + + /* setup mapping */ + if (tex->imaflag & TEX_IMAROT) { + fy = texvec[0]; + fx = texvec[1]; + } + else { + fx = texvec[0]; + fy = texvec[1]; + } + + if (tex->extend == TEX_CHECKER) { + int xs, ys; + + xs = (int)floor(fx); + ys = (int)floor(fy); + fx -= xs; + fy -= ys; + + if ((tex->flag & TEX_CHECKER_ODD) == 0) { + if ((xs + ys) & 1) { + /* pass */ + } + else { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + if ((tex->flag & TEX_CHECKER_EVEN) == 0) { + if ((xs + ys) & 1) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + /* scale around center, (0.5, 0.5) */ + if (tex->checkerdist < 1.0f) { + fx = (fx - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; + fy = (fy - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; + } + } + + x = xi = (int)floorf(fx * ibuf->x); + y = yi = (int)floorf(fy * ibuf->y); + + if (tex->extend == TEX_CLIPCUBE) { + if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y || texvec[2] < -1.0f || texvec[2] > 1.0f) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { + if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else { + if (tex->extend == TEX_EXTEND) { + if (x >= ibuf->x) + x = ibuf->x - 1; + else if (x < 0) + x = 0; + } + else { + x = x % ibuf->x; + if (x < 0) + x += ibuf->x; + } + if (tex->extend == TEX_EXTEND) { + if (y >= ibuf->y) + y = ibuf->y - 1; + else if (y < 0) + y = 0; + } + else { + y = y % ibuf->y; + if (y < 0) + y += ibuf->y; + } + } + + /* keep this before interpolation [#29761] */ + if (ima) { + if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { + if ((tex->imaflag & TEX_CALCALPHA) == 0) { + texres->talpha = true; + } + } + } + + /* interpolate */ + if (tex->imaflag & TEX_INTERPOL) { + float filterx, filtery; + filterx = (0.5f * tex->filtersize) / ibuf->x; + filtery = (0.5f * tex->filtersize) / ibuf->y; + + /* important that this value is wrapped [#27782] + * this applies the modifications made by the checks above, + * back to the floating point values */ + fx -= (float)(xi - x) / (float)ibuf->x; + fy -= (float)(yi - y) / (float)ibuf->y; + + boxsample(ibuf, + fx - filterx, + fy - filtery, + fx + filterx, + fy + filtery, + texres, + (tex->extend == TEX_REPEAT), + (tex->extend == TEX_EXTEND)); + } + else { /* no filtering */ + ibuf_get_color(&texres->tr, ibuf, x, y); + } + + if (texres->nor) { + if (tex->imaflag & TEX_NORMALMAP) { + /* qdn: normal from color + * The invert of the red channel is to make + * the normal map compliant with the outside world. + * It needs to be done because in Blender + * the normal used in the renderer points inward. It is generated + * this way in calc_vertexnormals(). Should this ever change + * this negate must be removed. */ + texres->nor[0] = -2.f * (texres->tr - 0.5f); + texres->nor[1] = 2.f * (texres->tg - 0.5f); + texres->nor[2] = 2.f * (texres->tb - 0.5f); + } + else { + /* bump: take three samples */ + val1 = texres->tr + texres->tg + texres->tb; + + if (x < ibuf->x - 1) { + float col[4]; + ibuf_get_color(col, ibuf, x + 1, y); + val2 = (col[0] + col[1] + col[2]); + } + else { + val2 = val1; + } + + if (y < ibuf->y - 1) { + float col[4]; + ibuf_get_color(col, ibuf, x, y + 1); + val3 = (col[0] + col[1] + col[2]); + } + else { + val3 = val1; + } + + /* do not mix up x and y here! */ + texres->nor[0] = (val1 - val2); + texres->nor[1] = (val1 - val3); + } + } + + if (texres->talpha) { + texres->tin = texres->ta; + } + else if (tex->imaflag & TEX_CALCALPHA) { + texres->ta = texres->tin = max_fff(texres->tr, texres->tg, texres->tb); + } + else { + texres->ta = texres->tin = 1.0; + } + + if (tex->flag & TEX_NEGALPHA) { + texres->ta = 1.0f - texres->ta; + } + + /* de-premul, this is being premulled in shade_input_do_shade() + * do not de-premul for generated alpha, it is already in straight */ + if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + fx = 1.0f / texres->ta; + texres->tr *= fx; + texres->tg *= fx; + texres->tb *= fx; + } + + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + + BRICONTRGB; + + return retval; } static void clipx_rctf_swap(rctf *stack, short *count, float x1, float x2) { - rctf *rf, *newrct; - short a; - - a = *count; - rf = stack; - for (; a > 0; a--) { - if (rf->xmin < x1) { - if (rf->xmax < x1) { - rf->xmin += (x2 - x1); - rf->xmax += (x2 - x1); - } - else { - if (rf->xmax > x2) rf->xmax = x2; - newrct = stack + *count; - (*count)++; - - newrct->xmax = x2; - newrct->xmin = rf->xmin + (x2 - x1); - newrct->ymin = rf->ymin; - newrct->ymax = rf->ymax; - - if (newrct->xmin == newrct->xmax) (*count)--; - - rf->xmin = x1; - } - } - else if (rf->xmax > x2) { - if (rf->xmin > x2) { - rf->xmin -= (x2 - x1); - rf->xmax -= (x2 - x1); - } - else { - if (rf->xmin < x1) rf->xmin = x1; - newrct = stack + *count; - (*count)++; - - newrct->xmin = x1; - newrct->xmax = rf->xmax - (x2 - x1); - newrct->ymin = rf->ymin; - newrct->ymax = rf->ymax; - - if (newrct->xmin == newrct->xmax) (*count)--; - - rf->xmax = x2; - } - } - rf++; - } - + rctf *rf, *newrct; + short a; + + a = *count; + rf = stack; + for (; a > 0; a--) { + if (rf->xmin < x1) { + if (rf->xmax < x1) { + rf->xmin += (x2 - x1); + rf->xmax += (x2 - x1); + } + else { + if (rf->xmax > x2) + rf->xmax = x2; + newrct = stack + *count; + (*count)++; + + newrct->xmax = x2; + newrct->xmin = rf->xmin + (x2 - x1); + newrct->ymin = rf->ymin; + newrct->ymax = rf->ymax; + + if (newrct->xmin == newrct->xmax) + (*count)--; + + rf->xmin = x1; + } + } + else if (rf->xmax > x2) { + if (rf->xmin > x2) { + rf->xmin -= (x2 - x1); + rf->xmax -= (x2 - x1); + } + else { + if (rf->xmin < x1) + rf->xmin = x1; + newrct = stack + *count; + (*count)++; + + newrct->xmin = x1; + newrct->xmax = rf->xmax - (x2 - x1); + newrct->ymin = rf->ymin; + newrct->ymax = rf->ymax; + + if (newrct->xmin == newrct->xmax) + (*count)--; + + rf->xmax = x2; + } + } + rf++; + } } static void clipy_rctf_swap(rctf *stack, short *count, float y1, float y2) { - rctf *rf, *newrct; - short a; - - a = *count; - rf = stack; - for (; a > 0; a--) { - if (rf->ymin < y1) { - if (rf->ymax < y1) { - rf->ymin += (y2 - y1); - rf->ymax += (y2 - y1); - } - else { - if (rf->ymax > y2) rf->ymax = y2; - newrct = stack + *count; - (*count)++; - - newrct->ymax = y2; - newrct->ymin = rf->ymin + (y2 - y1); - newrct->xmin = rf->xmin; - newrct->xmax = rf->xmax; - - if (newrct->ymin == newrct->ymax) (*count)--; - - rf->ymin = y1; - } - } - else if (rf->ymax > y2) { - if (rf->ymin > y2) { - rf->ymin -= (y2 - y1); - rf->ymax -= (y2 - y1); - } - else { - if (rf->ymin < y1) rf->ymin = y1; - newrct = stack + *count; - (*count)++; - - newrct->ymin = y1; - newrct->ymax = rf->ymax - (y2 - y1); - newrct->xmin = rf->xmin; - newrct->xmax = rf->xmax; - - if (newrct->ymin == newrct->ymax) (*count)--; - - rf->ymax = y2; - } - } - rf++; - } + rctf *rf, *newrct; + short a; + + a = *count; + rf = stack; + for (; a > 0; a--) { + if (rf->ymin < y1) { + if (rf->ymax < y1) { + rf->ymin += (y2 - y1); + rf->ymax += (y2 - y1); + } + else { + if (rf->ymax > y2) + rf->ymax = y2; + newrct = stack + *count; + (*count)++; + + newrct->ymax = y2; + newrct->ymin = rf->ymin + (y2 - y1); + newrct->xmin = rf->xmin; + newrct->xmax = rf->xmax; + + if (newrct->ymin == newrct->ymax) + (*count)--; + + rf->ymin = y1; + } + } + else if (rf->ymax > y2) { + if (rf->ymin > y2) { + rf->ymin -= (y2 - y1); + rf->ymax -= (y2 - y1); + } + else { + if (rf->ymin < y1) + rf->ymin = y1; + newrct = stack + *count; + (*count)++; + + newrct->ymin = y1; + newrct->ymax = rf->ymax - (y2 - y1); + newrct->xmin = rf->xmin; + newrct->xmax = rf->xmax; + + if (newrct->ymin == newrct->ymax) + (*count)--; + + rf->ymax = y2; + } + } + rf++; + } } static float square_rctf(rctf *rf) { - float x, y; + float x, y; - x = BLI_rctf_size_x(rf); - y = BLI_rctf_size_y(rf); - return x * y; + x = BLI_rctf_size_x(rf); + y = BLI_rctf_size_y(rf); + return x * y; } static float clipx_rctf(rctf *rf, float x1, float x2) { - float size; - - size = BLI_rctf_size_x(rf); - - if (rf->xmin < x1) { - rf->xmin = x1; - } - if (rf->xmax > x2) { - rf->xmax = x2; - } - if (rf->xmin > rf->xmax) { - rf->xmin = rf->xmax; - return 0.0; - } - else if (size != 0.0f) { - return BLI_rctf_size_x(rf) / size; - } - return 1.0; + float size; + + size = BLI_rctf_size_x(rf); + + if (rf->xmin < x1) { + rf->xmin = x1; + } + if (rf->xmax > x2) { + rf->xmax = x2; + } + if (rf->xmin > rf->xmax) { + rf->xmin = rf->xmax; + return 0.0; + } + else if (size != 0.0f) { + return BLI_rctf_size_x(rf) / size; + } + return 1.0; } static float clipy_rctf(rctf *rf, float y1, float y2) { - float size; - - size = BLI_rctf_size_y(rf); - - if (rf->ymin < y1) { - rf->ymin = y1; - } - if (rf->ymax > y2) { - rf->ymax = y2; - } - - if (rf->ymin > rf->ymax) { - rf->ymin = rf->ymax; - return 0.0; - } - else if (size != 0.0f) { - return BLI_rctf_size_y(rf) / size; - } - return 1.0; - + float size; + + size = BLI_rctf_size_y(rf); + + if (rf->ymin < y1) { + rf->ymin = y1; + } + if (rf->ymax > y2) { + rf->ymax = y2; + } + + if (rf->ymin > rf->ymax) { + rf->ymin = rf->ymax; + return 0.0; + } + else if (size != 0.0f) { + return BLI_rctf_size_y(rf) / size; + } + return 1.0; } static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) { - /* sample box, is clipped already, and minx etc. have been set at ibuf size. - * Enlarge with antialiased edges of the pixels */ - - float muly, mulx, div, col[4]; - int x, y, startx, endx, starty, endy; - - startx = (int)floor(rf->xmin); - endx = (int)floor(rf->xmax); - starty = (int)floor(rf->ymin); - endy = (int)floor(rf->ymax); - - if (startx < 0) startx = 0; - if (starty < 0) starty = 0; - if (endx >= ibuf->x) endx = ibuf->x - 1; - if (endy >= ibuf->y) endy = ibuf->y - 1; - - if (starty == endy && startx == endx) { - ibuf_get_color(&texres->tr, ibuf, startx, starty); - } - else { - div = texres->tr = texres->tg = texres->tb = texres->ta = 0.0; - for (y = starty; y <= endy; y++) { - - muly = 1.0; - - if (starty == endy) { - /* pass */ - } - else { - if (y == starty) muly = 1.0f - (rf->ymin - y); - if (y == endy) muly = (rf->ymax - y); - } - - if (startx == endx) { - mulx = muly; - - ibuf_get_color(col, ibuf, startx, y); - - texres->ta += mulx * col[3]; - texres->tr += mulx * col[0]; - texres->tg += mulx * col[1]; - texres->tb += mulx * col[2]; - div += mulx; - } - else { - for (x = startx; x <= endx; x++) { - mulx = muly; - if (x == startx) mulx *= 1.0f - (rf->xmin - x); - if (x == endx) mulx *= (rf->xmax - x); - - ibuf_get_color(col, ibuf, x, y); - - if (mulx == 1.0f) { - texres->ta += col[3]; - texres->tr += col[0]; - texres->tg += col[1]; - texres->tb += col[2]; - div += 1.0f; - } - else { - texres->ta += mulx * col[3]; - texres->tr += mulx * col[0]; - texres->tg += mulx * col[1]; - texres->tb += mulx * col[2]; - div += mulx; - } - } - } - } - - if (div != 0.0f) { - div = 1.0f / div; - texres->tb *= div; - texres->tg *= div; - texres->tr *= div; - texres->ta *= div; - } - else { - texres->tr = texres->tg = texres->tb = texres->ta = 0.0f; - } - } + /* sample box, is clipped already, and minx etc. have been set at ibuf size. + * Enlarge with antialiased edges of the pixels */ + + float muly, mulx, div, col[4]; + int x, y, startx, endx, starty, endy; + + startx = (int)floor(rf->xmin); + endx = (int)floor(rf->xmax); + starty = (int)floor(rf->ymin); + endy = (int)floor(rf->ymax); + + if (startx < 0) + startx = 0; + if (starty < 0) + starty = 0; + if (endx >= ibuf->x) + endx = ibuf->x - 1; + if (endy >= ibuf->y) + endy = ibuf->y - 1; + + if (starty == endy && startx == endx) { + ibuf_get_color(&texres->tr, ibuf, startx, starty); + } + else { + div = texres->tr = texres->tg = texres->tb = texres->ta = 0.0; + for (y = starty; y <= endy; y++) { + + muly = 1.0; + + if (starty == endy) { + /* pass */ + } + else { + if (y == starty) + muly = 1.0f - (rf->ymin - y); + if (y == endy) + muly = (rf->ymax - y); + } + + if (startx == endx) { + mulx = muly; + + ibuf_get_color(col, ibuf, startx, y); + + texres->ta += mulx * col[3]; + texres->tr += mulx * col[0]; + texres->tg += mulx * col[1]; + texres->tb += mulx * col[2]; + div += mulx; + } + else { + for (x = startx; x <= endx; x++) { + mulx = muly; + if (x == startx) + mulx *= 1.0f - (rf->xmin - x); + if (x == endx) + mulx *= (rf->xmax - x); + + ibuf_get_color(col, ibuf, x, y); + + if (mulx == 1.0f) { + texres->ta += col[3]; + texres->tr += col[0]; + texres->tg += col[1]; + texres->tb += col[2]; + div += 1.0f; + } + else { + texres->ta += mulx * col[3]; + texres->tr += mulx * col[0]; + texres->tg += mulx * col[1]; + texres->tb += mulx * col[2]; + div += mulx; + } + } + } + } + + if (div != 0.0f) { + div = 1.0f / div; + texres->tb *= div; + texres->tg *= div; + texres->tr *= div; + texres->ta *= div; + } + else { + texres->tr = texres->tg = texres->tb = texres->ta = 0.0f; + } + } } -static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, TexResult *texres, const short imaprepeat, const short imapextend) +static void boxsample(ImBuf *ibuf, + float minx, + float miny, + float maxx, + float maxy, + TexResult *texres, + const short imaprepeat, + const short imapextend) { - /* Sample box, performs clip. minx etc are in range 0.0 - 1.0 . - * Enlarge with antialiased edges of pixels. - * If variable 'imaprepeat' has been set, the - * clipped-away parts are sampled as well. - */ - /* note: actually minx etc isn't in the proper range... this due to filter size and offset vectors for bump */ - /* note: talpha must be initialized */ - /* note: even when 'imaprepeat' is set, this can only repeat once in any direction. - * the point which min/max is derived from is assumed to be wrapped */ - TexResult texr; - rctf *rf, stack[8]; - float opp, tot, alphaclip = 1.0; - short count = 1; - - rf = stack; - rf->xmin = minx * (ibuf->x); - rf->xmax = maxx * (ibuf->x); - rf->ymin = miny * (ibuf->y); - rf->ymax = maxy * (ibuf->y); - - texr.talpha = texres->talpha; /* is read by boxsample_clip */ - - if (imapextend) { - CLAMP(rf->xmin, 0.0f, ibuf->x - 1); - CLAMP(rf->xmax, 0.0f, ibuf->x - 1); - } - else if (imaprepeat) - clipx_rctf_swap(stack, &count, 0.0, (float)(ibuf->x)); - else { - alphaclip = clipx_rctf(rf, 0.0, (float)(ibuf->x)); - - if (alphaclip <= 0.0f) { - texres->tr = texres->tb = texres->tg = texres->ta = 0.0; - return; - } - } - - if (imapextend) { - CLAMP(rf->ymin, 0.0f, ibuf->y - 1); - CLAMP(rf->ymax, 0.0f, ibuf->y - 1); - } - else if (imaprepeat) - clipy_rctf_swap(stack, &count, 0.0, (float)(ibuf->y)); - else { - alphaclip *= clipy_rctf(rf, 0.0, (float)(ibuf->y)); - - if (alphaclip <= 0.0f) { - texres->tr = texres->tb = texres->tg = texres->ta = 0.0; - return; - } - } - - if (count > 1) { - tot = texres->tr = texres->tb = texres->tg = texres->ta = 0.0; - while (count--) { - boxsampleclip(ibuf, rf, &texr); - - opp = square_rctf(rf); - tot += opp; - - texres->tr += opp * texr.tr; - texres->tg += opp * texr.tg; - texres->tb += opp * texr.tb; - if (texres->talpha) texres->ta += opp * texr.ta; - rf++; - } - if (tot != 0.0f) { - texres->tr /= tot; - texres->tg /= tot; - texres->tb /= tot; - if (texres->talpha) texres->ta /= tot; - } - } - else - boxsampleclip(ibuf, rf, texres); - - if (texres->talpha == 0) texres->ta = 1.0; - - if (alphaclip != 1.0f) { - /* premul it all */ - texres->tr *= alphaclip; - texres->tg *= alphaclip; - texres->tb *= alphaclip; - texres->ta *= alphaclip; - } + /* Sample box, performs clip. minx etc are in range 0.0 - 1.0 . + * Enlarge with antialiased edges of pixels. + * If variable 'imaprepeat' has been set, the + * clipped-away parts are sampled as well. + */ + /* note: actually minx etc isn't in the proper range... this due to filter size and offset vectors for bump */ + /* note: talpha must be initialized */ + /* note: even when 'imaprepeat' is set, this can only repeat once in any direction. + * the point which min/max is derived from is assumed to be wrapped */ + TexResult texr; + rctf *rf, stack[8]; + float opp, tot, alphaclip = 1.0; + short count = 1; + + rf = stack; + rf->xmin = minx * (ibuf->x); + rf->xmax = maxx * (ibuf->x); + rf->ymin = miny * (ibuf->y); + rf->ymax = maxy * (ibuf->y); + + texr.talpha = texres->talpha; /* is read by boxsample_clip */ + + if (imapextend) { + CLAMP(rf->xmin, 0.0f, ibuf->x - 1); + CLAMP(rf->xmax, 0.0f, ibuf->x - 1); + } + else if (imaprepeat) + clipx_rctf_swap(stack, &count, 0.0, (float)(ibuf->x)); + else { + alphaclip = clipx_rctf(rf, 0.0, (float)(ibuf->x)); + + if (alphaclip <= 0.0f) { + texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + return; + } + } + + if (imapextend) { + CLAMP(rf->ymin, 0.0f, ibuf->y - 1); + CLAMP(rf->ymax, 0.0f, ibuf->y - 1); + } + else if (imaprepeat) + clipy_rctf_swap(stack, &count, 0.0, (float)(ibuf->y)); + else { + alphaclip *= clipy_rctf(rf, 0.0, (float)(ibuf->y)); + + if (alphaclip <= 0.0f) { + texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + return; + } + } + + if (count > 1) { + tot = texres->tr = texres->tb = texres->tg = texres->ta = 0.0; + while (count--) { + boxsampleclip(ibuf, rf, &texr); + + opp = square_rctf(rf); + tot += opp; + + texres->tr += opp * texr.tr; + texres->tg += opp * texr.tg; + texres->tb += opp * texr.tb; + if (texres->talpha) + texres->ta += opp * texr.ta; + rf++; + } + if (tot != 0.0f) { + texres->tr /= tot; + texres->tg /= tot; + texres->tb /= tot; + if (texres->talpha) + texres->ta /= tot; + } + } + else + boxsampleclip(ibuf, rf, texres); + + if (texres->talpha == 0) + texres->ta = 1.0; + + if (alphaclip != 1.0f) { + /* premul it all */ + texres->tr *= alphaclip; + texres->tg *= alphaclip; + texres->tb *= alphaclip; + texres->ta *= alphaclip; + } } /*----------------------------------------------------------------------------------------------------------------- @@ -638,1100 +687,1217 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max /* anisotropic filters, data struct used instead of long line of (possibly unused) func args */ typedef struct afdata_t { - float dxt[2], dyt[2]; - int intpol, extflag; - /* feline only */ - float majrad, minrad, theta; - int iProbes; - float dusc, dvsc; + float dxt[2], dyt[2]; + int intpol, extflag; + /* feline only */ + float majrad, minrad, theta; + int iProbes; + float dusc, dvsc; } afdata_t; /* this only used here to make it easier to pass extend flags as single int */ -enum {TXC_XMIR = 1, TXC_YMIR, TXC_REPT, TXC_EXTD}; +enum { TXC_XMIR = 1, TXC_YMIR, TXC_REPT, TXC_EXTD }; /* similar to ibuf_get_color() but clips/wraps coords according to repeat/extend flags * returns true if out of range in clipmode */ static int ibuf_get_color_clip(float col[4], ImBuf *ibuf, int x, int y, int extflag) { - int clip = 0; - switch (extflag) { - case TXC_XMIR: /* y rep */ - x %= 2 * ibuf->x; - x += x < 0 ? 2 * ibuf->x : 0; - x = x >= ibuf->x ? 2 * ibuf->x - x - 1 : x; - y %= ibuf->y; - y += y < 0 ? ibuf->y : 0; - break; - case TXC_YMIR: /* x rep */ - x %= ibuf->x; - x += x < 0 ? ibuf->x : 0; - y %= 2 * ibuf->y; - y += y < 0 ? 2 * ibuf->y : 0; - y = y >= ibuf->y ? 2 * ibuf->y - y - 1 : y; - break; - case TXC_EXTD: - x = (x < 0) ? 0 : ((x >= ibuf->x) ? (ibuf->x - 1) : x); - y = (y < 0) ? 0 : ((y >= ibuf->y) ? (ibuf->y - 1) : y); - break; - case TXC_REPT: - x %= ibuf->x; - x += (x < 0) ? ibuf->x : 0; - y %= ibuf->y; - y += (y < 0) ? ibuf->y : 0; - break; - default: - { /* as extend, if clipped, set alpha to 0.0 */ - if (x < 0) { x = 0; } /* TXF alpha: clip = 1; } */ - if (x >= ibuf->x) { x = ibuf->x - 1; } /* TXF alpha: clip = 1; } */ - if (y < 0) { y = 0; } /* TXF alpha: clip = 1; } */ - if (y >= ibuf->y) { y = ibuf->y - 1; } /* TXF alpha: clip = 1; } */ - } - } - - if (ibuf->rect_float) { - const float *fp = ibuf->rect_float + (x + y * ibuf->x) * ibuf->channels; - if (ibuf->channels == 1) - col[0] = col[1] = col[2] = col[3] = *fp; - else { - col[0] = fp[0]; - col[1] = fp[1]; - col[2] = fp[2]; - col[3] = clip ? 0.f : (ibuf->channels == 4 ? fp[3] : 1.f); - } - } - else { - const char *rect = (char *)(ibuf->rect + x + y * ibuf->x); - float inv_alpha_fac = (1.0f / 255.0f) * rect[3] * (1.0f / 255.0f); - col[0] = rect[0] * inv_alpha_fac; - col[1] = rect[1] * inv_alpha_fac; - col[2] = rect[2] * inv_alpha_fac; - col[3] = clip ? 0.f : rect[3] * (1.f / 255.f); - } - return clip; + int clip = 0; + switch (extflag) { + case TXC_XMIR: /* y rep */ + x %= 2 * ibuf->x; + x += x < 0 ? 2 * ibuf->x : 0; + x = x >= ibuf->x ? 2 * ibuf->x - x - 1 : x; + y %= ibuf->y; + y += y < 0 ? ibuf->y : 0; + break; + case TXC_YMIR: /* x rep */ + x %= ibuf->x; + x += x < 0 ? ibuf->x : 0; + y %= 2 * ibuf->y; + y += y < 0 ? 2 * ibuf->y : 0; + y = y >= ibuf->y ? 2 * ibuf->y - y - 1 : y; + break; + case TXC_EXTD: + x = (x < 0) ? 0 : ((x >= ibuf->x) ? (ibuf->x - 1) : x); + y = (y < 0) ? 0 : ((y >= ibuf->y) ? (ibuf->y - 1) : y); + break; + case TXC_REPT: + x %= ibuf->x; + x += (x < 0) ? ibuf->x : 0; + y %= ibuf->y; + y += (y < 0) ? ibuf->y : 0; + break; + default: { /* as extend, if clipped, set alpha to 0.0 */ + if (x < 0) { + x = 0; + } /* TXF alpha: clip = 1; } */ + if (x >= ibuf->x) { + x = ibuf->x - 1; + } /* TXF alpha: clip = 1; } */ + if (y < 0) { + y = 0; + } /* TXF alpha: clip = 1; } */ + if (y >= ibuf->y) { + y = ibuf->y - 1; + } /* TXF alpha: clip = 1; } */ + } + } + + if (ibuf->rect_float) { + const float *fp = ibuf->rect_float + (x + y * ibuf->x) * ibuf->channels; + if (ibuf->channels == 1) + col[0] = col[1] = col[2] = col[3] = *fp; + else { + col[0] = fp[0]; + col[1] = fp[1]; + col[2] = fp[2]; + col[3] = clip ? 0.f : (ibuf->channels == 4 ? fp[3] : 1.f); + } + } + else { + const char *rect = (char *)(ibuf->rect + x + y * ibuf->x); + float inv_alpha_fac = (1.0f / 255.0f) * rect[3] * (1.0f / 255.0f); + col[0] = rect[0] * inv_alpha_fac; + col[1] = rect[1] * inv_alpha_fac; + col[2] = rect[2] * inv_alpha_fac; + col[3] = clip ? 0.f : rect[3] * (1.f / 255.f); + } + return clip; } /* as above + bilerp */ -static int ibuf_get_color_clip_bilerp(float col[4], ImBuf *ibuf, float u, float v, int intpol, int extflag) +static int ibuf_get_color_clip_bilerp( + float col[4], ImBuf *ibuf, float u, float v, int intpol, int extflag) { - if (intpol) { - float c00[4], c01[4], c10[4], c11[4]; - const float ufl = floorf(u -= 0.5f), vfl = floorf(v -= 0.5f); - const float uf = u - ufl, vf = v - vfl; - const float w00 = (1.f - uf) * (1.f - vf), w10 = uf * (1.f - vf), w01 = (1.f - uf) * vf, w11 = uf * vf; - const int x1 = (int)ufl, y1 = (int)vfl, x2 = x1 + 1, y2 = y1 + 1; - int clip = ibuf_get_color_clip(c00, ibuf, x1, y1, extflag); - clip |= ibuf_get_color_clip(c10, ibuf, x2, y1, extflag); - clip |= ibuf_get_color_clip(c01, ibuf, x1, y2, extflag); - clip |= ibuf_get_color_clip(c11, ibuf, x2, y2, extflag); - col[0] = w00 * c00[0] + w10 * c10[0] + w01 * c01[0] + w11 * c11[0]; - col[1] = w00 * c00[1] + w10 * c10[1] + w01 * c01[1] + w11 * c11[1]; - col[2] = w00 * c00[2] + w10 * c10[2] + w01 * c01[2] + w11 * c11[2]; - col[3] = clip ? 0.f : w00 * c00[3] + w10 * c10[3] + w01 * c01[3] + w11 * c11[3]; - return clip; - } - return ibuf_get_color_clip(col, ibuf, (int)u, (int)v, extflag); + if (intpol) { + float c00[4], c01[4], c10[4], c11[4]; + const float ufl = floorf(u -= 0.5f), vfl = floorf(v -= 0.5f); + const float uf = u - ufl, vf = v - vfl; + const float w00 = (1.f - uf) * (1.f - vf), w10 = uf * (1.f - vf), w01 = (1.f - uf) * vf, + w11 = uf * vf; + const int x1 = (int)ufl, y1 = (int)vfl, x2 = x1 + 1, y2 = y1 + 1; + int clip = ibuf_get_color_clip(c00, ibuf, x1, y1, extflag); + clip |= ibuf_get_color_clip(c10, ibuf, x2, y1, extflag); + clip |= ibuf_get_color_clip(c01, ibuf, x1, y2, extflag); + clip |= ibuf_get_color_clip(c11, ibuf, x2, y2, extflag); + col[0] = w00 * c00[0] + w10 * c10[0] + w01 * c01[0] + w11 * c11[0]; + col[1] = w00 * c00[1] + w10 * c10[1] + w01 * c01[1] + w11 * c11[1]; + col[2] = w00 * c00[2] + w10 * c10[2] + w01 * c01[2] + w11 * c11[2]; + col[3] = clip ? 0.f : w00 * c00[3] + w10 * c10[3] + w01 * c01[3] + w11 * c11[3]; + return clip; + } + return ibuf_get_color_clip(col, ibuf, (int)u, (int)v, extflag); } static void area_sample(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t *AFD) { - int xs, ys, clip = 0; - float tc[4], xsd, ysd, cw = 0.f; - const float ux = ibuf->x * AFD->dxt[0], uy = ibuf->y * AFD->dxt[1]; - const float vx = ibuf->x * AFD->dyt[0], vy = ibuf->y * AFD->dyt[1]; - int xsam = (int)(0.5f * sqrtf(ux * ux + uy * uy) + 0.5f); - int ysam = (int)(0.5f * sqrtf(vx * vx + vy * vy) + 0.5f); - const int minsam = AFD->intpol ? 2 : 4; - xsam = CLAMPIS(xsam, minsam, ibuf->x * 2); - ysam = CLAMPIS(ysam, minsam, ibuf->y * 2); - xsd = 1.f / xsam; - ysd = 1.f / ysam; - texr->tr = texr->tg = texr->tb = texr->ta = 0.f; - for (ys = 0; ys < ysam; ++ys) { - for (xs = 0; xs < xsam; ++xs) { - const float su = (xs + ((ys & 1) + 0.5f) * 0.5f) * xsd - 0.5f; - const float sv = (ys + ((xs & 1) + 0.5f) * 0.5f) * ysd - 0.5f; - const float pu = fx + su * AFD->dxt[0] + sv * AFD->dyt[0]; - const float pv = fy + su * AFD->dxt[1] + sv * AFD->dyt[1]; - const int out = ibuf_get_color_clip_bilerp(tc, ibuf, pu * ibuf->x, pv * ibuf->y, AFD->intpol, AFD->extflag); - clip |= out; - cw += out ? 0.f : 1.f; - texr->tr += tc[0]; - texr->tg += tc[1]; - texr->tb += tc[2]; - texr->ta += texr->talpha ? tc[3] : 0.f; - } - } - xsd *= ysd; - texr->tr *= xsd; - texr->tg *= xsd; - texr->tb *= xsd; - /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ - texr->ta = texr->talpha ? texr->ta * xsd : (clip ? cw * xsd : 1.f); + int xs, ys, clip = 0; + float tc[4], xsd, ysd, cw = 0.f; + const float ux = ibuf->x * AFD->dxt[0], uy = ibuf->y * AFD->dxt[1]; + const float vx = ibuf->x * AFD->dyt[0], vy = ibuf->y * AFD->dyt[1]; + int xsam = (int)(0.5f * sqrtf(ux * ux + uy * uy) + 0.5f); + int ysam = (int)(0.5f * sqrtf(vx * vx + vy * vy) + 0.5f); + const int minsam = AFD->intpol ? 2 : 4; + xsam = CLAMPIS(xsam, minsam, ibuf->x * 2); + ysam = CLAMPIS(ysam, minsam, ibuf->y * 2); + xsd = 1.f / xsam; + ysd = 1.f / ysam; + texr->tr = texr->tg = texr->tb = texr->ta = 0.f; + for (ys = 0; ys < ysam; ++ys) { + for (xs = 0; xs < xsam; ++xs) { + const float su = (xs + ((ys & 1) + 0.5f) * 0.5f) * xsd - 0.5f; + const float sv = (ys + ((xs & 1) + 0.5f) * 0.5f) * ysd - 0.5f; + const float pu = fx + su * AFD->dxt[0] + sv * AFD->dyt[0]; + const float pv = fy + su * AFD->dxt[1] + sv * AFD->dyt[1]; + const int out = ibuf_get_color_clip_bilerp( + tc, ibuf, pu * ibuf->x, pv * ibuf->y, AFD->intpol, AFD->extflag); + clip |= out; + cw += out ? 0.f : 1.f; + texr->tr += tc[0]; + texr->tg += tc[1]; + texr->tb += tc[2]; + texr->ta += texr->talpha ? tc[3] : 0.f; + } + } + xsd *= ysd; + texr->tr *= xsd; + texr->tg *= xsd; + texr->tb *= xsd; + /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ + texr->ta = texr->talpha ? texr->ta * xsd : (clip ? cw * xsd : 1.f); } typedef struct ReadEWAData { - ImBuf *ibuf; - afdata_t *AFD; + ImBuf *ibuf; + afdata_t *AFD; } ReadEWAData; static void ewa_read_pixel_cb(void *userdata, int x, int y, float result[4]) { - ReadEWAData *data = (ReadEWAData *)userdata; - ibuf_get_color_clip(result, data->ibuf, x, y, data->AFD->extflag); + ReadEWAData *data = (ReadEWAData *)userdata; + ibuf_get_color_clip(result, data->ibuf, x, y, data->AFD->extflag); } static void ewa_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t *AFD) { - ReadEWAData data; - float uv[2] = {fx, fy}; - data.ibuf = ibuf; - data.AFD = AFD; - BLI_ewa_filter(ibuf->x, ibuf->y, - AFD->intpol != 0, - texr->talpha, - uv, AFD->dxt, AFD->dyt, - ewa_read_pixel_cb, - &data, - &texr->tr); - + ReadEWAData data; + float uv[2] = {fx, fy}; + data.ibuf = ibuf; + data.AFD = AFD; + BLI_ewa_filter(ibuf->x, + ibuf->y, + AFD->intpol != 0, + texr->talpha, + uv, + AFD->dxt, + AFD->dyt, + ewa_read_pixel_cb, + &data, + &texr->tr); } static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata_t *AFD) { - const int maxn = AFD->iProbes - 1; - const float ll = ((AFD->majrad == AFD->minrad) ? 2.f * AFD->majrad : 2.f * (AFD->majrad - AFD->minrad)) / (maxn ? (float)maxn : 1.f); - float du = maxn ? cosf(AFD->theta) * ll : 0.f; - float dv = maxn ? sinf(AFD->theta) * ll : 0.f; - /* const float D = -0.5f*(du*du + dv*dv) / (AFD->majrad*AFD->majrad); */ - const float D = (EWA_MAXIDX + 1) * 0.25f * (du * du + dv * dv) / (AFD->majrad * AFD->majrad); - float d; /* TXF alpha: cw = 0.f; */ - int n; /* TXF alpha: clip = 0; */ - /* have to use same scaling for du/dv here as for Ux/Vx/Uy/Vy (*after* D calc.) */ - du *= AFD->dusc; - dv *= AFD->dvsc; - d = texr->tr = texr->tb = texr->tg = texr->ta = 0.f; - for (n = -maxn; n <= maxn; n += 2) { - float tc[4]; - const float hn = n * 0.5f; - const float u = fx + hn * du, v = fy + hn * dv; - /*const float wt = expf(n*n*D); - * can use ewa table here too */ - const float wt = EWA_WTS[(int)(n * n * D)]; - /*const int out =*/ ibuf_get_color_clip_bilerp(tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag); - /* TXF alpha: clip |= out; - * TXF alpha: cw += out ? 0.f : wt; */ - texr->tr += tc[0] * wt; - texr->tg += tc[1] * wt; - texr->tb += tc[2] * wt; - texr->ta += texr->talpha ? tc[3] * wt : 0.f; - d += wt; - } - - d = 1.f / d; - texr->tr *= d; - texr->tg *= d; - texr->tb *= d; - /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ - texr->ta = texr->talpha ? texr->ta * d : 1.f; // TXF alpha: (clip ? cw*d : 1.f); + const int maxn = AFD->iProbes - 1; + const float ll = ((AFD->majrad == AFD->minrad) ? 2.f * AFD->majrad : + 2.f * (AFD->majrad - AFD->minrad)) / + (maxn ? (float)maxn : 1.f); + float du = maxn ? cosf(AFD->theta) * ll : 0.f; + float dv = maxn ? sinf(AFD->theta) * ll : 0.f; + /* const float D = -0.5f*(du*du + dv*dv) / (AFD->majrad*AFD->majrad); */ + const float D = (EWA_MAXIDX + 1) * 0.25f * (du * du + dv * dv) / (AFD->majrad * AFD->majrad); + float d; /* TXF alpha: cw = 0.f; */ + int n; /* TXF alpha: clip = 0; */ + /* have to use same scaling for du/dv here as for Ux/Vx/Uy/Vy (*after* D calc.) */ + du *= AFD->dusc; + dv *= AFD->dvsc; + d = texr->tr = texr->tb = texr->tg = texr->ta = 0.f; + for (n = -maxn; n <= maxn; n += 2) { + float tc[4]; + const float hn = n * 0.5f; + const float u = fx + hn * du, v = fy + hn * dv; + /*const float wt = expf(n*n*D); + * can use ewa table here too */ + const float wt = EWA_WTS[(int)(n * n * D)]; + /*const int out =*/ibuf_get_color_clip_bilerp( + tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag); + /* TXF alpha: clip |= out; + * TXF alpha: cw += out ? 0.f : wt; */ + texr->tr += tc[0] * wt; + texr->tg += tc[1] * wt; + texr->tb += tc[2] * wt; + texr->ta += texr->talpha ? tc[3] * wt : 0.f; + d += wt; + } + + d = 1.f / d; + texr->tr *= d; + texr->tg *= d; + texr->tb *= d; + /* clipping can be ignored if alpha used, texr->ta already includes filtered edge */ + texr->ta = texr->talpha ? texr->ta * d : 1.f; // TXF alpha: (clip ? cw*d : 1.f); } #undef EWA_MAXIDX -static void alpha_clip_aniso(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, int extflag, TexResult *texres) +static void alpha_clip_aniso( + ImBuf *ibuf, float minx, float miny, float maxx, float maxy, int extflag, TexResult *texres) { - float alphaclip; - rctf rf; - - /* TXF apha: we're doing the same alphaclip here as boxsample, but i'm doubting - * if this is actually correct for the all the filtering algorithms .. */ - - if (!(extflag == TXC_REPT || extflag == TXC_EXTD)) { - rf.xmin = minx * (ibuf->x); - rf.xmax = maxx * (ibuf->x); - rf.ymin = miny * (ibuf->y); - rf.ymax = maxy * (ibuf->y); - - alphaclip = clipx_rctf(&rf, 0.0, (float)(ibuf->x)); - alphaclip *= clipy_rctf(&rf, 0.0, (float)(ibuf->y)); - alphaclip = max_ff(alphaclip, 0.0f); - - if (alphaclip != 1.0f) { - /* premul it all */ - texres->tr *= alphaclip; - texres->tg *= alphaclip; - texres->tb *= alphaclip; - texres->ta *= alphaclip; - } - } + float alphaclip; + rctf rf; + + /* TXF apha: we're doing the same alphaclip here as boxsample, but i'm doubting + * if this is actually correct for the all the filtering algorithms .. */ + + if (!(extflag == TXC_REPT || extflag == TXC_EXTD)) { + rf.xmin = minx * (ibuf->x); + rf.xmax = maxx * (ibuf->x); + rf.ymin = miny * (ibuf->y); + rf.ymax = maxy * (ibuf->y); + + alphaclip = clipx_rctf(&rf, 0.0, (float)(ibuf->x)); + alphaclip *= clipy_rctf(&rf, 0.0, (float)(ibuf->y)); + alphaclip = max_ff(alphaclip, 0.0f); + + if (alphaclip != 1.0f) { + /* premul it all */ + texres->tr *= alphaclip; + texres->tg *= alphaclip; + texres->tb *= alphaclip; + texres->ta *= alphaclip; + } + } } static void image_mipmap_test(Tex *tex, ImBuf *ibuf) { - if (tex->imaflag & TEX_MIPMAP) { - if (ibuf->mipmap[0] && (ibuf->userflags & IB_MIPMAP_INVALID)) { - BLI_thread_lock(LOCK_IMAGE); - if (ibuf->userflags & IB_MIPMAP_INVALID) { - IMB_remakemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP); - ibuf->userflags &= ~IB_MIPMAP_INVALID; - } - BLI_thread_unlock(LOCK_IMAGE); - } - if (ibuf->mipmap[0] == NULL) { - BLI_thread_lock(LOCK_IMAGE); - if (ibuf->mipmap[0] == NULL) - IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP); - BLI_thread_unlock(LOCK_IMAGE); - } - /* if no mipmap could be made, fall back on non-mipmap render */ - if (ibuf->mipmap[0] == NULL) { - tex->imaflag &= ~TEX_MIPMAP; - } - } + if (tex->imaflag & TEX_MIPMAP) { + if (ibuf->mipmap[0] && (ibuf->userflags & IB_MIPMAP_INVALID)) { + BLI_thread_lock(LOCK_IMAGE); + if (ibuf->userflags & IB_MIPMAP_INVALID) { + IMB_remakemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP); + ibuf->userflags &= ~IB_MIPMAP_INVALID; + } + BLI_thread_unlock(LOCK_IMAGE); + } + if (ibuf->mipmap[0] == NULL) { + BLI_thread_lock(LOCK_IMAGE); + if (ibuf->mipmap[0] == NULL) + IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP); + BLI_thread_unlock(LOCK_IMAGE); + } + /* if no mipmap could be made, fall back on non-mipmap render */ + if (ibuf->mipmap[0] == NULL) { + tex->imaflag &= ~TEX_MIPMAP; + } + } } -static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], float dxt[2], float dyt[2], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) +static int imagewraposa_aniso(Tex *tex, + Image *ima, + ImBuf *ibuf, + const float texvec[3], + float dxt[2], + float dyt[2], + TexResult *texres, + struct ImagePool *pool, + const bool skip_load_image) { - TexResult texr; - float fx, fy, minx, maxx, miny, maxy; - float maxd, val1, val2, val3; - int curmap, retval, intpol, extflag = 0; - afdata_t AFD; - - void (*filterfunc)(TexResult *, ImBuf *, float, float, afdata_t *); - switch (tex->texfilter) { - case TXF_EWA: - filterfunc = ewa_eval; - break; - case TXF_FELINE: - filterfunc = feline_eval; - break; - case TXF_AREA: - default: - filterfunc = area_sample; - } - - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.f; - - /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; - - /* quick tests */ - if (ibuf == NULL && ima == NULL) return retval; - - if (ima) { /* hack for icon render */ - if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) { - return retval; - } - ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); - } - - if ((ibuf == NULL) || ((ibuf->rect == NULL) && (ibuf->rect_float == NULL))) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - - if (ima) { - ima->flag |= IMA_USED_FOR_RENDER; - } - - /* mipmap test */ - image_mipmap_test(tex, ibuf); - - if (ima) { - if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { - if ((tex->imaflag & TEX_CALCALPHA) == 0) { - texres->talpha = 1; - } - } - } - texr.talpha = texres->talpha; - - if (tex->imaflag & TEX_IMAROT) { - fy = texvec[0]; - fx = texvec[1]; - } - else { - fx = texvec[0]; - fy = texvec[1]; - } - - /* pixel coordinates */ - minx = min_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); - maxx = max_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); - miny = min_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); - maxy = max_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); - - /* tex_sharper has been removed */ - minx = (maxx - minx) * 0.5f; - miny = (maxy - miny) * 0.5f; - - if (tex->imaflag & TEX_FILTER_MIN) { - /* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */ - const float addval = (0.5f * tex->filtersize) / (float)MIN2(ibuf->x, ibuf->y); - if (addval > minx) minx = addval; - if (addval > miny) miny = addval; - } - else if (tex->filtersize != 1.f) { - minx *= tex->filtersize; - miny *= tex->filtersize; - dxt[0] *= tex->filtersize; - dxt[1] *= tex->filtersize; - dyt[0] *= tex->filtersize; - dyt[1] *= tex->filtersize; - } - - if (tex->imaflag & TEX_IMAROT) { - float t; - SWAP(float, minx, miny); - /* must rotate dxt/dyt 90 deg - * yet another blender problem is that swapping X/Y axes (or any tex proj switches) should do something similar, - * but it doesn't, it only swaps coords, so filter area will be incorrect in those cases. */ - t = dxt[0]; - dxt[0] = dxt[1]; - dxt[1] = -t; - t = dyt[0]; - dyt[0] = dyt[1]; - dyt[1] = -t; - } - - /* side faces of unit-cube */ - minx = (minx > 0.25f) ? 0.25f : ((minx < 1e-5f) ? 1e-5f : minx); - miny = (miny > 0.25f) ? 0.25f : ((miny < 1e-5f) ? 1e-5f : miny); - - /* repeat and clip */ - - if (tex->extend == TEX_REPEAT) { - if ((tex->flag & (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) == (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) - extflag = TXC_EXTD; - else if (tex->flag & TEX_REPEAT_XMIR) - extflag = TXC_XMIR; - else if (tex->flag & TEX_REPEAT_YMIR) - extflag = TXC_YMIR; - else - extflag = TXC_REPT; - } - else if (tex->extend == TEX_EXTEND) - extflag = TXC_EXTD; - - if (tex->extend == TEX_CHECKER) { - int xs = (int)floorf(fx), ys = (int)floorf(fy); - /* both checkers available, no boundary exceptions, checkerdist will eat aliasing */ - if ((tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN)) { - fx -= xs; - fy -= ys; - } - else if ((tex->flag & TEX_CHECKER_ODD) == 0 && - (tex->flag & TEX_CHECKER_EVEN) == 0) - { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - else { - int xs1 = (int)floorf(fx - minx); - int ys1 = (int)floorf(fy - miny); - int xs2 = (int)floorf(fx + minx); - int ys2 = (int)floorf(fy + miny); - if ((xs1 != xs2) || (ys1 != ys2)) { - if (tex->flag & TEX_CHECKER_ODD) { - fx -= ((xs1 + ys) & 1) ? xs2 : xs1; - fy -= ((ys1 + xs) & 1) ? ys2 : ys1; - } - if (tex->flag & TEX_CHECKER_EVEN) { - fx -= ((xs1 + ys) & 1) ? xs1 : xs2; - fy -= ((ys1 + xs) & 1) ? ys1 : ys2; - } - } - else { - if ((tex->flag & TEX_CHECKER_ODD) == 0 && ((xs + ys) & 1) == 0) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - if ((tex->flag & TEX_CHECKER_EVEN) == 0 && (xs + ys) & 1) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - fx -= xs; - fy -= ys; - } - } - /* scale around center, (0.5, 0.5) */ - if (tex->checkerdist < 1.f) { - const float omcd = 1.f / (1.f - tex->checkerdist); - fx = (fx - 0.5f) * omcd + 0.5f; - fy = (fy - 0.5f) * omcd + 0.5f; - minx *= omcd; - miny *= omcd; - } - } - - if (tex->extend == TEX_CLIPCUBE) { - if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f || texvec[2] < -1.f || texvec[2] > 1.f) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { - if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else { - if (tex->extend == TEX_EXTEND) { - fx = (fx > 1.f) ? 1.f : ((fx < 0.f) ? 0.f : fx); - fy = (fy > 1.f) ? 1.f : ((fy < 0.f) ? 0.f : fy); - } - else { - fx -= floorf(fx); - fy -= floorf(fy); - } - } - - intpol = tex->imaflag & TEX_INTERPOL; - - /* struct common data */ - copy_v2_v2(AFD.dxt, dxt); - copy_v2_v2(AFD.dyt, dyt); - AFD.intpol = intpol; - AFD.extflag = extflag; - - /* brecht: added stupid clamping here, large dx/dy can give very large - * filter sizes which take ages to render, it may be better to do this - * more intelligently later in the code .. probably it's not noticeable */ - if (AFD.dxt[0] * AFD.dxt[0] + AFD.dxt[1] * AFD.dxt[1] > 2.0f * 2.0f) - mul_v2_fl(AFD.dxt, 2.0f / len_v2(AFD.dxt)); - if (AFD.dyt[0] * AFD.dyt[0] + AFD.dyt[1] * AFD.dyt[1] > 2.0f * 2.0f) - mul_v2_fl(AFD.dyt, 2.0f / len_v2(AFD.dyt)); - - /* choice: */ - if (tex->imaflag & TEX_MIPMAP) { - ImBuf *previbuf, *curibuf; - float levf; - int maxlev; - ImBuf *mipmaps[IMB_MIPMAP_LEVELS + 1]; - - /* modify ellipse minor axis if too eccentric, use for area sampling as well - * scaling dxt/dyt as done in pbrt is not the same - * (as in ewa_eval(), scale by sqrt(ibuf->x) to maximize precision) */ - const float ff = sqrtf(ibuf->x), q = ibuf->y / ff; - const float Ux = dxt[0] * ff, Vx = dxt[1] * q, Uy = dyt[0] * ff, Vy = dyt[1] * q; - const float A = Vx * Vx + Vy * Vy; - const float B = -2.f * (Ux * Vx + Uy * Vy); - const float C = Ux * Ux + Uy * Uy; - const float F = A * C - B * B * 0.25f; - float a, b, th, ecc; - BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc); - if (tex->texfilter == TXF_FELINE) { - float fProbes; - a *= ff; - b *= ff; - a = max_ff(a, 1.0f); - b = max_ff(b, 1.0f); - fProbes = 2.f * (a / b) - 1.f; - AFD.iProbes = round_fl_to_int(fProbes); - AFD.iProbes = MIN2(AFD.iProbes, tex->afmax); - if (AFD.iProbes < fProbes) - b = 2.f * a / (float)(AFD.iProbes + 1); - AFD.majrad = a / ff; - AFD.minrad = b / ff; - AFD.theta = th; - AFD.dusc = 1.f / ff; - AFD.dvsc = ff / (float)ibuf->y; - } - else { /* EWA & area */ - if (ecc > (float)tex->afmax) b = a / (float)tex->afmax; - b *= ff; - } - maxd = max_ff(b, 1e-8f); - levf = ((float)M_LOG2E) * logf(maxd); - - curmap = 0; - maxlev = 1; - mipmaps[0] = ibuf; - while (curmap < IMB_MIPMAP_LEVELS) { - mipmaps[curmap + 1] = ibuf->mipmap[curmap]; - if (ibuf->mipmap[curmap]) maxlev++; - curmap++; - } - - /* mipmap level */ - if (levf < 0.f) { /* original image only */ - previbuf = curibuf = mipmaps[0]; - levf = 0.f; - } - else if (levf >= maxlev - 1) { - previbuf = curibuf = mipmaps[maxlev - 1]; - levf = 0.f; - if (tex->texfilter == TXF_FELINE) AFD.iProbes = 1; - } - else { - const int lev = isnan(levf) ? 0 : (int)levf; - curibuf = mipmaps[lev]; - previbuf = mipmaps[lev + 1]; - levf -= floorf(levf); - } - - /* filter functions take care of interpolation themselves, no need to modify dxt/dyt here */ - - if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { - /* color & normal */ - filterfunc(texres, curibuf, fx, fy, &AFD); - val1 = texres->tr + texres->tg + texres->tb; - filterfunc(&texr, curibuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 = texr.tr + texr.tg + texr.tb; - filterfunc(&texr, curibuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 = texr.tr + texr.tg + texr.tb; - /* don't switch x or y! */ - texres->nor[0] = val1 - val2; - texres->nor[1] = val1 - val3; - if (previbuf != curibuf) { /* interpolate */ - filterfunc(&texr, previbuf, fx, fy, &AFD); - /* rgb */ - texres->tr += levf * (texr.tr - texres->tr); - texres->tg += levf * (texr.tg - texres->tg); - texres->tb += levf * (texr.tb - texres->tb); - texres->ta += levf * (texr.ta - texres->ta); - /* normal */ - val1 += levf * ((texr.tr + texr.tg + texr.tb) - val1); - filterfunc(&texr, previbuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 += levf * ((texr.tr + texr.tg + texr.tb) - val2); - filterfunc(&texr, previbuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 += levf * ((texr.tr + texr.tg + texr.tb) - val3); - texres->nor[0] = val1 - val2; /* vals have been interpolated above! */ - texres->nor[1] = val1 - val3; - } - } - else { /* color */ - filterfunc(texres, curibuf, fx, fy, &AFD); - if (previbuf != curibuf) { /* interpolate */ - filterfunc(&texr, previbuf, fx, fy, &AFD); - texres->tr += levf * (texr.tr - texres->tr); - texres->tg += levf * (texr.tg - texres->tg); - texres->tb += levf * (texr.tb - texres->tb); - texres->ta += levf * (texr.ta - texres->ta); - } - - if (tex->texfilter != TXF_EWA) { - alpha_clip_aniso(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, extflag, texres); - } - } - } - else { /* no mipmap */ - /* filter functions take care of interpolation themselves, no need to modify dxt/dyt here */ - if (tex->texfilter == TXF_FELINE) { - const float ff = sqrtf(ibuf->x), q = ibuf->y / ff; - const float Ux = dxt[0] * ff, Vx = dxt[1] * q, Uy = dyt[0] * ff, Vy = dyt[1] * q; - const float A = Vx * Vx + Vy * Vy; - const float B = -2.f * (Ux * Vx + Uy * Vy); - const float C = Ux * Ux + Uy * Uy; - const float F = A * C - B * B * 0.25f; - float a, b, th, ecc, fProbes; - BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc); - a *= ff; - b *= ff; - a = max_ff(a, 1.0f); - b = max_ff(b, 1.0f); - fProbes = 2.f * (a / b) - 1.f; - /* no limit to number of Probes here */ - AFD.iProbes = round_fl_to_int(fProbes); - if (AFD.iProbes < fProbes) b = 2.f * a / (float)(AFD.iProbes + 1); - AFD.majrad = a / ff; - AFD.minrad = b / ff; - AFD.theta = th; - AFD.dusc = 1.f / ff; - AFD.dvsc = ff / (float)ibuf->y; - } - if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { - /* color & normal */ - filterfunc(texres, ibuf, fx, fy, &AFD); - val1 = texres->tr + texres->tg + texres->tb; - filterfunc(&texr, ibuf, fx + dxt[0], fy + dxt[1], &AFD); - val2 = texr.tr + texr.tg + texr.tb; - filterfunc(&texr, ibuf, fx + dyt[0], fy + dyt[1], &AFD); - val3 = texr.tr + texr.tg + texr.tb; - /* don't switch x or y! */ - texres->nor[0] = val1 - val2; - texres->nor[1] = val1 - val3; - } - else { - filterfunc(texres, ibuf, fx, fy, &AFD); - if (tex->texfilter != TXF_EWA) { - alpha_clip_aniso(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, extflag, texres); - } - } - } - - if (tex->imaflag & TEX_CALCALPHA) - texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); - else - texres->tin = texres->ta; - if (tex->flag & TEX_NEGALPHA) texres->ta = 1.f - texres->ta; - - if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { /* normal from color */ - /* The invert of the red channel is to make - * the normal map compliant with the outside world. - * It needs to be done because in Blender - * the normal used in the renderer points inward. It is generated - * this way in calc_vertexnormals(). Should this ever change - * this negate must be removed. */ - texres->nor[0] = -2.f * (texres->tr - 0.5f); - texres->nor[1] = 2.f * (texres->tg - 0.5f); - texres->nor[2] = 2.f * (texres->tb - 0.5f); - } - - /* de-premul, this is being premulled in shade_input_do_shade() - * TXF: this currently does not (yet?) work properly, destroys edge AA in clip/checker mode, so for now commented out - * also disabled in imagewraposa() to be able to compare results with blender's default texture filtering */ - - /* brecht: tried to fix this, see "TXF alpha" comments */ - - /* do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - fx = 1.f / texres->ta; - texres->tr *= fx; - texres->tg *= fx; - texres->tb *= fx; - } - - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - - BRICONTRGB; - - return retval; + TexResult texr; + float fx, fy, minx, maxx, miny, maxy; + float maxd, val1, val2, val3; + int curmap, retval, intpol, extflag = 0; + afdata_t AFD; + + void (*filterfunc)(TexResult *, ImBuf *, float, float, afdata_t *); + switch (tex->texfilter) { + case TXF_EWA: + filterfunc = ewa_eval; + break; + case TXF_FELINE: + filterfunc = feline_eval; + break; + case TXF_AREA: + default: + filterfunc = area_sample; + } + + texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.f; + + /* we need to set retval OK, otherwise texture code generates normals itself... */ + retval = texres->nor ? 3 : 1; + + /* quick tests */ + if (ibuf == NULL && ima == NULL) + return retval; + + if (ima) { /* hack for icon render */ + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) { + return retval; + } + ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); + } + + if ((ibuf == NULL) || ((ibuf->rect == NULL) && (ibuf->rect_float == NULL))) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + + if (ima) { + ima->flag |= IMA_USED_FOR_RENDER; + } + + /* mipmap test */ + image_mipmap_test(tex, ibuf); + + if (ima) { + if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { + if ((tex->imaflag & TEX_CALCALPHA) == 0) { + texres->talpha = 1; + } + } + } + texr.talpha = texres->talpha; + + if (tex->imaflag & TEX_IMAROT) { + fy = texvec[0]; + fx = texvec[1]; + } + else { + fx = texvec[0]; + fy = texvec[1]; + } + + /* pixel coordinates */ + minx = min_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); + maxx = max_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); + miny = min_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); + maxy = max_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); + + /* tex_sharper has been removed */ + minx = (maxx - minx) * 0.5f; + miny = (maxy - miny) * 0.5f; + + if (tex->imaflag & TEX_FILTER_MIN) { + /* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */ + const float addval = (0.5f * tex->filtersize) / (float)MIN2(ibuf->x, ibuf->y); + if (addval > minx) + minx = addval; + if (addval > miny) + miny = addval; + } + else if (tex->filtersize != 1.f) { + minx *= tex->filtersize; + miny *= tex->filtersize; + dxt[0] *= tex->filtersize; + dxt[1] *= tex->filtersize; + dyt[0] *= tex->filtersize; + dyt[1] *= tex->filtersize; + } + + if (tex->imaflag & TEX_IMAROT) { + float t; + SWAP(float, minx, miny); + /* must rotate dxt/dyt 90 deg + * yet another blender problem is that swapping X/Y axes (or any tex proj switches) should do something similar, + * but it doesn't, it only swaps coords, so filter area will be incorrect in those cases. */ + t = dxt[0]; + dxt[0] = dxt[1]; + dxt[1] = -t; + t = dyt[0]; + dyt[0] = dyt[1]; + dyt[1] = -t; + } + + /* side faces of unit-cube */ + minx = (minx > 0.25f) ? 0.25f : ((minx < 1e-5f) ? 1e-5f : minx); + miny = (miny > 0.25f) ? 0.25f : ((miny < 1e-5f) ? 1e-5f : miny); + + /* repeat and clip */ + + if (tex->extend == TEX_REPEAT) { + if ((tex->flag & (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) == (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) + extflag = TXC_EXTD; + else if (tex->flag & TEX_REPEAT_XMIR) + extflag = TXC_XMIR; + else if (tex->flag & TEX_REPEAT_YMIR) + extflag = TXC_YMIR; + else + extflag = TXC_REPT; + } + else if (tex->extend == TEX_EXTEND) + extflag = TXC_EXTD; + + if (tex->extend == TEX_CHECKER) { + int xs = (int)floorf(fx), ys = (int)floorf(fy); + /* both checkers available, no boundary exceptions, checkerdist will eat aliasing */ + if ((tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN)) { + fx -= xs; + fy -= ys; + } + else if ((tex->flag & TEX_CHECKER_ODD) == 0 && (tex->flag & TEX_CHECKER_EVEN) == 0) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + else { + int xs1 = (int)floorf(fx - minx); + int ys1 = (int)floorf(fy - miny); + int xs2 = (int)floorf(fx + minx); + int ys2 = (int)floorf(fy + miny); + if ((xs1 != xs2) || (ys1 != ys2)) { + if (tex->flag & TEX_CHECKER_ODD) { + fx -= ((xs1 + ys) & 1) ? xs2 : xs1; + fy -= ((ys1 + xs) & 1) ? ys2 : ys1; + } + if (tex->flag & TEX_CHECKER_EVEN) { + fx -= ((xs1 + ys) & 1) ? xs1 : xs2; + fy -= ((ys1 + xs) & 1) ? ys1 : ys2; + } + } + else { + if ((tex->flag & TEX_CHECKER_ODD) == 0 && ((xs + ys) & 1) == 0) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + if ((tex->flag & TEX_CHECKER_EVEN) == 0 && (xs + ys) & 1) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + fx -= xs; + fy -= ys; + } + } + /* scale around center, (0.5, 0.5) */ + if (tex->checkerdist < 1.f) { + const float omcd = 1.f / (1.f - tex->checkerdist); + fx = (fx - 0.5f) * omcd + 0.5f; + fy = (fy - 0.5f) * omcd + 0.5f; + minx *= omcd; + miny *= omcd; + } + } + + if (tex->extend == TEX_CLIPCUBE) { + if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f || + texvec[2] < -1.f || texvec[2] > 1.f) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { + if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else { + if (tex->extend == TEX_EXTEND) { + fx = (fx > 1.f) ? 1.f : ((fx < 0.f) ? 0.f : fx); + fy = (fy > 1.f) ? 1.f : ((fy < 0.f) ? 0.f : fy); + } + else { + fx -= floorf(fx); + fy -= floorf(fy); + } + } + + intpol = tex->imaflag & TEX_INTERPOL; + + /* struct common data */ + copy_v2_v2(AFD.dxt, dxt); + copy_v2_v2(AFD.dyt, dyt); + AFD.intpol = intpol; + AFD.extflag = extflag; + + /* brecht: added stupid clamping here, large dx/dy can give very large + * filter sizes which take ages to render, it may be better to do this + * more intelligently later in the code .. probably it's not noticeable */ + if (AFD.dxt[0] * AFD.dxt[0] + AFD.dxt[1] * AFD.dxt[1] > 2.0f * 2.0f) + mul_v2_fl(AFD.dxt, 2.0f / len_v2(AFD.dxt)); + if (AFD.dyt[0] * AFD.dyt[0] + AFD.dyt[1] * AFD.dyt[1] > 2.0f * 2.0f) + mul_v2_fl(AFD.dyt, 2.0f / len_v2(AFD.dyt)); + + /* choice: */ + if (tex->imaflag & TEX_MIPMAP) { + ImBuf *previbuf, *curibuf; + float levf; + int maxlev; + ImBuf *mipmaps[IMB_MIPMAP_LEVELS + 1]; + + /* modify ellipse minor axis if too eccentric, use for area sampling as well + * scaling dxt/dyt as done in pbrt is not the same + * (as in ewa_eval(), scale by sqrt(ibuf->x) to maximize precision) */ + const float ff = sqrtf(ibuf->x), q = ibuf->y / ff; + const float Ux = dxt[0] * ff, Vx = dxt[1] * q, Uy = dyt[0] * ff, Vy = dyt[1] * q; + const float A = Vx * Vx + Vy * Vy; + const float B = -2.f * (Ux * Vx + Uy * Vy); + const float C = Ux * Ux + Uy * Uy; + const float F = A * C - B * B * 0.25f; + float a, b, th, ecc; + BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc); + if (tex->texfilter == TXF_FELINE) { + float fProbes; + a *= ff; + b *= ff; + a = max_ff(a, 1.0f); + b = max_ff(b, 1.0f); + fProbes = 2.f * (a / b) - 1.f; + AFD.iProbes = round_fl_to_int(fProbes); + AFD.iProbes = MIN2(AFD.iProbes, tex->afmax); + if (AFD.iProbes < fProbes) + b = 2.f * a / (float)(AFD.iProbes + 1); + AFD.majrad = a / ff; + AFD.minrad = b / ff; + AFD.theta = th; + AFD.dusc = 1.f / ff; + AFD.dvsc = ff / (float)ibuf->y; + } + else { /* EWA & area */ + if (ecc > (float)tex->afmax) + b = a / (float)tex->afmax; + b *= ff; + } + maxd = max_ff(b, 1e-8f); + levf = ((float)M_LOG2E) * logf(maxd); + + curmap = 0; + maxlev = 1; + mipmaps[0] = ibuf; + while (curmap < IMB_MIPMAP_LEVELS) { + mipmaps[curmap + 1] = ibuf->mipmap[curmap]; + if (ibuf->mipmap[curmap]) + maxlev++; + curmap++; + } + + /* mipmap level */ + if (levf < 0.f) { /* original image only */ + previbuf = curibuf = mipmaps[0]; + levf = 0.f; + } + else if (levf >= maxlev - 1) { + previbuf = curibuf = mipmaps[maxlev - 1]; + levf = 0.f; + if (tex->texfilter == TXF_FELINE) + AFD.iProbes = 1; + } + else { + const int lev = isnan(levf) ? 0 : (int)levf; + curibuf = mipmaps[lev]; + previbuf = mipmaps[lev + 1]; + levf -= floorf(levf); + } + + /* filter functions take care of interpolation themselves, no need to modify dxt/dyt here */ + + if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { + /* color & normal */ + filterfunc(texres, curibuf, fx, fy, &AFD); + val1 = texres->tr + texres->tg + texres->tb; + filterfunc(&texr, curibuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 = texr.tr + texr.tg + texr.tb; + filterfunc(&texr, curibuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 = texr.tr + texr.tg + texr.tb; + /* don't switch x or y! */ + texres->nor[0] = val1 - val2; + texres->nor[1] = val1 - val3; + if (previbuf != curibuf) { /* interpolate */ + filterfunc(&texr, previbuf, fx, fy, &AFD); + /* rgb */ + texres->tr += levf * (texr.tr - texres->tr); + texres->tg += levf * (texr.tg - texres->tg); + texres->tb += levf * (texr.tb - texres->tb); + texres->ta += levf * (texr.ta - texres->ta); + /* normal */ + val1 += levf * ((texr.tr + texr.tg + texr.tb) - val1); + filterfunc(&texr, previbuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 += levf * ((texr.tr + texr.tg + texr.tb) - val2); + filterfunc(&texr, previbuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 += levf * ((texr.tr + texr.tg + texr.tb) - val3); + texres->nor[0] = val1 - val2; /* vals have been interpolated above! */ + texres->nor[1] = val1 - val3; + } + } + else { /* color */ + filterfunc(texres, curibuf, fx, fy, &AFD); + if (previbuf != curibuf) { /* interpolate */ + filterfunc(&texr, previbuf, fx, fy, &AFD); + texres->tr += levf * (texr.tr - texres->tr); + texres->tg += levf * (texr.tg - texres->tg); + texres->tb += levf * (texr.tb - texres->tb); + texres->ta += levf * (texr.ta - texres->ta); + } + + if (tex->texfilter != TXF_EWA) { + alpha_clip_aniso(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, extflag, texres); + } + } + } + else { /* no mipmap */ + /* filter functions take care of interpolation themselves, no need to modify dxt/dyt here */ + if (tex->texfilter == TXF_FELINE) { + const float ff = sqrtf(ibuf->x), q = ibuf->y / ff; + const float Ux = dxt[0] * ff, Vx = dxt[1] * q, Uy = dyt[0] * ff, Vy = dyt[1] * q; + const float A = Vx * Vx + Vy * Vy; + const float B = -2.f * (Ux * Vx + Uy * Vy); + const float C = Ux * Ux + Uy * Uy; + const float F = A * C - B * B * 0.25f; + float a, b, th, ecc, fProbes; + BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc); + a *= ff; + b *= ff; + a = max_ff(a, 1.0f); + b = max_ff(b, 1.0f); + fProbes = 2.f * (a / b) - 1.f; + /* no limit to number of Probes here */ + AFD.iProbes = round_fl_to_int(fProbes); + if (AFD.iProbes < fProbes) + b = 2.f * a / (float)(AFD.iProbes + 1); + AFD.majrad = a / ff; + AFD.minrad = b / ff; + AFD.theta = th; + AFD.dusc = 1.f / ff; + AFD.dvsc = ff / (float)ibuf->y; + } + if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { + /* color & normal */ + filterfunc(texres, ibuf, fx, fy, &AFD); + val1 = texres->tr + texres->tg + texres->tb; + filterfunc(&texr, ibuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 = texr.tr + texr.tg + texr.tb; + filterfunc(&texr, ibuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 = texr.tr + texr.tg + texr.tb; + /* don't switch x or y! */ + texres->nor[0] = val1 - val2; + texres->nor[1] = val1 - val3; + } + else { + filterfunc(texres, ibuf, fx, fy, &AFD); + if (tex->texfilter != TXF_EWA) { + alpha_clip_aniso(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, extflag, texres); + } + } + } + + if (tex->imaflag & TEX_CALCALPHA) + texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); + else + texres->tin = texres->ta; + if (tex->flag & TEX_NEGALPHA) + texres->ta = 1.f - texres->ta; + + if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { /* normal from color */ + /* The invert of the red channel is to make + * the normal map compliant with the outside world. + * It needs to be done because in Blender + * the normal used in the renderer points inward. It is generated + * this way in calc_vertexnormals(). Should this ever change + * this negate must be removed. */ + texres->nor[0] = -2.f * (texres->tr - 0.5f); + texres->nor[1] = 2.f * (texres->tg - 0.5f); + texres->nor[2] = 2.f * (texres->tb - 0.5f); + } + + /* de-premul, this is being premulled in shade_input_do_shade() + * TXF: this currently does not (yet?) work properly, destroys edge AA in clip/checker mode, so for now commented out + * also disabled in imagewraposa() to be able to compare results with blender's default texture filtering */ + + /* brecht: tried to fix this, see "TXF alpha" comments */ + + /* do not de-premul for generated alpha, it is already in straight */ + if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + fx = 1.f / texres->ta; + texres->tr *= fx; + texres->tg *= fx; + texres->tb *= fx; + } + + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + + BRICONTRGB; + + return retval; } - -int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const float DXT[2], const float DYT[2], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) +int imagewraposa(Tex *tex, + Image *ima, + ImBuf *ibuf, + const float texvec[3], + const float DXT[2], + const float DYT[2], + TexResult *texres, + struct ImagePool *pool, + const bool skip_load_image) { - TexResult texr; - float fx, fy, minx, maxx, miny, maxy, dx, dy, dxt[2], dyt[2]; - float maxd, pixsize, val1, val2, val3; - int curmap, retval, imaprepeat, imapextend; - - /* TXF: since dxt/dyt might be modified here and since they might be needed after imagewraposa() call, - * make a local copy here so that original vecs remain untouched */ - copy_v2_v2(dxt, DXT); - copy_v2_v2(dyt, DYT); - - /* anisotropic filtering */ - if (tex->texfilter != TXF_BOX) - return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image); - - texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; - - /* we need to set retval OK, otherwise texture code generates normals itself... */ - retval = texres->nor ? 3 : 1; - - /* quick tests */ - if (ibuf == NULL && ima == NULL) - return retval; - if (ima) { - - /* hack for icon render */ - if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) - return retval; - - ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); - - ima->flag |= IMA_USED_FOR_RENDER; - } - if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - - /* mipmap test */ - image_mipmap_test(tex, ibuf); - - if (ima) { - if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { - if ((tex->imaflag & TEX_CALCALPHA) == 0) { - texres->talpha = true; - } - } - } - - texr.talpha = texres->talpha; - - if (tex->imaflag & TEX_IMAROT) { - fy = texvec[0]; - fx = texvec[1]; - } - else { - fx = texvec[0]; - fy = texvec[1]; - } - - /* pixel coordinates */ - - minx = min_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); - maxx = max_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); - miny = min_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); - maxy = max_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); - - /* tex_sharper has been removed */ - minx = (maxx - minx) / 2.0f; - miny = (maxy - miny) / 2.0f; - - if (tex->imaflag & TEX_FILTER_MIN) { - /* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */ - float addval = (0.5f * tex->filtersize) / (float)MIN2(ibuf->x, ibuf->y); - - if (addval > minx) - minx = addval; - if (addval > miny) - miny = addval; - } - else if (tex->filtersize != 1.0f) { - minx *= tex->filtersize; - miny *= tex->filtersize; - - dxt[0] *= tex->filtersize; - dxt[1] *= tex->filtersize; - dyt[0] *= tex->filtersize; - dyt[1] *= tex->filtersize; - } - - if (tex->imaflag & TEX_IMAROT) SWAP(float, minx, miny); - - if (minx > 0.25f) minx = 0.25f; - else if (minx < 0.00001f) minx = 0.00001f; /* side faces of unit-cube */ - if (miny > 0.25f) miny = 0.25f; - else if (miny < 0.00001f) miny = 0.00001f; - - - /* repeat and clip */ - imaprepeat = (tex->extend == TEX_REPEAT); - imapextend = (tex->extend == TEX_EXTEND); - - if (tex->extend == TEX_REPEAT) { - if (tex->flag & (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) { - imaprepeat = 0; - imapextend = 1; - } - } - - if (tex->extend == TEX_CHECKER) { - int xs, ys, xs1, ys1, xs2, ys2, boundary; - - xs = (int)floor(fx); - ys = (int)floor(fy); - - /* both checkers available, no boundary exceptions, checkerdist will eat aliasing */ - if ( (tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN) ) { - fx -= xs; - fy -= ys; - } - else if ((tex->flag & TEX_CHECKER_ODD) == 0 && - (tex->flag & TEX_CHECKER_EVEN) == 0) - { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - else { - - xs1 = (int)floor(fx - minx); - ys1 = (int)floor(fy - miny); - xs2 = (int)floor(fx + minx); - ys2 = (int)floor(fy + miny); - boundary = (xs1 != xs2) || (ys1 != ys2); - - if (boundary == 0) { - if ( (tex->flag & TEX_CHECKER_ODD) == 0) { - if ((xs + ys) & 1) { - /* pass */ - } - else { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - if ( (tex->flag & TEX_CHECKER_EVEN) == 0) { - if ((xs + ys) & 1) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - fx -= xs; - fy -= ys; - } - else { - if (tex->flag & TEX_CHECKER_ODD) { - if ((xs1 + ys) & 1) fx -= xs2; - else fx -= xs1; - - if ((ys1 + xs) & 1) fy -= ys2; - else fy -= ys1; - } - if (tex->flag & TEX_CHECKER_EVEN) { - if ((xs1 + ys) & 1) fx -= xs1; - else fx -= xs2; - - if ((ys1 + xs) & 1) fy -= ys1; - else fy -= ys2; - } - } - } - - /* scale around center, (0.5, 0.5) */ - if (tex->checkerdist < 1.0f) { - fx = (fx - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; - fy = (fy - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; - minx /= (1.0f - tex->checkerdist); - miny /= (1.0f - tex->checkerdist); - } - } - - if (tex->extend == TEX_CLIPCUBE) { - if (fx + minx < 0.0f || fy + miny < 0.0f || fx - minx > 1.0f || fy - miny > 1.0f || texvec[2] < -1.0f || texvec[2] > 1.0f) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { - if (fx + minx < 0.0f || fy + miny < 0.0f || fx - minx > 1.0f || fy - miny > 1.0f) { - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - return retval; - } - } - else { - if (imapextend) { - if (fx > 1.0f) fx = 1.0f; - else if (fx < 0.0f) fx = 0.0f; - } - else { - if (fx > 1.0f) fx -= (int)(fx); - else if (fx < 0.0f) fx += 1 - (int)(fx); - } - - if (imapextend) { - if (fy > 1.0f) fy = 1.0f; - else if (fy < 0.0f) fy = 0.0f; - } - else { - if (fy > 1.0f) fy -= (int)(fy); - else if (fy < 0.0f) fy += 1 - (int)(fy); - } - } - - /* choice: */ - if (tex->imaflag & TEX_MIPMAP) { - ImBuf *previbuf, *curibuf; - float bumpscale; - - dx = minx; - dy = miny; - maxd = max_ff(dx, dy); - if (maxd > 0.5f) maxd = 0.5f; - - pixsize = 1.0f / (float)MIN2(ibuf->x, ibuf->y); - - bumpscale = pixsize / maxd; - if (bumpscale > 1.0f) bumpscale = 1.0f; - else bumpscale *= bumpscale; - - curmap = 0; - previbuf = curibuf = ibuf; - while (curmap < IMB_MIPMAP_LEVELS && ibuf->mipmap[curmap]) { - if (maxd < pixsize) break; - previbuf = curibuf; - curibuf = ibuf->mipmap[curmap]; - pixsize = 1.0f / (float)MIN2(curibuf->x, curibuf->y); - curmap++; - } - - if (previbuf != curibuf || (tex->imaflag & TEX_INTERPOL)) { - /* sample at least 1 pixel */ - if (minx < 0.5f / ibuf->x) minx = 0.5f / ibuf->x; - if (miny < 0.5f / ibuf->y) miny = 0.5f / ibuf->y; - } - - if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) { - /* a bit extra filter */ - //minx*= 1.35f; - //miny*= 1.35f; - - boxsample(curibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); - val1 = texres->tr + texres->tg + texres->tb; - boxsample(curibuf, fx - minx + dxt[0], fy - miny + dxt[1], fx + minx + dxt[0], fy + miny + dxt[1], &texr, imaprepeat, imapextend); - val2 = texr.tr + texr.tg + texr.tb; - boxsample(curibuf, fx - minx + dyt[0], fy - miny + dyt[1], fx + minx + dyt[0], fy + miny + dyt[1], &texr, imaprepeat, imapextend); - val3 = texr.tr + texr.tg + texr.tb; - - /* don't switch x or y! */ - texres->nor[0] = (val1 - val2); - texres->nor[1] = (val1 - val3); - - if (previbuf != curibuf) { /* interpolate */ - - boxsample(previbuf, fx - minx, fy - miny, fx + minx, fy + miny, &texr, imaprepeat, imapextend); - - /* calc rgb */ - dx = 2.0f * (pixsize - maxd) / pixsize; - if (dx >= 1.0f) { - texres->ta = texr.ta; texres->tb = texr.tb; - texres->tg = texr.tg; texres->tr = texr.tr; - } - else { - dy = 1.0f - dx; - texres->tb = dy * texres->tb + dx * texr.tb; - texres->tg = dy * texres->tg + dx * texr.tg; - texres->tr = dy * texres->tr + dx * texr.tr; - texres->ta = dy * texres->ta + dx * texr.ta; - } - - val1 = dy * val1 + dx * (texr.tr + texr.tg + texr.tb); - boxsample(previbuf, fx - minx + dxt[0], fy - miny + dxt[1], fx + minx + dxt[0], fy + miny + dxt[1], &texr, imaprepeat, imapextend); - val2 = dy * val2 + dx * (texr.tr + texr.tg + texr.tb); - boxsample(previbuf, fx - minx + dyt[0], fy - miny + dyt[1], fx + minx + dyt[0], fy + miny + dyt[1], &texr, imaprepeat, imapextend); - val3 = dy * val3 + dx * (texr.tr + texr.tg + texr.tb); - - texres->nor[0] = (val1 - val2); /* vals have been interpolated above! */ - texres->nor[1] = (val1 - val3); - - if (dx < 1.0f) { - dy = 1.0f - dx; - texres->tb = dy * texres->tb + dx * texr.tb; - texres->tg = dy * texres->tg + dx * texr.tg; - texres->tr = dy * texres->tr + dx * texr.tr; - texres->ta = dy * texres->ta + dx * texr.ta; - } - } - texres->nor[0] *= bumpscale; - texres->nor[1] *= bumpscale; - } - else { - maxx = fx + minx; - minx = fx - minx; - maxy = fy + miny; - miny = fy - miny; - - boxsample(curibuf, minx, miny, maxx, maxy, texres, imaprepeat, imapextend); - - if (previbuf != curibuf) { /* interpolate */ - boxsample(previbuf, minx, miny, maxx, maxy, &texr, imaprepeat, imapextend); - - fx = 2.0f * (pixsize - maxd) / pixsize; - - if (fx >= 1.0f) { - texres->ta = texr.ta; texres->tb = texr.tb; - texres->tg = texr.tg; texres->tr = texr.tr; - } - else { - fy = 1.0f - fx; - texres->tb = fy * texres->tb + fx * texr.tb; - texres->tg = fy * texres->tg + fx * texr.tg; - texres->tr = fy * texres->tr + fx * texr.tr; - texres->ta = fy * texres->ta + fx * texr.ta; - } - } - } - } - else { - const int intpol = tex->imaflag & TEX_INTERPOL; - if (intpol) { - /* sample 1 pixel minimum */ - if (minx < 0.5f / ibuf->x) minx = 0.5f / ibuf->x; - if (miny < 0.5f / ibuf->y) miny = 0.5f / ibuf->y; - } - - if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) { - boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); - val1 = texres->tr + texres->tg + texres->tb; - boxsample(ibuf, fx - minx + dxt[0], fy - miny + dxt[1], fx + minx + dxt[0], fy + miny + dxt[1], &texr, imaprepeat, imapextend); - val2 = texr.tr + texr.tg + texr.tb; - boxsample(ibuf, fx - minx + dyt[0], fy - miny + dyt[1], fx + minx + dyt[0], fy + miny + dyt[1], &texr, imaprepeat, imapextend); - val3 = texr.tr + texr.tg + texr.tb; - - /* don't switch x or y! */ - texres->nor[0] = (val1 - val2); - texres->nor[1] = (val1 - val3); - } - else - boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); - } - - if (tex->imaflag & TEX_CALCALPHA) { - texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); - } - else { - texres->tin = texres->ta; - } - - if (tex->flag & TEX_NEGALPHA) texres->ta = 1.0f - texres->ta; - - if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { - /* qdn: normal from color - * The invert of the red channel is to make - * the normal map compliant with the outside world. - * It needs to be done because in Blender - * the normal used in the renderer points inward. It is generated - * this way in calc_vertexnormals(). Should this ever change - * this negate must be removed. */ - texres->nor[0] = -2.f * (texres->tr - 0.5f); - texres->nor[1] = 2.f * (texres->tg - 0.5f); - texres->nor[2] = 2.f * (texres->tb - 0.5f); - } - - /* de-premul, this is being premulled in shade_input_do_shade() */ - /* do not de-premul for generated alpha, it is already in straight */ - if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { - mul_v3_fl(&texres->tr, 1.0f / texres->ta); - } - - if (ima) - BKE_image_pool_release_ibuf(ima, ibuf, pool); - - BRICONTRGB; - - return retval; + TexResult texr; + float fx, fy, minx, maxx, miny, maxy, dx, dy, dxt[2], dyt[2]; + float maxd, pixsize, val1, val2, val3; + int curmap, retval, imaprepeat, imapextend; + + /* TXF: since dxt/dyt might be modified here and since they might be needed after imagewraposa() call, + * make a local copy here so that original vecs remain untouched */ + copy_v2_v2(dxt, DXT); + copy_v2_v2(dyt, DYT); + + /* anisotropic filtering */ + if (tex->texfilter != TXF_BOX) + return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image); + + texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.0f; + + /* we need to set retval OK, otherwise texture code generates normals itself... */ + retval = texres->nor ? 3 : 1; + + /* quick tests */ + if (ibuf == NULL && ima == NULL) + return retval; + if (ima) { + + /* hack for icon render */ + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) + return retval; + + ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); + + ima->flag |= IMA_USED_FOR_RENDER; + } + if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + + /* mipmap test */ + image_mipmap_test(tex, ibuf); + + if (ima) { + if ((tex->imaflag & TEX_USEALPHA) && (ima->flag & IMA_IGNORE_ALPHA) == 0) { + if ((tex->imaflag & TEX_CALCALPHA) == 0) { + texres->talpha = true; + } + } + } + + texr.talpha = texres->talpha; + + if (tex->imaflag & TEX_IMAROT) { + fy = texvec[0]; + fx = texvec[1]; + } + else { + fx = texvec[0]; + fy = texvec[1]; + } + + /* pixel coordinates */ + + minx = min_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); + maxx = max_fff(dxt[0], dyt[0], dxt[0] + dyt[0]); + miny = min_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); + maxy = max_fff(dxt[1], dyt[1], dxt[1] + dyt[1]); + + /* tex_sharper has been removed */ + minx = (maxx - minx) / 2.0f; + miny = (maxy - miny) / 2.0f; + + if (tex->imaflag & TEX_FILTER_MIN) { + /* make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) */ + float addval = (0.5f * tex->filtersize) / (float)MIN2(ibuf->x, ibuf->y); + + if (addval > minx) + minx = addval; + if (addval > miny) + miny = addval; + } + else if (tex->filtersize != 1.0f) { + minx *= tex->filtersize; + miny *= tex->filtersize; + + dxt[0] *= tex->filtersize; + dxt[1] *= tex->filtersize; + dyt[0] *= tex->filtersize; + dyt[1] *= tex->filtersize; + } + + if (tex->imaflag & TEX_IMAROT) + SWAP(float, minx, miny); + + if (minx > 0.25f) + minx = 0.25f; + else if (minx < 0.00001f) + minx = 0.00001f; /* side faces of unit-cube */ + if (miny > 0.25f) + miny = 0.25f; + else if (miny < 0.00001f) + miny = 0.00001f; + + /* repeat and clip */ + imaprepeat = (tex->extend == TEX_REPEAT); + imapextend = (tex->extend == TEX_EXTEND); + + if (tex->extend == TEX_REPEAT) { + if (tex->flag & (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) { + imaprepeat = 0; + imapextend = 1; + } + } + + if (tex->extend == TEX_CHECKER) { + int xs, ys, xs1, ys1, xs2, ys2, boundary; + + xs = (int)floor(fx); + ys = (int)floor(fy); + + /* both checkers available, no boundary exceptions, checkerdist will eat aliasing */ + if ((tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN)) { + fx -= xs; + fy -= ys; + } + else if ((tex->flag & TEX_CHECKER_ODD) == 0 && (tex->flag & TEX_CHECKER_EVEN) == 0) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + else { + + xs1 = (int)floor(fx - minx); + ys1 = (int)floor(fy - miny); + xs2 = (int)floor(fx + minx); + ys2 = (int)floor(fy + miny); + boundary = (xs1 != xs2) || (ys1 != ys2); + + if (boundary == 0) { + if ((tex->flag & TEX_CHECKER_ODD) == 0) { + if ((xs + ys) & 1) { + /* pass */ + } + else { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + if ((tex->flag & TEX_CHECKER_EVEN) == 0) { + if ((xs + ys) & 1) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + fx -= xs; + fy -= ys; + } + else { + if (tex->flag & TEX_CHECKER_ODD) { + if ((xs1 + ys) & 1) + fx -= xs2; + else + fx -= xs1; + + if ((ys1 + xs) & 1) + fy -= ys2; + else + fy -= ys1; + } + if (tex->flag & TEX_CHECKER_EVEN) { + if ((xs1 + ys) & 1) + fx -= xs1; + else + fx -= xs2; + + if ((ys1 + xs) & 1) + fy -= ys1; + else + fy -= ys2; + } + } + } + + /* scale around center, (0.5, 0.5) */ + if (tex->checkerdist < 1.0f) { + fx = (fx - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; + fy = (fy - 0.5f) / (1.0f - tex->checkerdist) + 0.5f; + minx /= (1.0f - tex->checkerdist); + miny /= (1.0f - tex->checkerdist); + } + } + + if (tex->extend == TEX_CLIPCUBE) { + if (fx + minx < 0.0f || fy + miny < 0.0f || fx - minx > 1.0f || fy - miny > 1.0f || + texvec[2] < -1.0f || texvec[2] > 1.0f) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { + if (fx + minx < 0.0f || fy + miny < 0.0f || fx - minx > 1.0f || fy - miny > 1.0f) { + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + return retval; + } + } + else { + if (imapextend) { + if (fx > 1.0f) + fx = 1.0f; + else if (fx < 0.0f) + fx = 0.0f; + } + else { + if (fx > 1.0f) + fx -= (int)(fx); + else if (fx < 0.0f) + fx += 1 - (int)(fx); + } + + if (imapextend) { + if (fy > 1.0f) + fy = 1.0f; + else if (fy < 0.0f) + fy = 0.0f; + } + else { + if (fy > 1.0f) + fy -= (int)(fy); + else if (fy < 0.0f) + fy += 1 - (int)(fy); + } + } + + /* choice: */ + if (tex->imaflag & TEX_MIPMAP) { + ImBuf *previbuf, *curibuf; + float bumpscale; + + dx = minx; + dy = miny; + maxd = max_ff(dx, dy); + if (maxd > 0.5f) + maxd = 0.5f; + + pixsize = 1.0f / (float)MIN2(ibuf->x, ibuf->y); + + bumpscale = pixsize / maxd; + if (bumpscale > 1.0f) + bumpscale = 1.0f; + else + bumpscale *= bumpscale; + + curmap = 0; + previbuf = curibuf = ibuf; + while (curmap < IMB_MIPMAP_LEVELS && ibuf->mipmap[curmap]) { + if (maxd < pixsize) + break; + previbuf = curibuf; + curibuf = ibuf->mipmap[curmap]; + pixsize = 1.0f / (float)MIN2(curibuf->x, curibuf->y); + curmap++; + } + + if (previbuf != curibuf || (tex->imaflag & TEX_INTERPOL)) { + /* sample at least 1 pixel */ + if (minx < 0.5f / ibuf->x) + minx = 0.5f / ibuf->x; + if (miny < 0.5f / ibuf->y) + miny = 0.5f / ibuf->y; + } + + if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) { + /* a bit extra filter */ + //minx*= 1.35f; + //miny*= 1.35f; + + boxsample( + curibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); + val1 = texres->tr + texres->tg + texres->tb; + boxsample(curibuf, + fx - minx + dxt[0], + fy - miny + dxt[1], + fx + minx + dxt[0], + fy + miny + dxt[1], + &texr, + imaprepeat, + imapextend); + val2 = texr.tr + texr.tg + texr.tb; + boxsample(curibuf, + fx - minx + dyt[0], + fy - miny + dyt[1], + fx + minx + dyt[0], + fy + miny + dyt[1], + &texr, + imaprepeat, + imapextend); + val3 = texr.tr + texr.tg + texr.tb; + + /* don't switch x or y! */ + texres->nor[0] = (val1 - val2); + texres->nor[1] = (val1 - val3); + + if (previbuf != curibuf) { /* interpolate */ + + boxsample( + previbuf, fx - minx, fy - miny, fx + minx, fy + miny, &texr, imaprepeat, imapextend); + + /* calc rgb */ + dx = 2.0f * (pixsize - maxd) / pixsize; + if (dx >= 1.0f) { + texres->ta = texr.ta; + texres->tb = texr.tb; + texres->tg = texr.tg; + texres->tr = texr.tr; + } + else { + dy = 1.0f - dx; + texres->tb = dy * texres->tb + dx * texr.tb; + texres->tg = dy * texres->tg + dx * texr.tg; + texres->tr = dy * texres->tr + dx * texr.tr; + texres->ta = dy * texres->ta + dx * texr.ta; + } + + val1 = dy * val1 + dx * (texr.tr + texr.tg + texr.tb); + boxsample(previbuf, + fx - minx + dxt[0], + fy - miny + dxt[1], + fx + minx + dxt[0], + fy + miny + dxt[1], + &texr, + imaprepeat, + imapextend); + val2 = dy * val2 + dx * (texr.tr + texr.tg + texr.tb); + boxsample(previbuf, + fx - minx + dyt[0], + fy - miny + dyt[1], + fx + minx + dyt[0], + fy + miny + dyt[1], + &texr, + imaprepeat, + imapextend); + val3 = dy * val3 + dx * (texr.tr + texr.tg + texr.tb); + + texres->nor[0] = (val1 - val2); /* vals have been interpolated above! */ + texres->nor[1] = (val1 - val3); + + if (dx < 1.0f) { + dy = 1.0f - dx; + texres->tb = dy * texres->tb + dx * texr.tb; + texres->tg = dy * texres->tg + dx * texr.tg; + texres->tr = dy * texres->tr + dx * texr.tr; + texres->ta = dy * texres->ta + dx * texr.ta; + } + } + texres->nor[0] *= bumpscale; + texres->nor[1] *= bumpscale; + } + else { + maxx = fx + minx; + minx = fx - minx; + maxy = fy + miny; + miny = fy - miny; + + boxsample(curibuf, minx, miny, maxx, maxy, texres, imaprepeat, imapextend); + + if (previbuf != curibuf) { /* interpolate */ + boxsample(previbuf, minx, miny, maxx, maxy, &texr, imaprepeat, imapextend); + + fx = 2.0f * (pixsize - maxd) / pixsize; + + if (fx >= 1.0f) { + texres->ta = texr.ta; + texres->tb = texr.tb; + texres->tg = texr.tg; + texres->tr = texr.tr; + } + else { + fy = 1.0f - fx; + texres->tb = fy * texres->tb + fx * texr.tb; + texres->tg = fy * texres->tg + fx * texr.tg; + texres->tr = fy * texres->tr + fx * texr.tr; + texres->ta = fy * texres->ta + fx * texr.ta; + } + } + } + } + else { + const int intpol = tex->imaflag & TEX_INTERPOL; + if (intpol) { + /* sample 1 pixel minimum */ + if (minx < 0.5f / ibuf->x) + minx = 0.5f / ibuf->x; + if (miny < 0.5f / ibuf->y) + miny = 0.5f / ibuf->y; + } + + if (texres->nor && (tex->imaflag & TEX_NORMALMAP) == 0) { + boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); + val1 = texres->tr + texres->tg + texres->tb; + boxsample(ibuf, + fx - minx + dxt[0], + fy - miny + dxt[1], + fx + minx + dxt[0], + fy + miny + dxt[1], + &texr, + imaprepeat, + imapextend); + val2 = texr.tr + texr.tg + texr.tb; + boxsample(ibuf, + fx - minx + dyt[0], + fy - miny + dyt[1], + fx + minx + dyt[0], + fy + miny + dyt[1], + &texr, + imaprepeat, + imapextend); + val3 = texr.tr + texr.tg + texr.tb; + + /* don't switch x or y! */ + texres->nor[0] = (val1 - val2); + texres->nor[1] = (val1 - val3); + } + else + boxsample(ibuf, fx - minx, fy - miny, fx + minx, fy + miny, texres, imaprepeat, imapextend); + } + + if (tex->imaflag & TEX_CALCALPHA) { + texres->ta = texres->tin = texres->ta * max_fff(texres->tr, texres->tg, texres->tb); + } + else { + texres->tin = texres->ta; + } + + if (tex->flag & TEX_NEGALPHA) + texres->ta = 1.0f - texres->ta; + + if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { + /* qdn: normal from color + * The invert of the red channel is to make + * the normal map compliant with the outside world. + * It needs to be done because in Blender + * the normal used in the renderer points inward. It is generated + * this way in calc_vertexnormals(). Should this ever change + * this negate must be removed. */ + texres->nor[0] = -2.f * (texres->tr - 0.5f); + texres->nor[1] = 2.f * (texres->tg - 0.5f); + texres->nor[2] = 2.f * (texres->tb - 0.5f); + } + + /* de-premul, this is being premulled in shade_input_do_shade() */ + /* do not de-premul for generated alpha, it is already in straight */ + if (texres->ta != 1.0f && texres->ta > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) { + mul_v3_fl(&texres->tr, 1.0f / texres->ta); + } + + if (ima) + BKE_image_pool_release_ibuf(ima, ibuf, pool); + + BRICONTRGB; + + return retval; } -void image_sample(Image *ima, float fx, float fy, float dx, float dy, float result[4], struct ImagePool *pool) +void image_sample( + Image *ima, float fx, float fy, float dx, float dy, float result[4], struct ImagePool *pool) { - TexResult texres; - ImBuf *ibuf = BKE_image_pool_acquire_ibuf(ima, NULL, pool); + TexResult texres; + ImBuf *ibuf = BKE_image_pool_acquire_ibuf(ima, NULL, pool); - if (UNLIKELY(ibuf == NULL)) { - zero_v4(result); - return; - } + if (UNLIKELY(ibuf == NULL)) { + zero_v4(result); + return; + } - texres.talpha = true; /* boxsample expects to be initialized */ - boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1); - copy_v4_v4(result, &texres.tr); + texres.talpha = true; /* boxsample expects to be initialized */ + boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1); + copy_v4_v4(result, &texres.tr); - ima->flag |= IMA_USED_FOR_RENDER; + ima->flag |= IMA_USED_FOR_RENDER; - BKE_image_pool_release_ibuf(ima, ibuf, pool); + BKE_image_pool_release_ibuf(ima, ibuf, pool); } void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float result[4]) { - TexResult texres = {0}; - afdata_t AFD; + TexResult texres = {0}; + afdata_t AFD; - AFD.dxt[0] = dx; AFD.dxt[1] = dx; - AFD.dyt[0] = dy; AFD.dyt[1] = dy; - //copy_v2_v2(AFD.dxt, dx); - //copy_v2_v2(AFD.dyt, dy); + AFD.dxt[0] = dx; + AFD.dxt[1] = dx; + AFD.dyt[0] = dy; + AFD.dyt[1] = dy; + //copy_v2_v2(AFD.dxt, dx); + //copy_v2_v2(AFD.dyt, dy); - AFD.intpol = 1; - AFD.extflag = TXC_EXTD; + AFD.intpol = 1; + AFD.extflag = TXC_EXTD; - ewa_eval(&texres, ibuf, fx, fy, &AFD); + ewa_eval(&texres, ibuf, fx, fy, &AFD); - copy_v4_v4(result, &texres.tr); + copy_v4_v4(result, &texres.tr); } diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index ff0d2867426..e23d839297a 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -45,253 +45,266 @@ /* Own includes */ #include "initrender.h" - /* ****************** MASKS and LUTS **************** */ static float filt_quadratic(float x) { - if (x < 0.0f) x = -x; - if (x < 0.5f) return 0.75f - (x * x); - if (x < 1.5f) return 0.50f * (x - 1.5f) * (x - 1.5f); - return 0.0f; + if (x < 0.0f) + x = -x; + if (x < 0.5f) + return 0.75f - (x * x); + if (x < 1.5f) + return 0.50f * (x - 1.5f) * (x - 1.5f); + return 0.0f; } - static float filt_cubic(float x) { - float x2 = x * x; + float x2 = x * x; - if (x < 0.0f) x = -x; + if (x < 0.0f) + x = -x; - if (x < 1.0f) return 0.5f * x * x2 - x2 + 2.0f / 3.0f; - if (x < 2.0f) return (2.0f - x) * (2.0f - x) * (2.0f - x) / 6.0f; - return 0.0f; + if (x < 1.0f) + return 0.5f * x * x2 - x2 + 2.0f / 3.0f; + if (x < 2.0f) + return (2.0f - x) * (2.0f - x) * (2.0f - x) / 6.0f; + return 0.0f; } - static float filt_catrom(float x) { - float x2 = x * x; - - if (x < 0.0f) x = -x; - if (x < 1.0f) return 1.5f * x2 * x - 2.5f * x2 + 1.0f; - if (x < 2.0f) return -0.5f * x2 * x + 2.5f * x2 - 4.0f * x + 2.0f; - return 0.0f; + float x2 = x * x; + + if (x < 0.0f) + x = -x; + if (x < 1.0f) + return 1.5f * x2 * x - 2.5f * x2 + 1.0f; + if (x < 2.0f) + return -0.5f * x2 * x + 2.5f * x2 - 4.0f * x + 2.0f; + return 0.0f; } static float filt_mitchell(float x) /* Mitchell & Netravali's two-param cubic */ { - float b = 1.0f / 3.0f, c = 1.0f / 3.0f; - float p0 = ( 6.0f - 2.0f * b) / 6.0f; - float p2 = (-18.0f + 12.0f * b + 6.0f * c) / 6.0f; - float p3 = ( 12.0f - 9.0f * b - 6.0f * c) / 6.0f; - float q0 = ( 8.0f * b + 24.0f * c) / 6.0f; - float q1 = ( -12.0f * b - 48.0f * c) / 6.0f; - float q2 = ( 6.0f * b + 30.0f * c) / 6.0f; - float q3 = ( -b - 6.0f * c) / 6.0f; - - if (x < -2.0f) return 0.0f; - if (x < -1.0f) return (q0 - x * (q1 - x * (q2 - x * q3))); - if (x < 0.0f) return (p0 + x * x * (p2 - x * p3)); - if (x < 1.0f) return (p0 + x * x * (p2 + x * p3)); - if (x < 2.0f) return (q0 + x * (q1 + x * (q2 + x * q3))); - return 0.0f; + float b = 1.0f / 3.0f, c = 1.0f / 3.0f; + float p0 = (6.0f - 2.0f * b) / 6.0f; + float p2 = (-18.0f + 12.0f * b + 6.0f * c) / 6.0f; + float p3 = (12.0f - 9.0f * b - 6.0f * c) / 6.0f; + float q0 = (8.0f * b + 24.0f * c) / 6.0f; + float q1 = (-12.0f * b - 48.0f * c) / 6.0f; + float q2 = (6.0f * b + 30.0f * c) / 6.0f; + float q3 = (-b - 6.0f * c) / 6.0f; + + if (x < -2.0f) + return 0.0f; + if (x < -1.0f) + return (q0 - x * (q1 - x * (q2 - x * q3))); + if (x < 0.0f) + return (p0 + x * x * (p2 - x * p3)); + if (x < 1.0f) + return (p0 + x * x * (p2 + x * p3)); + if (x < 2.0f) + return (q0 + x * (q1 + x * (q2 + x * q3))); + return 0.0f; } /* x ranges from -1 to 1 */ float RE_filter_value(int type, float x) { - float gaussfac = 1.6f; + float gaussfac = 1.6f; - x = ABS(x); + x = ABS(x); - switch (type) { - case R_FILTER_BOX: - if (x > 1.0f) return 0.0f; - return 1.0f; + switch (type) { + case R_FILTER_BOX: + if (x > 1.0f) + return 0.0f; + return 1.0f; - case R_FILTER_TENT: - if (x > 1.0f) return 0.0f; - return 1.0f - x; + case R_FILTER_TENT: + if (x > 1.0f) + return 0.0f; + return 1.0f - x; - case R_FILTER_GAUSS: - { - const float two_gaussfac2 = 2.0f * gaussfac * gaussfac; - x *= 3.0f * gaussfac; - return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x * x / two_gaussfac2); - } + case R_FILTER_GAUSS: { + const float two_gaussfac2 = 2.0f * gaussfac * gaussfac; + x *= 3.0f * gaussfac; + return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x * x / two_gaussfac2); + } - case R_FILTER_MITCH: - return filt_mitchell(x * gaussfac); + case R_FILTER_MITCH: + return filt_mitchell(x * gaussfac); - case R_FILTER_QUAD: - return filt_quadratic(x * gaussfac); + case R_FILTER_QUAD: + return filt_quadratic(x * gaussfac); - case R_FILTER_CUBIC: - return filt_cubic(x * gaussfac); + case R_FILTER_CUBIC: + return filt_cubic(x * gaussfac); - case R_FILTER_CATROM: - return filt_catrom(x * gaussfac); - } - return 0.0f; + case R_FILTER_CATROM: + return filt_catrom(x * gaussfac); + } + return 0.0f; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ struct Object *RE_GetCamera(Render *re) { - Object *camera = re->camera_override ? re->camera_override : re->scene->camera; - return BKE_camera_multiview_render(re->scene, camera, re->viewname); + Object *camera = re->camera_override ? re->camera_override : re->scene->camera; + return BKE_camera_multiview_render(re->scene, camera, re->viewname); } static void re_camera_params_get(Render *re, CameraParams *params) { - copy_m4_m4(re->winmat, params->winmat); + copy_m4_m4(re->winmat, params->winmat); - re->clip_start = params->clip_start; - re->clip_end = params->clip_end; + re->clip_start = params->clip_start; + re->clip_end = params->clip_end; - re->viewplane = params->viewplane; + re->viewplane = params->viewplane; } void RE_SetOverrideCamera(Render *re, Object *camera) { - re->camera_override = camera; + re->camera_override = camera; } static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob) { - BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); + BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); } /* call this after InitState() */ /* per render, there's one persistent viewplane. Parts will set their own viewplanes */ void RE_SetCamera(Render *re, Object *cam_ob) { - CameraParams params; + CameraParams params; - /* setup parameters */ - BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, cam_ob); - re_camera_params_stereo3d(re, ¶ms, cam_ob); + /* setup parameters */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, cam_ob); + re_camera_params_stereo3d(re, ¶ms, cam_ob); - /* compute matrix, viewplane, .. */ - BKE_camera_params_compute_viewplane(¶ms, re->winx, re->winy, re->r.xasp, re->r.yasp); - BKE_camera_params_compute_matrix(¶ms); + /* compute matrix, viewplane, .. */ + BKE_camera_params_compute_viewplane(¶ms, re->winx, re->winy, re->r.xasp, re->r.yasp); + BKE_camera_params_compute_matrix(¶ms); - /* extract results */ - re_camera_params_get(re, ¶ms); + /* extract results */ + re_camera_params_get(re, ¶ms); } void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, float mat[4][4]) { - re->r.cfra = frame; - RE_SetCamera(re, camera); - copy_m4_m4(mat, re->winmat); + re->r.cfra = frame; + RE_SetCamera(re, camera); + copy_m4_m4(mat, re->winmat); } /* Must be called after RE_GetCameraWindow(), does not change re->winmat. */ void RE_GetCameraWindowWithOverscan(struct Render *re, float mat[4][4], float overscan) { - CameraParams params; - params.is_ortho = re->winmat[3][3] != 0.0f; - params.clip_start = re->clip_start; - params.clip_end = re->clip_end; - params.viewplane = re->viewplane; - - overscan *= max_ff(BLI_rctf_size_x(¶ms.viewplane), BLI_rctf_size_y(¶ms.viewplane)); - - params.viewplane.xmin -= overscan; - params.viewplane.xmax += overscan; - params.viewplane.ymin -= overscan; - params.viewplane.ymax += overscan; - BKE_camera_params_compute_matrix(¶ms); - copy_m4_m4(mat, params.winmat); + CameraParams params; + params.is_ortho = re->winmat[3][3] != 0.0f; + params.clip_start = re->clip_start; + params.clip_end = re->clip_end; + params.viewplane = re->viewplane; + + overscan *= max_ff(BLI_rctf_size_x(¶ms.viewplane), BLI_rctf_size_y(¶ms.viewplane)); + + params.viewplane.xmin -= overscan; + params.viewplane.xmax += overscan; + params.viewplane.ymin -= overscan; + params.viewplane.ymax += overscan; + BKE_camera_params_compute_matrix(¶ms); + copy_m4_m4(mat, params.winmat); } void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_mat[4][4]) { - BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat); + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat); } /* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ - void RE_parts_free(Render *re) { - BLI_freelistN(&re->parts); + BLI_freelistN(&re->parts); } void RE_parts_clamp(Render *re) { - /* part size */ - re->partx = max_ii(1, min_ii(re->r.tilex, re->rectx)); - re->party = max_ii(1, min_ii(re->r.tiley, re->recty)); + /* part size */ + re->partx = max_ii(1, min_ii(re->r.tilex, re->rectx)); + re->party = max_ii(1, min_ii(re->r.tiley, re->recty)); } void RE_parts_init(Render *re) { - int nr, xd, yd, partx, party, xparts, yparts; - int xminb, xmaxb, yminb, ymaxb; - - RE_parts_free(re); - - /* this is render info for caller, is not reset when parts are freed! */ - re->i.totpart = 0; - re->i.curpart = 0; - re->i.partsdone = 0; - - /* just for readable code.. */ - xminb = re->disprect.xmin; - yminb = re->disprect.ymin; - xmaxb = re->disprect.xmax; - ymaxb = re->disprect.ymax; - - RE_parts_clamp(re); - - partx = re->partx; - party = re->party; - /* part count */ - xparts = (re->rectx + partx - 1) / partx; - yparts = (re->recty + party - 1) / party; - - for (nr = 0; nr < xparts * yparts; nr++) { - rcti disprect; - int rectx, recty; - - xd = (nr % xparts); - yd = (nr - xd) / xparts; - - disprect.xmin = xminb + xd * partx; - disprect.ymin = yminb + yd * party; - - /* ensure we cover the entire picture, so last parts go to end */ - if (xd < xparts - 1) { - disprect.xmax = disprect.xmin + partx; - if (disprect.xmax > xmaxb) - disprect.xmax = xmaxb; - } - else disprect.xmax = xmaxb; - - if (yd < yparts - 1) { - disprect.ymax = disprect.ymin + party; - if (disprect.ymax > ymaxb) - disprect.ymax = ymaxb; - } - else disprect.ymax = ymaxb; - - rectx = BLI_rcti_size_x(&disprect); - recty = BLI_rcti_size_y(&disprect); - - /* so, now can we add this part? */ - if (rectx > 0 && recty > 0) { - RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part"); - - pa->disprect = disprect; - pa->rectx = rectx; - pa->recty = recty; - - BLI_addtail(&re->parts, pa); - re->i.totpart++; - } - } + int nr, xd, yd, partx, party, xparts, yparts; + int xminb, xmaxb, yminb, ymaxb; + + RE_parts_free(re); + + /* this is render info for caller, is not reset when parts are freed! */ + re->i.totpart = 0; + re->i.curpart = 0; + re->i.partsdone = 0; + + /* just for readable code.. */ + xminb = re->disprect.xmin; + yminb = re->disprect.ymin; + xmaxb = re->disprect.xmax; + ymaxb = re->disprect.ymax; + + RE_parts_clamp(re); + + partx = re->partx; + party = re->party; + /* part count */ + xparts = (re->rectx + partx - 1) / partx; + yparts = (re->recty + party - 1) / party; + + for (nr = 0; nr < xparts * yparts; nr++) { + rcti disprect; + int rectx, recty; + + xd = (nr % xparts); + yd = (nr - xd) / xparts; + + disprect.xmin = xminb + xd * partx; + disprect.ymin = yminb + yd * party; + + /* ensure we cover the entire picture, so last parts go to end */ + if (xd < xparts - 1) { + disprect.xmax = disprect.xmin + partx; + if (disprect.xmax > xmaxb) + disprect.xmax = xmaxb; + } + else + disprect.xmax = xmaxb; + + if (yd < yparts - 1) { + disprect.ymax = disprect.ymin + party; + if (disprect.ymax > ymaxb) + disprect.ymax = ymaxb; + } + else + disprect.ymax = ymaxb; + + rectx = BLI_rcti_size_x(&disprect); + recty = BLI_rcti_size_y(&disprect); + + /* so, now can we add this part? */ + if (rectx > 0 && recty > 0) { + RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part"); + + pa->disprect = disprect; + pa->rectx = rectx; + pa->recty = recty; + + BLI_addtail(&re->parts, pa); + re->i.totpart++; + } + } } diff --git a/source/blender/render/intern/source/multires_bake.c b/source/blender/render/intern/source/multires_bake.c index 8a8e4bbbb2a..1358b7a9438 100644 --- a/source/blender/render/intern/source/multires_bake.c +++ b/source/blender/render/intern/source/multires_bake.c @@ -51,340 +51,377 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" -typedef void (*MPassKnownData)(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void *thread_data, - void *bake_data, ImBuf *ibuf, const int face_index, const int lvl, - const float st[2], float tangmat[3][3], const int x, const int y); - -typedef void * (*MInitBakeData)(MultiresBakeRender *bkr, Image *ima); -typedef void (*MFreeBakeData)(void *bake_data); +typedef void (*MPassKnownData)(DerivedMesh *lores_dm, + DerivedMesh *hires_dm, + void *thread_data, + void *bake_data, + ImBuf *ibuf, + const int face_index, + const int lvl, + const float st[2], + float tangmat[3][3], + const int x, + const int y); + +typedef void *(*MInitBakeData)(MultiresBakeRender *bkr, Image *ima); +typedef void (*MFreeBakeData)(void *bake_data); typedef struct MultiresBakeResult { - float height_min, height_max; + float height_min, height_max; } MultiresBakeResult; typedef struct { - MVert *mvert; - MPoly *mpoly; - MLoop *mloop; - MLoopUV *mloopuv; - const MLoopTri *mlooptri; - float *pvtangent; - const float *precomputed_normals; - int w, h; - int tri_index; - DerivedMesh *lores_dm, *hires_dm; - int lvl; - void *thread_data; - void *bake_data; - ImBuf *ibuf; - MPassKnownData pass_data; - /* material aligned UV array */ - Image **image_array; + MVert *mvert; + MPoly *mpoly; + MLoop *mloop; + MLoopUV *mloopuv; + const MLoopTri *mlooptri; + float *pvtangent; + const float *precomputed_normals; + int w, h; + int tri_index; + DerivedMesh *lores_dm, *hires_dm; + int lvl; + void *thread_data; + void *bake_data; + ImBuf *ibuf; + MPassKnownData pass_data; + /* material aligned UV array */ + Image **image_array; } MResolvePixelData; typedef void (*MFlushPixel)(const MResolvePixelData *data, const int x, const int y); typedef struct { - int w, h; - char *texels; - const MResolvePixelData *data; - MFlushPixel flush_pixel; - short *do_update; + int w, h; + char *texels; + const MResolvePixelData *data; + MFlushPixel flush_pixel; + short *do_update; } MBakeRast; typedef struct { - float *heights; - Image *ima; - DerivedMesh *ssdm; - const int *orig_index_mp_to_orig; + float *heights; + Image *ima; + DerivedMesh *ssdm; + const int *orig_index_mp_to_orig; } MHeightBakeData; typedef struct { - const int *orig_index_mp_to_orig; + const int *orig_index_mp_to_orig; } MNormalBakeData; -static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int tri_num, const int vert_index) +static void multiresbake_get_normal(const MResolvePixelData *data, + float norm[], + const int tri_num, + const int vert_index) { - const int poly_index = data->mlooptri[tri_num].poly; - const MPoly *mp = &data->mpoly[poly_index]; - const bool smoothnormal = (mp->flag & ME_SMOOTH) != 0; - - if (!smoothnormal) { /* flat */ - if (data->precomputed_normals) { - copy_v3_v3(norm, &data->precomputed_normals[poly_index]); - } - else { - BKE_mesh_calc_poly_normal(mp, &data->mloop[mp->loopstart], data->mvert, norm); - } - } - else { - const int vi = data->mloop[data->mlooptri[tri_num].tri[vert_index]].v; - const short *no = data->mvert[vi].no; - - normal_short_to_float_v3(norm, no); - normalize_v3(norm); - } + const int poly_index = data->mlooptri[tri_num].poly; + const MPoly *mp = &data->mpoly[poly_index]; + const bool smoothnormal = (mp->flag & ME_SMOOTH) != 0; + + if (!smoothnormal) { /* flat */ + if (data->precomputed_normals) { + copy_v3_v3(norm, &data->precomputed_normals[poly_index]); + } + else { + BKE_mesh_calc_poly_normal(mp, &data->mloop[mp->loopstart], data->mvert, norm); + } + } + else { + const int vi = data->mloop[data->mlooptri[tri_num].tri[vert_index]].v; + const short *no = data->mvert[vi].no; + + normal_short_to_float_v3(norm, no); + normalize_v3(norm); + } } -static void init_bake_rast(MBakeRast *bake_rast, const ImBuf *ibuf, const MResolvePixelData *data, - MFlushPixel flush_pixel, short *do_update) +static void init_bake_rast(MBakeRast *bake_rast, + const ImBuf *ibuf, + const MResolvePixelData *data, + MFlushPixel flush_pixel, + short *do_update) { - BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; + BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; - memset(bake_rast, 0, sizeof(MBakeRast)); + memset(bake_rast, 0, sizeof(MBakeRast)); - bake_rast->texels = userdata->mask_buffer; - bake_rast->w = ibuf->x; - bake_rast->h = ibuf->y; - bake_rast->data = data; - bake_rast->flush_pixel = flush_pixel; - bake_rast->do_update = do_update; + bake_rast->texels = userdata->mask_buffer; + bake_rast->w = ibuf->x; + bake_rast->h = ibuf->y; + bake_rast->data = data; + bake_rast->flush_pixel = flush_pixel; + bake_rast->do_update = do_update; } static void flush_pixel(const MResolvePixelData *data, const int x, const int y) { - float st[2] = {(x + 0.5f) / data->w, (y + 0.5f) / data->h}; - const float *st0, *st1, *st2; - const float *tang0, *tang1, *tang2; - float no0[3], no1[3], no2[3]; - float fUV[2], from_tang[3][3], to_tang[3][3]; - float u, v, w, sign; - int r; - - st0 = data->mloopuv[data->mlooptri[data->tri_index].tri[0]].uv; - st1 = data->mloopuv[data->mlooptri[data->tri_index].tri[1]].uv; - st2 = data->mloopuv[data->mlooptri[data->tri_index].tri[2]].uv; - - multiresbake_get_normal(data, no0, data->tri_index, 0); /* can optimize these 3 into one call */ - multiresbake_get_normal(data, no1, data->tri_index, 1); - multiresbake_get_normal(data, no2, data->tri_index, 2); - - resolve_tri_uv_v2(fUV, st, st0, st1, st2); - - u = fUV[0]; - v = fUV[1]; - w = 1 - u - v; - - if (data->pvtangent) { - tang0 = data->pvtangent + data->mlooptri[data->tri_index].tri[0] * 4; - tang1 = data->pvtangent + data->mlooptri[data->tri_index].tri[1] * 4; - tang2 = data->pvtangent + data->mlooptri[data->tri_index].tri[2] * 4; - - /* the sign is the same at all face vertices for any non degenerate face. - * Just in case we clamp the interpolated value though. */ - sign = (tang0[3] * u + tang1[3] * v + tang2[3] * w) < 0 ? (-1.0f) : 1.0f; - - /* this sequence of math is designed specifically as is with great care - * to be compatible with our shader. Please don't change without good reason. */ - for (r = 0; r < 3; r++) { - from_tang[0][r] = tang0[r] * u + tang1[r] * v + tang2[r] * w; - from_tang[2][r] = no0[r] * u + no1[r] * v + no2[r] * w; - } - - cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]); /* B = sign * cross(N, T) */ - mul_v3_fl(from_tang[1], sign); - invert_m3_m3(to_tang, from_tang); - } - else { - zero_m3(to_tang); - } - - data->pass_data(data->lores_dm, data->hires_dm, data->thread_data, data->bake_data, - data->ibuf, data->tri_index, data->lvl, st, to_tang, x, y); + float st[2] = {(x + 0.5f) / data->w, (y + 0.5f) / data->h}; + const float *st0, *st1, *st2; + const float *tang0, *tang1, *tang2; + float no0[3], no1[3], no2[3]; + float fUV[2], from_tang[3][3], to_tang[3][3]; + float u, v, w, sign; + int r; + + st0 = data->mloopuv[data->mlooptri[data->tri_index].tri[0]].uv; + st1 = data->mloopuv[data->mlooptri[data->tri_index].tri[1]].uv; + st2 = data->mloopuv[data->mlooptri[data->tri_index].tri[2]].uv; + + multiresbake_get_normal(data, no0, data->tri_index, 0); /* can optimize these 3 into one call */ + multiresbake_get_normal(data, no1, data->tri_index, 1); + multiresbake_get_normal(data, no2, data->tri_index, 2); + + resolve_tri_uv_v2(fUV, st, st0, st1, st2); + + u = fUV[0]; + v = fUV[1]; + w = 1 - u - v; + + if (data->pvtangent) { + tang0 = data->pvtangent + data->mlooptri[data->tri_index].tri[0] * 4; + tang1 = data->pvtangent + data->mlooptri[data->tri_index].tri[1] * 4; + tang2 = data->pvtangent + data->mlooptri[data->tri_index].tri[2] * 4; + + /* the sign is the same at all face vertices for any non degenerate face. + * Just in case we clamp the interpolated value though. */ + sign = (tang0[3] * u + tang1[3] * v + tang2[3] * w) < 0 ? (-1.0f) : 1.0f; + + /* this sequence of math is designed specifically as is with great care + * to be compatible with our shader. Please don't change without good reason. */ + for (r = 0; r < 3; r++) { + from_tang[0][r] = tang0[r] * u + tang1[r] * v + tang2[r] * w; + from_tang[2][r] = no0[r] * u + no1[r] * v + no2[r] * w; + } + + cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]); /* B = sign * cross(N, T) */ + mul_v3_fl(from_tang[1], sign); + invert_m3_m3(to_tang, from_tang); + } + else { + zero_m3(to_tang); + } + + data->pass_data(data->lores_dm, + data->hires_dm, + data->thread_data, + data->bake_data, + data->ibuf, + data->tri_index, + data->lvl, + st, + to_tang, + x, + y); } static void set_rast_triangle(const MBakeRast *bake_rast, const int x, const int y) { - const int w = bake_rast->w; - const int h = bake_rast->h; - - if (x >= 0 && x < w && y >= 0 && y < h) { - if ((bake_rast->texels[y * w + x]) == 0) { - bake_rast->texels[y * w + x] = FILTER_MASK_USED; - flush_pixel(bake_rast->data, x, y); - if (bake_rast->do_update) { - *bake_rast->do_update = true; - } - } - } + const int w = bake_rast->w; + const int h = bake_rast->h; + + if (x >= 0 && x < w && y >= 0 && y < h) { + if ((bake_rast->texels[y * w + x]) == 0) { + bake_rast->texels[y * w + x] = FILTER_MASK_USED; + flush_pixel(bake_rast->data, x, y); + if (bake_rast->do_update) { + *bake_rast->do_update = true; + } + } + } } static void rasterize_half(const MBakeRast *bake_rast, - const float s0_s, const float t0_s, const float s1_s, const float t1_s, - const float s0_l, const float t0_l, const float s1_l, const float t1_l, - const int y0_in, const int y1_in, const int is_mid_right) + const float s0_s, + const float t0_s, + const float s1_s, + const float t1_s, + const float s0_l, + const float t0_l, + const float s1_l, + const float t1_l, + const int y0_in, + const int y1_in, + const int is_mid_right) { - const int s_stable = fabsf(t1_s - t0_s) > FLT_EPSILON ? 1 : 0; - const int l_stable = fabsf(t1_l - t0_l) > FLT_EPSILON ? 1 : 0; - const int w = bake_rast->w; - const int h = bake_rast->h; - int y, y0, y1; - - if (y1_in <= 0 || y0_in >= h) - return; - - y0 = y0_in < 0 ? 0 : y0_in; - y1 = y1_in >= h ? h : y1_in; - - for (y = y0; y < y1; y++) { - /*-b(x-x0) + a(y-y0) = 0 */ - int iXl, iXr, x; - float x_l = s_stable != 0 ? (s0_s + (((s1_s - s0_s) * (y - t0_s)) / (t1_s - t0_s))) : s0_s; - float x_r = l_stable != 0 ? (s0_l + (((s1_l - s0_l) * (y - t0_l)) / (t1_l - t0_l))) : s0_l; - - if (is_mid_right != 0) - SWAP(float, x_l, x_r); - - iXl = (int)ceilf(x_l); - iXr = (int)ceilf(x_r); - - if (iXr > 0 && iXl < w) { - iXl = iXl < 0 ? 0 : iXl; - iXr = iXr >= w ? w : iXr; - - for (x = iXl; x < iXr; x++) - set_rast_triangle(bake_rast, x, y); - } - } + const int s_stable = fabsf(t1_s - t0_s) > FLT_EPSILON ? 1 : 0; + const int l_stable = fabsf(t1_l - t0_l) > FLT_EPSILON ? 1 : 0; + const int w = bake_rast->w; + const int h = bake_rast->h; + int y, y0, y1; + + if (y1_in <= 0 || y0_in >= h) + return; + + y0 = y0_in < 0 ? 0 : y0_in; + y1 = y1_in >= h ? h : y1_in; + + for (y = y0; y < y1; y++) { + /*-b(x-x0) + a(y-y0) = 0 */ + int iXl, iXr, x; + float x_l = s_stable != 0 ? (s0_s + (((s1_s - s0_s) * (y - t0_s)) / (t1_s - t0_s))) : s0_s; + float x_r = l_stable != 0 ? (s0_l + (((s1_l - s0_l) * (y - t0_l)) / (t1_l - t0_l))) : s0_l; + + if (is_mid_right != 0) + SWAP(float, x_l, x_r); + + iXl = (int)ceilf(x_l); + iXr = (int)ceilf(x_r); + + if (iXr > 0 && iXl < w) { + iXl = iXl < 0 ? 0 : iXl; + iXr = iXr >= w ? w : iXr; + + for (x = iXl; x < iXr; x++) + set_rast_triangle(bake_rast, x, y); + } + } } -static void bake_rasterize(const MBakeRast *bake_rast, const float st0_in[2], const float st1_in[2], const float st2_in[2]) +static void bake_rasterize(const MBakeRast *bake_rast, + const float st0_in[2], + const float st1_in[2], + const float st2_in[2]) { - const int w = bake_rast->w; - const int h = bake_rast->h; - float slo = st0_in[0] * w - 0.5f; - float tlo = st0_in[1] * h - 0.5f; - float smi = st1_in[0] * w - 0.5f; - float tmi = st1_in[1] * h - 0.5f; - float shi = st2_in[0] * w - 0.5f; - float thi = st2_in[1] * h - 0.5f; - int is_mid_right = 0, ylo, yhi, yhi_beg; - - /* skip degenerates */ - if ((slo == smi && tlo == tmi) || (slo == shi && tlo == thi) || (smi == shi && tmi == thi)) - return; - - /* sort by T */ - if (tlo > tmi && tlo > thi) { - SWAP(float, shi, slo); - SWAP(float, thi, tlo); - } - else if (tmi > thi) { - SWAP(float, shi, smi); - SWAP(float, thi, tmi); - } - - if (tlo > tmi) { - SWAP(float, slo, smi); - SWAP(float, tlo, tmi); - } - - /* check if mid point is to the left or to the right of the lo-hi edge */ - is_mid_right = (-(shi - slo) * (tmi - thi) + (thi - tlo) * (smi - shi)) > 0 ? 1 : 0; - ylo = (int)ceilf(tlo); - yhi_beg = (int)ceilf(tmi); - yhi = (int)ceilf(thi); - - /*if (fTmi>ceilf(fTlo))*/ - rasterize_half(bake_rast, slo, tlo, smi, tmi, slo, tlo, shi, thi, ylo, yhi_beg, is_mid_right); - rasterize_half(bake_rast, smi, tmi, shi, thi, slo, tlo, shi, thi, yhi_beg, yhi, is_mid_right); + const int w = bake_rast->w; + const int h = bake_rast->h; + float slo = st0_in[0] * w - 0.5f; + float tlo = st0_in[1] * h - 0.5f; + float smi = st1_in[0] * w - 0.5f; + float tmi = st1_in[1] * h - 0.5f; + float shi = st2_in[0] * w - 0.5f; + float thi = st2_in[1] * h - 0.5f; + int is_mid_right = 0, ylo, yhi, yhi_beg; + + /* skip degenerates */ + if ((slo == smi && tlo == tmi) || (slo == shi && tlo == thi) || (smi == shi && tmi == thi)) + return; + + /* sort by T */ + if (tlo > tmi && tlo > thi) { + SWAP(float, shi, slo); + SWAP(float, thi, tlo); + } + else if (tmi > thi) { + SWAP(float, shi, smi); + SWAP(float, thi, tmi); + } + + if (tlo > tmi) { + SWAP(float, slo, smi); + SWAP(float, tlo, tmi); + } + + /* check if mid point is to the left or to the right of the lo-hi edge */ + is_mid_right = (-(shi - slo) * (tmi - thi) + (thi - tlo) * (smi - shi)) > 0 ? 1 : 0; + ylo = (int)ceilf(tlo); + yhi_beg = (int)ceilf(tmi); + yhi = (int)ceilf(thi); + + /*if (fTmi>ceilf(fTlo))*/ + rasterize_half(bake_rast, slo, tlo, smi, tmi, slo, tlo, shi, thi, ylo, yhi_beg, is_mid_right); + rasterize_half(bake_rast, smi, tmi, shi, thi, slo, tlo, shi, thi, yhi_beg, yhi, is_mid_right); } static int multiresbake_test_break(MultiresBakeRender *bkr) { - if (!bkr->stop) { - /* this means baker is executed outside from job system */ - return 0; - } + if (!bkr->stop) { + /* this means baker is executed outside from job system */ + return 0; + } - return *bkr->stop || G.is_break; + return *bkr->stop || G.is_break; } /* **** Threading routines **** */ typedef struct MultiresBakeQueue { - int cur_tri; - int tot_tri; - SpinLock spin; + int cur_tri; + int tot_tri; + SpinLock spin; } MultiresBakeQueue; typedef struct MultiresBakeThread { - /* this data is actually shared between all the threads */ - MultiresBakeQueue *queue; - MultiresBakeRender *bkr; - Image *image; - void *bake_data; - - /* thread-specific data */ - MBakeRast bake_rast; - MResolvePixelData data; - - /* displacement-specific data */ - float height_min, height_max; + /* this data is actually shared between all the threads */ + MultiresBakeQueue *queue; + MultiresBakeRender *bkr; + Image *image; + void *bake_data; + + /* thread-specific data */ + MBakeRast bake_rast; + MResolvePixelData data; + + /* displacement-specific data */ + float height_min, height_max; } MultiresBakeThread; static int multires_bake_queue_next_tri(MultiresBakeQueue *queue) { - int face = -1; + int face = -1; - /* TODO: it could worth making it so thread will handle neighbor faces - * for better memory cache utilization - */ + /* TODO: it could worth making it so thread will handle neighbor faces + * for better memory cache utilization + */ - BLI_spin_lock(&queue->spin); - if (queue->cur_tri < queue->tot_tri) { - face = queue->cur_tri; - queue->cur_tri++; - } - BLI_spin_unlock(&queue->spin); + BLI_spin_lock(&queue->spin); + if (queue->cur_tri < queue->tot_tri) { + face = queue->cur_tri; + queue->cur_tri++; + } + BLI_spin_unlock(&queue->spin); - return face; + return face; } static void *do_multires_bake_thread(void *data_v) { - MultiresBakeThread *handle = (MultiresBakeThread *)data_v; - MResolvePixelData *data = &handle->data; - MBakeRast *bake_rast = &handle->bake_rast; - MultiresBakeRender *bkr = handle->bkr; - int tri_index; + MultiresBakeThread *handle = (MultiresBakeThread *)data_v; + MResolvePixelData *data = &handle->data; + MBakeRast *bake_rast = &handle->bake_rast; + MultiresBakeRender *bkr = handle->bkr; + int tri_index; - while ((tri_index = multires_bake_queue_next_tri(handle->queue)) >= 0) { - const MLoopTri *lt = &data->mlooptri[tri_index]; - const MPoly *mp = &data->mpoly[lt->poly]; - const short mat_nr = mp->mat_nr; - const MLoopUV *mloopuv = data->mloopuv; + while ((tri_index = multires_bake_queue_next_tri(handle->queue)) >= 0) { + const MLoopTri *lt = &data->mlooptri[tri_index]; + const MPoly *mp = &data->mpoly[lt->poly]; + const short mat_nr = mp->mat_nr; + const MLoopUV *mloopuv = data->mloopuv; - if (multiresbake_test_break(bkr)) - break; + if (multiresbake_test_break(bkr)) + break; - Image *tri_image = mat_nr < bkr->ob_image.len ? bkr->ob_image.array[mat_nr] : NULL; - if (tri_image != handle->image) - continue; + Image *tri_image = mat_nr < bkr->ob_image.len ? bkr->ob_image.array[mat_nr] : NULL; + if (tri_image != handle->image) + continue; - data->tri_index = tri_index; + data->tri_index = tri_index; - bake_rasterize(bake_rast, mloopuv[lt->tri[0]].uv, mloopuv[lt->tri[1]].uv, mloopuv[lt->tri[2]].uv); + bake_rasterize( + bake_rast, mloopuv[lt->tri[0]].uv, mloopuv[lt->tri[1]].uv, mloopuv[lt->tri[2]].uv); - /* tag image buffer for refresh */ - if (data->ibuf->rect_float) - data->ibuf->userflags |= IB_RECT_INVALID; + /* tag image buffer for refresh */ + if (data->ibuf->rect_float) + data->ibuf->userflags |= IB_RECT_INVALID; - data->ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + data->ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - /* update progress */ - BLI_spin_lock(&handle->queue->spin); - bkr->baked_faces++; + /* update progress */ + BLI_spin_lock(&handle->queue->spin); + bkr->baked_faces++; - if (bkr->do_update) - *bkr->do_update = true; + if (bkr->do_update) + *bkr->do_update = true; - if (bkr->progress) - *bkr->progress = ((float)bkr->baked_objects + (float)bkr->baked_faces / handle->queue->tot_tri) / bkr->tot_obj; - BLI_spin_unlock(&handle->queue->spin); - } + if (bkr->progress) + *bkr->progress = ((float)bkr->baked_objects + + (float)bkr->baked_faces / handle->queue->tot_tri) / + bkr->tot_obj; + BLI_spin_unlock(&handle->queue->spin); + } - return NULL; + return NULL; } /* some of arrays inside ccgdm are lazy-initialized, which will generally @@ -393,313 +430,347 @@ static void *do_multires_bake_thread(void *data_v) */ static void init_ccgdm_arrays(DerivedMesh *dm) { - CCGElem **grid_data; - CCGKey key; - int grid_size; - const int *grid_offset; - - grid_size = dm->getGridSize(dm); - grid_data = dm->getGridData(dm); - grid_offset = dm->getGridOffset(dm); - dm->getGridKey(dm, &key); - - (void)grid_size; - (void)grid_data; - (void)grid_offset; + CCGElem **grid_data; + CCGKey key; + int grid_size; + const int *grid_offset; + + grid_size = dm->getGridSize(dm); + grid_data = dm->getGridData(dm); + grid_offset = dm->getGridOffset(dm); + dm->getGridKey(dm, &key); + + (void)grid_size; + (void)grid_data; + (void)grid_offset; } -static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, bool require_tangent, MPassKnownData passKnownData, - MInitBakeData initBakeData, MFreeBakeData freeBakeData, MultiresBakeResult *result) +static void do_multires_bake(MultiresBakeRender *bkr, + Image *ima, + bool require_tangent, + MPassKnownData passKnownData, + MInitBakeData initBakeData, + MFreeBakeData freeBakeData, + MultiresBakeResult *result) { - DerivedMesh *dm = bkr->lores_dm; - const MLoopTri *mlooptri = dm->getLoopTriArray(dm); - const int lvl = bkr->lvl; - int tot_tri = dm->getNumLoopTri(dm); - - if (tot_tri > 0) { - MultiresBakeThread *handles; - MultiresBakeQueue queue; - - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - MVert *mvert = dm->getVertArray(dm); - MPoly *mpoly = dm->getPolyArray(dm); - MLoop *mloop = dm->getLoopArray(dm); - MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL); - float *pvtangent = NULL; - - ListBase threads; - int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); - - void *bake_data = NULL; - - if (require_tangent) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) - DM_calc_loop_tangents(dm, true, NULL, 0); - - pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); - } - - /* all threads shares the same custom bake data */ - if (initBakeData) - bake_data = initBakeData(bkr, ima); - - if (tot_thread > 1) - BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); - - handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); - - init_ccgdm_arrays(bkr->hires_dm); - - /* faces queue */ - queue.cur_tri = 0; - queue.tot_tri = tot_tri; - BLI_spin_init(&queue.spin); - - /* fill in threads handles */ - for (i = 0; i < tot_thread; i++) { - MultiresBakeThread *handle = &handles[i]; - - handle->bkr = bkr; - handle->image = ima; - handle->queue = &queue; - - handle->data.mpoly = mpoly; - handle->data.mvert = mvert; - handle->data.mloopuv = mloopuv; - handle->data.mlooptri = mlooptri; - handle->data.mloop = mloop; - handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */ - handle->data.w = ibuf->x; - handle->data.h = ibuf->y; - handle->data.lores_dm = dm; - handle->data.hires_dm = bkr->hires_dm; - handle->data.lvl = lvl; - handle->data.pass_data = passKnownData; - handle->data.thread_data = handle; - handle->data.bake_data = bake_data; - handle->data.ibuf = ibuf; - - handle->height_min = FLT_MAX; - handle->height_max = -FLT_MAX; - - init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - - if (tot_thread > 1) - BLI_threadpool_insert(&threads, handle); - } - - /* run threads */ - if (tot_thread > 1) - BLI_threadpool_end(&threads); - else - do_multires_bake_thread(&handles[0]); - - /* construct bake result */ - result->height_min = handles[0].height_min; - result->height_max = handles[0].height_max; - - for (i = 1; i < tot_thread; i++) { - result->height_min = min_ff(result->height_min, handles[i].height_min); - result->height_max = max_ff(result->height_max, handles[i].height_max); - } - - BLI_spin_end(&queue.spin); - - /* finalize baking */ - if (freeBakeData) - freeBakeData(bake_data); - - MEM_freeN(handles); - - BKE_image_release_ibuf(ima, ibuf, NULL); - } + DerivedMesh *dm = bkr->lores_dm; + const MLoopTri *mlooptri = dm->getLoopTriArray(dm); + const int lvl = bkr->lvl; + int tot_tri = dm->getNumLoopTri(dm); + + if (tot_tri > 0) { + MultiresBakeThread *handles; + MultiresBakeQueue queue; + + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + MVert *mvert = dm->getVertArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); + const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL); + float *pvtangent = NULL; + + ListBase threads; + int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); + + void *bake_data = NULL; + + if (require_tangent) { + if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) + DM_calc_loop_tangents(dm, true, NULL, 0); + + pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + } + + /* all threads shares the same custom bake data */ + if (initBakeData) + bake_data = initBakeData(bkr, ima); + + if (tot_thread > 1) + BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); + + handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); + + init_ccgdm_arrays(bkr->hires_dm); + + /* faces queue */ + queue.cur_tri = 0; + queue.tot_tri = tot_tri; + BLI_spin_init(&queue.spin); + + /* fill in threads handles */ + for (i = 0; i < tot_thread; i++) { + MultiresBakeThread *handle = &handles[i]; + + handle->bkr = bkr; + handle->image = ima; + handle->queue = &queue; + + handle->data.mpoly = mpoly; + handle->data.mvert = mvert; + handle->data.mloopuv = mloopuv; + handle->data.mlooptri = mlooptri; + handle->data.mloop = mloop; + handle->data.pvtangent = pvtangent; + handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */ + handle->data.w = ibuf->x; + handle->data.h = ibuf->y; + handle->data.lores_dm = dm; + handle->data.hires_dm = bkr->hires_dm; + handle->data.lvl = lvl; + handle->data.pass_data = passKnownData; + handle->data.thread_data = handle; + handle->data.bake_data = bake_data; + handle->data.ibuf = ibuf; + + handle->height_min = FLT_MAX; + handle->height_max = -FLT_MAX; + + init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); + + if (tot_thread > 1) + BLI_threadpool_insert(&threads, handle); + } + + /* run threads */ + if (tot_thread > 1) + BLI_threadpool_end(&threads); + else + do_multires_bake_thread(&handles[0]); + + /* construct bake result */ + result->height_min = handles[0].height_min; + result->height_max = handles[0].height_max; + + for (i = 1; i < tot_thread; i++) { + result->height_min = min_ff(result->height_min, handles[i].height_min); + result->height_max = max_ff(result->height_max, handles[i].height_max); + } + + BLI_spin_end(&queue.spin); + + /* finalize baking */ + if (freeBakeData) + freeBakeData(bake_data); + + MEM_freeN(handles); + + BKE_image_release_ibuf(ima, ibuf, NULL); + } } /* mode = 0: interpolate normals, * mode = 1: interpolate coord */ -static void interp_bilinear_grid(CCGKey *key, CCGElem *grid, float crn_x, float crn_y, int mode, float res[3]) +static void interp_bilinear_grid( + CCGKey *key, CCGElem *grid, float crn_x, float crn_y, int mode, float res[3]) { - int x0, x1, y0, y1; - float u, v; - float data[4][3]; - - x0 = (int)crn_x; - x1 = x0 >= (key->grid_size - 1) ? (key->grid_size - 1) : (x0 + 1); - - y0 = (int)crn_y; - y1 = y0 >= (key->grid_size - 1) ? (key->grid_size - 1) : (y0 + 1); - - u = crn_x - x0; - v = crn_y - y0; - - if (mode == 0) { - copy_v3_v3(data[0], CCG_grid_elem_no(key, grid, x0, y0)); - copy_v3_v3(data[1], CCG_grid_elem_no(key, grid, x1, y0)); - copy_v3_v3(data[2], CCG_grid_elem_no(key, grid, x1, y1)); - copy_v3_v3(data[3], CCG_grid_elem_no(key, grid, x0, y1)); - } - else { - copy_v3_v3(data[0], CCG_grid_elem_co(key, grid, x0, y0)); - copy_v3_v3(data[1], CCG_grid_elem_co(key, grid, x1, y0)); - copy_v3_v3(data[2], CCG_grid_elem_co(key, grid, x1, y1)); - copy_v3_v3(data[3], CCG_grid_elem_co(key, grid, x0, y1)); - } - - interp_bilinear_quad_v3(data, u, v, res); + int x0, x1, y0, y1; + float u, v; + float data[4][3]; + + x0 = (int)crn_x; + x1 = x0 >= (key->grid_size - 1) ? (key->grid_size - 1) : (x0 + 1); + + y0 = (int)crn_y; + y1 = y0 >= (key->grid_size - 1) ? (key->grid_size - 1) : (y0 + 1); + + u = crn_x - x0; + v = crn_y - y0; + + if (mode == 0) { + copy_v3_v3(data[0], CCG_grid_elem_no(key, grid, x0, y0)); + copy_v3_v3(data[1], CCG_grid_elem_no(key, grid, x1, y0)); + copy_v3_v3(data[2], CCG_grid_elem_no(key, grid, x1, y1)); + copy_v3_v3(data[3], CCG_grid_elem_no(key, grid, x0, y1)); + } + else { + copy_v3_v3(data[0], CCG_grid_elem_co(key, grid, x0, y0)); + copy_v3_v3(data[1], CCG_grid_elem_co(key, grid, x1, y0)); + copy_v3_v3(data[2], CCG_grid_elem_co(key, grid, x1, y1)); + copy_v3_v3(data[3], CCG_grid_elem_co(key, grid, x0, y1)); + } + + interp_bilinear_quad_v3(data, u, v, res); } -static void get_ccgdm_data(DerivedMesh *lodm, DerivedMesh *hidm, +static void get_ccgdm_data(DerivedMesh *lodm, + DerivedMesh *hidm, const int *index_mp_to_orig, - const int lvl, const MLoopTri *lt, const float u, const float v, float co[3], float n[3]) + const int lvl, + const MLoopTri *lt, + const float u, + const float v, + float co[3], + float n[3]) { - CCGElem **grid_data; - CCGKey key; - float crn_x, crn_y; - int grid_size, S, face_side; - int *grid_offset, g_index; - int poly_index = lt->poly; - - grid_size = hidm->getGridSize(hidm); - grid_data = hidm->getGridData(hidm); - grid_offset = hidm->getGridOffset(hidm); - hidm->getGridKey(hidm, &key); - - if (lvl == 0) { - MPoly *mpoly; - face_side = (grid_size << 1) - 1; - - mpoly = lodm->getPolyArray(lodm) + poly_index; - g_index = grid_offset[poly_index]; - S = mdisp_rot_face_to_crn(lodm->getVertArray(lodm), mpoly, lodm->getLoopArray(lodm), lt, face_side, u * (face_side - 1), v * (face_side - 1), &crn_x, &crn_y); - } - else { - /* number of faces per grid side */ - int polys_per_grid_side = (1 << (lvl - 1)); - /* get the original cage face index */ - int cage_face_index = index_mp_to_orig ? index_mp_to_orig[poly_index] : poly_index; - /* local offset in total cage face grids - * (1 << (2 * lvl)) is number of all polys for one cage face */ - int loc_cage_poly_offs = poly_index % (1 << (2 * lvl)); - /* local offset in the vertex grid itself */ - int cell_index = loc_cage_poly_offs % (polys_per_grid_side * polys_per_grid_side); - int cell_side = (grid_size - 1) / polys_per_grid_side; - /* row and column based on grid side */ - int row = cell_index / polys_per_grid_side; - int col = cell_index % polys_per_grid_side; - - /* S is the vertex whose grid we are examining */ - S = poly_index / (1 << (2 * (lvl - 1))) - grid_offset[cage_face_index]; - /* get offset of grid data for original cage face */ - g_index = grid_offset[cage_face_index]; - - crn_y = (row * cell_side) + u * cell_side; - crn_x = (col * cell_side) + v * cell_side; - } - - CLAMP(crn_x, 0.0f, grid_size); - CLAMP(crn_y, 0.0f, grid_size); - - if (n != NULL) - interp_bilinear_grid(&key, grid_data[g_index + S], crn_x, crn_y, 0, n); - - if (co != NULL) - interp_bilinear_grid(&key, grid_data[g_index + S], crn_x, crn_y, 1, co); + CCGElem **grid_data; + CCGKey key; + float crn_x, crn_y; + int grid_size, S, face_side; + int *grid_offset, g_index; + int poly_index = lt->poly; + + grid_size = hidm->getGridSize(hidm); + grid_data = hidm->getGridData(hidm); + grid_offset = hidm->getGridOffset(hidm); + hidm->getGridKey(hidm, &key); + + if (lvl == 0) { + MPoly *mpoly; + face_side = (grid_size << 1) - 1; + + mpoly = lodm->getPolyArray(lodm) + poly_index; + g_index = grid_offset[poly_index]; + S = mdisp_rot_face_to_crn(lodm->getVertArray(lodm), + mpoly, + lodm->getLoopArray(lodm), + lt, + face_side, + u * (face_side - 1), + v * (face_side - 1), + &crn_x, + &crn_y); + } + else { + /* number of faces per grid side */ + int polys_per_grid_side = (1 << (lvl - 1)); + /* get the original cage face index */ + int cage_face_index = index_mp_to_orig ? index_mp_to_orig[poly_index] : poly_index; + /* local offset in total cage face grids + * (1 << (2 * lvl)) is number of all polys for one cage face */ + int loc_cage_poly_offs = poly_index % (1 << (2 * lvl)); + /* local offset in the vertex grid itself */ + int cell_index = loc_cage_poly_offs % (polys_per_grid_side * polys_per_grid_side); + int cell_side = (grid_size - 1) / polys_per_grid_side; + /* row and column based on grid side */ + int row = cell_index / polys_per_grid_side; + int col = cell_index % polys_per_grid_side; + + /* S is the vertex whose grid we are examining */ + S = poly_index / (1 << (2 * (lvl - 1))) - grid_offset[cage_face_index]; + /* get offset of grid data for original cage face */ + g_index = grid_offset[cage_face_index]; + + crn_y = (row * cell_side) + u * cell_side; + crn_x = (col * cell_side) + v * cell_side; + } + + CLAMP(crn_x, 0.0f, grid_size); + CLAMP(crn_y, 0.0f, grid_size); + + if (n != NULL) + interp_bilinear_grid(&key, grid_data[g_index + S], crn_x, crn_y, 0, n); + + if (co != NULL) + interp_bilinear_grid(&key, grid_data[g_index + S], crn_x, crn_y, 1, co); } /* mode = 0: interpolate normals, * mode = 1: interpolate coord */ -static void interp_bilinear_mpoly(DerivedMesh *dm, MLoop *mloop, MPoly *mpoly, const float u, const float v, const int mode, float res[3]) +static void interp_bilinear_mpoly(DerivedMesh *dm, + MLoop *mloop, + MPoly *mpoly, + const float u, + const float v, + const int mode, + float res[3]) { - float data[4][3]; - - if (mode == 0) { - dm->getVertNo(dm, mloop[mpoly->loopstart].v, data[0]); - dm->getVertNo(dm, mloop[mpoly->loopstart + 1].v, data[1]); - dm->getVertNo(dm, mloop[mpoly->loopstart + 2].v, data[2]); - dm->getVertNo(dm, mloop[mpoly->loopstart + 3].v, data[3]); - } - else { - dm->getVertCo(dm, mloop[mpoly->loopstart].v, data[0]); - dm->getVertCo(dm, mloop[mpoly->loopstart + 1].v, data[1]); - dm->getVertCo(dm, mloop[mpoly->loopstart + 2].v, data[2]); - dm->getVertCo(dm, mloop[mpoly->loopstart + 3].v, data[3]); - } - - interp_bilinear_quad_v3(data, u, v, res); + float data[4][3]; + + if (mode == 0) { + dm->getVertNo(dm, mloop[mpoly->loopstart].v, data[0]); + dm->getVertNo(dm, mloop[mpoly->loopstart + 1].v, data[1]); + dm->getVertNo(dm, mloop[mpoly->loopstart + 2].v, data[2]); + dm->getVertNo(dm, mloop[mpoly->loopstart + 3].v, data[3]); + } + else { + dm->getVertCo(dm, mloop[mpoly->loopstart].v, data[0]); + dm->getVertCo(dm, mloop[mpoly->loopstart + 1].v, data[1]); + dm->getVertCo(dm, mloop[mpoly->loopstart + 2].v, data[2]); + dm->getVertCo(dm, mloop[mpoly->loopstart + 3].v, data[3]); + } + + interp_bilinear_quad_v3(data, u, v, res); } -static void interp_barycentric_mlooptri(DerivedMesh *dm, MLoop *mloop, const MLoopTri *lt, const float u, const float v, const int mode, float res[3]) +static void interp_barycentric_mlooptri(DerivedMesh *dm, + MLoop *mloop, + const MLoopTri *lt, + const float u, + const float v, + const int mode, + float res[3]) { - float data[3][3]; - - if (mode == 0) { - dm->getVertNo(dm, mloop[lt->tri[0]].v, data[0]); - dm->getVertNo(dm, mloop[lt->tri[1]].v, data[1]); - dm->getVertNo(dm, mloop[lt->tri[2]].v, data[2]); - } - else { - dm->getVertCo(dm, mloop[lt->tri[0]].v, data[0]); - dm->getVertCo(dm, mloop[lt->tri[1]].v, data[1]); - dm->getVertCo(dm, mloop[lt->tri[2]].v, data[2]); - } - - interp_barycentric_tri_v3(data, u, v, res); + float data[3][3]; + + if (mode == 0) { + dm->getVertNo(dm, mloop[lt->tri[0]].v, data[0]); + dm->getVertNo(dm, mloop[lt->tri[1]].v, data[1]); + dm->getVertNo(dm, mloop[lt->tri[2]].v, data[2]); + } + else { + dm->getVertCo(dm, mloop[lt->tri[0]].v, data[0]); + dm->getVertCo(dm, mloop[lt->tri[1]].v, data[1]); + dm->getVertCo(dm, mloop[lt->tri[2]].v, data[2]); + } + + interp_barycentric_tri_v3(data, u, v, res); } /* **************** Displacement Baker **************** */ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima) { - MHeightBakeData *height_data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - DerivedMesh *lodm = bkr->lores_dm; - BakeImBufuserData *userdata = ibuf->userdata; + MHeightBakeData *height_data; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + DerivedMesh *lodm = bkr->lores_dm; + BakeImBufuserData *userdata = ibuf->userdata; - if (userdata->displacement_buffer == NULL) - userdata->displacement_buffer = MEM_callocN(sizeof(float) * ibuf->x * ibuf->y, "MultiresBake heights"); + if (userdata->displacement_buffer == NULL) + userdata->displacement_buffer = MEM_callocN(sizeof(float) * ibuf->x * ibuf->y, + "MultiresBake heights"); - height_data = MEM_callocN(sizeof(MHeightBakeData), "MultiresBake heightData"); + height_data = MEM_callocN(sizeof(MHeightBakeData), "MultiresBake heightData"); - height_data->ima = ima; - height_data->heights = userdata->displacement_buffer; + height_data->ima = ima; + height_data->heights = userdata->displacement_buffer; - if (!bkr->use_lores_mesh) { - SubsurfModifierData smd = {{NULL}}; - int ss_lvl = bkr->tot_lvl - bkr->lvl; + if (!bkr->use_lores_mesh) { + SubsurfModifierData smd = {{NULL}}; + int ss_lvl = bkr->tot_lvl - bkr->lvl; - CLAMP(ss_lvl, 0, 6); + CLAMP(ss_lvl, 0, 6); - if (ss_lvl > 0) { - smd.levels = smd.renderLevels = ss_lvl; - smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS; - smd.quality = 3; + if (ss_lvl > 0) { + smd.levels = smd.renderLevels = ss_lvl; + smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS; + smd.quality = 3; - if (bkr->simple) - smd.subdivType = ME_SIMPLE_SUBSURF; + if (bkr->simple) + smd.subdivType = ME_SIMPLE_SUBSURF; - height_data->ssdm = subsurf_make_derived_from_derived(bkr->lores_dm, &smd, bkr->scene, NULL, 0); - init_ccgdm_arrays(height_data->ssdm); - } - } + height_data->ssdm = subsurf_make_derived_from_derived( + bkr->lores_dm, &smd, bkr->scene, NULL, 0); + init_ccgdm_arrays(height_data->ssdm); + } + } - height_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); + height_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, NULL); - return (void *)height_data; + return (void *)height_data; } static void free_heights_data(void *bake_data) { - MHeightBakeData *height_data = (MHeightBakeData *)bake_data; + MHeightBakeData *height_data = (MHeightBakeData *)bake_data; - if (height_data->ssdm) - height_data->ssdm->release(height_data->ssdm); + if (height_data->ssdm) + height_data->ssdm->release(height_data->ssdm); - MEM_freeN(height_data); + MEM_freeN(height_data); } /* MultiresBake callback for heights baking @@ -708,98 +779,111 @@ static void free_heights_data(void *bake_data) * - find coord of point and normal with specified UV in lo-res mesh (or subdivided lo-res * mesh to make texture smoother) let's call this point p0 and n. * - height wound be dot(n, p1-p0) */ -static void apply_heights_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void *thread_data_v, void *bake_data, - ImBuf *ibuf, const int tri_index, const int lvl, const float st[2], - float UNUSED(tangmat[3][3]), const int x, const int y) +static void apply_heights_callback(DerivedMesh *lores_dm, + DerivedMesh *hires_dm, + void *thread_data_v, + void *bake_data, + ImBuf *ibuf, + const int tri_index, + const int lvl, + const float st[2], + float UNUSED(tangmat[3][3]), + const int x, + const int y) { - const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; - MLoop *mloop = lores_dm->getLoopArray(lores_dm); - MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; - MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); - MHeightBakeData *height_data = (MHeightBakeData *)bake_data; - MultiresBakeThread *thread_data = (MultiresBakeThread *)thread_data_v; - float uv[2], *st0, *st1, *st2, *st3; - int pixel = ibuf->x * y + x; - float vec[3], p0[3], p1[3], n[3], len; - - /* ideally we would work on triangles only, however, we rely on quads to get orthogonal - * coordinates for use in grid space (triangle barycentric is not orthogonal) */ - if (mpoly->totloop == 4) { - st0 = mloopuv[mpoly->loopstart].uv; - st1 = mloopuv[mpoly->loopstart + 1].uv; - st2 = mloopuv[mpoly->loopstart + 2].uv; - st3 = mloopuv[mpoly->loopstart + 3].uv; - resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); - } - else { - st0 = mloopuv[lt->tri[0]].uv; - st1 = mloopuv[lt->tri[1]].uv; - st2 = mloopuv[lt->tri[2]].uv; - resolve_tri_uv_v2(uv, st, st0, st1, st2); - } - - CLAMP(uv[0], 0.0f, 1.0f); - CLAMP(uv[1], 0.0f, 1.0f); - - get_ccgdm_data(lores_dm, hires_dm, - height_data->orig_index_mp_to_orig, - lvl, lt, uv[0], uv[1], p1, NULL); - - if (height_data->ssdm) { - get_ccgdm_data(lores_dm, height_data->ssdm, - height_data->orig_index_mp_to_orig, - 0, lt, uv[0], uv[1], p0, n); - } - else { - if (mpoly->totloop == 4) { - interp_bilinear_mpoly(lores_dm, mloop, mpoly, uv[0], uv[1], 1, p0); - interp_bilinear_mpoly(lores_dm, mloop, mpoly, uv[0], uv[1], 0, n); - } - else { - interp_barycentric_mlooptri(lores_dm, mloop, lt, uv[0], uv[1], 1, p0); - interp_barycentric_mlooptri(lores_dm, mloop, lt, uv[0], uv[1], 0, n); - } - } - - sub_v3_v3v3(vec, p1, p0); - len = dot_v3v3(n, vec); - - height_data->heights[pixel] = len; - - thread_data->height_min = min_ff(thread_data->height_min, len); - thread_data->height_max = max_ff(thread_data->height_max, len); - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + pixel * 4; - rrgbf[0] = rrgbf[1] = rrgbf[2] = len; - rrgbf[3] = 1.0f; - } - else { - char *rrgb = (char *)ibuf->rect + pixel * 4; - rrgb[0] = rrgb[1] = rrgb[2] = unit_float_to_uchar_clamp(len); - rrgb[3] = 255; - } + const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; + MLoop *mloop = lores_dm->getLoopArray(lores_dm); + MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; + MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); + MHeightBakeData *height_data = (MHeightBakeData *)bake_data; + MultiresBakeThread *thread_data = (MultiresBakeThread *)thread_data_v; + float uv[2], *st0, *st1, *st2, *st3; + int pixel = ibuf->x * y + x; + float vec[3], p0[3], p1[3], n[3], len; + + /* ideally we would work on triangles only, however, we rely on quads to get orthogonal + * coordinates for use in grid space (triangle barycentric is not orthogonal) */ + if (mpoly->totloop == 4) { + st0 = mloopuv[mpoly->loopstart].uv; + st1 = mloopuv[mpoly->loopstart + 1].uv; + st2 = mloopuv[mpoly->loopstart + 2].uv; + st3 = mloopuv[mpoly->loopstart + 3].uv; + resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); + } + else { + st0 = mloopuv[lt->tri[0]].uv; + st1 = mloopuv[lt->tri[1]].uv; + st2 = mloopuv[lt->tri[2]].uv; + resolve_tri_uv_v2(uv, st, st0, st1, st2); + } + + CLAMP(uv[0], 0.0f, 1.0f); + CLAMP(uv[1], 0.0f, 1.0f); + + get_ccgdm_data( + lores_dm, hires_dm, height_data->orig_index_mp_to_orig, lvl, lt, uv[0], uv[1], p1, NULL); + + if (height_data->ssdm) { + get_ccgdm_data(lores_dm, + height_data->ssdm, + height_data->orig_index_mp_to_orig, + 0, + lt, + uv[0], + uv[1], + p0, + n); + } + else { + if (mpoly->totloop == 4) { + interp_bilinear_mpoly(lores_dm, mloop, mpoly, uv[0], uv[1], 1, p0); + interp_bilinear_mpoly(lores_dm, mloop, mpoly, uv[0], uv[1], 0, n); + } + else { + interp_barycentric_mlooptri(lores_dm, mloop, lt, uv[0], uv[1], 1, p0); + interp_barycentric_mlooptri(lores_dm, mloop, lt, uv[0], uv[1], 0, n); + } + } + + sub_v3_v3v3(vec, p1, p0); + len = dot_v3v3(n, vec); + + height_data->heights[pixel] = len; + + thread_data->height_min = min_ff(thread_data->height_min, len); + thread_data->height_max = max_ff(thread_data->height_max, len); + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + pixel * 4; + rrgbf[0] = rrgbf[1] = rrgbf[2] = len; + rrgbf[3] = 1.0f; + } + else { + char *rrgb = (char *)ibuf->rect + pixel * 4; + rrgb[0] = rrgb[1] = rrgb[2] = unit_float_to_uchar_clamp(len); + rrgb[3] = 255; + } } /* **************** Normal Maps Baker **************** */ static void *init_normal_data(MultiresBakeRender *bkr, Image *UNUSED(ima)) { - MNormalBakeData *normal_data; - DerivedMesh *lodm = bkr->lores_dm; + MNormalBakeData *normal_data; + DerivedMesh *lodm = bkr->lores_dm; - normal_data = MEM_callocN(sizeof(MNormalBakeData), "MultiresBake normalData"); + normal_data = MEM_callocN(sizeof(MNormalBakeData), "MultiresBake normalData"); - normal_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); + normal_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); - return (void *)normal_data; + return (void *)normal_data; } static void free_normal_data(void *bake_data) { - MNormalBakeData *normal_data = (MNormalBakeData *)bake_data; + MNormalBakeData *normal_data = (MNormalBakeData *)bake_data; - MEM_freeN(normal_data); + MEM_freeN(normal_data); } /** @@ -810,57 +894,64 @@ static void free_normal_data(void *bake_data) * - Multiply it by tangmat. * - Vector in color space would be `norm(vec) / 2 + (0.5, 0.5, 0.5)`. */ -static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void *UNUSED(thread_data), - void *bake_data, ImBuf *ibuf, const int tri_index, const int lvl, - const float st[2], float tangmat[3][3], const int x, const int y) +static void apply_tangmat_callback(DerivedMesh *lores_dm, + DerivedMesh *hires_dm, + void *UNUSED(thread_data), + void *bake_data, + ImBuf *ibuf, + const int tri_index, + const int lvl, + const float st[2], + float tangmat[3][3], + const int x, + const int y) { - const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; - MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; - MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); - MNormalBakeData *normal_data = (MNormalBakeData *)bake_data; - float uv[2], *st0, *st1, *st2, *st3; - int pixel = ibuf->x * y + x; - float n[3], vec[3], tmp[3] = {0.5, 0.5, 0.5}; - - /* ideally we would work on triangles only, however, we rely on quads to get orthogonal - * coordinates for use in grid space (triangle barycentric is not orthogonal) */ - if (mpoly->totloop == 4) { - st0 = mloopuv[mpoly->loopstart].uv; - st1 = mloopuv[mpoly->loopstart + 1].uv; - st2 = mloopuv[mpoly->loopstart + 2].uv; - st3 = mloopuv[mpoly->loopstart + 3].uv; - resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); - } - else { - st0 = mloopuv[lt->tri[0]].uv; - st1 = mloopuv[lt->tri[1]].uv; - st2 = mloopuv[lt->tri[2]].uv; - resolve_tri_uv_v2(uv, st, st0, st1, st2); - } - - CLAMP(uv[0], 0.0f, 1.0f); - CLAMP(uv[1], 0.0f, 1.0f); - - get_ccgdm_data(lores_dm, hires_dm, - normal_data->orig_index_mp_to_orig, - lvl, lt, uv[0], uv[1], NULL, n); - - mul_v3_m3v3(vec, tangmat, n); - normalize_v3_length(vec, 0.5); - add_v3_v3(vec, tmp); - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + pixel * 4; - rrgbf[0] = vec[0]; - rrgbf[1] = vec[1]; - rrgbf[2] = vec[2]; - rrgbf[3] = 1.0f; - } - else { - unsigned char *rrgb = (unsigned char *)ibuf->rect + pixel * 4; - rgb_float_to_uchar(rrgb, vec); - rrgb[3] = 255; - } + const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; + MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; + MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); + MNormalBakeData *normal_data = (MNormalBakeData *)bake_data; + float uv[2], *st0, *st1, *st2, *st3; + int pixel = ibuf->x * y + x; + float n[3], vec[3], tmp[3] = {0.5, 0.5, 0.5}; + + /* ideally we would work on triangles only, however, we rely on quads to get orthogonal + * coordinates for use in grid space (triangle barycentric is not orthogonal) */ + if (mpoly->totloop == 4) { + st0 = mloopuv[mpoly->loopstart].uv; + st1 = mloopuv[mpoly->loopstart + 1].uv; + st2 = mloopuv[mpoly->loopstart + 2].uv; + st3 = mloopuv[mpoly->loopstart + 3].uv; + resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); + } + else { + st0 = mloopuv[lt->tri[0]].uv; + st1 = mloopuv[lt->tri[1]].uv; + st2 = mloopuv[lt->tri[2]].uv; + resolve_tri_uv_v2(uv, st, st0, st1, st2); + } + + CLAMP(uv[0], 0.0f, 1.0f); + CLAMP(uv[1], 0.0f, 1.0f); + + get_ccgdm_data( + lores_dm, hires_dm, normal_data->orig_index_mp_to_orig, lvl, lt, uv[0], uv[1], NULL, n); + + mul_v3_m3v3(vec, tangmat, n); + normalize_v3_length(vec, 0.5); + add_v3_v3(vec, tmp); + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + pixel * 4; + rrgbf[0] = vec[0]; + rrgbf[1] = vec[1]; + rrgbf[2] = vec[2]; + rrgbf[3] = 1.0f; + } + else { + unsigned char *rrgb = (unsigned char *)ibuf->rect + pixel * 4; + rgb_float_to_uchar(rrgb, vec); + rrgb[3] = 255; + } } /* TODO: restore ambient occlusion baking support, using BLI BVH? */ @@ -868,294 +959,294 @@ static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, /* **************** Ambient Occlusion Baker **************** */ // must be a power of two -#define MAX_NUMBER_OF_AO_RAYS 1024 +# define MAX_NUMBER_OF_AO_RAYS 1024 static unsigned short ao_random_table_1[MAX_NUMBER_OF_AO_RAYS]; static unsigned short ao_random_table_2[MAX_NUMBER_OF_AO_RAYS]; static void init_ao_random(void) { - int i; + int i; - for (i = 0; i < MAX_NUMBER_OF_AO_RAYS; i++) { - ao_random_table_1[i] = rand() & 0xffff; - ao_random_table_2[i] = rand() & 0xffff; - } + for (i = 0; i < MAX_NUMBER_OF_AO_RAYS; i++) { + ao_random_table_1[i] = rand() & 0xffff; + ao_random_table_2[i] = rand() & 0xffff; + } } static unsigned short get_ao_random1(const int i) { - return ao_random_table_1[i & (MAX_NUMBER_OF_AO_RAYS - 1)]; + return ao_random_table_1[i & (MAX_NUMBER_OF_AO_RAYS - 1)]; } static unsigned short get_ao_random2(const int i) { - return ao_random_table_2[i & (MAX_NUMBER_OF_AO_RAYS - 1)]; + return ao_random_table_2[i & (MAX_NUMBER_OF_AO_RAYS - 1)]; } static void build_permutation_table(unsigned short permutation[], unsigned short temp_permutation[], const int number_of_rays, const int is_first_perm_table) { - int i, k; - - for (i = 0; i < number_of_rays; i++) - temp_permutation[i] = i; - - for (i = 0; i < number_of_rays; i++) { - const unsigned int nr_entries_left = number_of_rays - i; - unsigned short rnd = is_first_perm_table != false ? get_ao_random1(i) : get_ao_random2(i); - const unsigned short entry = rnd % nr_entries_left; - - /* pull entry */ - permutation[i] = temp_permutation[entry]; - - /* delete entry */ - for (k = entry; k < nr_entries_left - 1; k++) { - temp_permutation[k] = temp_permutation[k + 1]; - } - } - - /* verify permutation table - * every entry must appear exactly once - */ -#if 0 - for (i = 0; i < number_of_rays; i++) temp_permutation[i] = 0; - for (i = 0; i < number_of_rays; i++) ++temp_permutation[permutation[i]]; - for (i = 0; i < number_of_rays; i++) BLI_assert(temp_permutation[i] == 1); -#endif + int i, k; + + for (i = 0; i < number_of_rays; i++) + temp_permutation[i] = i; + + for (i = 0; i < number_of_rays; i++) { + const unsigned int nr_entries_left = number_of_rays - i; + unsigned short rnd = is_first_perm_table != false ? get_ao_random1(i) : get_ao_random2(i); + const unsigned short entry = rnd % nr_entries_left; + + /* pull entry */ + permutation[i] = temp_permutation[entry]; + + /* delete entry */ + for (k = entry; k < nr_entries_left - 1; k++) { + temp_permutation[k] = temp_permutation[k + 1]; + } + } + + /* verify permutation table + * every entry must appear exactly once + */ +# if 0 + for (i = 0; i < number_of_rays; i++) temp_permutation[i] = 0; + for (i = 0; i < number_of_rays; i++) ++temp_permutation[permutation[i]]; + for (i = 0; i < number_of_rays; i++) BLI_assert(temp_permutation[i] == 1); +# endif } static void create_ao_raytree(MultiresBakeRender *bkr, MAOBakeData *ao_data) { - DerivedMesh *hidm = bkr->hires_dm; - RayObject *raytree; - RayFace *face; - CCGElem **grid_data; - CCGKey key; - int num_grids, grid_size /*, face_side */, num_faces; - int i; - - num_grids = hidm->getNumGrids(hidm); - grid_size = hidm->getGridSize(hidm); - grid_data = hidm->getGridData(hidm); - hidm->getGridKey(hidm, &key); - - /* face_side = (grid_size << 1) - 1; */ /* UNUSED */ - num_faces = num_grids * (grid_size - 1) * (grid_size - 1); - - raytree = ao_data->raytree = RE_rayobject_create(bkr->raytrace_structure, num_faces, bkr->octree_resolution); - face = ao_data->rayfaces = (RayFace *)MEM_callocN(num_faces * sizeof(RayFace), "ObjectRen faces"); - - for (i = 0; i < num_grids; i++) { - int x, y; - for (x = 0; x < grid_size - 1; x++) { - for (y = 0; y < grid_size - 1; y++) { - float co[4][3]; - - copy_v3_v3(co[0], CCG_grid_elem_co(&key, grid_data[i], x, y)); - copy_v3_v3(co[1], CCG_grid_elem_co(&key, grid_data[i], x, y + 1)); - copy_v3_v3(co[2], CCG_grid_elem_co(&key, grid_data[i], x + 1, y + 1)); - copy_v3_v3(co[3], CCG_grid_elem_co(&key, grid_data[i], x + 1, y)); - - RE_rayface_from_coords(face, ao_data, face, co[0], co[1], co[2], co[3]); - RE_rayobject_add(raytree, RE_rayobject_unalignRayFace(face)); - - face++; - } - } - } - - RE_rayobject_done(raytree); + DerivedMesh *hidm = bkr->hires_dm; + RayObject *raytree; + RayFace *face; + CCGElem **grid_data; + CCGKey key; + int num_grids, grid_size /*, face_side */, num_faces; + int i; + + num_grids = hidm->getNumGrids(hidm); + grid_size = hidm->getGridSize(hidm); + grid_data = hidm->getGridData(hidm); + hidm->getGridKey(hidm, &key); + + /* face_side = (grid_size << 1) - 1; */ /* UNUSED */ + num_faces = num_grids * (grid_size - 1) * (grid_size - 1); + + raytree = ao_data->raytree = RE_rayobject_create(bkr->raytrace_structure, num_faces, bkr->octree_resolution); + face = ao_data->rayfaces = (RayFace *)MEM_callocN(num_faces * sizeof(RayFace), "ObjectRen faces"); + + for (i = 0; i < num_grids; i++) { + int x, y; + for (x = 0; x < grid_size - 1; x++) { + for (y = 0; y < grid_size - 1; y++) { + float co[4][3]; + + copy_v3_v3(co[0], CCG_grid_elem_co(&key, grid_data[i], x, y)); + copy_v3_v3(co[1], CCG_grid_elem_co(&key, grid_data[i], x, y + 1)); + copy_v3_v3(co[2], CCG_grid_elem_co(&key, grid_data[i], x + 1, y + 1)); + copy_v3_v3(co[3], CCG_grid_elem_co(&key, grid_data[i], x + 1, y)); + + RE_rayface_from_coords(face, ao_data, face, co[0], co[1], co[2], co[3]); + RE_rayobject_add(raytree, RE_rayobject_unalignRayFace(face)); + + face++; + } + } + } + + RE_rayobject_done(raytree); } static void *init_ao_data(MultiresBakeRender *bkr, Image *UNUSED(ima)) { - MAOBakeData *ao_data; - DerivedMesh *lodm = bkr->lores_dm; - unsigned short *temp_permutation_table; - size_t permutation_size; + MAOBakeData *ao_data; + DerivedMesh *lodm = bkr->lores_dm; + unsigned short *temp_permutation_table; + size_t permutation_size; - init_ao_random(); + init_ao_random(); - ao_data = MEM_callocN(sizeof(MAOBakeData), "MultiresBake aoData"); + ao_data = MEM_callocN(sizeof(MAOBakeData), "MultiresBake aoData"); - ao_data->number_of_rays = bkr->number_of_rays; - ao_data->bias = bkr->bias; + ao_data->number_of_rays = bkr->number_of_rays; + ao_data->bias = bkr->bias; - ao_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); + ao_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); - create_ao_raytree(bkr, ao_data); + create_ao_raytree(bkr, ao_data); - /* initialize permutation tables */ - permutation_size = sizeof(unsigned short) * bkr->number_of_rays; - ao_data->permutation_table_1 = MEM_callocN(permutation_size, "multires AO baker perm1"); - ao_data->permutation_table_2 = MEM_callocN(permutation_size, "multires AO baker perm2"); - temp_permutation_table = MEM_callocN(permutation_size, "multires AO baker temp perm"); + /* initialize permutation tables */ + permutation_size = sizeof(unsigned short) * bkr->number_of_rays; + ao_data->permutation_table_1 = MEM_callocN(permutation_size, "multires AO baker perm1"); + ao_data->permutation_table_2 = MEM_callocN(permutation_size, "multires AO baker perm2"); + temp_permutation_table = MEM_callocN(permutation_size, "multires AO baker temp perm"); - build_permutation_table(ao_data->permutation_table_1, temp_permutation_table, bkr->number_of_rays, 1); - build_permutation_table(ao_data->permutation_table_2, temp_permutation_table, bkr->number_of_rays, 0); + build_permutation_table(ao_data->permutation_table_1, temp_permutation_table, bkr->number_of_rays, 1); + build_permutation_table(ao_data->permutation_table_2, temp_permutation_table, bkr->number_of_rays, 0); - MEM_freeN(temp_permutation_table); + MEM_freeN(temp_permutation_table); - return (void *)ao_data; + return (void *)ao_data; } static void free_ao_data(void *bake_data) { - MAOBakeData *ao_data = (MAOBakeData *)bake_data; + MAOBakeData *ao_data = (MAOBakeData *)bake_data; - RE_rayobject_free(ao_data->raytree); - MEM_freeN(ao_data->rayfaces); + RE_rayobject_free(ao_data->raytree); + MEM_freeN(ao_data->rayfaces); - MEM_freeN(ao_data->permutation_table_1); - MEM_freeN(ao_data->permutation_table_2); + MEM_freeN(ao_data->permutation_table_1); + MEM_freeN(ao_data->permutation_table_2); - MEM_freeN(ao_data); + MEM_freeN(ao_data); } /* builds an X and a Y axis from the given Z axis */ static void build_coordinate_frame(float axisX[3], float axisY[3], const float axisZ[3]) { - const float faX = fabsf(axisZ[0]); - const float faY = fabsf(axisZ[1]); - const float faZ = fabsf(axisZ[2]); - - if (faX <= faY && faX <= faZ) { - const float len = sqrtf(axisZ[1] * axisZ[1] + axisZ[2] * axisZ[2]); - axisY[0] = 0; axisY[1] = axisZ[2] / len; axisY[2] = -axisZ[1] / len; - cross_v3_v3v3(axisX, axisY, axisZ); - } - else if (faY <= faZ) { - const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[2] * axisZ[2]); - axisX[0] = axisZ[2] / len; axisX[1] = 0; axisX[2] = -axisZ[0] / len; - cross_v3_v3v3(axisY, axisZ, axisX); - } - else { - const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[1] * axisZ[1]); - axisX[0] = axisZ[1] / len; axisX[1] = -axisZ[0] / len; axisX[2] = 0; - cross_v3_v3v3(axisY, axisZ, axisX); - } + const float faX = fabsf(axisZ[0]); + const float faY = fabsf(axisZ[1]); + const float faZ = fabsf(axisZ[2]); + + if (faX <= faY && faX <= faZ) { + const float len = sqrtf(axisZ[1] * axisZ[1] + axisZ[2] * axisZ[2]); + axisY[0] = 0; axisY[1] = axisZ[2] / len; axisY[2] = -axisZ[1] / len; + cross_v3_v3v3(axisX, axisY, axisZ); + } + else if (faY <= faZ) { + const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[2] * axisZ[2]); + axisX[0] = axisZ[2] / len; axisX[1] = 0; axisX[2] = -axisZ[0] / len; + cross_v3_v3v3(axisY, axisZ, axisX); + } + else { + const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[1] * axisZ[1]); + axisX[0] = axisZ[1] / len; axisX[1] = -axisZ[0] / len; axisX[2] = 0; + cross_v3_v3v3(axisY, axisZ, axisX); + } } /* return false if nothing was hit and true otherwise */ static int trace_ao_ray(MAOBakeData *ao_data, float ray_start[3], float ray_direction[3]) { - Isect isect = {{0}}; + Isect isect = {{0}}; - isect.dist = RE_RAYTRACE_MAXDIST; - copy_v3_v3(isect.start, ray_start); - copy_v3_v3(isect.dir, ray_direction); - isect.lay = -1; + isect.dist = RE_RAYTRACE_MAXDIST; + copy_v3_v3(isect.start, ray_start); + copy_v3_v3(isect.dir, ray_direction); + isect.lay = -1; - normalize_v3(isect.dir); + normalize_v3(isect.dir); - return RE_rayobject_raycast(ao_data->raytree, &isect); + return RE_rayobject_raycast(ao_data->raytree, &isect); } static void apply_ao_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void *UNUSED(thread_data), void *bake_data, ImBuf *ibuf, const int tri_index, const int lvl, const float st[2], float UNUSED(tangmat[3][3]), const int x, const int y) { - const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; - MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; - MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); - MAOBakeData *ao_data = (MAOBakeData *)bake_data; - - int i, k, perm_offs; - float pos[3], nrm[3]; - float cen[3]; - float axisX[3], axisY[3], axisZ[3]; - float shadow = 0; - float value; - int pixel = ibuf->x * y + x; - float uv[2], *st0, *st1, *st2, *st3; - - /* ideally we would work on triangles only, however, we rely on quads to get orthogonal - * coordinates for use in grid space (triangle barycentric is not orthogonal) */ - if (mpoly->totloop == 4) { - st0 = mloopuv[mpoly->loopstart].uv; - st1 = mloopuv[mpoly->loopstart + 1].uv; - st2 = mloopuv[mpoly->loopstart + 2].uv; - st3 = mloopuv[mpoly->loopstart + 3].uv; - resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); - } - else { - st0 = mloopuv[lt->tri[0]].uv; - st1 = mloopuv[lt->tri[1]].uv; - st2 = mloopuv[lt->tri[2]].uv; - resolve_tri_uv_v2(uv, st, st0, st1, st2); - } - - CLAMP(uv[0], 0.0f, 1.0f); - CLAMP(uv[1], 0.0f, 1.0f); - - get_ccgdm_data(lores_dm, hires_dm, - ao_data->orig_index_mp_to_orig, - lvl, lt, uv[0], uv[1], pos, nrm); - - /* offset ray origin by user bias along normal */ - for (i = 0; i < 3; i++) - cen[i] = pos[i] + ao_data->bias * nrm[i]; - - /* build tangent frame */ - for (i = 0; i < 3; i++) - axisZ[i] = nrm[i]; - - build_coordinate_frame(axisX, axisY, axisZ); - - /* static noise */ - perm_offs = (get_ao_random2(get_ao_random1(x) + y)) & (MAX_NUMBER_OF_AO_RAYS - 1); - - /* importance sample shadow rays (cosine weighted) */ - for (i = 0; i < ao_data->number_of_rays; i++) { - int hit_something; - - /* use N-Rooks to distribute our N ray samples across - * a multi-dimensional domain (2D) - */ - const unsigned short I = ao_data->permutation_table_1[(i + perm_offs) % ao_data->number_of_rays]; - const unsigned short J = ao_data->permutation_table_2[i]; - - const float JitPh = (get_ao_random2(I + perm_offs) & (MAX_NUMBER_OF_AO_RAYS - 1)) / ((float)MAX_NUMBER_OF_AO_RAYS); - const float JitTh = (get_ao_random1(J + perm_offs) & (MAX_NUMBER_OF_AO_RAYS - 1)) / ((float)MAX_NUMBER_OF_AO_RAYS); - const float SiSqPhi = (I + JitPh) / ao_data->number_of_rays; - const float Theta = (float)(2 * M_PI) * ((J + JitTh) / ao_data->number_of_rays); - - /* this gives results identical to the so-called cosine - * weighted distribution relative to the north pole. - */ - float SiPhi = sqrtf(SiSqPhi); - float CoPhi = SiSqPhi < 1.0f ? sqrtf(1.0f - SiSqPhi) : 0; - float CoThe = cosf(Theta); - float SiThe = sinf(Theta); - - const float dx = CoThe * CoPhi; - const float dy = SiThe * CoPhi; - const float dz = SiPhi; - - /* transform ray direction out of tangent frame */ - float dv[3]; - for (k = 0; k < 3; k++) - dv[k] = axisX[k] * dx + axisY[k] * dy + axisZ[k] * dz; - - hit_something = trace_ao_ray(ao_data, cen, dv); - - if (hit_something != 0) - shadow += 1; - } - - value = 1.0f - (shadow / ao_data->number_of_rays); - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + pixel * 4; - rrgbf[0] = rrgbf[1] = rrgbf[2] = value; - rrgbf[3] = 1.0f; - } - else { - unsigned char *rrgb = (unsigned char *)ibuf->rect + pixel * 4; - rrgb[0] = rrgb[1] = rrgb[2] = unit_float_to_uchar_clamp(value); - rrgb[3] = 255; - } + const MLoopTri *lt = lores_dm->getLoopTriArray(lores_dm) + tri_index; + MPoly *mpoly = lores_dm->getPolyArray(lores_dm) + lt->poly; + MLoopUV *mloopuv = lores_dm->getLoopDataArray(lores_dm, CD_MLOOPUV); + MAOBakeData *ao_data = (MAOBakeData *)bake_data; + + int i, k, perm_offs; + float pos[3], nrm[3]; + float cen[3]; + float axisX[3], axisY[3], axisZ[3]; + float shadow = 0; + float value; + int pixel = ibuf->x * y + x; + float uv[2], *st0, *st1, *st2, *st3; + + /* ideally we would work on triangles only, however, we rely on quads to get orthogonal + * coordinates for use in grid space (triangle barycentric is not orthogonal) */ + if (mpoly->totloop == 4) { + st0 = mloopuv[mpoly->loopstart].uv; + st1 = mloopuv[mpoly->loopstart + 1].uv; + st2 = mloopuv[mpoly->loopstart + 2].uv; + st3 = mloopuv[mpoly->loopstart + 3].uv; + resolve_quad_uv_v2(uv, st, st0, st1, st2, st3); + } + else { + st0 = mloopuv[lt->tri[0]].uv; + st1 = mloopuv[lt->tri[1]].uv; + st2 = mloopuv[lt->tri[2]].uv; + resolve_tri_uv_v2(uv, st, st0, st1, st2); + } + + CLAMP(uv[0], 0.0f, 1.0f); + CLAMP(uv[1], 0.0f, 1.0f); + + get_ccgdm_data(lores_dm, hires_dm, + ao_data->orig_index_mp_to_orig, + lvl, lt, uv[0], uv[1], pos, nrm); + + /* offset ray origin by user bias along normal */ + for (i = 0; i < 3; i++) + cen[i] = pos[i] + ao_data->bias * nrm[i]; + + /* build tangent frame */ + for (i = 0; i < 3; i++) + axisZ[i] = nrm[i]; + + build_coordinate_frame(axisX, axisY, axisZ); + + /* static noise */ + perm_offs = (get_ao_random2(get_ao_random1(x) + y)) & (MAX_NUMBER_OF_AO_RAYS - 1); + + /* importance sample shadow rays (cosine weighted) */ + for (i = 0; i < ao_data->number_of_rays; i++) { + int hit_something; + + /* use N-Rooks to distribute our N ray samples across + * a multi-dimensional domain (2D) + */ + const unsigned short I = ao_data->permutation_table_1[(i + perm_offs) % ao_data->number_of_rays]; + const unsigned short J = ao_data->permutation_table_2[i]; + + const float JitPh = (get_ao_random2(I + perm_offs) & (MAX_NUMBER_OF_AO_RAYS - 1)) / ((float)MAX_NUMBER_OF_AO_RAYS); + const float JitTh = (get_ao_random1(J + perm_offs) & (MAX_NUMBER_OF_AO_RAYS - 1)) / ((float)MAX_NUMBER_OF_AO_RAYS); + const float SiSqPhi = (I + JitPh) / ao_data->number_of_rays; + const float Theta = (float)(2 * M_PI) * ((J + JitTh) / ao_data->number_of_rays); + + /* this gives results identical to the so-called cosine + * weighted distribution relative to the north pole. + */ + float SiPhi = sqrtf(SiSqPhi); + float CoPhi = SiSqPhi < 1.0f ? sqrtf(1.0f - SiSqPhi) : 0; + float CoThe = cosf(Theta); + float SiThe = sinf(Theta); + + const float dx = CoThe * CoPhi; + const float dy = SiThe * CoPhi; + const float dz = SiPhi; + + /* transform ray direction out of tangent frame */ + float dv[3]; + for (k = 0; k < 3; k++) + dv[k] = axisX[k] * dx + axisY[k] * dy + axisZ[k] * dz; + + hit_something = trace_ao_ray(ao_data, cen, dv); + + if (hit_something != 0) + shadow += 1; + } + + value = 1.0f - (shadow / ao_data->number_of_rays); + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + pixel * 4; + rrgbf[0] = rrgbf[1] = rrgbf[2] = value; + rrgbf[3] = 1.0f; + } + else { + unsigned char *rrgb = (unsigned char *)ibuf->rect + pixel * 4; + rrgb[0] = rrgb[1] = rrgb[2] = unit_float_to_uchar_clamp(value); + rrgb[3] = 255; + } } #endif @@ -1163,181 +1254,193 @@ static void apply_ao_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void static void bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter) { - /* must check before filtering */ - const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); - - /* Margin */ - if (filter) { - IMB_filter_extend(ibuf, mask, filter); - } - - /* if the bake results in new alpha then change the image setting */ - if (is_new_alpha) { - ibuf->planes = R_IMF_PLANES_RGBA; - } - else { - if (filter && ibuf->planes != R_IMF_PLANES_RGBA) { - /* clear alpha added by filtering */ - IMB_rectfill_alpha(ibuf, 1.0f); - } - } + /* must check before filtering */ + const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); + + /* Margin */ + if (filter) { + IMB_filter_extend(ibuf, mask, filter); + } + + /* if the bake results in new alpha then change the image setting */ + if (is_new_alpha) { + ibuf->planes = R_IMF_PLANES_RGBA; + } + else { + if (filter && ibuf->planes != R_IMF_PLANES_RGBA) { + /* clear alpha added by filtering */ + IMB_rectfill_alpha(ibuf, 1.0f); + } + } } -static void bake_ibuf_normalize_displacement(ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max) +static void bake_ibuf_normalize_displacement( + ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max) { - int i; - const float *current_displacement = displacement; - const char *current_mask = mask; - float max_distance; - - max_distance = max_ff(fabsf(displacement_min), fabsf(displacement_max)); - - for (i = 0; i < ibuf->x * ibuf->y; i++) { - if (*current_mask == FILTER_MASK_USED) { - float normalized_displacement; - - if (max_distance > 1e-5f) - normalized_displacement = (*current_displacement + max_distance) / (max_distance * 2); - else - normalized_displacement = 0.5f; - - if (ibuf->rect_float) { - /* currently baking happens to RGBA only */ - float *fp = ibuf->rect_float + i * 4; - fp[0] = fp[1] = fp[2] = normalized_displacement; - fp[3] = 1.0f; - } - - if (ibuf->rect) { - unsigned char *cp = (unsigned char *)(ibuf->rect + i); - cp[0] = cp[1] = cp[2] = unit_float_to_uchar_clamp(normalized_displacement); - cp[3] = 255; - } - } - - current_displacement++; - current_mask++; - } + int i; + const float *current_displacement = displacement; + const char *current_mask = mask; + float max_distance; + + max_distance = max_ff(fabsf(displacement_min), fabsf(displacement_max)); + + for (i = 0; i < ibuf->x * ibuf->y; i++) { + if (*current_mask == FILTER_MASK_USED) { + float normalized_displacement; + + if (max_distance > 1e-5f) + normalized_displacement = (*current_displacement + max_distance) / (max_distance * 2); + else + normalized_displacement = 0.5f; + + if (ibuf->rect_float) { + /* currently baking happens to RGBA only */ + float *fp = ibuf->rect_float + i * 4; + fp[0] = fp[1] = fp[2] = normalized_displacement; + fp[3] = 1.0f; + } + + if (ibuf->rect) { + unsigned char *cp = (unsigned char *)(ibuf->rect + i); + cp[0] = cp[1] = cp[2] = unit_float_to_uchar_clamp(normalized_displacement); + cp[3] = 255; + } + } + + current_displacement++; + current_mask++; + } } /* **************** Common functions public API relates on **************** */ static void count_images(MultiresBakeRender *bkr) { - BLI_listbase_clear(&bkr->image); - bkr->tot_image = 0; - - for (int i = 0; i < bkr->ob_image.len; i++) { - Image *ima = bkr->ob_image.array[i]; - if (ima) { - ima->id.tag &= ~LIB_TAG_DOIT; - } - } - - for (int i = 0; i < bkr->ob_image.len; i++) { - Image *ima = bkr->ob_image.array[i]; - if (ima) { - if ((ima->id.tag & LIB_TAG_DOIT) == 0) { - LinkData *data = BLI_genericNodeN(ima); - BLI_addtail(&bkr->image, data); - bkr->tot_image++; - ima->id.tag |= LIB_TAG_DOIT; - } - } - } - - for (int i = 0; i < bkr->ob_image.len; i++) { - Image *ima = bkr->ob_image.array[i]; - if (ima) { - ima->id.tag &= ~LIB_TAG_DOIT; - } - } + BLI_listbase_clear(&bkr->image); + bkr->tot_image = 0; + + for (int i = 0; i < bkr->ob_image.len; i++) { + Image *ima = bkr->ob_image.array[i]; + if (ima) { + ima->id.tag &= ~LIB_TAG_DOIT; + } + } + + for (int i = 0; i < bkr->ob_image.len; i++) { + Image *ima = bkr->ob_image.array[i]; + if (ima) { + if ((ima->id.tag & LIB_TAG_DOIT) == 0) { + LinkData *data = BLI_genericNodeN(ima); + BLI_addtail(&bkr->image, data); + bkr->tot_image++; + ima->id.tag |= LIB_TAG_DOIT; + } + } + } + + for (int i = 0; i < bkr->ob_image.len; i++) { + Image *ima = bkr->ob_image.array[i]; + if (ima) { + ima->id.tag &= ~LIB_TAG_DOIT; + } + } } static void bake_images(MultiresBakeRender *bkr, MultiresBakeResult *result) { - LinkData *link; - - for (link = bkr->image.first; link; link = link->next) { - Image *ima = (Image *)link->data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - if (ibuf->x > 0 && ibuf->y > 0) { - BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData), "MultiresBake userdata"); - userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask"); - ibuf->userdata = userdata; - - switch (bkr->mode) { - case RE_BAKE_NORMALS: - do_multires_bake(bkr, ima, true, apply_tangmat_callback, init_normal_data, free_normal_data, result); - break; - case RE_BAKE_DISPLACEMENT: - do_multires_bake(bkr, ima, false, apply_heights_callback, init_heights_data, free_heights_data, result); - break; + LinkData *link; + + for (link = bkr->image.first; link; link = link->next) { + Image *ima = (Image *)link->data; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + + if (ibuf->x > 0 && ibuf->y > 0) { + BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData), + "MultiresBake userdata"); + userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask"); + ibuf->userdata = userdata; + + switch (bkr->mode) { + case RE_BAKE_NORMALS: + do_multires_bake( + bkr, ima, true, apply_tangmat_callback, init_normal_data, free_normal_data, result); + break; + case RE_BAKE_DISPLACEMENT: + do_multires_bake(bkr, + ima, + false, + apply_heights_callback, + init_heights_data, + free_heights_data, + result); + break; /* TODO: restore ambient occlusion baking support. */ #if 0 - case RE_BAKE_AO: - do_multires_bake(bkr, ima, false, apply_ao_callback, init_ao_data, free_ao_data, result); - break; + case RE_BAKE_AO: + do_multires_bake(bkr, ima, false, apply_ao_callback, init_ao_data, free_ao_data, result); + break; #endif - } - } + } + } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, NULL); - ima->id.tag |= LIB_TAG_DOIT; - } + ima->id.tag |= LIB_TAG_DOIT; + } } static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result) { - LinkData *link; - bool use_displacement_buffer = bkr->mode == RE_BAKE_DISPLACEMENT; + LinkData *link; + bool use_displacement_buffer = bkr->mode == RE_BAKE_DISPLACEMENT; - for (link = bkr->image.first; link; link = link->next) { - Image *ima = (Image *)link->data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; + for (link = bkr->image.first; link; link = link->next) { + Image *ima = (Image *)link->data; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; - if (ibuf->x <= 0 || ibuf->y <= 0) - continue; + if (ibuf->x <= 0 || ibuf->y <= 0) + continue; - if (use_displacement_buffer) { - bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer, - result->height_min, result->height_max); - } + if (use_displacement_buffer) { + bake_ibuf_normalize_displacement(ibuf, + userdata->displacement_buffer, + userdata->mask_buffer, + result->height_min, + result->height_max); + } - bake_ibuf_filter(ibuf, userdata->mask_buffer, bkr->bake_filter); + bake_ibuf_filter(ibuf, userdata->mask_buffer, bkr->bake_filter); - ibuf->userflags |= IB_BITMAPDIRTY | IB_DISPLAY_BUFFER_INVALID; + ibuf->userflags |= IB_BITMAPDIRTY | IB_DISPLAY_BUFFER_INVALID; - if (ibuf->rect_float) - ibuf->userflags |= IB_RECT_INVALID; + if (ibuf->rect_float) + ibuf->userflags |= IB_RECT_INVALID; - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; - imb_freemipmapImBuf(ibuf); - } + if (ibuf->mipmap[0]) { + ibuf->userflags |= IB_MIPMAP_INVALID; + imb_freemipmapImBuf(ibuf); + } - if (ibuf->userdata) { - if (userdata->displacement_buffer) - MEM_freeN(userdata->displacement_buffer); + if (ibuf->userdata) { + if (userdata->displacement_buffer) + MEM_freeN(userdata->displacement_buffer); - MEM_freeN(userdata->mask_buffer); - MEM_freeN(userdata); - ibuf->userdata = NULL; - } + MEM_freeN(userdata->mask_buffer); + MEM_freeN(userdata); + ibuf->userdata = NULL; + } - BKE_image_release_ibuf(ima, ibuf, NULL); - DEG_id_tag_update(&ima->id, 0); - } + BKE_image_release_ibuf(ima, ibuf, NULL); + DEG_id_tag_update(&ima->id, 0); + } } void RE_multires_bake_images(MultiresBakeRender *bkr) { - MultiresBakeResult result; + MultiresBakeResult result; - count_images(bkr); - bake_images(bkr, &result); - finish_images(bkr, &result); + count_images(bkr); + bake_images(bkr, &result); + finish_images(bkr, &result); } diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 021abe89105..b6aa9904747 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -52,7 +52,7 @@ #include "BLT_translation.h" -#include "BKE_animsys.h" /* <------ should this be here?, needed for sequencer update */ +#include "BKE_animsys.h" /* <------ should this be here?, needed for sequencer update */ #include "BKE_camera.h" #include "BKE_colortools.h" #include "BKE_context.h" /* XXX needed by wm_window.h */ @@ -70,7 +70,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_sound.h" -#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ +#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -86,7 +86,7 @@ #include "RE_pipeline.h" #include "RE_render_ext.h" -#include "../../../windowmanager/WM_api.h" /* XXX */ +#include "../../../windowmanager/WM_api.h" /* XXX */ #include "../../../windowmanager/wm_window.h" /* XXX */ #include "GPU_context.h" @@ -126,239 +126,273 @@ * - save file or append in movie */ - /* ********* globals ******** */ /* here we store all renders */ static struct { - ListBase renderlist; + ListBase renderlist; } RenderGlobal = {{NULL, NULL}}; /* ********* alloc and free ******** */ -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const int totvideos, const char *name_override); +static int do_write_image_or_movie(Render *re, + Main *bmain, + Scene *scene, + bMovieHandle *mh, + const int totvideos, + const char *name_override); /* default callbacks, set in each new render */ -static void result_nothing(void *UNUSED(arg), RenderResult *UNUSED(rr)) {} -static void result_rcti_nothing(void *UNUSED(arg), RenderResult *UNUSED(rr), volatile struct rcti *UNUSED(rect)) {} -static void current_scene_nothing(void *UNUSED(arg), Scene *UNUSED(scene)) {} -static void stats_nothing(void *UNUSED(arg), RenderStats *UNUSED(rs)) {} -static void float_nothing(void *UNUSED(arg), float UNUSED(val)) {} -static int default_break(void *UNUSED(arg)) { return G.is_break == true; } - -static void stats_background(void *UNUSED(arg), RenderStats *rs) +static void result_nothing(void *UNUSED(arg), RenderResult *UNUSED(rr)) { - uintptr_t mem_in_use, mmap_in_use, peak_memory; - float megs_used_memory, mmap_used_memory, megs_peak_memory; - char info_time_str[32]; - - mem_in_use = MEM_get_memory_in_use(); - mmap_in_use = MEM_get_mapped_memory_in_use(); - peak_memory = MEM_get_peak_memory(); - - megs_used_memory = (mem_in_use - mmap_in_use) / (1024.0 * 1024.0); - mmap_used_memory = (mmap_in_use) / (1024.0 * 1024.0); - megs_peak_memory = (peak_memory) / (1024.0 * 1024.0); - - fprintf(stdout, IFACE_("Fra:%d Mem:%.2fM (%.2fM, Peak %.2fM) "), rs->cfra, - megs_used_memory, mmap_used_memory, megs_peak_memory); - - if (rs->curfield) - fprintf(stdout, IFACE_("Field %d "), rs->curfield); - if (rs->curblur) - fprintf(stdout, IFACE_("Blur %d "), rs->curblur); - - BLI_timecode_string_from_time_simple(info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime); - fprintf(stdout, IFACE_("| Time:%s | "), info_time_str); - - if (rs->infostr) { - fprintf(stdout, "%s", rs->infostr); - } - else { - if (rs->tothalo) - fprintf(stdout, IFACE_("Sce: %s Ve:%d Fa:%d Ha:%d La:%d"), - rs->scene_name, rs->totvert, rs->totface, rs->tothalo, rs->totlamp); - else - fprintf(stdout, IFACE_("Sce: %s Ve:%d Fa:%d La:%d"), rs->scene_name, rs->totvert, rs->totface, rs->totlamp); - } - - /* Flush stdout to be sure python callbacks are printing stuff after blender. */ - fflush(stdout); - - /* NOTE: using G_MAIN seems valid here??? Not sure it's actually even used anyway, we could as well pass NULL? */ - BLI_callback_exec(G_MAIN, NULL, BLI_CB_EVT_RENDER_STATS); - - fputc('\n', stdout); - fflush(stdout); } - -static void render_print_save_message( - ReportList *reports, const char *name, int ok, int err) +static void result_rcti_nothing(void *UNUSED(arg), + RenderResult *UNUSED(rr), + volatile struct rcti *UNUSED(rect)) { - if (ok) { - /* no need to report, just some helpful console info */ - printf("Saved: '%s'\n", name); - } - else { - /* report on error since users will want to know what failed */ - BKE_reportf(reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), name); - } } - -static int render_imbuf_write_stamp_test( - ReportList *reports, - Scene *scene, struct RenderResult *rr, ImBuf *ibuf, const char *name, - const ImageFormatData *imf, bool stamp) +static void current_scene_nothing(void *UNUSED(arg), Scene *UNUSED(scene)) { - int ok; - - if (stamp) { - /* writes the name of the individual cameras */ - ok = BKE_imbuf_write_stamp(scene, rr, ibuf, name, imf); - } - else { - ok = BKE_imbuf_write(ibuf, name, imf); - } - - render_print_save_message(reports, name, ok, errno); +} +static void stats_nothing(void *UNUSED(arg), RenderStats *UNUSED(rs)) +{ +} +static void float_nothing(void *UNUSED(arg), float UNUSED(val)) +{ +} +static int default_break(void *UNUSED(arg)) +{ + return G.is_break == true; +} - return ok; +static void stats_background(void *UNUSED(arg), RenderStats *rs) +{ + uintptr_t mem_in_use, mmap_in_use, peak_memory; + float megs_used_memory, mmap_used_memory, megs_peak_memory; + char info_time_str[32]; + + mem_in_use = MEM_get_memory_in_use(); + mmap_in_use = MEM_get_mapped_memory_in_use(); + peak_memory = MEM_get_peak_memory(); + + megs_used_memory = (mem_in_use - mmap_in_use) / (1024.0 * 1024.0); + mmap_used_memory = (mmap_in_use) / (1024.0 * 1024.0); + megs_peak_memory = (peak_memory) / (1024.0 * 1024.0); + + fprintf(stdout, + IFACE_("Fra:%d Mem:%.2fM (%.2fM, Peak %.2fM) "), + rs->cfra, + megs_used_memory, + mmap_used_memory, + megs_peak_memory); + + if (rs->curfield) + fprintf(stdout, IFACE_("Field %d "), rs->curfield); + if (rs->curblur) + fprintf(stdout, IFACE_("Blur %d "), rs->curblur); + + BLI_timecode_string_from_time_simple( + info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime); + fprintf(stdout, IFACE_("| Time:%s | "), info_time_str); + + if (rs->infostr) { + fprintf(stdout, "%s", rs->infostr); + } + else { + if (rs->tothalo) + fprintf(stdout, + IFACE_("Sce: %s Ve:%d Fa:%d Ha:%d La:%d"), + rs->scene_name, + rs->totvert, + rs->totface, + rs->tothalo, + rs->totlamp); + else + fprintf(stdout, + IFACE_("Sce: %s Ve:%d Fa:%d La:%d"), + rs->scene_name, + rs->totvert, + rs->totface, + rs->totlamp); + } + + /* Flush stdout to be sure python callbacks are printing stuff after blender. */ + fflush(stdout); + + /* NOTE: using G_MAIN seems valid here??? Not sure it's actually even used anyway, we could as well pass NULL? */ + BLI_callback_exec(G_MAIN, NULL, BLI_CB_EVT_RENDER_STATS); + + fputc('\n', stdout); + fflush(stdout); +} + +static void render_print_save_message(ReportList *reports, const char *name, int ok, int err) +{ + if (ok) { + /* no need to report, just some helpful console info */ + printf("Saved: '%s'\n", name); + } + else { + /* report on error since users will want to know what failed */ + BKE_reportf(reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), name); + } +} + +static int render_imbuf_write_stamp_test(ReportList *reports, + Scene *scene, + struct RenderResult *rr, + ImBuf *ibuf, + const char *name, + const ImageFormatData *imf, + bool stamp) +{ + int ok; + + if (stamp) { + /* writes the name of the individual cameras */ + ok = BKE_imbuf_write_stamp(scene, rr, ibuf, name, imf); + } + else { + ok = BKE_imbuf_write(ibuf, name, imf); + } + + render_print_save_message(reports, name, ok, errno); + + return ok; } void RE_FreeRenderResult(RenderResult *res) { - render_result_free(res); + render_result_free(res); } float *RE_RenderLayerGetPass(volatile RenderLayer *rl, const char *name, const char *viewname) { - RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname); - return rpass ? rpass->rect : NULL; + RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname); + return rpass ? rpass->rect : NULL; } RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name) { - if (rr == NULL) { - return NULL; - } - else { - return BLI_findstring(&rr->layers, name, offsetof(RenderLayer, name)); - } + if (rr == NULL) { + return NULL; + } + else { + return BLI_findstring(&rr->layers, name, offsetof(RenderLayer, name)); + } } bool RE_HasSingleLayer(Render *re) { - return (re->r.scemode & R_SINGLE_LAYER); + return (re->r.scemode & R_SINGLE_LAYER); } -RenderResult *RE_MultilayerConvert(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) +RenderResult *RE_MultilayerConvert( + void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) { - return render_result_new_from_exr(exrhandle, colorspace, predivide, rectx, recty); + return render_result_new_from_exr(exrhandle, colorspace, predivide, rectx, recty); } RenderLayer *render_get_active_layer(Render *re, RenderResult *rr) { - ViewLayer *view_layer = BLI_findlink(&re->view_layers, re->active_view_layer); + ViewLayer *view_layer = BLI_findlink(&re->view_layers, re->active_view_layer); - if (view_layer) { - RenderLayer *rl = BLI_findstring(&rr->layers, - view_layer->name, - offsetof(RenderLayer, name)); + if (view_layer) { + RenderLayer *rl = BLI_findstring(&rr->layers, view_layer->name, offsetof(RenderLayer, name)); - if (rl) { - return rl; - } - } + if (rl) { + return rl; + } + } - return rr->layers.first; + return rr->layers.first; } static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_layer) { - if (single_layer) { - return true; - } + if (single_layer) { + return true; + } - ViewLayer *view_layer; - for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { - if (view_layer->flag & VIEW_LAYER_RENDER) { - return true; - } - } - return false; + ViewLayer *view_layer; + for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { + if (view_layer->flag & VIEW_LAYER_RENDER) { + return true; + } + } + return false; } /* *************************************************** */ Render *RE_GetRender(const char *name) { - Render *re; + Render *re; - /* search for existing renders */ - for (re = RenderGlobal.renderlist.first; re; re = re->next) - if (STREQLEN(re->name, name, RE_MAXNAME)) - break; + /* search for existing renders */ + for (re = RenderGlobal.renderlist.first; re; re = re->next) + if (STREQLEN(re->name, name, RE_MAXNAME)) + break; - return re; + return re; } /* if you want to know exactly what has been done */ RenderResult *RE_AcquireResultRead(Render *re) { - if (re) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); - return re->result; - } + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + return re->result; + } - return NULL; + return NULL; } RenderResult *RE_AcquireResultWrite(Render *re) { - if (re) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - return re->result; - } + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + return re->result; + } - return NULL; + return NULL; } void RE_ClearResult(Render *re) { - if (re) { - render_result_free(re->result); - re->result = NULL; - } + if (re) { + render_result_free(re->result); + re->result = NULL; + } } void RE_SwapResult(Render *re, RenderResult **rr) { - /* for keeping render buffers */ - if (re) { - SWAP(RenderResult *, re->result, *rr); - } + /* for keeping render buffers */ + if (re) { + SWAP(RenderResult *, re->result, *rr); + } } - void RE_ReleaseResult(Render *re) { - if (re) - BLI_rw_mutex_unlock(&re->resultmutex); + if (re) + BLI_rw_mutex_unlock(&re->resultmutex); } /* displist.c util.... */ Scene *RE_GetScene(Render *re) { - if (re) - return re->scene; - return NULL; + if (re) + return re->scene; + return NULL; } void RE_SetScene(Render *re, Scene *sce) { - if (re) { - re->scene = sce; - } + if (re) { + re->scene = sce; + } } /** @@ -368,58 +402,58 @@ void RE_SetScene(Render *re, Scene *sce) */ void RE_AcquireResultImageViews(Render *re, RenderResult *rr) { - memset(rr, 0, sizeof(RenderResult)); + memset(rr, 0, sizeof(RenderResult)); - if (re) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); - if (re->result) { - RenderLayer *rl; - RenderView *rv, *rview; + if (re->result) { + RenderLayer *rl; + RenderView *rv, *rview; - rr->rectx = re->result->rectx; - rr->recty = re->result->recty; + rr->rectx = re->result->rectx; + rr->recty = re->result->recty; - /* creates a temporary duplication of views */ - render_result_views_shallowcopy(rr, re->result); + /* creates a temporary duplication of views */ + render_result_views_shallowcopy(rr, re->result); - rv = rr->views.first; - rr->have_combined = (rv->rectf != NULL); + rv = rr->views.first; + rr->have_combined = (rv->rectf != NULL); - /* active layer */ - rl = render_get_active_layer(re, re->result); + /* active layer */ + rl = render_get_active_layer(re, re->result); - if (rl) { - if (rv->rectf == NULL) { - for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { - rview->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rview->name); - } - } + if (rl) { + if (rv->rectf == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rview->name); + } + } - if (rv->rectz == NULL) { - for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { - rview->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rview->name); - } - } - } + if (rv->rectz == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rview->name); + } + } + } - rr->layers = re->result->layers; - rr->xof = re->disprect.xmin; - rr->yof = re->disprect.ymin; - rr->stamp_data = re->result->stamp_data; - } - } + rr->layers = re->result->layers; + rr->xof = re->disprect.xmin; + rr->yof = re->disprect.ymin; + rr->stamp_data = re->result->stamp_data; + } + } } /* clear temporary renderresult struct */ void RE_ReleaseResultImageViews(Render *re, RenderResult *rr) { - if (re) { - if (rr) { - render_result_views_shallowdelete(rr); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } + if (re) { + if (rr) { + render_result_views_shallowdelete(rr); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } } /* fill provided result struct with what's currently active or done */ @@ -427,224 +461,236 @@ void RE_ReleaseResultImageViews(Render *re, RenderResult *rr) /* always having at least one RenderView */ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id) { - memset(rr, 0, sizeof(RenderResult)); + memset(rr, 0, sizeof(RenderResult)); - if (re) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); - if (re->result) { - RenderLayer *rl; - RenderView *rv; + if (re->result) { + RenderLayer *rl; + RenderView *rv; - rr->rectx = re->result->rectx; - rr->recty = re->result->recty; + rr->rectx = re->result->rectx; + rr->recty = re->result->recty; - /* actview view */ - rv = RE_RenderViewGetById(re->result, view_id); - rr->have_combined = (rv->rectf != NULL); + /* actview view */ + rv = RE_RenderViewGetById(re->result, view_id); + rr->have_combined = (rv->rectf != NULL); - rr->rectf = rv->rectf; - rr->rectz = rv->rectz; - rr->rect32 = rv->rect32; + rr->rectf = rv->rectf; + rr->rectz = rv->rectz; + rr->rect32 = rv->rect32; - /* active layer */ - rl = render_get_active_layer(re, re->result); + /* active layer */ + rl = render_get_active_layer(re, re->result); - if (rl) { - if (rv->rectf == NULL) - rr->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rv->name); + if (rl) { + if (rv->rectf == NULL) + rr->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rv->name); - if (rv->rectz == NULL) - rr->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rv->name); - } + if (rv->rectz == NULL) + rr->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rv->name); + } - rr->layers = re->result->layers; - rr->views = re->result->views; + rr->layers = re->result->layers; + rr->views = re->result->views; - rr->xof = re->disprect.xmin; - rr->yof = re->disprect.ymin; + rr->xof = re->disprect.xmin; + rr->yof = re->disprect.ymin; - rr->stamp_data = re->result->stamp_data; - } - } + rr->stamp_data = re->result->stamp_data; + } + } } void RE_ReleaseResultImage(Render *re) { - if (re) - BLI_rw_mutex_unlock(&re->resultmutex); + if (re) + BLI_rw_mutex_unlock(&re->resultmutex); } /* caller is responsible for allocating rect in correct size! */ void RE_ResultGet32(Render *re, unsigned int *rect) { - RenderResult rres; - const int view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname); + RenderResult rres; + const int view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname); - RE_AcquireResultImageViews(re, &rres); - render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id); - RE_ReleaseResultImageViews(re, &rres); + RE_AcquireResultImageViews(re, &rres); + render_result_rect_get_pixels(&rres, + rect, + re->rectx, + re->recty, + &re->scene->view_settings, + &re->scene->display_settings, + view_id); + RE_ReleaseResultImageViews(re, &rres); } /* caller is responsible for allocating rect in correct size! */ /* Only for acquired results, for lock */ -void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect, const int view_id) +void RE_AcquiredResultGet32(Render *re, + RenderResult *result, + unsigned int *rect, + const int view_id) { - render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id); + render_result_rect_get_pixels(result, + rect, + re->rectx, + re->recty, + &re->scene->view_settings, + &re->scene->display_settings, + view_id); } RenderStats *RE_GetStats(Render *re) { - return &re->i; + return &re->i; } Render *RE_NewRender(const char *name) { - Render *re; + Render *re; - /* only one render per name exists */ - re = RE_GetRender(name); - if (re == NULL) { + /* only one render per name exists */ + re = RE_GetRender(name); + if (re == NULL) { - /* new render data struct */ - re = MEM_callocN(sizeof(Render), "new render"); - BLI_addtail(&RenderGlobal.renderlist, re); - BLI_strncpy(re->name, name, RE_MAXNAME); - BLI_rw_mutex_init(&re->resultmutex); - BLI_rw_mutex_init(&re->partsmutex); - } + /* new render data struct */ + re = MEM_callocN(sizeof(Render), "new render"); + BLI_addtail(&RenderGlobal.renderlist, re); + BLI_strncpy(re->name, name, RE_MAXNAME); + BLI_rw_mutex_init(&re->resultmutex); + BLI_rw_mutex_init(&re->partsmutex); + } - RE_InitRenderCB(re); + RE_InitRenderCB(re); - return re; + return re; } /* MAX_ID_NAME + sizeof(Library->name) + space + null-terminator. */ #define MAX_SCENE_RENDER_NAME (MAX_ID_NAME + 1024 + 2) -static void scene_render_name_get(const Scene *scene, - const size_t max_size, - char *render_name) +static void scene_render_name_get(const Scene *scene, const size_t max_size, char *render_name) { - if (ID_IS_LINKED(scene)) { - BLI_snprintf(render_name, max_size, "%s %s", - scene->id.lib->id.name, scene->id.name); - } - else { - BLI_snprintf(render_name, max_size, "%s", scene->id.name); - } + if (ID_IS_LINKED(scene)) { + BLI_snprintf(render_name, max_size, "%s %s", scene->id.lib->id.name, scene->id.name); + } + else { + BLI_snprintf(render_name, max_size, "%s", scene->id.name); + } } Render *RE_GetSceneRender(const Scene *scene) { - char render_name[MAX_SCENE_RENDER_NAME]; - scene_render_name_get(scene, sizeof(render_name), render_name); - return RE_GetRender(render_name); + char render_name[MAX_SCENE_RENDER_NAME]; + scene_render_name_get(scene, sizeof(render_name), render_name); + return RE_GetRender(render_name); } Render *RE_NewSceneRender(const Scene *scene) { - char render_name[MAX_SCENE_RENDER_NAME]; - scene_render_name_get(scene, sizeof(render_name), render_name); - return RE_NewRender(render_name); + char render_name[MAX_SCENE_RENDER_NAME]; + scene_render_name_get(scene, sizeof(render_name), render_name); + return RE_NewRender(render_name); } /* called for new renders and when finishing rendering so * we always have valid callbacks on a render */ void RE_InitRenderCB(Render *re) { - /* set default empty callbacks */ - re->display_init = result_nothing; - re->display_clear = result_nothing; - re->display_update = result_rcti_nothing; - re->current_scene_update = current_scene_nothing; - re->progress = float_nothing; - re->test_break = default_break; - if (G.background) - re->stats_draw = stats_background; - else - re->stats_draw = stats_nothing; - /* clear callback handles */ - re->dih = re->dch = re->duh = re->sdh = re->prh = re->tbh = NULL; + /* set default empty callbacks */ + re->display_init = result_nothing; + re->display_clear = result_nothing; + re->display_update = result_rcti_nothing; + re->current_scene_update = current_scene_nothing; + re->progress = float_nothing; + re->test_break = default_break; + if (G.background) + re->stats_draw = stats_background; + else + re->stats_draw = stats_nothing; + /* clear callback handles */ + re->dih = re->dch = re->duh = re->sdh = re->prh = re->tbh = NULL; } /* only call this while you know it will remove the link too */ void RE_FreeRender(Render *re) { - if (re->engine) - RE_engine_free(re->engine); + if (re->engine) + RE_engine_free(re->engine); - BLI_rw_mutex_end(&re->resultmutex); - BLI_rw_mutex_end(&re->partsmutex); + BLI_rw_mutex_end(&re->resultmutex); + BLI_rw_mutex_end(&re->partsmutex); - BLI_freelistN(&re->view_layers); - BLI_freelistN(&re->r.views); + BLI_freelistN(&re->view_layers); + BLI_freelistN(&re->r.views); - curvemapping_free_data(&re->r.mblur_shutter_curve); + curvemapping_free_data(&re->r.mblur_shutter_curve); - /* main dbase can already be invalid now, some database-free code checks it */ - re->main = NULL; - re->scene = NULL; + /* main dbase can already be invalid now, some database-free code checks it */ + re->main = NULL; + re->scene = NULL; - render_result_free(re->result); - render_result_free(re->pushedresult); + render_result_free(re->result); + render_result_free(re->pushedresult); - BLI_remlink(&RenderGlobal.renderlist, re); - MEM_freeN(re); + BLI_remlink(&RenderGlobal.renderlist, re); + MEM_freeN(re); } /* exit blender */ void RE_FreeAllRender(void) { - while (RenderGlobal.renderlist.first) { - RE_FreeRender(RenderGlobal.renderlist.first); - } + while (RenderGlobal.renderlist.first) { + RE_FreeRender(RenderGlobal.renderlist.first); + } #ifdef WITH_FREESTYLE - /* finalize Freestyle */ - FRS_exit(); + /* finalize Freestyle */ + FRS_exit(); #endif } void RE_FreeAllPersistentData(void) { - Render *re; - for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) { - if ((re->r.mode & R_PERSISTENT_DATA) != 0 && re->engine != NULL) { - RE_engine_free(re->engine); - re->engine = NULL; - } - } + Render *re; + for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) { + if ((re->r.mode & R_PERSISTENT_DATA) != 0 && re->engine != NULL) { + RE_engine_free(re->engine); + re->engine = NULL; + } + } } /* on file load, free all re */ void RE_FreeAllRenderResults(void) { - Render *re; + Render *re; - for (re = RenderGlobal.renderlist.first; re; re = re->next) { - render_result_free(re->result); - render_result_free(re->pushedresult); + for (re = RenderGlobal.renderlist.first; re; re = re->next) { + render_result_free(re->result); + render_result_free(re->pushedresult); - re->result = NULL; - re->pushedresult = NULL; - } + re->result = NULL; + re->pushedresult = NULL; + } } void RE_FreePersistentData(void) { - Render *re; + Render *re; - /* render engines can be kept around for quick re-render, this clears all */ - for (re = RenderGlobal.renderlist.first; re; re = re->next) { - if (re->engine) { - /* if engine is currently rendering, just tag it to be freed when render is finished */ - if (!(re->engine->flag & RE_ENGINE_RENDERING)) - RE_engine_free(re->engine); + /* render engines can be kept around for quick re-render, this clears all */ + for (re = RenderGlobal.renderlist.first; re; re = re->next) { + if (re->engine) { + /* if engine is currently rendering, just tag it to be freed when render is finished */ + if (!(re->engine->flag & RE_ENGINE_RENDERING)) + RE_engine_free(re->engine); - re->engine = NULL; - } - } + re->engine = NULL; + } + } } /* ********* initialize state ******** */ @@ -652,244 +698,238 @@ void RE_FreePersistentData(void) /* clear full sample and tile flags if needed */ static int check_mode_full_sample(RenderData *rd) { - int scemode = rd->scemode; + int scemode = rd->scemode; - /* not supported by any current renderer */ - scemode &= ~R_FULL_SAMPLE; + /* not supported by any current renderer */ + scemode &= ~R_FULL_SAMPLE; #ifdef WITH_OPENEXR - if (scemode & R_FULL_SAMPLE) - scemode |= R_EXR_TILE_FILE; /* enable automatic */ + if (scemode & R_FULL_SAMPLE) + scemode |= R_EXR_TILE_FILE; /* enable automatic */ #else - /* can't do this without openexr support */ - scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); + /* can't do this without openexr support */ + scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); #endif - return scemode; + return scemode; } -static void re_init_resolution(Render *re, Render *source, - int winx, int winy, rcti *disprect) +static void re_init_resolution(Render *re, Render *source, int winx, int winy, rcti *disprect) { - re->winx = winx; - re->winy = winy; - if (source && (source->r.mode & R_BORDER)) { - /* eeh, doesn't seem original bordered disprect is storing anywhere - * after insertion on black happening in do_render(), - * so for now simply re-calculate disprect using border from source - * renderer (sergey) - */ + re->winx = winx; + re->winy = winy; + if (source && (source->r.mode & R_BORDER)) { + /* eeh, doesn't seem original bordered disprect is storing anywhere + * after insertion on black happening in do_render(), + * so for now simply re-calculate disprect using border from source + * renderer (sergey) + */ - re->disprect.xmin = source->r.border.xmin * winx; - re->disprect.xmax = source->r.border.xmax * winx; + re->disprect.xmin = source->r.border.xmin * winx; + re->disprect.xmax = source->r.border.xmax * winx; - re->disprect.ymin = source->r.border.ymin * winy; - re->disprect.ymax = source->r.border.ymax * winy; + re->disprect.ymin = source->r.border.ymin * winy; + re->disprect.ymax = source->r.border.ymax * winy; - re->rectx = BLI_rcti_size_x(&re->disprect); - re->recty = BLI_rcti_size_y(&re->disprect); + re->rectx = BLI_rcti_size_x(&re->disprect); + re->recty = BLI_rcti_size_y(&re->disprect); - /* copy border itself, since it could be used by external engines */ - re->r.border = source->r.border; - } - else if (disprect) { - re->disprect = *disprect; - re->rectx = BLI_rcti_size_x(&re->disprect); - re->recty = BLI_rcti_size_y(&re->disprect); - } - else { - re->disprect.xmin = re->disprect.ymin = 0; - re->disprect.xmax = winx; - re->disprect.ymax = winy; - re->rectx = winx; - re->recty = winy; - } + /* copy border itself, since it could be used by external engines */ + re->r.border = source->r.border; + } + else if (disprect) { + re->disprect = *disprect; + re->rectx = BLI_rcti_size_x(&re->disprect); + re->recty = BLI_rcti_size_y(&re->disprect); + } + else { + re->disprect.xmin = re->disprect.ymin = 0; + re->disprect.xmax = winx; + re->disprect.ymax = winy; + re->rectx = winx; + re->recty = winy; + } } void render_copy_renderdata(RenderData *to, RenderData *from) { - BLI_freelistN(&to->views); - curvemapping_free_data(&to->mblur_shutter_curve); + BLI_freelistN(&to->views); + curvemapping_free_data(&to->mblur_shutter_curve); - *to = *from; + *to = *from; - BLI_duplicatelist(&to->views, &from->views); - curvemapping_copy_data(&to->mblur_shutter_curve, &from->mblur_shutter_curve); + BLI_duplicatelist(&to->views, &from->views); + curvemapping_copy_data(&to->mblur_shutter_curve, &from->mblur_shutter_curve); } /* what doesn't change during entire render sequence */ /* disprect is optional, if NULL it assumes full window render */ -void RE_InitState(Render *re, Render *source, RenderData *rd, - ListBase *render_layers, ViewLayer *single_layer, - int winx, int winy, rcti *disprect) -{ - bool had_freestyle = (re->r.mode & R_EDGE_FRS) != 0; - - re->ok = true; /* maybe flag */ - - re->i.starttime = PIL_check_seconds_timer(); - - /* copy render data and render layers for thread safety */ - render_copy_renderdata(&re->r, rd); - BLI_freelistN(&re->view_layers); - BLI_duplicatelist(&re->view_layers, render_layers); - re->active_view_layer = 0; - - if (source) { - /* reuse border flags from source renderer */ - re->r.mode &= ~(R_BORDER | R_CROP); - re->r.mode |= source->r.mode & (R_BORDER | R_CROP); - - /* dimensions shall be shared between all renderers */ - re->r.xsch = source->r.xsch; - re->r.ysch = source->r.ysch; - re->r.size = source->r.size; - } - - re_init_resolution(re, source, winx, winy, disprect); - - /* disable border if it's a full render anyway */ - if (re->r.border.xmin == 0.0f && re->r.border.xmax == 1.0f && - re->r.border.ymin == 0.0f && re->r.border.ymax == 1.0f) - { - re->r.mode &= ~R_BORDER; - } - - if (re->rectx < 1 || re->recty < 1 || (BKE_imtype_is_movie(rd->im_format.imtype) && - (re->rectx < 16 || re->recty < 16) )) - { - BKE_report(re->reports, RPT_ERROR, "Image too small"); - re->ok = 0; - return; - } - - re->r.scemode = check_mode_full_sample(&re->r); - - if (single_layer) { - int index = BLI_findindex(render_layers, single_layer); - if (index != -1) { - re->active_view_layer = index; - re->r.scemode |= R_SINGLE_LAYER; - } - } - - /* if preview render, we try to keep old result */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - - if (re->r.scemode & R_BUTS_PREVIEW) { - if (had_freestyle || (re->r.mode & R_EDGE_FRS)) { - /* freestyle manipulates render layers so always have to free */ - render_result_free(re->result); - re->result = NULL; - } - else if (re->result) { - ViewLayer *active_render_layer = BLI_findlink(&re->view_layers, re->active_view_layer); - RenderLayer *rl; - bool have_layer = false; - - for (rl = re->result->layers.first; rl; rl = rl->next) - if (STREQ(rl->name, active_render_layer->name)) - have_layer = true; - - if (re->result->rectx == re->rectx && re->result->recty == re->recty && - have_layer) - { - /* keep render result, this avoids flickering black tiles - * when the preview changes */ - } - else { - /* free because resolution changed */ - render_result_free(re->result); - re->result = NULL; - } - } - } - else { - - /* make empty render result, so display callbacks can initialize */ - render_result_free(re->result); - re->result = MEM_callocN(sizeof(RenderResult), "new render result"); - re->result->rectx = re->rectx; - re->result->recty = re->recty; - render_result_view_new(re->result, ""); - } - - /* ensure renderdatabase can use part settings correct */ - RE_parts_clamp(re); - - BLI_rw_mutex_unlock(&re->resultmutex); - - RE_init_threadcount(re); - - RE_point_density_fix_linking(); +void RE_InitState(Render *re, + Render *source, + RenderData *rd, + ListBase *render_layers, + ViewLayer *single_layer, + int winx, + int winy, + rcti *disprect) +{ + bool had_freestyle = (re->r.mode & R_EDGE_FRS) != 0; + + re->ok = true; /* maybe flag */ + + re->i.starttime = PIL_check_seconds_timer(); + + /* copy render data and render layers for thread safety */ + render_copy_renderdata(&re->r, rd); + BLI_freelistN(&re->view_layers); + BLI_duplicatelist(&re->view_layers, render_layers); + re->active_view_layer = 0; + + if (source) { + /* reuse border flags from source renderer */ + re->r.mode &= ~(R_BORDER | R_CROP); + re->r.mode |= source->r.mode & (R_BORDER | R_CROP); + + /* dimensions shall be shared between all renderers */ + re->r.xsch = source->r.xsch; + re->r.ysch = source->r.ysch; + re->r.size = source->r.size; + } + + re_init_resolution(re, source, winx, winy, disprect); + + /* disable border if it's a full render anyway */ + if (re->r.border.xmin == 0.0f && re->r.border.xmax == 1.0f && re->r.border.ymin == 0.0f && + re->r.border.ymax == 1.0f) { + re->r.mode &= ~R_BORDER; + } + + if (re->rectx < 1 || re->recty < 1 || + (BKE_imtype_is_movie(rd->im_format.imtype) && (re->rectx < 16 || re->recty < 16))) { + BKE_report(re->reports, RPT_ERROR, "Image too small"); + re->ok = 0; + return; + } + + re->r.scemode = check_mode_full_sample(&re->r); + + if (single_layer) { + int index = BLI_findindex(render_layers, single_layer); + if (index != -1) { + re->active_view_layer = index; + re->r.scemode |= R_SINGLE_LAYER; + } + } + + /* if preview render, we try to keep old result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + if (re->r.scemode & R_BUTS_PREVIEW) { + if (had_freestyle || (re->r.mode & R_EDGE_FRS)) { + /* freestyle manipulates render layers so always have to free */ + render_result_free(re->result); + re->result = NULL; + } + else if (re->result) { + ViewLayer *active_render_layer = BLI_findlink(&re->view_layers, re->active_view_layer); + RenderLayer *rl; + bool have_layer = false; + + for (rl = re->result->layers.first; rl; rl = rl->next) + if (STREQ(rl->name, active_render_layer->name)) + have_layer = true; + + if (re->result->rectx == re->rectx && re->result->recty == re->recty && have_layer) { + /* keep render result, this avoids flickering black tiles + * when the preview changes */ + } + else { + /* free because resolution changed */ + render_result_free(re->result); + re->result = NULL; + } + } + } + else { + + /* make empty render result, so display callbacks can initialize */ + render_result_free(re->result); + re->result = MEM_callocN(sizeof(RenderResult), "new render result"); + re->result->rectx = re->rectx; + re->result->recty = re->recty; + render_result_view_new(re->result, ""); + } + + /* ensure renderdatabase can use part settings correct */ + RE_parts_clamp(re); + + BLI_rw_mutex_unlock(&re->resultmutex); + + RE_init_threadcount(re); + + RE_point_density_fix_linking(); } /* This function is only called by view3d rendering, which doesn't support * multiview at the moment. so handle only one view here */ static void render_result_rescale(Render *re) { - RenderResult *result = re->result; - RenderView *rv; - int x, y; - float scale_x, scale_y; - float *src_rectf; - - rv = RE_RenderViewGetById(result, 0); - src_rectf = rv->rectf; - - if (src_rectf == NULL) { - RenderLayer *rl = render_get_active_layer(re, re->result); - if (rl != NULL) { - src_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL); - } - } - - if (src_rectf != NULL) { - float *dst_rectf = NULL; - re->result = render_result_new(re, - &re->disprect, - 0, - RR_USE_MEM, - RR_ALL_LAYERS, - ""); - - if (re->result != NULL) { - dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf; - if (dst_rectf == NULL) { - RenderLayer *rl; - rl = render_get_active_layer(re, re->result); - if (rl != NULL) { - dst_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL); - } - } - - scale_x = (float)result->rectx / re->result->rectx; - scale_y = (float)result->recty / re->result->recty; - for (x = 0; x < re->result->rectx; ++x) { - for (y = 0; y < re->result->recty; ++y) { - int src_x = x * scale_x; - int src_y = y * scale_y; - int dst_index = y * re->result->rectx + x; - int src_index = src_y * result->rectx + src_x; - copy_v4_v4(dst_rectf + dst_index * 4, - src_rectf + src_index * 4); - } - } - } - render_result_free(result); - } + RenderResult *result = re->result; + RenderView *rv; + int x, y; + float scale_x, scale_y; + float *src_rectf; + + rv = RE_RenderViewGetById(result, 0); + src_rectf = rv->rectf; + + if (src_rectf == NULL) { + RenderLayer *rl = render_get_active_layer(re, re->result); + if (rl != NULL) { + src_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL); + } + } + + if (src_rectf != NULL) { + float *dst_rectf = NULL; + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, ""); + + if (re->result != NULL) { + dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf; + if (dst_rectf == NULL) { + RenderLayer *rl; + rl = render_get_active_layer(re, re->result); + if (rl != NULL) { + dst_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL); + } + } + + scale_x = (float)result->rectx / re->result->rectx; + scale_y = (float)result->recty / re->result->recty; + for (x = 0; x < re->result->rectx; ++x) { + for (y = 0; y < re->result->recty; ++y) { + int src_x = x * scale_x; + int src_y = y * scale_y; + int dst_index = y * re->result->rectx + x; + int src_index = src_y * result->rectx + src_x; + copy_v4_v4(dst_rectf + dst_index * 4, src_rectf + src_index * 4); + } + } + } + render_result_free(result); + } } void RE_ChangeResolution(Render *re, int winx, int winy, rcti *disprect) { - re_init_resolution(re, NULL, winx, winy, disprect); - RE_parts_clamp(re); + re_init_resolution(re, NULL, winx, winy, disprect); + RE_parts_clamp(re); - if (re->result) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_rescale(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } + if (re->result) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_rescale(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } } /* TODO(sergey): This is a bit hackish, used to temporary disable freestyle when @@ -898,158 +938,167 @@ void RE_ChangeResolution(Render *re, int winx, int winy, rcti *disprect) */ void RE_ChangeModeFlag(Render *re, int flag, bool clear) { - if (clear) { - re->r.mode &= ~flag; - } - else { - re->r.mode |= flag; - } + if (clear) { + re->r.mode &= ~flag; + } + else { + re->r.mode |= flag; + } } /* update some variables that can be animated, and otherwise wouldn't be due to * RenderData getting copied once at the start of animation render */ void render_update_anim_renderdata(Render *re, RenderData *rd, ListBase *render_layers) { - /* filter */ - re->r.gauss = rd->gauss; + /* filter */ + re->r.gauss = rd->gauss; - /* motion blur */ - re->r.blurfac = rd->blurfac; + /* motion blur */ + re->r.blurfac = rd->blurfac; - /* freestyle */ - re->r.line_thickness_mode = rd->line_thickness_mode; - re->r.unit_line_thickness = rd->unit_line_thickness; + /* freestyle */ + re->r.line_thickness_mode = rd->line_thickness_mode; + re->r.unit_line_thickness = rd->unit_line_thickness; - /* render layers */ - BLI_freelistN(&re->view_layers); - BLI_duplicatelist(&re->view_layers, render_layers); + /* render layers */ + BLI_freelistN(&re->view_layers); + BLI_duplicatelist(&re->view_layers, render_layers); - /* render views */ - BLI_freelistN(&re->r.views); - BLI_duplicatelist(&re->r.views, &rd->views); + /* render views */ + BLI_freelistN(&re->r.views); + BLI_duplicatelist(&re->r.views, &rd->views); } void RE_SetWindow(Render *re, const rctf *viewplane, float clip_start, float clip_end) { - /* re->ok flag? */ - - re->viewplane = *viewplane; - re->clip_start = clip_start; - re->clip_end = clip_end; + /* re->ok flag? */ - perspective_m4(re->winmat, - re->viewplane.xmin, re->viewplane.xmax, - re->viewplane.ymin, re->viewplane.ymax, re->clip_start, re->clip_end); + re->viewplane = *viewplane; + re->clip_start = clip_start; + re->clip_end = clip_end; + perspective_m4(re->winmat, + re->viewplane.xmin, + re->viewplane.xmax, + re->viewplane.ymin, + re->viewplane.ymax, + re->clip_start, + re->clip_end); } void RE_SetOrtho(Render *re, const rctf *viewplane, float clip_start, float clip_end) { - /* re->ok flag? */ + /* re->ok flag? */ - re->viewplane = *viewplane; - re->clip_start = clip_start; - re->clip_end = clip_end; + re->viewplane = *viewplane; + re->clip_start = clip_start; + re->clip_end = clip_end; - orthographic_m4(re->winmat, - re->viewplane.xmin, re->viewplane.xmax, - re->viewplane.ymin, re->viewplane.ymax, re->clip_start, re->clip_end); + orthographic_m4(re->winmat, + re->viewplane.xmin, + re->viewplane.xmax, + re->viewplane.ymin, + re->viewplane.ymax, + re->clip_start, + re->clip_end); } void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect) { - *r_viewplane = re->viewplane; + *r_viewplane = re->viewplane; - /* make disprect zero when no border render, is needed to detect changes in 3d view render */ - if (re->r.mode & R_BORDER) { - *r_disprect = re->disprect; - } - else { - BLI_rcti_init(r_disprect, 0, 0, 0, 0); - } + /* make disprect zero when no border render, is needed to detect changes in 3d view render */ + if (re->r.mode & R_BORDER) { + *r_disprect = re->disprect; + } + else { + BLI_rcti_init(r_disprect, 0, 0, 0, 0); + } } /* image and movie output has to move to either imbuf or kernel */ void RE_display_init_cb(Render *re, void *handle, void (*f)(void *handle, RenderResult *rr)) { - re->display_init = f; - re->dih = handle; + re->display_init = f; + re->dih = handle; } void RE_display_clear_cb(Render *re, void *handle, void (*f)(void *handle, RenderResult *rr)) { - re->display_clear = f; - re->dch = handle; + re->display_clear = f; + re->dch = handle; } -void RE_display_update_cb(Render *re, void *handle, void (*f)(void *handle, RenderResult *rr, volatile rcti *rect)) +void RE_display_update_cb(Render *re, + void *handle, + void (*f)(void *handle, RenderResult *rr, volatile rcti *rect)) { - re->display_update = f; - re->duh = handle; + re->display_update = f; + re->duh = handle; } void RE_current_scene_update_cb(Render *re, void *handle, void (*f)(void *handle, Scene *scene)) { - re->current_scene_update = f; - re->suh = handle; + re->current_scene_update = f; + re->suh = handle; } void RE_stats_draw_cb(Render *re, void *handle, void (*f)(void *handle, RenderStats *rs)) { - re->stats_draw = f; - re->sdh = handle; + re->stats_draw = f; + re->sdh = handle; } void RE_progress_cb(Render *re, void *handle, void (*f)(void *handle, float)) { - re->progress = f; - re->prh = handle; + re->progress = f; + re->prh = handle; } void RE_draw_lock_cb(Render *re, void *handle, void (*f)(void *handle, int i)) { - re->draw_lock = f; - re->dlh = handle; + re->draw_lock = f; + re->dlh = handle; } void RE_test_break_cb(Render *re, void *handle, int (*f)(void *handle)) { - re->test_break = f; - re->tbh = handle; + re->test_break = f; + re->tbh = handle; } /* ********* GL Context ******** */ void RE_gl_context_create(Render *re) { - /* Needs to be created in the main ogl thread. */ - re->gl_context = WM_opengl_context_create(); - /* So we activate the window's one afterwards. */ - wm_window_reset_drawable(); + /* Needs to be created in the main ogl thread. */ + re->gl_context = WM_opengl_context_create(); + /* So we activate the window's one afterwards. */ + wm_window_reset_drawable(); } void RE_gl_context_destroy(Render *re) { - /* Needs to be called from the thread which used the ogl context for rendering. */ - if (re->gl_context) { - if (re->gpu_context) { - WM_opengl_context_activate(re->gl_context); - GPU_context_active_set(re->gpu_context); - GPU_context_discard(re->gpu_context); - re->gpu_context = NULL; - } + /* Needs to be called from the thread which used the ogl context for rendering. */ + if (re->gl_context) { + if (re->gpu_context) { + WM_opengl_context_activate(re->gl_context); + GPU_context_active_set(re->gpu_context); + GPU_context_discard(re->gpu_context); + re->gpu_context = NULL; + } - WM_opengl_context_dispose(re->gl_context); - re->gl_context = NULL; - } + WM_opengl_context_dispose(re->gl_context); + re->gl_context = NULL; + } } void *RE_gl_context_get(Render *re) { - return re->gl_context; + return re->gl_context; } void *RE_gpu_context_get(Render *re) { - if (re->gpu_context == NULL) { - re->gpu_context = GPU_context_create(); - } - return re->gpu_context; + if (re->gpu_context == NULL) { + re->gpu_context = GPU_context_create(); + } + return re->gpu_context; } /* ********* add object data (later) ******** */ @@ -1071,520 +1120,529 @@ static void add_freestyle(Render *re, int render); static void free_all_freestyle_renders(void); #endif - /* ************ This part uses API, for rendering Blender scenes ********** */ static void do_render_3d(Render *re) { - re->current_scene_update(re->suh, re->scene); - RE_engine_render(re, 0); + re->current_scene_update(re->suh, re->scene); + RE_engine_render(re, 0); } /* make sure disprect is not affected by the render border */ static void render_result_disprect_to_full_resolution(Render *re) { - re->disprect.xmin = re->disprect.ymin = 0; - re->disprect.xmax = re->winx; - re->disprect.ymax = re->winy; - re->rectx = re->winx; - re->recty = re->winy; + re->disprect.xmin = re->disprect.ymin = 0; + re->disprect.xmax = re->winx; + re->disprect.ymax = re->winy; + re->rectx = re->winx; + re->recty = re->winy; } static void render_result_uncrop(Render *re) { - /* when using border render with crop disabled, insert render result into - * full size with black pixels outside */ - if (re->result && (re->r.mode & R_BORDER)) { - if ((re->r.mode & R_CROP) == 0) { - RenderResult *rres; + /* when using border render with crop disabled, insert render result into + * full size with black pixels outside */ + if (re->result && (re->r.mode & R_BORDER)) { + if ((re->r.mode & R_CROP) == 0) { + RenderResult *rres; - /* backup */ - const rcti orig_disprect = re->disprect; - const int orig_rectx = re->rectx, orig_recty = re->recty; + /* backup */ + const rcti orig_disprect = re->disprect; + const int orig_rectx = re->rectx, orig_recty = re->recty; - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - /* sub-rect for merge call later on */ - re->result->tilerect = re->disprect; + /* sub-rect for merge call later on */ + re->result->tilerect = re->disprect; - /* weak is: it chances disprect from border */ - render_result_disprect_to_full_resolution(re); + /* weak is: it chances disprect from border */ + render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); - render_result_clone_passes(re, rres, NULL); + render_result_clone_passes(re, rres, NULL); - render_result_merge(rres, re->result); - render_result_free(re->result); - re->result = rres; + render_result_merge(rres, re->result); + render_result_free(re->result); + re->result = rres; - /* weak... the display callback wants an active renderlayer pointer... */ - re->result->renlay = render_get_active_layer(re, re->result); + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay = render_get_active_layer(re, re->result); - BLI_rw_mutex_unlock(&re->resultmutex); + BLI_rw_mutex_unlock(&re->resultmutex); - re->display_init(re->dih, re->result); - re->display_update(re->duh, re->result, NULL); + re->display_init(re->dih, re->result); + re->display_update(re->duh, re->result, NULL); - /* restore the disprect from border */ - re->disprect = orig_disprect; - re->rectx = orig_rectx; - re->recty = orig_recty; - } - else { - /* set offset (again) for use in compositor, disprect was manipulated. */ - re->result->xof = 0; - re->result->yof = 0; - } - } + /* restore the disprect from border */ + re->disprect = orig_disprect; + re->rectx = orig_rectx; + re->recty = orig_recty; + } + else { + /* set offset (again) for use in compositor, disprect was manipulated. */ + re->result->xof = 0; + re->result->yof = 0; + } + } } /* main render routine, no compositing */ static void do_render(Render *re) { - Object *camera = RE_GetCamera(re); - /* also check for camera here */ - if (camera == NULL) { - BKE_report(re->reports, RPT_ERROR, "Cannot render, no camera"); - G.is_break = true; - return; - } + Object *camera = RE_GetCamera(re); + /* also check for camera here */ + if (camera == NULL) { + BKE_report(re->reports, RPT_ERROR, "Cannot render, no camera"); + G.is_break = true; + return; + } - /* now use renderdata and camera to set viewplane */ - RE_SetCamera(re, camera); + /* now use renderdata and camera to set viewplane */ + RE_SetCamera(re, camera); - do_render_3d(re); + do_render_3d(re); - /* when border render, check if we have to insert it in black */ - render_result_uncrop(re); + /* when border render, check if we have to insert it in black */ + render_result_uncrop(re); } - /* within context of current Render *re, render another scene. * it uses current render image size and disprect, but doesn't execute composite */ static void render_scene(Render *re, Scene *sce, int cfra) { - Render *resc = RE_NewSceneRender(sce); - int winx = re->winx, winy = re->winy; + Render *resc = RE_NewSceneRender(sce); + int winx = re->winx, winy = re->winy; - sce->r.cfra = cfra; + sce->r.cfra = cfra; - BKE_scene_camera_switch_update(sce); + BKE_scene_camera_switch_update(sce); - /* exception: scene uses own size (unfinished code) */ - if (0) { - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; - } + /* exception: scene uses own size (unfinished code) */ + if (0) { + winx = (sce->r.size * sce->r.xsch) / 100; + winy = (sce->r.size * sce->r.ysch) / 100; + } - /* initial setup */ - RE_InitState(resc, re, &sce->r, &sce->view_layers, NULL, winx, winy, &re->disprect); + /* initial setup */ + RE_InitState(resc, re, &sce->r, &sce->view_layers, NULL, winx, winy, &re->disprect); - /* We still want to use 'rendercache' setting from org (main) scene... */ - resc->r.scemode = (resc->r.scemode & ~R_EXR_CACHE_FILE) | (re->r.scemode & R_EXR_CACHE_FILE); + /* We still want to use 'rendercache' setting from org (main) scene... */ + resc->r.scemode = (resc->r.scemode & ~R_EXR_CACHE_FILE) | (re->r.scemode & R_EXR_CACHE_FILE); - /* still unsure entity this... */ - resc->main = re->main; - resc->scene = sce; + /* still unsure entity this... */ + resc->main = re->main; + resc->scene = sce; - /* copy callbacks */ - resc->display_update = re->display_update; - resc->duh = re->duh; - resc->test_break = re->test_break; - resc->tbh = re->tbh; - resc->stats_draw = re->stats_draw; - resc->sdh = re->sdh; - resc->current_scene_update = re->current_scene_update; - resc->suh = re->suh; + /* copy callbacks */ + resc->display_update = re->display_update; + resc->duh = re->duh; + resc->test_break = re->test_break; + resc->tbh = re->tbh; + resc->stats_draw = re->stats_draw; + resc->sdh = re->sdh; + resc->current_scene_update = re->current_scene_update; + resc->suh = re->suh; - do_render(resc); + do_render(resc); } /* helper call to detect if this scene needs a render, or if there's a any render layer to render */ static int composite_needs_render(Scene *sce, int this_scene) { - bNodeTree *ntree = sce->nodetree; - bNode *node; + bNodeTree *ntree = sce->nodetree; + bNode *node; - if (ntree == NULL) return 1; - if (sce->use_nodes == false) return 1; - if ((sce->r.scemode & R_DOCOMP) == 0) return 1; + if (ntree == NULL) + return 1; + if (sce->use_nodes == false) + return 1; + if ((sce->r.scemode & R_DOCOMP) == 0) + return 1; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) - if (this_scene == 0 || node->id == NULL || node->id == &sce->id) - return 1; - } - return 0; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) + if (this_scene == 0 || node->id == NULL || node->id == &sce->id) + return 1; + } + return 0; } bool RE_allow_render_generic_object(Object *ob) { - /* override not showing object when duplis are used with particles */ - if (ob->transflag & OB_DUPLIPARTS) { - /* pass */ /* let particle system(s) handle showing vs. not showing */ - } - else if (ob->transflag & OB_DUPLI) { - return false; - } - return true; + /* override not showing object when duplis are used with particles */ + if (ob->transflag & OB_DUPLIPARTS) { + /* pass */ /* let particle system(s) handle showing vs. not showing */ + } + else if (ob->transflag & OB_DUPLI) { + return false; + } + return true; } static void ntree_render_scenes(Render *re) { - bNode *node; - int cfra = re->scene->r.cfra; - Scene *restore_scene = re->scene; - - if (re->scene->nodetree == NULL) return; - - /* now foreach render-result node we do a full render */ - /* results are stored in a way compositor will find it */ - GSet *scenes_rendered = BLI_gset_ptr_new(__func__); - for (node = re->scene->nodetree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) { - if (node->id && node->id != (ID *)re->scene) { - Scene *scene = (Scene *)node->id; - if (!BLI_gset_haskey(scenes_rendered, scene) && render_scene_has_layers_to_render(scene, false)) { - render_scene(re, scene, cfra); - BLI_gset_add(scenes_rendered, scene); - nodeUpdate(restore_scene->nodetree, node); - } - } - } - } - BLI_gset_free(scenes_rendered, NULL); + bNode *node; + int cfra = re->scene->r.cfra; + Scene *restore_scene = re->scene; + + if (re->scene->nodetree == NULL) + return; + + /* now foreach render-result node we do a full render */ + /* results are stored in a way compositor will find it */ + GSet *scenes_rendered = BLI_gset_ptr_new(__func__); + for (node = re->scene->nodetree->nodes.first; node; node = node->next) { + if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) { + if (node->id && node->id != (ID *)re->scene) { + Scene *scene = (Scene *)node->id; + if (!BLI_gset_haskey(scenes_rendered, scene) && + render_scene_has_layers_to_render(scene, false)) { + render_scene(re, scene, cfra); + BLI_gset_add(scenes_rendered, scene); + nodeUpdate(restore_scene->nodetree, node); + } + } + } + } + BLI_gset_free(scenes_rendered, NULL); } /* bad call... need to think over proper method still */ static void render_composit_stats(void *arg, const char *str) { - Render *re = (Render *)arg; + Render *re = (Render *)arg; - RenderStats i; - memcpy(&i, &re->i, sizeof(i)); - i.infostr = str; - re->stats_draw(re->sdh, &i); + RenderStats i; + memcpy(&i, &re->i, sizeof(i)); + i.infostr = str; + re->stats_draw(re->sdh, &i); } #ifdef WITH_FREESTYLE /* init Freestyle renderer */ static void init_freestyle(Render *re) { - re->freestyle_bmain = BKE_main_new(); + re->freestyle_bmain = BKE_main_new(); - /* We use the same window manager for freestyle bmain as - * real bmain uses. This is needed because freestyle's - * bmain could be used to tag scenes for update, which - * implies call of ED_render_scene_update in some cases - * and that function requires proper window manager - * to present (sergey) - */ - re->freestyle_bmain->wm = re->main->wm; + /* We use the same window manager for freestyle bmain as + * real bmain uses. This is needed because freestyle's + * bmain could be used to tag scenes for update, which + * implies call of ED_render_scene_update in some cases + * and that function requires proper window manager + * to present (sergey) + */ + re->freestyle_bmain->wm = re->main->wm; - FRS_init_stroke_renderer(re); + FRS_init_stroke_renderer(re); } /* invokes Freestyle stroke rendering */ static void add_freestyle(Render *re, int render) { - ViewLayer *view_layer, *active_view_layer; - LinkData *link; - Render *r; + ViewLayer *view_layer, *active_view_layer; + LinkData *link; + Render *r; - active_view_layer = BLI_findlink(&re->view_layers, re->active_view_layer); + active_view_layer = BLI_findlink(&re->view_layers, re->active_view_layer); - FRS_begin_stroke_rendering(re); + FRS_begin_stroke_rendering(re); - for (view_layer = (ViewLayer *)re->view_layers.first; view_layer; view_layer = view_layer->next) { - link = (LinkData *)MEM_callocN(sizeof(LinkData), "LinkData to Freestyle render"); - BLI_addtail(&re->freestyle_renders, link); + for (view_layer = (ViewLayer *)re->view_layers.first; view_layer; + view_layer = view_layer->next) { + link = (LinkData *)MEM_callocN(sizeof(LinkData), "LinkData to Freestyle render"); + BLI_addtail(&re->freestyle_renders, link); - if ((re->r.scemode & R_SINGLE_LAYER) && view_layer != active_view_layer) - continue; - if (FRS_is_freestyle_enabled(view_layer)) { - r = FRS_do_stroke_rendering(re, view_layer, render); - link->data = (void *)r; - } - } + if ((re->r.scemode & R_SINGLE_LAYER) && view_layer != active_view_layer) + continue; + if (FRS_is_freestyle_enabled(view_layer)) { + r = FRS_do_stroke_rendering(re, view_layer, render); + link->data = (void *)r; + } + } - FRS_end_stroke_rendering(re); + FRS_end_stroke_rendering(re); } /* releases temporary scenes and renders for Freestyle stroke rendering */ static void free_all_freestyle_renders(void) { - Render *re1; - LinkData *link; + Render *re1; + LinkData *link; - for (re1 = RenderGlobal.renderlist.first; re1; re1 = re1->next) { - for (link = (LinkData *)re1->freestyle_renders.first; link; link = link->next) { - Render *freestyle_render = (Render *)link->data; + for (re1 = RenderGlobal.renderlist.first; re1; re1 = re1->next) { + for (link = (LinkData *)re1->freestyle_renders.first; link; link = link->next) { + Render *freestyle_render = (Render *)link->data; - if (freestyle_render) { - Scene *freestyle_scene = freestyle_render->scene; - RE_FreeRender(freestyle_render); + if (freestyle_render) { + Scene *freestyle_scene = freestyle_render->scene; + RE_FreeRender(freestyle_render); - if (freestyle_scene) { - BKE_libblock_unlink(re1->freestyle_bmain, freestyle_scene, false, false); - BKE_id_free(re1->freestyle_bmain, freestyle_scene); - } - } - } - BLI_freelistN(&re1->freestyle_renders); + if (freestyle_scene) { + BKE_libblock_unlink(re1->freestyle_bmain, freestyle_scene, false, false); + BKE_id_free(re1->freestyle_bmain, freestyle_scene); + } + } + } + BLI_freelistN(&re1->freestyle_renders); - if (re1->freestyle_bmain) { - /* detach the window manager from freestyle bmain (see comments - * in add_freestyle() for more detail) - */ - BLI_listbase_clear(&re1->freestyle_bmain->wm); + if (re1->freestyle_bmain) { + /* detach the window manager from freestyle bmain (see comments + * in add_freestyle() for more detail) + */ + BLI_listbase_clear(&re1->freestyle_bmain->wm); - BKE_main_free(re1->freestyle_bmain); - re1->freestyle_bmain = NULL; - } - } + BKE_main_free(re1->freestyle_bmain); + re1->freestyle_bmain = NULL; + } + } } #endif /* returns fully composited render-result on given time step (in RenderData) */ static void do_render_composite(Render *re) { - bNodeTree *ntree = re->scene->nodetree; - int update_newframe = 0; - - if (composite_needs_render(re->scene, 1)) { - /* save memory... free all cached images */ - ntreeFreeCache(ntree); - - /* render the frames - * it could be optimized to render only the needed view - * but what if a scene has a different number of views - * than the main scene? */ - do_render(re); - } - else { - re->i.cfra = re->r.cfra; - - /* ensure new result gets added, like for regular renders */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - - render_result_free(re->result); - if ((re->r.mode & R_CROP) == 0) { - render_result_disprect_to_full_resolution(re); - } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - - BLI_rw_mutex_unlock(&re->resultmutex); - - /* scene render process already updates animsys */ - update_newframe = 1; - } - - /* swap render result */ - if (re->r.scemode & R_SINGLE_LAYER) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_single_layer_end(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - if (!re->test_break(re->tbh)) { - - if (ntree) { - ntreeCompositTagRender(re->scene); - ntreeCompositTagAnimated(ntree); - } - - if (ntree && re->scene->use_nodes && re->r.scemode & R_DOCOMP) { - /* checks if there are render-result nodes that need scene */ - if ((re->r.scemode & R_SINGLE_LAYER) == 0) - ntree_render_scenes(re); - - if (!re->test_break(re->tbh)) { - ntree->stats_draw = render_composit_stats; - ntree->test_break = re->test_break; - ntree->progress = re->progress; - ntree->sdh = re; - ntree->tbh = re->tbh; - ntree->prh = re->prh; - - if (update_newframe) { - /* If we have consistent depsgraph now would be a time to update them. */ - } - - RenderView *rv; - for (rv = re->result->views.first; rv; rv = rv->next) { - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); - } - - ntree->stats_draw = NULL; - ntree->test_break = NULL; - ntree->progress = NULL; - ntree->tbh = ntree->sdh = ntree->prh = NULL; - } - } - } + bNodeTree *ntree = re->scene->nodetree; + int update_newframe = 0; + + if (composite_needs_render(re->scene, 1)) { + /* save memory... free all cached images */ + ntreeFreeCache(ntree); + + /* render the frames + * it could be optimized to render only the needed view + * but what if a scene has a different number of views + * than the main scene? */ + do_render(re); + } + else { + re->i.cfra = re->r.cfra; + + /* ensure new result gets added, like for regular renders */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + render_result_free(re->result); + if ((re->r.mode & R_CROP) == 0) { + render_result_disprect_to_full_resolution(re); + } + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + + BLI_rw_mutex_unlock(&re->resultmutex); + + /* scene render process already updates animsys */ + update_newframe = 1; + } + + /* swap render result */ + if (re->r.scemode & R_SINGLE_LAYER) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_single_layer_end(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + if (!re->test_break(re->tbh)) { + + if (ntree) { + ntreeCompositTagRender(re->scene); + ntreeCompositTagAnimated(ntree); + } + + if (ntree && re->scene->use_nodes && re->r.scemode & R_DOCOMP) { + /* checks if there are render-result nodes that need scene */ + if ((re->r.scemode & R_SINGLE_LAYER) == 0) + ntree_render_scenes(re); + + if (!re->test_break(re->tbh)) { + ntree->stats_draw = render_composit_stats; + ntree->test_break = re->test_break; + ntree->progress = re->progress; + ntree->sdh = re; + ntree->tbh = re->tbh; + ntree->prh = re->prh; + + if (update_newframe) { + /* If we have consistent depsgraph now would be a time to update them. */ + } + + RenderView *rv; + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, + ntree, + &re->r, + true, + G.background == 0, + &re->scene->view_settings, + &re->scene->display_settings, + rv->name); + } + + ntree->stats_draw = NULL; + ntree->test_break = NULL; + ntree->progress = NULL; + ntree->tbh = ntree->sdh = ntree->prh = NULL; + } + } + } #ifdef WITH_FREESTYLE - free_all_freestyle_renders(); + free_all_freestyle_renders(); #endif - /* weak... the display callback wants an active renderlayer pointer... */ - if (re->result != NULL) { - re->result->renlay = render_get_active_layer(re, re->result); - re->display_update(re->duh, re->result, NULL); - } + /* weak... the display callback wants an active renderlayer pointer... */ + if (re->result != NULL) { + re->result->renlay = render_get_active_layer(re, re->result); + re->display_update(re->duh, re->result, NULL); + } } static void renderresult_stampinfo(Render *re) { - RenderResult rres; - RenderView *rv; - int nr; - - /* this is the basic trick to get the displayed float or char rect from render result */ - nr = 0; - for (rv = re->result->views.first; rv; rv = rv->next, nr++) { - RE_SetActiveRenderView(re, rv->name); - RE_AcquireResultImage(re, &rres, nr); - BKE_image_stamp_buf(re->scene, - RE_GetCamera(re), - (re->r.stamp & R_STAMP_STRIPMETA) ? rres.stamp_data : NULL, - (unsigned char *)rres.rect32, - rres.rectf, - rres.rectx, rres.recty, - 4); - RE_ReleaseResultImage(re); - } + RenderResult rres; + RenderView *rv; + int nr; + + /* this is the basic trick to get the displayed float or char rect from render result */ + nr = 0; + for (rv = re->result->views.first; rv; rv = rv->next, nr++) { + RE_SetActiveRenderView(re, rv->name); + RE_AcquireResultImage(re, &rres, nr); + BKE_image_stamp_buf(re->scene, + RE_GetCamera(re), + (re->r.stamp & R_STAMP_STRIPMETA) ? rres.stamp_data : NULL, + (unsigned char *)rres.rect32, + rres.rectf, + rres.rectx, + rres.recty, + 4); + RE_ReleaseResultImage(re); + } } int RE_seq_render_active(Scene *scene, RenderData *rd) { - Editing *ed; - Sequence *seq; + Editing *ed; + Sequence *seq; - ed = scene->ed; + ed = scene->ed; - if (!(rd->scemode & R_DOSEQ) || !ed || !ed->seqbase.first) - return 0; + if (!(rd->scemode & R_DOSEQ) || !ed || !ed->seqbase.first) + return 0; - for (seq = ed->seqbase.first; seq; seq = seq->next) { - if (seq->type != SEQ_TYPE_SOUND_RAM) - return 1; - } + for (seq = ed->seqbase.first; seq; seq = seq->next) { + if (seq->type != SEQ_TYPE_SOUND_RAM) + return 1; + } - return 0; + return 0; } static void do_render_seq(Render *re) { - static int recurs_depth = 0; - struct ImBuf *out; - RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ - int cfra = re->r.cfra; - SeqRenderData context; - int view_id, tot_views; - struct ImBuf **ibuf_arr; - int re_x, re_y; - - re->i.cfra = cfra; - - recurs_depth++; - - if ((re->r.mode & R_BORDER) && (re->r.mode & R_CROP) == 0) { - /* if border rendering is used and cropping is disabled, final buffer should - * be as large as the whole frame */ - re_x = re->winx; - re_y = re->winy; - } - else { - re_x = re->result->rectx; - re_y = re->result->recty; - } - - tot_views = BKE_scene_multiview_num_views_get(&re->r); - ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs"); - - /* TODO(sergey): Currently depsgraph is only used to check whether it is an active - * edit window or not to deal with unkeyed changes. We don't have depsgraph here yet, - * but we also dont' deal with unkeyed changes. But still nice to get proper depsgraph - * within tjhe render pipeline, somehow. - */ - BKE_sequencer_new_render_data( - re->main, NULL, re->scene, - re_x, re_y, 100, true, - &context); - - /* the renderresult gets destroyed during the rendering, so we first collect all ibufs - * and then we populate the final renderesult */ - - for (view_id = 0; view_id < tot_views; view_id++) { - context.view_id = view_id; - out = BKE_sequencer_give_ibuf(&context, cfra, 0); - - if (out) { - ibuf_arr[view_id] = IMB_dupImBuf(out); - IMB_metadata_copy(ibuf_arr[view_id], out); - IMB_freeImBuf(out); - BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]); - } - else { - ibuf_arr[view_id] = NULL; - } - } - - rr = re->result; - - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_views_new(rr, &re->r); - BLI_rw_mutex_unlock(&re->resultmutex); - - for (view_id = 0; view_id < tot_views; view_id++) { - RenderView *rv = RE_RenderViewGetById(rr, view_id); - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - - if (ibuf_arr[view_id]) { - /* copy ibuf into combined pixel rect */ - RE_render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); - - if (ibuf_arr[view_id]->metadata && (re->r.stamp & R_STAMP_STRIPMETA)) { - /* ensure render stamp info first */ - BKE_render_result_stamp_info(NULL, NULL, rr, true); - BKE_stamp_info_from_imbuf(rr, ibuf_arr[view_id]); - } - - if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ - Editing *ed = re->scene->ed; - if (ed) - BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); - } - IMB_freeImBuf(ibuf_arr[view_id]); - } - else { - /* render result is delivered empty in most cases, nevertheless we handle all cases */ - render_result_rect_fill_zero(rr, view_id); - } - - BLI_rw_mutex_unlock(&re->resultmutex); - - /* would mark display buffers as invalid */ - RE_SetActiveRenderView(re, rv->name); - re->display_update(re->duh, re->result, NULL); - } - - MEM_freeN(ibuf_arr); - - recurs_depth--; - - /* just in case this flag went missing at some point */ - re->r.scemode |= R_DOSEQ; - - /* set overall progress of sequence rendering */ - if (re->r.efra != re->r.sfra) - re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra)); - else - re->progress(re->prh, 1.0f); + static int recurs_depth = 0; + struct ImBuf *out; + RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ + int cfra = re->r.cfra; + SeqRenderData context; + int view_id, tot_views; + struct ImBuf **ibuf_arr; + int re_x, re_y; + + re->i.cfra = cfra; + + recurs_depth++; + + if ((re->r.mode & R_BORDER) && (re->r.mode & R_CROP) == 0) { + /* if border rendering is used and cropping is disabled, final buffer should + * be as large as the whole frame */ + re_x = re->winx; + re_y = re->winy; + } + else { + re_x = re->result->rectx; + re_y = re->result->recty; + } + + tot_views = BKE_scene_multiview_num_views_get(&re->r); + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs"); + + /* TODO(sergey): Currently depsgraph is only used to check whether it is an active + * edit window or not to deal with unkeyed changes. We don't have depsgraph here yet, + * but we also dont' deal with unkeyed changes. But still nice to get proper depsgraph + * within tjhe render pipeline, somehow. + */ + BKE_sequencer_new_render_data(re->main, NULL, re->scene, re_x, re_y, 100, true, &context); + + /* the renderresult gets destroyed during the rendering, so we first collect all ibufs + * and then we populate the final renderesult */ + + for (view_id = 0; view_id < tot_views; view_id++) { + context.view_id = view_id; + out = BKE_sequencer_give_ibuf(&context, cfra, 0); + + if (out) { + ibuf_arr[view_id] = IMB_dupImBuf(out); + IMB_metadata_copy(ibuf_arr[view_id], out); + IMB_freeImBuf(out); + BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]); + } + else { + ibuf_arr[view_id] = NULL; + } + } + + rr = re->result; + + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_views_new(rr, &re->r); + BLI_rw_mutex_unlock(&re->resultmutex); + + for (view_id = 0; view_id < tot_views; view_id++) { + RenderView *rv = RE_RenderViewGetById(rr, view_id); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + if (ibuf_arr[view_id]) { + /* copy ibuf into combined pixel rect */ + RE_render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); + + if (ibuf_arr[view_id]->metadata && (re->r.stamp & R_STAMP_STRIPMETA)) { + /* ensure render stamp info first */ + BKE_render_result_stamp_info(NULL, NULL, rr, true); + BKE_stamp_info_from_imbuf(rr, ibuf_arr[view_id]); + } + + if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ + Editing *ed = re->scene->ed; + if (ed) + BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + } + IMB_freeImBuf(ibuf_arr[view_id]); + } + else { + /* render result is delivered empty in most cases, nevertheless we handle all cases */ + render_result_rect_fill_zero(rr, view_id); + } + + BLI_rw_mutex_unlock(&re->resultmutex); + + /* would mark display buffers as invalid */ + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); + } + + MEM_freeN(ibuf_arr); + + recurs_depth--; + + /* just in case this flag went missing at some point */ + re->r.scemode |= R_DOSEQ; + + /* set overall progress of sequence rendering */ + if (re->r.efra != re->r.sfra) + re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra)); + else + re->progress(re->prh, 1.0f); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -1592,986 +1650,1052 @@ static void do_render_seq(Render *re) /* main loop: doing sequence + 3d render + compositing */ static void do_render_all_options(Render *re) { - Object *camera; - bool render_seq = false; - int cfra = re->r.cfra; + Object *camera; + bool render_seq = false; + int cfra = re->r.cfra; - re->current_scene_update(re->suh, re->scene); + re->current_scene_update(re->suh, re->scene); - BKE_scene_camera_switch_update(re->scene); + BKE_scene_camera_switch_update(re->scene); - re->i.starttime = PIL_check_seconds_timer(); + re->i.starttime = PIL_check_seconds_timer(); - /* ensure no images are in memory from previous animated sequences */ - BKE_image_all_free_anim_ibufs(re->main, re->r.cfra); - BKE_sequencer_all_free_anim_ibufs(re->main, re->r.cfra); + /* ensure no images are in memory from previous animated sequences */ + BKE_image_all_free_anim_ibufs(re->main, re->r.cfra); + BKE_sequencer_all_free_anim_ibufs(re->main, re->r.cfra); - /* Update for sequencer and compositing animation. - * TODO: ideally we would create a depsgraph with a copy of the scene - * like the render engine, but sequencer and compositing do not (yet?) - * work with copy-on-write. */ - BKE_animsys_evaluate_all_animation(re->main, NULL, re->scene, (float)cfra); + /* Update for sequencer and compositing animation. + * TODO: ideally we would create a depsgraph with a copy of the scene + * like the render engine, but sequencer and compositing do not (yet?) + * work with copy-on-write. */ + BKE_animsys_evaluate_all_animation(re->main, NULL, re->scene, (float)cfra); - /* Update for masks (these do not use animsys but own lighter weight structure to define animation). */ - BKE_mask_evaluate_all_masks(re->main, (float)cfra, true); + /* Update for masks (these do not use animsys but own lighter weight structure to define animation). */ + BKE_mask_evaluate_all_masks(re->main, (float)cfra, true); - if (RE_engine_render(re, 1)) { - /* in this case external render overrides all */ - } - else if (RE_seq_render_active(re->scene, &re->r)) { - /* note: do_render_seq() frees rect32 when sequencer returns float images */ - if (!re->test_break(re->tbh)) { - do_render_seq(re); - render_seq = true; - } + if (RE_engine_render(re, 1)) { + /* in this case external render overrides all */ + } + else if (RE_seq_render_active(re->scene, &re->r)) { + /* note: do_render_seq() frees rect32 when sequencer returns float images */ + if (!re->test_break(re->tbh)) { + do_render_seq(re); + render_seq = true; + } - re->stats_draw(re->sdh, &re->i); - re->display_update(re->duh, re->result, NULL); - } - else { - do_render_composite(re); - } + re->stats_draw(re->sdh, &re->i); + re->display_update(re->duh, re->result, NULL); + } + else { + do_render_composite(re); + } - re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; + re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; - re->stats_draw(re->sdh, &re->i); + re->stats_draw(re->sdh, &re->i); - /* save render result stamp if needed */ - if (re->result != NULL) { - camera = RE_GetCamera(re); - /* sequence rendering should have taken care of that already */ - if (!(render_seq && (re->r.stamp & R_STAMP_STRIPMETA))) - BKE_render_result_stamp_info(re->scene, camera, re->result, false); + /* save render result stamp if needed */ + if (re->result != NULL) { + camera = RE_GetCamera(re); + /* sequence rendering should have taken care of that already */ + if (!(render_seq && (re->r.stamp & R_STAMP_STRIPMETA))) + BKE_render_result_stamp_info(re->scene, camera, re->result, false); - /* stamp image info here */ - if ((re->r.stamp & R_STAMP_ALL) && (re->r.stamp & R_STAMP_DRAW)) { - renderresult_stampinfo(re); - re->display_update(re->duh, re->result, NULL); - } - } + /* stamp image info here */ + if ((re->r.stamp & R_STAMP_ALL) && (re->r.stamp & R_STAMP_DRAW)) { + renderresult_stampinfo(re); + re->display_update(re->duh, re->result, NULL); + } + } } static bool check_valid_compositing_camera(Scene *scene, Object *camera_override) { - if (scene->r.scemode & R_DOCOMP && scene->use_nodes) { - bNode *node = scene->nodetree->nodes.first; - - while (node) { - if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) { - Scene *sce = node->id ? (Scene *)node->id : scene; - if (sce->camera == NULL) { - sce->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(sce)); - } - if (sce->camera == NULL) { - /* all render layers nodes need camera */ - return false; - } - } - node = node->next; - } - - return true; - } - else { - return (camera_override != NULL || scene->camera != NULL); - } + if (scene->r.scemode & R_DOCOMP && scene->use_nodes) { + bNode *node = scene->nodetree->nodes.first; + + while (node) { + if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) { + Scene *sce = node->id ? (Scene *)node->id : scene; + if (sce->camera == NULL) { + sce->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(sce)); + } + if (sce->camera == NULL) { + /* all render layers nodes need camera */ + return false; + } + } + node = node->next; + } + + return true; + } + else { + return (camera_override != NULL || scene->camera != NULL); + } } static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports) { - SceneRenderView *srv; - bool active_view = false; + SceneRenderView *srv; + bool active_view = false; - if (camera == NULL || (scene->r.scemode & R_MULTIVIEW) == 0) - return true; + if (camera == NULL || (scene->r.scemode & R_MULTIVIEW) == 0) + return true; - for (srv = scene->r.views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { - active_view = true; + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + active_view = true; - if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { - Object *view_camera; - view_camera = BKE_camera_multiview_render(scene, camera, srv->name); + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + Object *view_camera; + view_camera = BKE_camera_multiview_render(scene, camera, srv->name); - if (view_camera == camera) { - /* if the suffix is not in the camera, means we are using the fallback camera */ - if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) { - BKE_reportf(reports, RPT_ERROR, "Camera \"%s\" is not a multi-view camera", - camera->id.name + 2); - return false; - } - } - } - } - } + if (view_camera == camera) { + /* if the suffix is not in the camera, means we are using the fallback camera */ + if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) { + BKE_reportf(reports, + RPT_ERROR, + "Camera \"%s\" is not a multi-view camera", + camera->id.name + 2); + return false; + } + } + } + } + } - if (!active_view) { - BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2); - return false; - } + if (!active_view) { + BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2); + return false; + } - return true; + return true; } static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports) { - const char *err_msg = "No camera found in scene \"%s\""; - - if (camera_override == NULL && scene->camera == NULL) - scene->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(scene)); - - if (!check_valid_camera_multiview(scene, scene->camera, reports)) - return false; - - if (RE_seq_render_active(scene, &scene->r)) { - if (scene->ed) { - Sequence *seq = scene->ed->seqbase.first; - - while (seq) { - if ((seq->type == SEQ_TYPE_SCENE) && - ((seq->flag & SEQ_SCENE_STRIPS) == 0) && - (seq->scene != NULL)) - { - if (!seq->scene_camera) { - if (!seq->scene->camera && - !BKE_view_layer_camera_find(BKE_view_layer_default_render(seq->scene))) - { - /* camera could be unneeded due to composite nodes */ - Object *override = (seq->scene == scene) ? camera_override : NULL; - - if (!check_valid_compositing_camera(seq->scene, override)) { - BKE_reportf(reports, RPT_ERROR, err_msg, seq->scene->id.name + 2); - return false; - } - } - } - else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports)) - return false; - } - - seq = seq->next; - } - } - } - else if (!check_valid_compositing_camera(scene, camera_override)) { - BKE_reportf(reports, RPT_ERROR, err_msg, scene->id.name + 2); - return false; - } - - return true; + const char *err_msg = "No camera found in scene \"%s\""; + + if (camera_override == NULL && scene->camera == NULL) + scene->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(scene)); + + if (!check_valid_camera_multiview(scene, scene->camera, reports)) + return false; + + if (RE_seq_render_active(scene, &scene->r)) { + if (scene->ed) { + Sequence *seq = scene->ed->seqbase.first; + + while (seq) { + if ((seq->type == SEQ_TYPE_SCENE) && ((seq->flag & SEQ_SCENE_STRIPS) == 0) && + (seq->scene != NULL)) { + if (!seq->scene_camera) { + if (!seq->scene->camera && + !BKE_view_layer_camera_find(BKE_view_layer_default_render(seq->scene))) { + /* camera could be unneeded due to composite nodes */ + Object *override = (seq->scene == scene) ? camera_override : NULL; + + if (!check_valid_compositing_camera(seq->scene, override)) { + BKE_reportf(reports, RPT_ERROR, err_msg, seq->scene->id.name + 2); + return false; + } + } + } + else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports)) + return false; + } + + seq = seq->next; + } + } + } + else if (!check_valid_compositing_camera(scene, camera_override)) { + BKE_reportf(reports, RPT_ERROR, err_msg, scene->id.name + 2); + return false; + } + + return true; } static bool node_tree_has_composite_output(bNodeTree *ntree) { - bNode *node; + bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { - if (ELEM(node->type, CMP_NODE_COMPOSITE, CMP_NODE_OUTPUT_FILE)) { - return true; - } - else if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { - if (node->id) { - if (node_tree_has_composite_output((bNodeTree *)node->id)) { - return true; - } - } - } - } + for (node = ntree->nodes.first; node; node = node->next) { + if (ELEM(node->type, CMP_NODE_COMPOSITE, CMP_NODE_OUTPUT_FILE)) { + return true; + } + else if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { + if (node->id) { + if (node_tree_has_composite_output((bNodeTree *)node->id)) { + return true; + } + } + } + } - return false; + return false; } static int check_composite_output(Scene *scene) { - return node_tree_has_composite_output(scene->nodetree); -} - -bool RE_is_rendering_allowed(Scene *scene, ViewLayer *single_layer, Object *camera_override, ReportList *reports) -{ - int scemode = check_mode_full_sample(&scene->r); - - if (scene->r.mode & R_BORDER) { - if (scene->r.border.xmax <= scene->r.border.xmin || - scene->r.border.ymax <= scene->r.border.ymin) - { - BKE_report(reports, RPT_ERROR, "No border area selected"); - return 0; - } - } - - if (scemode & (R_EXR_TILE_FILE | R_FULL_SAMPLE)) { - char str[FILE_MAX]; - - render_result_exr_file_path(scene, "", 0, str); - - if (!BLI_file_is_writable(str)) { - BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path"); - return 0; - } - } - - if (scemode & R_DOCOMP) { - if (scene->use_nodes) { - if (!scene->nodetree) { - BKE_report(reports, RPT_ERROR, "No node tree in scene"); - return 0; - } - - if (!check_composite_output(scene)) { - BKE_report(reports, RPT_ERROR, "No render output node in scene"); - return 0; - } - - if (scemode & R_FULL_SAMPLE) { - if (composite_needs_render(scene, 0) == 0) { - BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering"); - return 0; - } - } - } - } - - /* check valid camera, without camera render is OK (compo, seq) */ - if (!check_valid_camera(scene, camera_override, reports)) { - return 0; - } - - if (RE_seq_render_active(scene, &scene->r)) { - if (scene->r.mode & R_BORDER) { - BKE_report(reports, RPT_ERROR, "Border rendering is not supported by sequencer"); - return false; - } - } - - /* layer flag tests */ - if (!render_scene_has_layers_to_render(scene, single_layer)) { - BKE_report(reports, RPT_ERROR, "All render layers are disabled"); - return 0; - } - - return 1; + return node_tree_has_composite_output(scene->nodetree); +} + +bool RE_is_rendering_allowed(Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + ReportList *reports) +{ + int scemode = check_mode_full_sample(&scene->r); + + if (scene->r.mode & R_BORDER) { + if (scene->r.border.xmax <= scene->r.border.xmin || + scene->r.border.ymax <= scene->r.border.ymin) { + BKE_report(reports, RPT_ERROR, "No border area selected"); + return 0; + } + } + + if (scemode & (R_EXR_TILE_FILE | R_FULL_SAMPLE)) { + char str[FILE_MAX]; + + render_result_exr_file_path(scene, "", 0, str); + + if (!BLI_file_is_writable(str)) { + BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path"); + return 0; + } + } + + if (scemode & R_DOCOMP) { + if (scene->use_nodes) { + if (!scene->nodetree) { + BKE_report(reports, RPT_ERROR, "No node tree in scene"); + return 0; + } + + if (!check_composite_output(scene)) { + BKE_report(reports, RPT_ERROR, "No render output node in scene"); + return 0; + } + + if (scemode & R_FULL_SAMPLE) { + if (composite_needs_render(scene, 0) == 0) { + BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering"); + return 0; + } + } + } + } + + /* check valid camera, without camera render is OK (compo, seq) */ + if (!check_valid_camera(scene, camera_override, reports)) { + return 0; + } + + if (RE_seq_render_active(scene, &scene->r)) { + if (scene->r.mode & R_BORDER) { + BKE_report(reports, RPT_ERROR, "Border rendering is not supported by sequencer"); + return false; + } + } + + /* layer flag tests */ + if (!render_scene_has_layers_to_render(scene, single_layer)) { + BKE_report(reports, RPT_ERROR, "All render layers are disabled"); + return 0; + } + + return 1; } static void validate_render_settings(Render *re) { - if (RE_engine_is_external(re)) { - /* not supported yet */ - re->r.scemode &= ~(R_FULL_SAMPLE); - } + if (RE_engine_is_external(re)) { + /* not supported yet */ + re->r.scemode &= ~(R_FULL_SAMPLE); + } } -static void update_physics_cache(Render *re, Scene *scene, ViewLayer *view_layer, int UNUSED(anim_init)) +static void update_physics_cache(Render *re, + Scene *scene, + ViewLayer *view_layer, + int UNUSED(anim_init)) { - PTCacheBaker baker; + PTCacheBaker baker; - memset(&baker, 0, sizeof(baker)); - baker.bmain = re->main; - baker.scene = scene; - baker.view_layer = view_layer; - baker.depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); - baker.bake = 0; - baker.render = 1; - baker.anim_init = 1; - baker.quick_step = 1; + memset(&baker, 0, sizeof(baker)); + baker.bmain = re->main; + baker.scene = scene; + baker.view_layer = view_layer; + baker.depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + baker.bake = 0; + baker.render = 1; + baker.anim_init = 1; + baker.quick_step = 1; - BKE_ptcache_bake(&baker); + BKE_ptcache_bake(&baker); } void RE_SetActiveRenderView(Render *re, const char *viewname) { - BLI_strncpy(re->viewname, viewname, sizeof(re->viewname)); + BLI_strncpy(re->viewname, viewname, sizeof(re->viewname)); } const char *RE_GetActiveRenderView(Render *re) { - return re->viewname; + return re->viewname; } /* evaluating scene options for general Blender render */ -static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, - ViewLayer *single_layer, Object *camera_override, - int anim, int anim_init) -{ - int winx, winy; - rcti disprect; - - /* r.xsch and r.ysch has the actual view window size - * r.border is the clipping rect */ - - /* calculate actual render result and display size */ - winx = (rd->size * rd->xsch) / 100; - winy = (rd->size * rd->ysch) / 100; - - /* we always render smaller part, inserting it in larger image is compositor bizz, it uses disprect for it */ - if (scene->r.mode & R_BORDER) { - disprect.xmin = rd->border.xmin * winx; - disprect.xmax = rd->border.xmax * winx; - - disprect.ymin = rd->border.ymin * winy; - disprect.ymax = rd->border.ymax * winy; - } - else { - disprect.xmin = disprect.ymin = 0; - disprect.xmax = winx; - disprect.ymax = winy; - } - - re->main = bmain; - re->scene = scene; - re->camera_override = camera_override; - re->viewname[0] = '\0'; - - /* not too nice, but it survives anim-border render */ - if (anim) { - render_update_anim_renderdata(re, &scene->r, &scene->view_layers); - re->disprect = disprect; - return 1; - } - - /* - * Disabled completely for now, - * can be later set as render profile option - * and default for background render. - */ - if (0) { - /* make sure dynamics are up to date */ - ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene); - update_physics_cache(re, scene, view_layer, anim_init); - } - - if (single_layer || scene->r.scemode & R_SINGLE_LAYER) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_single_layer_begin(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - RE_InitState(re, NULL, &scene->r, &scene->view_layers, single_layer, winx, winy, &disprect); - if (!re->ok) /* if an error was printed, abort */ - return 0; - - /* initstate makes new result, have to send changed tags around */ - ntreeCompositTagRender(re->scene); - - validate_render_settings(re); - - re->display_init(re->dih, re->result); - re->display_clear(re->dch, re->result); - - return 1; +static int render_initialize_from_main(Render *re, + RenderData *rd, + Main *bmain, + Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + int anim, + int anim_init) +{ + int winx, winy; + rcti disprect; + + /* r.xsch and r.ysch has the actual view window size + * r.border is the clipping rect */ + + /* calculate actual render result and display size */ + winx = (rd->size * rd->xsch) / 100; + winy = (rd->size * rd->ysch) / 100; + + /* we always render smaller part, inserting it in larger image is compositor bizz, it uses disprect for it */ + if (scene->r.mode & R_BORDER) { + disprect.xmin = rd->border.xmin * winx; + disprect.xmax = rd->border.xmax * winx; + + disprect.ymin = rd->border.ymin * winy; + disprect.ymax = rd->border.ymax * winy; + } + else { + disprect.xmin = disprect.ymin = 0; + disprect.xmax = winx; + disprect.ymax = winy; + } + + re->main = bmain; + re->scene = scene; + re->camera_override = camera_override; + re->viewname[0] = '\0'; + + /* not too nice, but it survives anim-border render */ + if (anim) { + render_update_anim_renderdata(re, &scene->r, &scene->view_layers); + re->disprect = disprect; + return 1; + } + + /* + * Disabled completely for now, + * can be later set as render profile option + * and default for background render. + */ + if (0) { + /* make sure dynamics are up to date */ + ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene); + update_physics_cache(re, scene, view_layer, anim_init); + } + + if (single_layer || scene->r.scemode & R_SINGLE_LAYER) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_single_layer_begin(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + RE_InitState(re, NULL, &scene->r, &scene->view_layers, single_layer, winx, winy, &disprect); + if (!re->ok) /* if an error was printed, abort */ + return 0; + + /* initstate makes new result, have to send changed tags around */ + ntreeCompositTagRender(re->scene); + + validate_render_settings(re); + + re->display_init(re->dih, re->result); + re->display_clear(re->dch, re->result); + + return 1; } void RE_SetReports(Render *re, ReportList *reports) { - re->reports = reports; + re->reports = reports; } /* general Blender frame render call */ -void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, ViewLayer *single_layer, Object *camera_override, - int frame, const bool write_still) -{ - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); - - /* ugly global still... is to prevent preview events and signal subsurfs etc to make full resol */ - G.is_rendering = true; - - scene->r.cfra = frame; - - if (render_initialize_from_main(re, &scene->r, bmain, scene, single_layer, - camera_override, 0, 0)) - { - MEM_reset_peak_memory(); - - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_PRE); - - do_render_all_options(re); - - if (write_still && !G.is_break) { - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - /* operator checks this but in case its called from elsewhere */ - printf("Error: cant write single images with a movie format!\n"); - } - else { - char name[FILE_MAX]; - BKE_image_path_from_imformat( - name, scene->r.pic, BKE_main_blendfile_path(bmain), scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); - - /* reports only used for Movie */ - do_write_image_or_movie(re, bmain, scene, NULL, 0, name); - } - } - - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_POST); /* keep after file save */ - if (write_still) { - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_WRITE); - } - } - - BLI_callback_exec(re->main, (ID *)scene, G.is_break ? BLI_CB_EVT_RENDER_CANCEL : BLI_CB_EVT_RENDER_COMPLETE); - - /* Destroy the opengl context in the correct thread. */ - RE_gl_context_destroy(re); - - /* UGLY WARNING */ - G.is_rendering = false; +void RE_BlenderFrame(Render *re, + Main *bmain, + Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + int frame, + const bool write_still) +{ + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); + + /* ugly global still... is to prevent preview events and signal subsurfs etc to make full resol */ + G.is_rendering = true; + + scene->r.cfra = frame; + + if (render_initialize_from_main( + re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { + MEM_reset_peak_memory(); + + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_PRE); + + do_render_all_options(re); + + if (write_still && !G.is_break) { + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + /* operator checks this but in case its called from elsewhere */ + printf("Error: cant write single images with a movie format!\n"); + } + else { + char name[FILE_MAX]; + BKE_image_path_from_imformat(name, + scene->r.pic, + BKE_main_blendfile_path(bmain), + scene->r.cfra, + &scene->r.im_format, + (scene->r.scemode & R_EXTENSION) != 0, + false, + NULL); + + /* reports only used for Movie */ + do_write_image_or_movie(re, bmain, scene, NULL, 0, name); + } + } + + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_POST); /* keep after file save */ + if (write_still) { + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_WRITE); + } + } + + BLI_callback_exec( + re->main, (ID *)scene, G.is_break ? BLI_CB_EVT_RENDER_CANCEL : BLI_CB_EVT_RENDER_COMPLETE); + + /* Destroy the opengl context in the correct thread. */ + RE_gl_context_destroy(re); + + /* UGLY WARNING */ + G.is_rendering = false; } #ifdef WITH_FREESTYLE void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render) { - re->result_ok = 0; - if (render_initialize_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { - if (render) - do_render_3d(re); - } - re->result_ok = 1; + re->result_ok = 0; + if (render_initialize_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { + if (render) + do_render_3d(re); + } + re->result_ok = 1; } void RE_RenderFreestyleExternal(Render *re) { - if (!re->test_break(re->tbh)) { - RenderView *rv; + if (!re->test_break(re->tbh)) { + RenderView *rv; - init_freestyle(re); + init_freestyle(re); - for (rv = re->result->views.first; rv; rv = rv->next) { - RE_SetActiveRenderView(re, rv->name); - add_freestyle(re, 1); - } - } + for (rv = re->result->views.first; rv; rv = rv->next) { + RE_SetActiveRenderView(re, rv->name); + add_freestyle(re, 1); + } + } } #endif -bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, const bool stamp, char *name) -{ - bool ok = true; - RenderData *rd = &scene->r; - - if (!rr) - return false; - - bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; - bool is_exr_rr = ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && - RE_HasFloatPixels(rr); - - if (rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) - { - ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, NULL, -1); - render_print_save_message(reports, name, ok, errno); - } - - /* mono, legacy code */ - else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) - { - RenderView *rv; - int view_id; - char filepath[FILE_MAX]; - - BLI_strncpy(filepath, name, sizeof(filepath)); - - for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { - if (!is_mono) { - BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); - } - - if (is_exr_rr) { - ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, rv->name, -1); - render_print_save_message(reports, name, ok, errno); - - /* optional preview images for exr */ - if (ok && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = rd->im_format; - imf.imtype = R_IMF_IMTYPE_JPEG90; - - if (BLI_path_extension_check(name, ".exr")) - name[strlen(name) - 4] = 0; - BKE_image_path_ensure_ext_from_imformat(name, &imf); - - ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); - ibuf->planes = 24; - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &imf); - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &imf, stamp); - - IMB_freeImBuf(ibuf); - } - } - else { - ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); - - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &rd->im_format); - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &rd->im_format, stamp); - - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); - } - } - } - else { /* R_IMF_VIEWS_STEREO_3D */ - BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); - - if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - printf("Stereo 3D not supported for MultiLayer image: %s\n", name); - } - else { - ImBuf *ibuf_arr[3] = {NULL}; - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - int i; - - for (i = 0; i < 2; i++) { - int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); - ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); - IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); - } - - ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); - - /* optional preview images for exr */ - if (ok && is_exr_rr && - (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) - { - ImageFormatData imf = rd->im_format; - imf.imtype = R_IMF_IMTYPE_JPEG90; - - if (BLI_path_extension_check(name, ".exr")) - name[strlen(name) - 4] = 0; - - BKE_image_path_ensure_ext_from_imformat(name, &imf); - ibuf_arr[2]->planes = 24; - - ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); - } - - /* imbuf knows which rects are not part of ibuf */ - for (i = 0; i < 3; i++) { - IMB_freeImBuf(ibuf_arr[i]); - } - } - } - - return ok; -} - -bool RE_WriteRenderViewsMovie( - ReportList *reports, RenderResult *rr, Scene *scene, RenderData *rd, bMovieHandle *mh, - void **movie_ctx_arr, const int totvideos, bool preview) -{ - bool is_mono; - bool ok = true; - - if (!rr) - return false; - - is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; - - if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { - int view_id; - for (view_id = 0; view_id < totvideos; view_id++) { - const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); - ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); - - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - - ok &= mh->append_movie(movie_ctx_arr[view_id], rd, preview ? scene->r.psfra : scene->r.sfra, scene->r.cfra, - (int *)ibuf->rect, ibuf->x, ibuf->y, suffix, reports); - - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); - } - printf("Append frame %d\n", scene->r.cfra); - } - else { /* R_IMF_VIEWS_STEREO_3D */ - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - ImBuf *ibuf_arr[3] = {NULL}; - int i; - - BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); - - for (i = 0; i < 2; i++) { - int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); - ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); - - IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - } - - ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - - ok = mh->append_movie(movie_ctx_arr[0], rd, preview ? scene->r.psfra : scene->r.sfra, scene->r.cfra, (int *)ibuf_arr[2]->rect, - ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports); - - for (i = 0; i < 3; i++) { - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf_arr[i]); - } - } - - return ok; -} - -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const int totvideos, const char *name_override) -{ - char name[FILE_MAX]; - RenderResult rres; - double render_time; - bool ok = true; - - RE_AcquireResultImageViews(re, &rres); - - /* write movie or image */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - RE_WriteRenderViewsMovie(re->reports, &rres, scene, &re->r, mh, re->movie_ctx_arr, totvideos, false); - } - else { - if (name_override) - BLI_strncpy(name, name_override, sizeof(name)); - else - BKE_image_path_from_imformat( - name, scene->r.pic, BKE_main_blendfile_path(bmain), scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); - - /* write images as individual images or stereo */ - ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name); - } - - RE_ReleaseResultImageViews(re, &rres); - - render_time = re->i.lastframetime; - re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; - - BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime); - printf(" Time: %s", name); - - /* Flush stdout to be sure python callbacks are printing stuff after blender. */ - fflush(stdout); - - /* NOTE: using G_MAIN seems valid here??? Not sure it's actually even used anyway, we could as well pass NULL? */ - BLI_callback_exec(G_MAIN, NULL, BLI_CB_EVT_RENDER_STATS); - - BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time); - printf(" (Saving: %s)\n", name); - - fputc('\n', stdout); - fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */ - - return ok; -} - -static void get_videos_dimensions( - Render *re, RenderData *rd, - size_t *r_width, size_t *r_height) -{ - size_t width, height; - if (re->r.mode & R_BORDER) { - if ((re->r.mode & R_CROP) == 0) { - width = re->winx; - height = re->winy; - } - else { - width = re->rectx; - height = re->recty; - } - } - else { - width = re->rectx; - height = re->recty; - } - - BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height); +bool RE_WriteRenderViewsImage( + ReportList *reports, RenderResult *rr, Scene *scene, const bool stamp, char *name) +{ + bool ok = true; + RenderData *rd = &scene->r; + + if (!rr) + return false; + + bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; + bool is_exr_rr = ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + RE_HasFloatPixels(rr); + + if (rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) { + ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, NULL, -1); + render_print_save_message(reports, name, ok, errno); + } + + /* mono, legacy code */ + else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + RenderView *rv; + int view_id; + char filepath[FILE_MAX]; + + BLI_strncpy(filepath, name, sizeof(filepath)); + + for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { + if (!is_mono) { + BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); + } + + if (is_exr_rr) { + ok = RE_WriteRenderResult(reports, rr, name, &rd->im_format, rv->name, -1); + render_print_save_message(reports, name, ok, errno); + + /* optional preview images for exr */ + if (ok && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + ImageFormatData imf = rd->im_format; + imf.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_path_extension_check(name, ".exr")) + name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); + + ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); + ibuf->planes = 24; + IMB_colormanagement_imbuf_for_write( + ibuf, true, false, &scene->view_settings, &scene->display_settings, &imf); + + ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &imf, stamp); + + IMB_freeImBuf(ibuf); + } + } + else { + ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); + + IMB_colormanagement_imbuf_for_write( + ibuf, true, false, &scene->view_settings, &scene->display_settings, &rd->im_format); + + ok = render_imbuf_write_stamp_test(reports, scene, rr, ibuf, name, &rd->im_format, stamp); + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + } + } + else { /* R_IMF_VIEWS_STEREO_3D */ + BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); + + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + printf("Stereo 3D not supported for MultiLayer image: %s\n", name); + } + else { + ImBuf *ibuf_arr[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], + true, + false, + &scene->view_settings, + &scene->display_settings, + &scene->r.im_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + + ok = render_imbuf_write_stamp_test( + reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); + + /* optional preview images for exr */ + if (ok && is_exr_rr && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + ImageFormatData imf = rd->im_format; + imf.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_path_extension_check(name, ".exr")) + name[strlen(name) - 4] = 0; + + BKE_image_path_ensure_ext_from_imformat(name, &imf); + ibuf_arr[2]->planes = 24; + + ok = render_imbuf_write_stamp_test( + reports, scene, rr, ibuf_arr[2], name, &rd->im_format, stamp); + } + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ok; +} + +bool RE_WriteRenderViewsMovie(ReportList *reports, + RenderResult *rr, + Scene *scene, + RenderData *rd, + bMovieHandle *mh, + void **movie_ctx_arr, + const int totvideos, + bool preview) +{ + bool is_mono; + bool ok = true; + + if (!rr) + return false; + + is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2; + + if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + int view_id; + for (view_id = 0; view_id < totvideos; view_id++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + IMB_colormanagement_imbuf_for_write( + ibuf, true, false, &scene->view_settings, &scene->display_settings, &scene->r.im_format); + + ok &= mh->append_movie(movie_ctx_arr[view_id], + rd, + preview ? scene->r.psfra : scene->r.sfra, + scene->r.cfra, + (int *)ibuf->rect, + ibuf->x, + ibuf->y, + suffix, + reports); + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + printf("Append frame %d\n", scene->r.cfra); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + ImBuf *ibuf_arr[3] = {NULL}; + int i; + + BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], + true, + false, + &scene->view_settings, + &scene->display_settings, + &scene->r.im_format); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + + ok = mh->append_movie(movie_ctx_arr[0], + rd, + preview ? scene->r.psfra : scene->r.sfra, + scene->r.cfra, + (int *)ibuf_arr[2]->rect, + ibuf_arr[2]->x, + ibuf_arr[2]->y, + "", + reports); + + for (i = 0; i < 3; i++) { + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf_arr[i]); + } + } + + return ok; +} + +static int do_write_image_or_movie(Render *re, + Main *bmain, + Scene *scene, + bMovieHandle *mh, + const int totvideos, + const char *name_override) +{ + char name[FILE_MAX]; + RenderResult rres; + double render_time; + bool ok = true; + + RE_AcquireResultImageViews(re, &rres); + + /* write movie or image */ + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + RE_WriteRenderViewsMovie( + re->reports, &rres, scene, &re->r, mh, re->movie_ctx_arr, totvideos, false); + } + else { + if (name_override) + BLI_strncpy(name, name_override, sizeof(name)); + else + BKE_image_path_from_imformat(name, + scene->r.pic, + BKE_main_blendfile_path(bmain), + scene->r.cfra, + &scene->r.im_format, + (scene->r.scemode & R_EXTENSION) != 0, + true, + NULL); + + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, true, name); + } + + RE_ReleaseResultImageViews(re, &rres); + + render_time = re->i.lastframetime; + re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; + + BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime); + printf(" Time: %s", name); + + /* Flush stdout to be sure python callbacks are printing stuff after blender. */ + fflush(stdout); + + /* NOTE: using G_MAIN seems valid here??? Not sure it's actually even used anyway, we could as well pass NULL? */ + BLI_callback_exec(G_MAIN, NULL, BLI_CB_EVT_RENDER_STATS); + + BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time); + printf(" (Saving: %s)\n", name); + + fputc('\n', stdout); + fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */ + + return ok; +} + +static void get_videos_dimensions(Render *re, RenderData *rd, size_t *r_width, size_t *r_height) +{ + size_t width, height; + if (re->r.mode & R_BORDER) { + if ((re->r.mode & R_CROP) == 0) { + width = re->winx; + height = re->winy; + } + else { + width = re->rectx; + height = re->recty; + } + } + else { + width = re->rectx; + height = re->recty; + } + + BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height); } static void re_movie_free_all(Render *re, bMovieHandle *mh, int totvideos) { - int i; + int i; - for (i = 0; i < totvideos; i++) { - mh->end_movie(re->movie_ctx_arr[i]); - mh->context_free(re->movie_ctx_arr[i]); - } + for (i = 0; i < totvideos; i++) { + mh->end_movie(re->movie_ctx_arr[i]); + mh->context_free(re->movie_ctx_arr[i]); + } - MEM_SAFE_FREE(re->movie_ctx_arr); + MEM_SAFE_FREE(re->movie_ctx_arr); } /* saves images to disk */ -void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, ViewLayer *single_layer, Object *camera_override, - int sfra, int efra, int tfra) -{ - RenderData rd = scene->r; - bMovieHandle *mh = NULL; - int cfrao = scene->r.cfra; - int nfra, totrendered = 0, totskipped = 0; - const int totvideos = BKE_scene_multiview_num_videos_get(&rd); - const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); - const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 && - (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); - - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); - - /* do not fully call for each frame, it initializes & pops output window */ - if (!render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) - return; - - if (is_movie) { - size_t width, height; - int i; - bool is_error = false; - - get_videos_dimensions(re, &rd, &width, &height); - - mh = BKE_movie_handle_get(scene->r.im_format.imtype); - if (mh == NULL) { - BKE_report(re->reports, RPT_ERROR, "Movie format unsupported"); - return; - } - - re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context"); - - for (i = 0; i < totvideos; i++) { - const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i); - - re->movie_ctx_arr[i] = mh->context_create(); - - if (!mh->start_movie(re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix)) { - is_error = true; - break; - } - } - - if (is_error) { - /* report is handled above */ - re_movie_free_all(re, mh, i + 1); - return; - } - } - - /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ - /* is also set by caller renderwin.c */ - G.is_rendering = true; - - re->flag |= R_ANIMATION; - - { - for (nfra = sfra, scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) { - char name[FILE_MAX]; - - /* Special case for 'mh->get_next_frame' - * this overrides regular frame stepping logic */ - if (mh && mh->get_next_frame) { - while (G.is_break == false) { - int nfra_test = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports); - if (nfra_test >= 0 && nfra_test >= sfra && nfra_test <= efra) { - nfra = nfra_test; - break; - } - else { - if (re->test_break(re->tbh)) { - G.is_break = true; - } - } - } - } - - /* Here is a feedback loop exists -- render initialization requires updated - * render layers settings which could be animated, but scene evaluation for - * the frame happens later because it depends on what layers are visible to - * render engine. - * - * The idea here is to only evaluate animation data associated with the scene, - * which will make sure render layer settings are up-to-date, initialize the - * render database itself and then perform full scene update with only needed - * layers. - * -sergey- - */ - { - float ctime = BKE_scene_frame_get(scene); - AnimData *adt = BKE_animdata_from_id(&scene->id); - /* TODO(sergey): Currently depsgraph is only used to check whether it is an active - * edit window or not to deal with unkeyed changes. We don't have depsgraph here yet, - * but we also dont' deal with unkeyed changes. But still nice to get proper depsgraph - * within tjhe render pipeline, somehow. - */ - BKE_animsys_evaluate_animdata(NULL, scene, &scene->id, adt, ctime, ADT_RECALC_ALL); - } - - /* only border now, todo: camera lens. (ton) */ - render_initialize_from_main(re, &rd, bmain, scene, - single_layer, camera_override, 1, 0); - - if (nfra != scene->r.cfra) { - /* Skip this frame, but could update for physics and particles system. */ - continue; - } - else - nfra += tfra; - - /* Touch/NoOverwrite options are only valid for image's */ - if (is_movie == false) { - if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) - BKE_image_path_from_imformat( - name, scene->r.pic, BKE_main_blendfile_path(bmain), scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); - - if (scene->r.mode & R_NO_OVERWRITE) { - if (!is_multiview_name) { - if (BLI_exists(name)) { - printf("skipping existing frame \"%s\"\n", name); - totskipped++; - continue; - } - } - else { - SceneRenderView *srv; - bool is_skip = false; - char filepath[FILE_MAX]; - - for (srv = scene->r.views.first; srv; srv = srv->next) { - if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) - continue; - - BKE_scene_multiview_filepath_get(srv, name, filepath); - - if (BLI_exists(filepath)) { - is_skip = true; - printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name); - } - } - - if (is_skip) { - totskipped++; - continue; - } - } - } - - if (scene->r.mode & R_TOUCH) { - if (!is_multiview_name) { - if (!BLI_exists(name)) { - BLI_make_existing_file(name); /* makes the dir if its not there */ - BLI_file_touch(name); - } - } - else { - SceneRenderView *srv; - char filepath[FILE_MAX]; - - for (srv = scene->r.views.first; srv; srv = srv->next) { - if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) - continue; - - BKE_scene_multiview_filepath_get(srv, name, filepath); - - if (!BLI_exists(filepath)) { - BLI_make_existing_file(filepath); /* makes the dir if its not there */ - BLI_file_touch(filepath); - } - } - } - } - } - - re->r.cfra = scene->r.cfra; /* weak.... */ - - /* run callbacs before rendering, before the scene is updated */ - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_PRE); - - - do_render_all_options(re); - totrendered++; - - if (re->test_break(re->tbh) == 0) { - if (!G.is_break) - if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) - G.is_break = true; - } - else - G.is_break = true; - - if (G.is_break == true) { - /* remove touched file */ - if (is_movie == false) { - if ((scene->r.mode & R_TOUCH)) { - if (!is_multiview_name) { - if ((BLI_file_size(name) == 0)) { - /* BLI_exists(name) is implicit */ - BLI_delete(name, false, false); - } - } - else { - SceneRenderView *srv; - char filepath[FILE_MAX]; - - for (srv = scene->r.views.first; srv; srv = srv->next) { - if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) - continue; - - BKE_scene_multiview_filepath_get(srv, name, filepath); - - if ((BLI_file_size(filepath) == 0)) { - /* BLI_exists(filepath) is implicit */ - BLI_delete(filepath, false, false); - } - } - } - } - } - - break; - } - - if (G.is_break == false) { - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_POST); /* keep after file save */ - BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_WRITE); - } - } - } - - /* end movie */ - if (is_movie) { - re_movie_free_all(re, mh, totvideos); - } - - if (totskipped && totrendered == 0) - BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); - - scene->r.cfra = cfrao; - - re->flag &= ~R_ANIMATION; - - BLI_callback_exec(re->main, (ID *)scene, G.is_break ? BLI_CB_EVT_RENDER_CANCEL : BLI_CB_EVT_RENDER_COMPLETE); - BKE_sound_reset_scene_specs(scene); - - /* Destroy the opengl context in the correct thread. */ - RE_gl_context_destroy(re); - - /* UGLY WARNING */ - G.is_rendering = false; +void RE_BlenderAnim(Render *re, + Main *bmain, + Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + int sfra, + int efra, + int tfra) +{ + RenderData rd = scene->r; + bMovieHandle *mh = NULL; + int cfrao = scene->r.cfra; + int nfra, totrendered = 0, totskipped = 0; + const int totvideos = BKE_scene_multiview_num_videos_get(&rd); + const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); + const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 && + (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); + + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); + + /* do not fully call for each frame, it initializes & pops output window */ + if (!render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) + return; + + if (is_movie) { + size_t width, height; + int i; + bool is_error = false; + + get_videos_dimensions(re, &rd, &width, &height); + + mh = BKE_movie_handle_get(scene->r.im_format.imtype); + if (mh == NULL) { + BKE_report(re->reports, RPT_ERROR, "Movie format unsupported"); + return; + } + + re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context"); + + for (i = 0; i < totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i); + + re->movie_ctx_arr[i] = mh->context_create(); + + if (!mh->start_movie( + re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix)) { + is_error = true; + break; + } + } + + if (is_error) { + /* report is handled above */ + re_movie_free_all(re, mh, i + 1); + return; + } + } + + /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ + /* is also set by caller renderwin.c */ + G.is_rendering = true; + + re->flag |= R_ANIMATION; + + { + for (nfra = sfra, scene->r.cfra = sfra; scene->r.cfra <= efra; scene->r.cfra++) { + char name[FILE_MAX]; + + /* Special case for 'mh->get_next_frame' + * this overrides regular frame stepping logic */ + if (mh && mh->get_next_frame) { + while (G.is_break == false) { + int nfra_test = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports); + if (nfra_test >= 0 && nfra_test >= sfra && nfra_test <= efra) { + nfra = nfra_test; + break; + } + else { + if (re->test_break(re->tbh)) { + G.is_break = true; + } + } + } + } + + /* Here is a feedback loop exists -- render initialization requires updated + * render layers settings which could be animated, but scene evaluation for + * the frame happens later because it depends on what layers are visible to + * render engine. + * + * The idea here is to only evaluate animation data associated with the scene, + * which will make sure render layer settings are up-to-date, initialize the + * render database itself and then perform full scene update with only needed + * layers. + * -sergey- + */ + { + float ctime = BKE_scene_frame_get(scene); + AnimData *adt = BKE_animdata_from_id(&scene->id); + /* TODO(sergey): Currently depsgraph is only used to check whether it is an active + * edit window or not to deal with unkeyed changes. We don't have depsgraph here yet, + * but we also dont' deal with unkeyed changes. But still nice to get proper depsgraph + * within tjhe render pipeline, somehow. + */ + BKE_animsys_evaluate_animdata(NULL, scene, &scene->id, adt, ctime, ADT_RECALC_ALL); + } + + /* only border now, todo: camera lens. (ton) */ + render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 1, 0); + + if (nfra != scene->r.cfra) { + /* Skip this frame, but could update for physics and particles system. */ + continue; + } + else + nfra += tfra; + + /* Touch/NoOverwrite options are only valid for image's */ + if (is_movie == false) { + if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) + BKE_image_path_from_imformat(name, + scene->r.pic, + BKE_main_blendfile_path(bmain), + scene->r.cfra, + &scene->r.im_format, + (scene->r.scemode & R_EXTENSION) != 0, + true, + NULL); + + if (scene->r.mode & R_NO_OVERWRITE) { + if (!is_multiview_name) { + if (BLI_exists(name)) { + printf("skipping existing frame \"%s\"\n", name); + totskipped++; + continue; + } + } + else { + SceneRenderView *srv; + bool is_skip = false; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (BLI_exists(filepath)) { + is_skip = true; + printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name); + } + } + + if (is_skip) { + totskipped++; + continue; + } + } + } + + if (scene->r.mode & R_TOUCH) { + if (!is_multiview_name) { + if (!BLI_exists(name)) { + BLI_make_existing_file(name); /* makes the dir if its not there */ + BLI_file_touch(name); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (!BLI_exists(filepath)) { + BLI_make_existing_file(filepath); /* makes the dir if its not there */ + BLI_file_touch(filepath); + } + } + } + } + } + + re->r.cfra = scene->r.cfra; /* weak.... */ + + /* run callbacs before rendering, before the scene is updated */ + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_PRE); + + do_render_all_options(re); + totrendered++; + + if (re->test_break(re->tbh) == 0) { + if (!G.is_break) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) + G.is_break = true; + } + else + G.is_break = true; + + if (G.is_break == true) { + /* remove touched file */ + if (is_movie == false) { + if ((scene->r.mode & R_TOUCH)) { + if (!is_multiview_name) { + if ((BLI_file_size(name) == 0)) { + /* BLI_exists(name) is implicit */ + BLI_delete(name, false, false); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if ((BLI_file_size(filepath) == 0)) { + /* BLI_exists(filepath) is implicit */ + BLI_delete(filepath, false, false); + } + } + } + } + } + + break; + } + + if (G.is_break == false) { + BLI_callback_exec( + re->main, (ID *)scene, BLI_CB_EVT_RENDER_POST); /* keep after file save */ + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_WRITE); + } + } + } + + /* end movie */ + if (is_movie) { + re_movie_free_all(re, mh, totvideos); + } + + if (totskipped && totrendered == 0) + BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); + + scene->r.cfra = cfrao; + + re->flag &= ~R_ANIMATION; + + BLI_callback_exec( + re->main, (ID *)scene, G.is_break ? BLI_CB_EVT_RENDER_CANCEL : BLI_CB_EVT_RENDER_COMPLETE); + BKE_sound_reset_scene_specs(scene); + + /* Destroy the opengl context in the correct thread. */ + RE_gl_context_destroy(re); + + /* UGLY WARNING */ + G.is_rendering = false; } void RE_PreviewRender(Render *re, Main *bmain, Scene *sce) { - Object *camera; - int winx, winy; + Object *camera; + int winx, winy; - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; + winx = (sce->r.size * sce->r.xsch) / 100; + winy = (sce->r.size * sce->r.ysch) / 100; - RE_InitState(re, NULL, &sce->r, &sce->view_layers, NULL, winx, winy, NULL); + RE_InitState(re, NULL, &sce->r, &sce->view_layers, NULL, winx, winy, NULL); - re->main = bmain; - re->scene = sce; + re->main = bmain; + re->scene = sce; - camera = RE_GetCamera(re); - RE_SetCamera(re, camera); + camera = RE_GetCamera(re); + RE_SetCamera(re, camera); - do_render_3d(re); + do_render_3d(re); } /* note; repeated win/disprect calc... solve that nicer, also in compo */ @@ -2579,215 +2703,226 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce) /* only the temp file! */ bool RE_ReadRenderResult(Scene *scene, Scene *scenode) { - Render *re; - int winx, winy; - bool success; - rcti disprect; + Render *re; + int winx, winy; + bool success; + rcti disprect; - /* calculate actual render result and display size */ - winx = (scene->r.size * scene->r.xsch) / 100; - winy = (scene->r.size * scene->r.ysch) / 100; + /* calculate actual render result and display size */ + winx = (scene->r.size * scene->r.xsch) / 100; + winy = (scene->r.size * scene->r.ysch) / 100; - /* only in movie case we render smaller part */ - if (scene->r.mode & R_BORDER) { - disprect.xmin = scene->r.border.xmin * winx; - disprect.xmax = scene->r.border.xmax * winx; + /* only in movie case we render smaller part */ + if (scene->r.mode & R_BORDER) { + disprect.xmin = scene->r.border.xmin * winx; + disprect.xmax = scene->r.border.xmax * winx; - disprect.ymin = scene->r.border.ymin * winy; - disprect.ymax = scene->r.border.ymax * winy; - } - else { - disprect.xmin = disprect.ymin = 0; - disprect.xmax = winx; - disprect.ymax = winy; - } + disprect.ymin = scene->r.border.ymin * winy; + disprect.ymax = scene->r.border.ymax * winy; + } + else { + disprect.xmin = disprect.ymin = 0; + disprect.xmax = winx; + disprect.ymax = winy; + } - if (scenode) - scene = scenode; + if (scenode) + scene = scenode; - /* get render: it can be called from UI with draw callbacks */ - re = RE_GetSceneRender(scene); - if (re == NULL) - re = RE_NewSceneRender(scene); - RE_InitState(re, NULL, &scene->r, &scene->view_layers, NULL, winx, winy, &disprect); - re->scene = scene; + /* get render: it can be called from UI with draw callbacks */ + re = RE_GetSceneRender(scene); + if (re == NULL) + re = RE_NewSceneRender(scene); + RE_InitState(re, NULL, &scene->r, &scene->view_layers, NULL, winx, winy, &disprect); + re->scene = scene; - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - success = render_result_exr_file_cache_read(re); - BLI_rw_mutex_unlock(&re->resultmutex); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + success = render_result_exr_file_cache_read(re); + BLI_rw_mutex_unlock(&re->resultmutex); - render_result_uncrop(re); + render_result_uncrop(re); - return success; + return success; } void RE_init_threadcount(Render *re) { - re->r.threads = BKE_render_num_threads(&re->r); + re->r.threads = BKE_render_num_threads(&re->r); } /* loads in image into a result, size must match * x/y offsets are only used on a partial copy when dimensions don't match */ -void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char *filename, int x, int y) -{ - /* OCIO_TODO: assume layer was saved in defaule color space */ - ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); - RenderPass *rpass = NULL; - - /* multiview: since the API takes no 'view', we use the first combined pass found */ - for (rpass = layer->passes.first; rpass; rpass = rpass->next) - if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) - break; - - if (rpass == NULL) - BKE_reportf(reports, RPT_ERROR, "%s: no Combined pass found in the render layer '%s'", __func__, filename); - - if (ibuf && (ibuf->rect || ibuf->rect_float)) { - if (ibuf->x == layer->rectx && ibuf->y == layer->recty) { - if (ibuf->rect_float == NULL) - IMB_float_from_rect(ibuf); - - memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); - } - else { - if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) { - ImBuf *ibuf_clip; - - if (ibuf->rect_float == NULL) - IMB_float_from_rect(ibuf); - - ibuf_clip = IMB_allocImBuf(layer->rectx, layer->recty, 32, IB_rectfloat); - if (ibuf_clip) { - IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty); - - memcpy(rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); - IMB_freeImBuf(ibuf_clip); - } - else { - BKE_reportf(reports, RPT_ERROR, "%s: failed to allocate clip buffer '%s'", __func__, filename); - } - } - else { - BKE_reportf(reports, RPT_ERROR, "%s: incorrect dimensions for partial copy '%s'", __func__, filename); - } - } - - IMB_freeImBuf(ibuf); - } - else { - BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); - } +void RE_layer_load_from_file( + RenderLayer *layer, ReportList *reports, const char *filename, int x, int y) +{ + /* OCIO_TODO: assume layer was saved in defaule color space */ + ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); + RenderPass *rpass = NULL; + + /* multiview: since the API takes no 'view', we use the first combined pass found */ + for (rpass = layer->passes.first; rpass; rpass = rpass->next) + if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) + break; + + if (rpass == NULL) + BKE_reportf(reports, + RPT_ERROR, + "%s: no Combined pass found in the render layer '%s'", + __func__, + filename); + + if (ibuf && (ibuf->rect || ibuf->rect_float)) { + if (ibuf->x == layer->rectx && ibuf->y == layer->recty) { + if (ibuf->rect_float == NULL) + IMB_float_from_rect(ibuf); + + memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + } + else { + if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) { + ImBuf *ibuf_clip; + + if (ibuf->rect_float == NULL) + IMB_float_from_rect(ibuf); + + ibuf_clip = IMB_allocImBuf(layer->rectx, layer->recty, 32, IB_rectfloat); + if (ibuf_clip) { + IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty); + + memcpy( + rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + IMB_freeImBuf(ibuf_clip); + } + else { + BKE_reportf( + reports, RPT_ERROR, "%s: failed to allocate clip buffer '%s'", __func__, filename); + } + } + else { + BKE_reportf(reports, + RPT_ERROR, + "%s: incorrect dimensions for partial copy '%s'", + __func__, + filename); + } + } + + IMB_freeImBuf(ibuf); + } + else { + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); + } } void RE_result_load_from_file(RenderResult *result, ReportList *reports, const char *filename) { - if (!render_result_exr_file_read_path(result, NULL, filename)) { - BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); - return; - } + if (!render_result_exr_file_read_path(result, NULL, filename)) { + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); + return; + } } /* Used in the interface to decide whether to show layers or passes. */ bool RE_layers_have_name(struct RenderResult *rr) { - switch (BLI_listbase_count_at_most(&rr->layers, 2)) { - case 0: - return false; - case 1: - return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); - default: - return true; - } - return false; + switch (BLI_listbase_count_at_most(&rr->layers, 2)) { + case 0: + return false; + case 1: + return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); + default: + return true; + } + return false; } bool RE_passes_have_name(struct RenderLayer *rl) { - for (RenderPass *rp = rl->passes.first; rp; rp = rp->next) { - if (!STREQ(rp->name, "Combined")) { - return true; - } - } + for (RenderPass *rp = rl->passes.first; rp; rp = rp->next) { + if (!STREQ(rp->name, "Combined")) { + return true; + } + } - return false; + return false; } RenderPass *RE_pass_find_by_name(volatile RenderLayer *rl, const char *name, const char *viewname) { - RenderPass *rp = NULL; + RenderPass *rp = NULL; - for (rp = rl->passes.last; rp; rp = rp->prev) { - if (STREQ(rp->name, name)) { - if (viewname == NULL || viewname[0] == '\0') - break; - else if (STREQ(rp->view, viewname)) - break; - } - } - return rp; + for (rp = rl->passes.last; rp; rp = rp->prev) { + if (STREQ(rp->name, name)) { + if (viewname == NULL || viewname[0] == '\0') + break; + else if (STREQ(rp->view, viewname)) + break; + } + } + return rp; } /* Only provided for API compatibility, don't use this in new code! */ RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const char *viewname) { #define CHECK_PASS(NAME) \ - if (passtype == SCE_PASS_ ## NAME) \ - return RE_pass_find_by_name(rl, RE_PASSNAME_ ## NAME, viewname); - - CHECK_PASS(COMBINED); - CHECK_PASS(Z); - CHECK_PASS(VECTOR); - CHECK_PASS(NORMAL); - CHECK_PASS(UV); - CHECK_PASS(EMIT); - CHECK_PASS(SHADOW); - CHECK_PASS(AO); - CHECK_PASS(ENVIRONMENT); - CHECK_PASS(INDEXOB); - CHECK_PASS(INDEXMA); - CHECK_PASS(MIST); - CHECK_PASS(RAYHITS); - CHECK_PASS(DIFFUSE_DIRECT); - CHECK_PASS(DIFFUSE_INDIRECT); - CHECK_PASS(DIFFUSE_COLOR); - CHECK_PASS(GLOSSY_DIRECT); - CHECK_PASS(GLOSSY_INDIRECT); - CHECK_PASS(GLOSSY_COLOR); - CHECK_PASS(TRANSM_DIRECT); - CHECK_PASS(TRANSM_INDIRECT); - CHECK_PASS(TRANSM_COLOR); - CHECK_PASS(SUBSURFACE_DIRECT); - CHECK_PASS(SUBSURFACE_INDIRECT); - CHECK_PASS(SUBSURFACE_COLOR); + if (passtype == SCE_PASS_##NAME) \ + return RE_pass_find_by_name(rl, RE_PASSNAME_##NAME, viewname); + + CHECK_PASS(COMBINED); + CHECK_PASS(Z); + CHECK_PASS(VECTOR); + CHECK_PASS(NORMAL); + CHECK_PASS(UV); + CHECK_PASS(EMIT); + CHECK_PASS(SHADOW); + CHECK_PASS(AO); + CHECK_PASS(ENVIRONMENT); + CHECK_PASS(INDEXOB); + CHECK_PASS(INDEXMA); + CHECK_PASS(MIST); + CHECK_PASS(RAYHITS); + CHECK_PASS(DIFFUSE_DIRECT); + CHECK_PASS(DIFFUSE_INDIRECT); + CHECK_PASS(DIFFUSE_COLOR); + CHECK_PASS(GLOSSY_DIRECT); + CHECK_PASS(GLOSSY_INDIRECT); + CHECK_PASS(GLOSSY_COLOR); + CHECK_PASS(TRANSM_DIRECT); + CHECK_PASS(TRANSM_INDIRECT); + CHECK_PASS(TRANSM_COLOR); + CHECK_PASS(SUBSURFACE_DIRECT); + CHECK_PASS(SUBSURFACE_INDIRECT); + CHECK_PASS(SUBSURFACE_COLOR); #undef CHECK_PASS - return NULL; + return NULL; } /* create a renderlayer and renderpass for grease pencil layer */ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const char *viewname) { - RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name)); - /* only create render layer if not exist */ - if (!rl) { - rl = MEM_callocN(sizeof(RenderLayer), layername); - BLI_addtail(&rr->layers, rl); - BLI_strncpy(rl->name, layername, sizeof(rl->name)); - rl->layflag = SCE_LAY_SOLID; - rl->passflag = SCE_PASS_COMBINED; - rl->rectx = rr->rectx; - rl->recty = rr->recty; - } - - /* clear previous pass if exist or the new image will be over previous one*/ - RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname); - if (rp) { - if (rp->rect) { - MEM_freeN(rp->rect); - } - BLI_freelinkN(&rl->passes, rp); - } - /* create a totally new pass */ - return gp_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname); + RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name)); + /* only create render layer if not exist */ + if (!rl) { + rl = MEM_callocN(sizeof(RenderLayer), layername); + BLI_addtail(&rr->layers, rl); + BLI_strncpy(rl->name, layername, sizeof(rl->name)); + rl->layflag = SCE_LAY_SOLID; + rl->passflag = SCE_PASS_COMBINED; + rl->rectx = rr->rectx; + rl->recty = rr->recty; + } + + /* clear previous pass if exist or the new image will be over previous one*/ + RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname); + if (rp) { + if (rp->rect) { + MEM_freeN(rp->rect); + } + BLI_freelinkN(&rl->passes, rp); + } + /* create a totally new pass */ + return gp_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname); } diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index d3e6b6c6c51..a38c0d71a79 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -21,7 +21,6 @@ * \ingroup render */ - #include <math.h> #include <stdlib.h> #include <stdio.h> @@ -64,67 +63,70 @@ static ThreadMutex sample_mutex = PTHREAD_MUTEX_INITIALIZER; static int point_data_used(PointDensity *pd) { - int pd_bitflag = 0; - - if (pd->source == TEX_PD_PSYS) { - if ((pd->falloff_type == TEX_PD_FALLOFF_PARTICLE_VEL) || - (pd->color_source == TEX_PD_COLOR_PARTVEL) || - (pd->color_source == TEX_PD_COLOR_PARTSPEED)) - { - pd_bitflag |= POINT_DATA_VEL; - } - if ((pd->color_source == TEX_PD_COLOR_PARTAGE) || - (pd->falloff_type == TEX_PD_FALLOFF_PARTICLE_AGE)) - { - pd_bitflag |= POINT_DATA_LIFE; - } - } - else if (pd->source == TEX_PD_OBJECT) { - if (ELEM(pd->ob_color_source, TEX_PD_COLOR_VERTCOL, TEX_PD_COLOR_VERTWEIGHT, TEX_PD_COLOR_VERTNOR)) { - pd_bitflag |= POINT_DATA_COLOR; - } - } - - return pd_bitflag; + int pd_bitflag = 0; + + if (pd->source == TEX_PD_PSYS) { + if ((pd->falloff_type == TEX_PD_FALLOFF_PARTICLE_VEL) || + (pd->color_source == TEX_PD_COLOR_PARTVEL) || + (pd->color_source == TEX_PD_COLOR_PARTSPEED)) { + pd_bitflag |= POINT_DATA_VEL; + } + if ((pd->color_source == TEX_PD_COLOR_PARTAGE) || + (pd->falloff_type == TEX_PD_FALLOFF_PARTICLE_AGE)) { + pd_bitflag |= POINT_DATA_LIFE; + } + } + else if (pd->source == TEX_PD_OBJECT) { + if (ELEM(pd->ob_color_source, + TEX_PD_COLOR_VERTCOL, + TEX_PD_COLOR_VERTWEIGHT, + TEX_PD_COLOR_VERTNOR)) { + pd_bitflag |= POINT_DATA_COLOR; + } + } + + return pd_bitflag; } static void point_data_pointers(PointDensity *pd, - float **r_data_velocity, float **r_data_life, float **r_data_color) + float **r_data_velocity, + float **r_data_life, + float **r_data_color) { - const int data_used = point_data_used(pd); - const int totpoint = pd->totpoints; - float *data = pd->point_data; - int offset = 0; - - if (data_used & POINT_DATA_VEL) { - if (r_data_velocity) - *r_data_velocity = data + offset; - offset += 3 * totpoint; - } - else { - if (r_data_velocity) - *r_data_velocity = NULL; - } - - if (data_used & POINT_DATA_LIFE) { - if (r_data_life) - *r_data_life = data + offset; - offset += totpoint; - } - else { - if (r_data_life) - *r_data_life = NULL; - } - - if (data_used & POINT_DATA_COLOR) { - if (r_data_color) - *r_data_color = data + offset; - offset += 3 * totpoint; - } - else { - if (r_data_color) - *r_data_color = NULL; - } + const int data_used = point_data_used(pd); + const int totpoint = pd->totpoints; + float *data = pd->point_data; + int offset = 0; + + if (data_used & POINT_DATA_VEL) { + if (r_data_velocity) + *r_data_velocity = data + offset; + offset += 3 * totpoint; + } + else { + if (r_data_velocity) + *r_data_velocity = NULL; + } + + if (data_used & POINT_DATA_LIFE) { + if (r_data_life) + *r_data_life = data + offset; + offset += totpoint; + } + else { + if (r_data_life) + *r_data_life = NULL; + } + + if (data_used & POINT_DATA_COLOR) { + if (r_data_color) + *r_data_color = data + offset; + offset += 3 * totpoint; + } + else { + if (r_data_color) + *r_data_color = NULL; + } } /* additional data stored alongside the point density BVH, @@ -132,458 +134,460 @@ static void point_data_pointers(PointDensity *pd, * such as particle velocity or lifetime */ static void alloc_point_data(PointDensity *pd) { - const int totpoints = pd->totpoints; - int data_used = point_data_used(pd); - int data_size = 0; - - if (data_used & POINT_DATA_VEL) { - /* store 3 channels of velocity data */ - data_size += 3; - } - if (data_used & POINT_DATA_LIFE) { - /* store 1 channel of lifetime data */ - data_size += 1; - } - if (data_used & POINT_DATA_COLOR) { - /* store 3 channels of RGB data */ - data_size += 3; - } - - if (data_size) { - pd->point_data = MEM_callocN(sizeof(float) * data_size * totpoints, - "particle point data"); - } + const int totpoints = pd->totpoints; + int data_used = point_data_used(pd); + int data_size = 0; + + if (data_used & POINT_DATA_VEL) { + /* store 3 channels of velocity data */ + data_size += 3; + } + if (data_used & POINT_DATA_LIFE) { + /* store 1 channel of lifetime data */ + data_size += 1; + } + if (data_used & POINT_DATA_COLOR) { + /* store 3 channels of RGB data */ + data_size += 3; + } + + if (data_size) { + pd->point_data = MEM_callocN(sizeof(float) * data_size * totpoints, "particle point data"); + } } -static void pointdensity_cache_psys(Depsgraph *depsgraph, Scene *scene, - PointDensity *pd, - Object *ob, - ParticleSystem *psys) +static void pointdensity_cache_psys( + Depsgraph *depsgraph, Scene *scene, PointDensity *pd, Object *ob, ParticleSystem *psys) { - ParticleKey state; - ParticleCacheKey *cache; - ParticleSimulationData sim = {NULL}; - ParticleData *pa = NULL; - float cfra = BKE_scene_frame_get(scene); - int i /*, childexists*/ /* UNUSED */; - int total_particles; - int data_used; - float *data_vel, *data_life; - float partco[3]; - const bool use_render_params = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); - - data_used = point_data_used(pd); - - if (!psys_check_enabled(ob, psys, use_render_params)) { - return; - } - - sim.depsgraph = depsgraph; - sim.scene = scene; - sim.ob = ob; - sim.psys = psys; - sim.psmd = psys_get_modifier(ob, psys); - - /* in case ob->imat isn't up-to-date */ - invert_m4_m4(ob->imat, ob->obmat); - - total_particles = psys->totpart + psys->totchild; - psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); - - pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6); - pd->totpoints = total_particles; - alloc_point_data(pd); - point_data_pointers(pd, &data_vel, &data_life, NULL); + ParticleKey state; + ParticleCacheKey *cache; + ParticleSimulationData sim = {NULL}; + ParticleData *pa = NULL; + float cfra = BKE_scene_frame_get(scene); + int i /*, childexists*/ /* UNUSED */; + int total_particles; + int data_used; + float *data_vel, *data_life; + float partco[3]; + const bool use_render_params = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + + data_used = point_data_used(pd); + + if (!psys_check_enabled(ob, psys, use_render_params)) { + return; + } + + sim.depsgraph = depsgraph; + sim.scene = scene; + sim.ob = ob; + sim.psys = psys; + sim.psmd = psys_get_modifier(ob, psys); + + /* in case ob->imat isn't up-to-date */ + invert_m4_m4(ob->imat, ob->obmat); + + total_particles = psys->totpart + psys->totchild; + psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6); + pd->totpoints = total_particles; + alloc_point_data(pd); + point_data_pointers(pd, &data_vel, &data_life, NULL); #if 0 /* UNUSED */ - if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) - childexists = 1; + if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) + childexists = 1; #endif - for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) { - - if (psys->part->type == PART_HAIR) { - /* hair particles */ - if (i < psys->totpart && psys->pathcache) - cache = psys->pathcache[i]; - else if (i >= psys->totpart && psys->childcache) - cache = psys->childcache[i - psys->totpart]; - else - continue; - - cache += cache->segments; /* use endpoint */ - - copy_v3_v3(state.co, cache->co); - zero_v3(state.vel); - state.time = 0.0f; - } - else { - /* emitter particles */ - state.time = cfra; - - if (!psys_get_particle_state(&sim, i, &state, 0)) - continue; - - if (data_used & POINT_DATA_LIFE) { - if (i < psys->totpart) { - state.time = (cfra - pa->time) / pa->lifetime; - } - else { - ChildParticle *cpa = (psys->child + i) - psys->totpart; - float pa_birthtime, pa_dietime; - - state.time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime); - } - } - } - - copy_v3_v3(partco, state.co); - - if (pd->psys_cache_space == TEX_PD_OBJECTSPACE) - mul_m4_v3(ob->imat, partco); - else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) { - sub_v3_v3(partco, ob->loc); - } - else { - /* TEX_PD_WORLDSPACE */ - } - - BLI_bvhtree_insert(pd->point_tree, i, partco, 1); - - if (data_vel) { - data_vel[i * 3 + 0] = state.vel[0]; - data_vel[i * 3 + 1] = state.vel[1]; - data_vel[i * 3 + 2] = state.vel[2]; - } - if (data_life) { - data_life[i] = state.time; - } - } - - BLI_bvhtree_balance(pd->point_tree); - - if (psys->lattice_deform_data) { - end_latt_deform(psys->lattice_deform_data); - psys->lattice_deform_data = NULL; - } + for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) { + + if (psys->part->type == PART_HAIR) { + /* hair particles */ + if (i < psys->totpart && psys->pathcache) + cache = psys->pathcache[i]; + else if (i >= psys->totpart && psys->childcache) + cache = psys->childcache[i - psys->totpart]; + else + continue; + + cache += cache->segments; /* use endpoint */ + + copy_v3_v3(state.co, cache->co); + zero_v3(state.vel); + state.time = 0.0f; + } + else { + /* emitter particles */ + state.time = cfra; + + if (!psys_get_particle_state(&sim, i, &state, 0)) + continue; + + if (data_used & POINT_DATA_LIFE) { + if (i < psys->totpart) { + state.time = (cfra - pa->time) / pa->lifetime; + } + else { + ChildParticle *cpa = (psys->child + i) - psys->totpart; + float pa_birthtime, pa_dietime; + + state.time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime); + } + } + } + + copy_v3_v3(partco, state.co); + + if (pd->psys_cache_space == TEX_PD_OBJECTSPACE) + mul_m4_v3(ob->imat, partco); + else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) { + sub_v3_v3(partco, ob->loc); + } + else { + /* TEX_PD_WORLDSPACE */ + } + + BLI_bvhtree_insert(pd->point_tree, i, partco, 1); + + if (data_vel) { + data_vel[i * 3 + 0] = state.vel[0]; + data_vel[i * 3 + 1] = state.vel[1]; + data_vel[i * 3 + 2] = state.vel[2]; + } + if (data_life) { + data_life[i] = state.time; + } + } + + BLI_bvhtree_balance(pd->point_tree); + + if (psys->lattice_deform_data) { + end_latt_deform(psys->lattice_deform_data); + psys->lattice_deform_data = NULL; + } } - -static void pointdensity_cache_vertex_color(PointDensity *pd, Object *UNUSED(ob), Mesh *mesh, float *data_color) +static void pointdensity_cache_vertex_color(PointDensity *pd, + Object *UNUSED(ob), + Mesh *mesh, + float *data_color) { - const MLoop *mloop = mesh->mloop; - const int totloop = mesh->totloop; - const MLoopCol *mcol; - char layername[MAX_CUSTOMDATA_LAYER_NAME]; - int i; - - BLI_assert(data_color); - - if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPCOL)) - return; - CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPCOL, pd->vertex_attribute_name, layername); - mcol = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPCOL, layername); - if (!mcol) - return; - - /* Stores the number of MLoops using the same vertex, so we can normalize colors. */ - int *mcorners = MEM_callocN(sizeof(int) * pd->totpoints, "point density corner count"); - - for (i = 0; i < totloop; i++) { - int v = mloop[i].v; - - if (mcorners[v] == 0) { - rgb_uchar_to_float(&data_color[v * 3], &mcol[i].r); - } - else { - float col[3]; - rgb_uchar_to_float(col, &mcol[i].r); - add_v3_v3(&data_color[v * 3], col); - } - - ++mcorners[v]; - } - - /* Normalize colors by averaging over mcorners. - * All the corners share the same vertex, ie. occupy the same point in space. - */ - for (i = 0; i < pd->totpoints; i++) { - if (mcorners[i] > 0) - mul_v3_fl(&data_color[i * 3], 1.0f / mcorners[i]); - } - - MEM_freeN(mcorners); + const MLoop *mloop = mesh->mloop; + const int totloop = mesh->totloop; + const MLoopCol *mcol; + char layername[MAX_CUSTOMDATA_LAYER_NAME]; + int i; + + BLI_assert(data_color); + + if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPCOL)) + return; + CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPCOL, pd->vertex_attribute_name, layername); + mcol = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPCOL, layername); + if (!mcol) + return; + + /* Stores the number of MLoops using the same vertex, so we can normalize colors. */ + int *mcorners = MEM_callocN(sizeof(int) * pd->totpoints, "point density corner count"); + + for (i = 0; i < totloop; i++) { + int v = mloop[i].v; + + if (mcorners[v] == 0) { + rgb_uchar_to_float(&data_color[v * 3], &mcol[i].r); + } + else { + float col[3]; + rgb_uchar_to_float(col, &mcol[i].r); + add_v3_v3(&data_color[v * 3], col); + } + + ++mcorners[v]; + } + + /* Normalize colors by averaging over mcorners. + * All the corners share the same vertex, ie. occupy the same point in space. + */ + for (i = 0; i < pd->totpoints; i++) { + if (mcorners[i] > 0) + mul_v3_fl(&data_color[i * 3], 1.0f / mcorners[i]); + } + + MEM_freeN(mcorners); } -static void pointdensity_cache_vertex_weight(PointDensity *pd, Object *ob, Mesh *mesh, float *data_color) +static void pointdensity_cache_vertex_weight(PointDensity *pd, + Object *ob, + Mesh *mesh, + float *data_color) { - const int totvert = mesh->totvert; - const MDeformVert *mdef, *dv; - int mdef_index; - int i; - - BLI_assert(data_color); - - mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); - if (!mdef) - return; - mdef_index = defgroup_name_index(ob, pd->vertex_attribute_name); - if (mdef_index < 0) - mdef_index = ob->actdef - 1; - if (mdef_index < 0) - return; - - for (i = 0, dv = mdef; i < totvert; ++i, ++dv, data_color += 3) { - MDeformWeight *dw; - int j; - - for (j = 0, dw = dv->dw; j < dv->totweight; ++j, ++dw) { - if (dw->def_nr == mdef_index) { - copy_v3_fl(data_color, dw->weight); - break; - } - } - } + const int totvert = mesh->totvert; + const MDeformVert *mdef, *dv; + int mdef_index; + int i; + + BLI_assert(data_color); + + mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + if (!mdef) + return; + mdef_index = defgroup_name_index(ob, pd->vertex_attribute_name); + if (mdef_index < 0) + mdef_index = ob->actdef - 1; + if (mdef_index < 0) + return; + + for (i = 0, dv = mdef; i < totvert; ++i, ++dv, data_color += 3) { + MDeformWeight *dw; + int j; + + for (j = 0, dw = dv->dw; j < dv->totweight; ++j, ++dw) { + if (dw->def_nr == mdef_index) { + copy_v3_fl(data_color, dw->weight); + break; + } + } + } } -static void pointdensity_cache_vertex_normal(PointDensity *pd, Object *UNUSED(ob), Mesh *mesh, float *data_color) +static void pointdensity_cache_vertex_normal(PointDensity *pd, + Object *UNUSED(ob), + Mesh *mesh, + float *data_color) { - MVert *mvert = mesh->mvert, *mv; - int i; + MVert *mvert = mesh->mvert, *mv; + int i; - BLI_assert(data_color); + BLI_assert(data_color); - for (i = 0, mv = mvert; i < pd->totpoints; i++, mv++, data_color += 3) { - normal_short_to_float_v3(data_color, mv->no); - } + for (i = 0, mv = mvert; i < pd->totpoints; i++, mv++, data_color += 3) { + normal_short_to_float_v3(data_color, mv->no); + } } -static void pointdensity_cache_object(PointDensity *pd, - Object *ob) +static void pointdensity_cache_object(PointDensity *pd, Object *ob) { - float *data_color; - int i; - MVert *mvert = NULL, *mv; - Mesh *mesh = ob->data; - -#if 0 /* UNUSED */ - CustomData_MeshMasks mask = CD_MASK_BAREMESH; - mask.fmask |= CD_MASK_MTFACE | CD_MASK_MCOL; - switch (pd->ob_color_source) { - case TEX_PD_COLOR_VERTCOL: - mask.lmask |= CD_MASK_MLOOPCOL; - break; - case TEX_PD_COLOR_VERTWEIGHT: - mask.vmask |= CD_MASK_MDEFORMVERT; - break; - } + float *data_color; + int i; + MVert *mvert = NULL, *mv; + Mesh *mesh = ob->data; + +#if 0 /* UNUSED */ + CustomData_MeshMasks mask = CD_MASK_BAREMESH; + mask.fmask |= CD_MASK_MTFACE | CD_MASK_MCOL; + switch (pd->ob_color_source) { + case TEX_PD_COLOR_VERTCOL: + mask.lmask |= CD_MASK_MLOOPCOL; + break; + case TEX_PD_COLOR_VERTWEIGHT: + mask.vmask |= CD_MASK_MDEFORMVERT; + break; + } #endif - mvert = mesh->mvert; /* local object space */ - pd->totpoints = mesh->totvert; - if (pd->totpoints == 0) { - return; - } - - pd->point_tree = BLI_bvhtree_new(pd->totpoints, 0.0, 4, 6); - alloc_point_data(pd); - point_data_pointers(pd, NULL, NULL, &data_color); - - for (i = 0, mv = mvert; i < pd->totpoints; i++, mv++) { - float co[3]; - - copy_v3_v3(co, mv->co); - - switch (pd->ob_cache_space) { - case TEX_PD_OBJECTSPACE: - break; - case TEX_PD_OBJECTLOC: - mul_m4_v3(ob->obmat, co); - sub_v3_v3(co, ob->loc); - break; - case TEX_PD_WORLDSPACE: - default: - mul_m4_v3(ob->obmat, co); - break; - } - - BLI_bvhtree_insert(pd->point_tree, i, co, 1); - } - - switch (pd->ob_color_source) { - case TEX_PD_COLOR_VERTCOL: - pointdensity_cache_vertex_color(pd, ob, mesh, data_color); - break; - case TEX_PD_COLOR_VERTWEIGHT: - pointdensity_cache_vertex_weight(pd, ob, mesh, data_color); - break; - case TEX_PD_COLOR_VERTNOR: - pointdensity_cache_vertex_normal(pd, ob, mesh, data_color); - break; - } - - BLI_bvhtree_balance(pd->point_tree); + mvert = mesh->mvert; /* local object space */ + pd->totpoints = mesh->totvert; + if (pd->totpoints == 0) { + return; + } + + pd->point_tree = BLI_bvhtree_new(pd->totpoints, 0.0, 4, 6); + alloc_point_data(pd); + point_data_pointers(pd, NULL, NULL, &data_color); + + for (i = 0, mv = mvert; i < pd->totpoints; i++, mv++) { + float co[3]; + + copy_v3_v3(co, mv->co); + + switch (pd->ob_cache_space) { + case TEX_PD_OBJECTSPACE: + break; + case TEX_PD_OBJECTLOC: + mul_m4_v3(ob->obmat, co); + sub_v3_v3(co, ob->loc); + break; + case TEX_PD_WORLDSPACE: + default: + mul_m4_v3(ob->obmat, co); + break; + } + + BLI_bvhtree_insert(pd->point_tree, i, co, 1); + } + + switch (pd->ob_color_source) { + case TEX_PD_COLOR_VERTCOL: + pointdensity_cache_vertex_color(pd, ob, mesh, data_color); + break; + case TEX_PD_COLOR_VERTWEIGHT: + pointdensity_cache_vertex_weight(pd, ob, mesh, data_color); + break; + case TEX_PD_COLOR_VERTNOR: + pointdensity_cache_vertex_normal(pd, ob, mesh, data_color); + break; + } + + BLI_bvhtree_balance(pd->point_tree); } -static void cache_pointdensity(Depsgraph *depsgraph, - Scene *scene, - PointDensity *pd) +static void cache_pointdensity(Depsgraph *depsgraph, Scene *scene, PointDensity *pd) { - if (pd == NULL) { - return; - } - - if (pd->point_tree) { - BLI_bvhtree_free(pd->point_tree); - pd->point_tree = NULL; - } - - if (pd->source == TEX_PD_PSYS) { - Object *ob = pd->object; - ParticleSystem *psys; - - if (!ob || !pd->psys) { - return; - } - - psys = BLI_findlink(&ob->particlesystem, pd->psys - 1); - if (!psys) { - return; - } - - pointdensity_cache_psys(depsgraph, - scene, - pd, - ob, - psys); - } - else if (pd->source == TEX_PD_OBJECT) { - Object *ob = pd->object; - if (ob && ob->type == OB_MESH) - pointdensity_cache_object(pd, ob); - } + if (pd == NULL) { + return; + } + + if (pd->point_tree) { + BLI_bvhtree_free(pd->point_tree); + pd->point_tree = NULL; + } + + if (pd->source == TEX_PD_PSYS) { + Object *ob = pd->object; + ParticleSystem *psys; + + if (!ob || !pd->psys) { + return; + } + + psys = BLI_findlink(&ob->particlesystem, pd->psys - 1); + if (!psys) { + return; + } + + pointdensity_cache_psys(depsgraph, scene, pd, ob, psys); + } + else if (pd->source == TEX_PD_OBJECT) { + Object *ob = pd->object; + if (ob && ob->type == OB_MESH) + pointdensity_cache_object(pd, ob); + } } static void free_pointdensity(PointDensity *pd) { - if (pd == NULL) { - return; - } - - if (pd->point_tree) { - BLI_bvhtree_free(pd->point_tree); - pd->point_tree = NULL; - } - - if (pd->point_data) { - MEM_freeN(pd->point_data); - pd->point_data = NULL; - } - pd->totpoints = 0; + if (pd == NULL) { + return; + } + + if (pd->point_tree) { + BLI_bvhtree_free(pd->point_tree); + pd->point_tree = NULL; + } + + if (pd->point_data) { + MEM_freeN(pd->point_data); + pd->point_data = NULL; + } + pd->totpoints = 0; } typedef struct PointDensityRangeData { - float *density; - float squared_radius; - float *point_data_life; - float *point_data_velocity; - float *point_data_color; - float *vec; - float *col; - float softness; - short falloff_type; - short noise_influence; - float *age; - struct CurveMapping *density_curve; - float velscale; + float *density; + float squared_radius; + float *point_data_life; + float *point_data_velocity; + float *point_data_color; + float *vec; + float *col; + float softness; + short falloff_type; + short noise_influence; + float *age; + struct CurveMapping *density_curve; + float velscale; } PointDensityRangeData; static float density_falloff(PointDensityRangeData *pdr, int index, float squared_dist) { - const float dist = (pdr->squared_radius - squared_dist) / pdr->squared_radius * 0.5f; - float density = 0.0f; - - switch (pdr->falloff_type) { - case TEX_PD_FALLOFF_STD: - density = dist; - break; - case TEX_PD_FALLOFF_SMOOTH: - density = 3.0f * dist * dist - 2.0f * dist * dist * dist; - break; - case TEX_PD_FALLOFF_SOFT: - density = pow(dist, pdr->softness); - break; - case TEX_PD_FALLOFF_CONSTANT: - density = pdr->squared_radius; - break; - case TEX_PD_FALLOFF_ROOT: - density = sqrtf(dist); - break; - case TEX_PD_FALLOFF_PARTICLE_AGE: - if (pdr->point_data_life) - density = dist * MIN2(pdr->point_data_life[index], 1.0f); - else - density = dist; - break; - case TEX_PD_FALLOFF_PARTICLE_VEL: - if (pdr->point_data_velocity) - density = dist * len_v3(&pdr->point_data_velocity[index * 3]) * pdr->velscale; - else - density = dist; - break; - } - - if (pdr->density_curve && dist != 0.0f) { - curvemapping_initialize(pdr->density_curve); - density = curvemapping_evaluateF(pdr->density_curve, 0, density / dist) * dist; - } - - return density; + const float dist = (pdr->squared_radius - squared_dist) / pdr->squared_radius * 0.5f; + float density = 0.0f; + + switch (pdr->falloff_type) { + case TEX_PD_FALLOFF_STD: + density = dist; + break; + case TEX_PD_FALLOFF_SMOOTH: + density = 3.0f * dist * dist - 2.0f * dist * dist * dist; + break; + case TEX_PD_FALLOFF_SOFT: + density = pow(dist, pdr->softness); + break; + case TEX_PD_FALLOFF_CONSTANT: + density = pdr->squared_radius; + break; + case TEX_PD_FALLOFF_ROOT: + density = sqrtf(dist); + break; + case TEX_PD_FALLOFF_PARTICLE_AGE: + if (pdr->point_data_life) + density = dist * MIN2(pdr->point_data_life[index], 1.0f); + else + density = dist; + break; + case TEX_PD_FALLOFF_PARTICLE_VEL: + if (pdr->point_data_velocity) + density = dist * len_v3(&pdr->point_data_velocity[index * 3]) * pdr->velscale; + else + density = dist; + break; + } + + if (pdr->density_curve && dist != 0.0f) { + curvemapping_initialize(pdr->density_curve); + density = curvemapping_evaluateF(pdr->density_curve, 0, density / dist) * dist; + } + + return density; } static void accum_density(void *userdata, int index, const float co[3], float squared_dist) { - PointDensityRangeData *pdr = (PointDensityRangeData *)userdata; - float density = 0.0f; - - UNUSED_VARS(co); - - if (pdr->point_data_velocity) { - pdr->vec[0] += pdr->point_data_velocity[index * 3 + 0]; // * density; - pdr->vec[1] += pdr->point_data_velocity[index * 3 + 1]; // * density; - pdr->vec[2] += pdr->point_data_velocity[index * 3 + 2]; // * density; - } - if (pdr->point_data_life) { - *pdr->age += pdr->point_data_life[index]; // * density; - } - if (pdr->point_data_color) { - add_v3_v3(pdr->col, &pdr->point_data_color[index * 3]); // * density; - } - - density = density_falloff(pdr, index, squared_dist); - - *pdr->density += density; + PointDensityRangeData *pdr = (PointDensityRangeData *)userdata; + float density = 0.0f; + + UNUSED_VARS(co); + + if (pdr->point_data_velocity) { + pdr->vec[0] += pdr->point_data_velocity[index * 3 + 0]; // * density; + pdr->vec[1] += pdr->point_data_velocity[index * 3 + 1]; // * density; + pdr->vec[2] += pdr->point_data_velocity[index * 3 + 2]; // * density; + } + if (pdr->point_data_life) { + *pdr->age += pdr->point_data_life[index]; // * density; + } + if (pdr->point_data_color) { + add_v3_v3(pdr->col, &pdr->point_data_color[index * 3]); // * density; + } + + density = density_falloff(pdr, index, squared_dist); + + *pdr->density += density; } - -static void init_pointdensityrangedata( - PointDensity *pd, PointDensityRangeData *pdr, - float *density, float *vec, float *age, float *col, struct CurveMapping *density_curve, float velscale) +static void init_pointdensityrangedata(PointDensity *pd, + PointDensityRangeData *pdr, + float *density, + float *vec, + float *age, + float *col, + struct CurveMapping *density_curve, + float velscale) { - pdr->squared_radius = pd->radius * pd->radius; - pdr->density = density; - pdr->falloff_type = pd->falloff_type; - pdr->vec = vec; - pdr->age = age; - pdr->col = col; - pdr->softness = pd->falloff_softness; - pdr->noise_influence = pd->noise_influence; - point_data_pointers(pd, &pdr->point_data_velocity, &pdr->point_data_life, &pdr->point_data_color); - pdr->density_curve = density_curve; - pdr->velscale = velscale; + pdr->squared_radius = pd->radius * pd->radius; + pdr->density = density; + pdr->falloff_type = pd->falloff_type; + pdr->vec = vec; + pdr->age = age; + pdr->col = col; + pdr->softness = pd->falloff_softness; + pdr->noise_influence = pd->noise_influence; + point_data_pointers( + pd, &pdr->point_data_velocity, &pdr->point_data_life, &pdr->point_data_color); + pdr->density_curve = density_curve; + pdr->velscale = velscale; } - static int pointdensity(PointDensity *pd, const float texvec[3], TexResult *texres, @@ -591,149 +595,159 @@ static int pointdensity(PointDensity *pd, float *r_age, float r_col[3]) { - int retval = TEX_INT; - PointDensityRangeData pdr; - float density = 0.0f, age = 0.0f; - float vec[3] = {0.0f, 0.0f, 0.0f}, col[3] = {0.0f, 0.0f, 0.0f}, co[3]; - float turb, noise_fac; - int num = 0; - - texres->tin = 0.0f; - - init_pointdensityrangedata(pd, &pdr, &density, vec, &age, col, - (pd->flag & TEX_PD_FALLOFF_CURVE ? pd->falloff_curve : NULL), - pd->falloff_speed_scale * 0.001f); - noise_fac = pd->noise_fac * 0.5f; /* better default */ - - copy_v3_v3(co, texvec); - - if (point_data_used(pd)) { - /* does a BVH lookup to find accumulated density and additional point data * - * stores particle velocity vector in 'vec', and particle lifetime in 'time' */ - num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); - if (num > 0) { - age /= num; - mul_v3_fl(vec, 1.0f / num); - mul_v3_fl(col, 1.0f / num); - } - - /* reset */ - density = 0.0f; - zero_v3(vec); - zero_v3(col); - } - - if (pd->flag & TEX_PD_TURBULENCE) { - turb = BLI_gTurbulence(pd->noise_size, texvec[0] + vec[0], texvec[1] + vec[1], texvec[2] + vec[2], - pd->noise_depth, 0, pd->noise_basis); - - turb -= 0.5f; /* re-center 0.0-1.0 range around 0 to prevent offsetting result */ - - /* now we have an offset coordinate to use for the density lookup */ - co[0] = texvec[0] + noise_fac * turb; - co[1] = texvec[1] + noise_fac * turb; - co[2] = texvec[2] + noise_fac * turb; - } - - /* BVH query with the potentially perturbed coordinates */ - num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); - if (num > 0) { - age /= num; - mul_v3_fl(vec, 1.0f / num); - mul_v3_fl(col, 1.0f / num); - } - - texres->tin = density; - if (r_age != NULL) { - *r_age = age; - } - if (r_vec != NULL) { - copy_v3_v3(r_vec, vec); - } - if (r_col != NULL) { - copy_v3_v3(r_col, col); - } - - return retval; + int retval = TEX_INT; + PointDensityRangeData pdr; + float density = 0.0f, age = 0.0f; + float vec[3] = {0.0f, 0.0f, 0.0f}, col[3] = {0.0f, 0.0f, 0.0f}, co[3]; + float turb, noise_fac; + int num = 0; + + texres->tin = 0.0f; + + init_pointdensityrangedata(pd, + &pdr, + &density, + vec, + &age, + col, + (pd->flag & TEX_PD_FALLOFF_CURVE ? pd->falloff_curve : NULL), + pd->falloff_speed_scale * 0.001f); + noise_fac = pd->noise_fac * 0.5f; /* better default */ + + copy_v3_v3(co, texvec); + + if (point_data_used(pd)) { + /* does a BVH lookup to find accumulated density and additional point data * + * stores particle velocity vector in 'vec', and particle lifetime in 'time' */ + num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); + if (num > 0) { + age /= num; + mul_v3_fl(vec, 1.0f / num); + mul_v3_fl(col, 1.0f / num); + } + + /* reset */ + density = 0.0f; + zero_v3(vec); + zero_v3(col); + } + + if (pd->flag & TEX_PD_TURBULENCE) { + turb = BLI_gTurbulence(pd->noise_size, + texvec[0] + vec[0], + texvec[1] + vec[1], + texvec[2] + vec[2], + pd->noise_depth, + 0, + pd->noise_basis); + + turb -= 0.5f; /* re-center 0.0-1.0 range around 0 to prevent offsetting result */ + + /* now we have an offset coordinate to use for the density lookup */ + co[0] = texvec[0] + noise_fac * turb; + co[1] = texvec[1] + noise_fac * turb; + co[2] = texvec[2] + noise_fac * turb; + } + + /* BVH query with the potentially perturbed coordinates */ + num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); + if (num > 0) { + age /= num; + mul_v3_fl(vec, 1.0f / num); + mul_v3_fl(col, 1.0f / num); + } + + texres->tin = density; + if (r_age != NULL) { + *r_age = age; + } + if (r_vec != NULL) { + copy_v3_v3(r_vec, vec); + } + if (r_col != NULL) { + copy_v3_v3(r_col, col); + } + + return retval; } -static void pointdensity_color(PointDensity *pd, TexResult *texres, float age, const float vec[3], const float col[3]) +static void pointdensity_color( + PointDensity *pd, TexResult *texres, float age, const float vec[3], const float col[3]) { - texres->tr = texres->tg = texres->tb = texres->ta = 1.0f; - - if (pd->source == TEX_PD_PSYS) { - float rgba[4]; - - switch (pd->color_source) { - case TEX_PD_COLOR_PARTAGE: - if (pd->coba) { - if (BKE_colorband_evaluate(pd->coba, age, rgba)) { - texres->talpha = true; - copy_v3_v3(&texres->tr, rgba); - texres->tin *= rgba[3]; - texres->ta = texres->tin; - } - } - break; - case TEX_PD_COLOR_PARTSPEED: - { - float speed = len_v3(vec) * pd->speed_scale; - - if (pd->coba) { - if (BKE_colorband_evaluate(pd->coba, speed, rgba)) { - texres->talpha = true; - copy_v3_v3(&texres->tr, rgba); - texres->tin *= rgba[3]; - texres->ta = texres->tin; - } - } - break; - } - case TEX_PD_COLOR_PARTVEL: - texres->talpha = true; - mul_v3_v3fl(&texres->tr, vec, pd->speed_scale); - texres->ta = texres->tin; - break; - case TEX_PD_COLOR_CONSTANT: - default: - break; - } - } - else { - float rgba[4]; - - switch (pd->ob_color_source) { - case TEX_PD_COLOR_VERTCOL: - texres->talpha = true; - copy_v3_v3(&texres->tr, col); - texres->ta = texres->tin; - break; - case TEX_PD_COLOR_VERTWEIGHT: - texres->talpha = true; - if (pd->coba && BKE_colorband_evaluate(pd->coba, col[0], rgba)) { - copy_v3_v3(&texres->tr, rgba); - texres->tin *= rgba[3]; - } - else { - copy_v3_v3(&texres->tr, col); - } - texres->ta = texres->tin; - break; - case TEX_PD_COLOR_VERTNOR: - texres->talpha = true; - copy_v3_v3(&texres->tr, col); - texres->ta = texres->tin; - break; - case TEX_PD_COLOR_CONSTANT: - default: - break; - } - } + texres->tr = texres->tg = texres->tb = texres->ta = 1.0f; + + if (pd->source == TEX_PD_PSYS) { + float rgba[4]; + + switch (pd->color_source) { + case TEX_PD_COLOR_PARTAGE: + if (pd->coba) { + if (BKE_colorband_evaluate(pd->coba, age, rgba)) { + texres->talpha = true; + copy_v3_v3(&texres->tr, rgba); + texres->tin *= rgba[3]; + texres->ta = texres->tin; + } + } + break; + case TEX_PD_COLOR_PARTSPEED: { + float speed = len_v3(vec) * pd->speed_scale; + + if (pd->coba) { + if (BKE_colorband_evaluate(pd->coba, speed, rgba)) { + texres->talpha = true; + copy_v3_v3(&texres->tr, rgba); + texres->tin *= rgba[3]; + texres->ta = texres->tin; + } + } + break; + } + case TEX_PD_COLOR_PARTVEL: + texres->talpha = true; + mul_v3_v3fl(&texres->tr, vec, pd->speed_scale); + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_CONSTANT: + default: + break; + } + } + else { + float rgba[4]; + + switch (pd->ob_color_source) { + case TEX_PD_COLOR_VERTCOL: + texres->talpha = true; + copy_v3_v3(&texres->tr, col); + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_VERTWEIGHT: + texres->talpha = true; + if (pd->coba && BKE_colorband_evaluate(pd->coba, col[0], rgba)) { + copy_v3_v3(&texres->tr, rgba); + texres->tin *= rgba[3]; + } + else { + copy_v3_v3(&texres->tr, col); + } + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_VERTNOR: + texres->talpha = true; + copy_v3_v3(&texres->tr, col); + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_CONSTANT: + default: + break; + } + } } static void sample_dummy_point_density(int resolution, float *values) { - memset(values, 0, sizeof(float) * 4 * resolution * resolution * resolution); + memset(values, 0, sizeof(float) * 4 * resolution * resolution * resolution); } static void particle_system_minmax(Depsgraph *depsgraph, @@ -741,220 +755,205 @@ static void particle_system_minmax(Depsgraph *depsgraph, Object *object, ParticleSystem *psys, float radius, - float min[3], float max[3]) + float min[3], + float max[3]) { - const float size[3] = {radius, radius, radius}; - const float cfra = BKE_scene_frame_get(scene); - ParticleSettings *part = psys->part; - ParticleSimulationData sim = {NULL}; - ParticleData *pa = NULL; - int i; - int total_particles; - float mat[4][4], imat[4][4]; - - INIT_MINMAX(min, max); - if (part->type == PART_HAIR) { - /* TOOD(sergey): Not supported currently. */ - return; - } - - unit_m4(mat); - - sim.depsgraph = depsgraph; - sim.scene = scene; - sim.ob = object; - sim.psys = psys; - sim.psmd = psys_get_modifier(object, psys); - - invert_m4_m4(imat, object->obmat); - total_particles = psys->totpart + psys->totchild; - psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); - - for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) { - float co_object[3], co_min[3], co_max[3]; - ParticleKey state; - state.time = cfra; - if (!psys_get_particle_state(&sim, i, &state, 0)) { - continue; - } - mul_v3_m4v3(co_object, imat, state.co); - sub_v3_v3v3(co_min, co_object, size); - add_v3_v3v3(co_max, co_object, size); - minmax_v3v3_v3(min, max, co_min); - minmax_v3v3_v3(min, max, co_max); - } - - if (psys->lattice_deform_data) { - end_latt_deform(psys->lattice_deform_data); - psys->lattice_deform_data = NULL; - } + const float size[3] = {radius, radius, radius}; + const float cfra = BKE_scene_frame_get(scene); + ParticleSettings *part = psys->part; + ParticleSimulationData sim = {NULL}; + ParticleData *pa = NULL; + int i; + int total_particles; + float mat[4][4], imat[4][4]; + + INIT_MINMAX(min, max); + if (part->type == PART_HAIR) { + /* TOOD(sergey): Not supported currently. */ + return; + } + + unit_m4(mat); + + sim.depsgraph = depsgraph; + sim.scene = scene; + sim.ob = object; + sim.psys = psys; + sim.psmd = psys_get_modifier(object, psys); + + invert_m4_m4(imat, object->obmat); + total_particles = psys->totpart + psys->totchild; + psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) { + float co_object[3], co_min[3], co_max[3]; + ParticleKey state; + state.time = cfra; + if (!psys_get_particle_state(&sim, i, &state, 0)) { + continue; + } + mul_v3_m4v3(co_object, imat, state.co); + sub_v3_v3v3(co_min, co_object, size); + add_v3_v3v3(co_max, co_object, size); + minmax_v3v3_v3(min, max, co_min); + minmax_v3v3_v3(min, max, co_max); + } + + if (psys->lattice_deform_data) { + end_latt_deform(psys->lattice_deform_data); + psys->lattice_deform_data = NULL; + } } -void RE_point_density_cache( - struct Depsgraph *depsgraph, - PointDensity *pd) +void RE_point_density_cache(struct Depsgraph *depsgraph, PointDensity *pd) { - Scene *scene = DEG_get_evaluated_scene(depsgraph); + Scene *scene = DEG_get_evaluated_scene(depsgraph); - /* Same matricies/resolution as dupli_render_particle_set(). */ - BLI_mutex_lock(&sample_mutex); - cache_pointdensity(depsgraph, scene, pd); - BLI_mutex_unlock(&sample_mutex); + /* Same matricies/resolution as dupli_render_particle_set(). */ + BLI_mutex_lock(&sample_mutex); + cache_pointdensity(depsgraph, scene, pd); + BLI_mutex_unlock(&sample_mutex); } -void RE_point_density_minmax( - struct Depsgraph *depsgraph, - struct PointDensity *pd, - float r_min[3], float r_max[3]) +void RE_point_density_minmax(struct Depsgraph *depsgraph, + struct PointDensity *pd, + float r_min[3], + float r_max[3]) { - Scene *scene = DEG_get_evaluated_scene(depsgraph); - Object *object = pd->object; - if (object == NULL) { - zero_v3(r_min); - zero_v3(r_max); - return; - } - if (pd->source == TEX_PD_PSYS) { - ParticleSystem *psys; - - if (pd->psys == 0) { - zero_v3(r_min); - zero_v3(r_max); - return; - } - psys = BLI_findlink(&object->particlesystem, pd->psys - 1); - if (psys == NULL) { - zero_v3(r_min); - zero_v3(r_max); - return; - } - - particle_system_minmax(depsgraph, - scene, - object, - psys, - pd->radius, - r_min, r_max); - } - else { - float radius[3] = {pd->radius, pd->radius, pd->radius}; - BoundBox *bb = BKE_object_boundbox_get(object); - - if (bb != NULL) { - BLI_assert((bb->flag & BOUNDBOX_DIRTY) == 0); - copy_v3_v3(r_min, bb->vec[0]); - copy_v3_v3(r_max, bb->vec[6]); - /* Adjust texture space to include density points on the boundaries. */ - sub_v3_v3(r_min, radius); - add_v3_v3(r_max, radius); - } - else { - zero_v3(r_min); - zero_v3(r_max); - } - } + Scene *scene = DEG_get_evaluated_scene(depsgraph); + Object *object = pd->object; + if (object == NULL) { + zero_v3(r_min); + zero_v3(r_max); + return; + } + if (pd->source == TEX_PD_PSYS) { + ParticleSystem *psys; + + if (pd->psys == 0) { + zero_v3(r_min); + zero_v3(r_max); + return; + } + psys = BLI_findlink(&object->particlesystem, pd->psys - 1); + if (psys == NULL) { + zero_v3(r_min); + zero_v3(r_max); + return; + } + + particle_system_minmax(depsgraph, scene, object, psys, pd->radius, r_min, r_max); + } + else { + float radius[3] = {pd->radius, pd->radius, pd->radius}; + BoundBox *bb = BKE_object_boundbox_get(object); + + if (bb != NULL) { + BLI_assert((bb->flag & BOUNDBOX_DIRTY) == 0); + copy_v3_v3(r_min, bb->vec[0]); + copy_v3_v3(r_max, bb->vec[6]); + /* Adjust texture space to include density points on the boundaries. */ + sub_v3_v3(r_min, radius); + add_v3_v3(r_max, radius); + } + else { + zero_v3(r_min); + zero_v3(r_max); + } + } } typedef struct SampleCallbackData { - PointDensity *pd; - int resolution; - float *min, *dim; - float *values; + PointDensity *pd; + int resolution; + float *min, *dim; + float *values; } SampleCallbackData; -static void point_density_sample_func( - void *__restrict data_v, - const int iter, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void point_density_sample_func(void *__restrict data_v, + const int iter, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SampleCallbackData *data = (SampleCallbackData *)data_v; - - const int resolution = data->resolution; - const int resolution2 = resolution * resolution; - const float *min = data->min, *dim = data->dim; - PointDensity *pd = data->pd; - float *values = data->values; - - if (!pd || !pd->point_tree) { - return; - } - - size_t z = (size_t)iter; - for (size_t y = 0; y < resolution; ++y) { - for (size_t x = 0; x < resolution; ++x) { - size_t index = z * resolution2 + y * resolution + x; - float texvec[3]; - float age, vec[3], col[3]; - TexResult texres; - - copy_v3_v3(texvec, min); - texvec[0] += dim[0] * (float)x / (float)resolution; - texvec[1] += dim[1] * (float)y / (float)resolution; - texvec[2] += dim[2] * (float)z / (float)resolution; - - pointdensity(pd, texvec, &texres, vec, &age, col); - pointdensity_color(pd, &texres, age, vec, col); - - copy_v3_v3(&values[index * 4 + 0], &texres.tr); - values[index * 4 + 3] = texres.tin; - } - } + SampleCallbackData *data = (SampleCallbackData *)data_v; + + const int resolution = data->resolution; + const int resolution2 = resolution * resolution; + const float *min = data->min, *dim = data->dim; + PointDensity *pd = data->pd; + float *values = data->values; + + if (!pd || !pd->point_tree) { + return; + } + + size_t z = (size_t)iter; + for (size_t y = 0; y < resolution; ++y) { + for (size_t x = 0; x < resolution; ++x) { + size_t index = z * resolution2 + y * resolution + x; + float texvec[3]; + float age, vec[3], col[3]; + TexResult texres; + + copy_v3_v3(texvec, min); + texvec[0] += dim[0] * (float)x / (float)resolution; + texvec[1] += dim[1] * (float)y / (float)resolution; + texvec[2] += dim[2] * (float)z / (float)resolution; + + pointdensity(pd, texvec, &texres, vec, &age, col); + pointdensity_color(pd, &texres, age, vec, col); + + copy_v3_v3(&values[index * 4 + 0], &texres.tr); + values[index * 4 + 3] = texres.tin; + } + } } /* NOTE 1: Requires RE_point_density_cache() to be called first. * NOTE 2: Frees point density structure after sampling. */ -void RE_point_density_sample( - Depsgraph *depsgraph, - PointDensity *pd, - const int resolution, - float *values) +void RE_point_density_sample(Depsgraph *depsgraph, + PointDensity *pd, + const int resolution, + float *values) { - Object *object = pd->object; - float min[3], max[3], dim[3]; - - /* TODO(sergey): Implement some sort of assert() that point density - * was cached already. - */ - - if (object == NULL) { - sample_dummy_point_density(resolution, values); - return; - } - - BLI_mutex_lock(&sample_mutex); - RE_point_density_minmax(depsgraph, - pd, - min, - max); - BLI_mutex_unlock(&sample_mutex); - sub_v3_v3v3(dim, max, min); - if (dim[0] <= 0.0f || dim[1] <= 0.0f || dim[2] <= 0.0f) { - sample_dummy_point_density(resolution, values); - return; - } - - SampleCallbackData data; - data.pd = pd; - data.resolution = resolution; - data.min = min; - data.dim = dim; - data.values = values; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (resolution > 32); - BLI_task_parallel_range(0, - resolution, - &data, - point_density_sample_func, - &settings); - - free_pointdensity(pd); + Object *object = pd->object; + float min[3], max[3], dim[3]; + + /* TODO(sergey): Implement some sort of assert() that point density + * was cached already. + */ + + if (object == NULL) { + sample_dummy_point_density(resolution, values); + return; + } + + BLI_mutex_lock(&sample_mutex); + RE_point_density_minmax(depsgraph, pd, min, max); + BLI_mutex_unlock(&sample_mutex); + sub_v3_v3v3(dim, max, min); + if (dim[0] <= 0.0f || dim[1] <= 0.0f || dim[2] <= 0.0f) { + sample_dummy_point_density(resolution, values); + return; + } + + SampleCallbackData data; + data.pd = pd; + data.resolution = resolution; + data.min = min; + data.dim = dim; + data.values = values; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = (resolution > 32); + BLI_task_parallel_range(0, resolution, &data, point_density_sample_func, &settings); + + free_pointdensity(pd); } void RE_point_density_free(struct PointDensity *pd) { - free_pointdensity(pd); + free_pointdensity(pd); } void RE_point_density_fix_linking(void) diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index c00fc48bcd0..0140aa44645 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -58,78 +58,83 @@ static void render_result_views_free(RenderResult *res) { - while (res->views.first) { - RenderView *rv = res->views.first; - BLI_remlink(&res->views, rv); + while (res->views.first) { + RenderView *rv = res->views.first; + BLI_remlink(&res->views, rv); - if (rv->rect32) - MEM_freeN(rv->rect32); + if (rv->rect32) + MEM_freeN(rv->rect32); - if (rv->rectz) - MEM_freeN(rv->rectz); + if (rv->rectz) + MEM_freeN(rv->rectz); - if (rv->rectf) - MEM_freeN(rv->rectf); + if (rv->rectf) + MEM_freeN(rv->rectf); - MEM_freeN(rv); - } + MEM_freeN(rv); + } - res->have_combined = false; + res->have_combined = false; } void render_result_free(RenderResult *res) { - if (res == NULL) return; - - while (res->layers.first) { - RenderLayer *rl = res->layers.first; - - /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ - if (rl->acolrect) MEM_freeN(rl->acolrect); - if (rl->scolrect) MEM_freeN(rl->scolrect); - if (rl->display_buffer) MEM_freeN(rl->display_buffer); - - while (rl->passes.first) { - RenderPass *rpass = rl->passes.first; - if (rpass->rect) MEM_freeN(rpass->rect); - BLI_remlink(&rl->passes, rpass); - MEM_freeN(rpass); - } - BLI_remlink(&res->layers, rl); - MEM_freeN(rl); - } - - render_result_views_free(res); - - if (res->rect32) - MEM_freeN(res->rect32); - if (res->rectz) - MEM_freeN(res->rectz); - if (res->rectf) - MEM_freeN(res->rectf); - if (res->text) - MEM_freeN(res->text); - if (res->error) - MEM_freeN(res->error); - - BKE_stamp_data_free(res->stamp_data); - - MEM_freeN(res); + if (res == NULL) + return; + + while (res->layers.first) { + RenderLayer *rl = res->layers.first; + + /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ + if (rl->acolrect) + MEM_freeN(rl->acolrect); + if (rl->scolrect) + MEM_freeN(rl->scolrect); + if (rl->display_buffer) + MEM_freeN(rl->display_buffer); + + while (rl->passes.first) { + RenderPass *rpass = rl->passes.first; + if (rpass->rect) + MEM_freeN(rpass->rect); + BLI_remlink(&rl->passes, rpass); + MEM_freeN(rpass); + } + BLI_remlink(&res->layers, rl); + MEM_freeN(rl); + } + + render_result_views_free(res); + + if (res->rect32) + MEM_freeN(res->rect32); + if (res->rectz) + MEM_freeN(res->rectz); + if (res->rectf) + MEM_freeN(res->rectf); + if (res->text) + MEM_freeN(res->text); + if (res->error) + MEM_freeN(res->error); + + BKE_stamp_data_free(res->stamp_data); + + MEM_freeN(res); } /* version that's compatible with fullsample buffers */ void render_result_free_list(ListBase *lb, RenderResult *rr) { - RenderResult *rrnext; + RenderResult *rrnext; - for (; rr; rr = rrnext) { - rrnext = rr->next; + for (; rr; rr = rrnext) { + rrnext = rr->next; - if (lb && lb->first) - BLI_remlink(lb, rr); + if (lb && lb->first) + BLI_remlink(lb, rr); - render_result_free(rr); - } + render_result_free(rr); + } } /********************************* multiview *************************************/ @@ -137,611 +142,658 @@ void render_result_free_list(ListBase *lb, RenderResult *rr) /* create a new views Listbase in rr without duplicating the memory pointers */ void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src) { - RenderView *rview; + RenderView *rview; - if (dst == NULL || src == NULL) - return; + if (dst == NULL || src == NULL) + return; - for (rview = src->views.first; rview; rview = rview->next) { - RenderView *rv; + for (rview = src->views.first; rview; rview = rview->next) { + RenderView *rv; - rv = MEM_mallocN(sizeof(RenderView), "new render view"); - BLI_addtail(&dst->views, rv); + rv = MEM_mallocN(sizeof(RenderView), "new render view"); + BLI_addtail(&dst->views, rv); - BLI_strncpy(rv->name, rview->name, sizeof(rv->name)); - rv->rectf = rview->rectf; - rv->rectz = rview->rectz; - rv->rect32 = rview->rect32; - } + BLI_strncpy(rv->name, rview->name, sizeof(rv->name)); + rv->rectf = rview->rectf; + rv->rectz = rview->rectz; + rv->rect32 = rview->rect32; + } } /* free the views created temporarily */ void render_result_views_shallowdelete(RenderResult *rr) { - if (rr == NULL) - return; - - while (rr->views.first) { - RenderView *rv = rr->views.first; - BLI_remlink(&rr->views, rv); - MEM_freeN(rv); - } + if (rr == NULL) + return; + + while (rr->views.first) { + RenderView *rv = rr->views.first; + BLI_remlink(&rr->views, rv); + MEM_freeN(rv); + } } - static char *set_pass_name(char *outname, const char *name, int channel, const char *chan_id) { - BLI_strncpy(outname, name, EXR_PASS_MAXNAME); - if (channel >= 0) { - char token[3] = {'.', chan_id[channel], '\0'}; - strncat(outname, token, EXR_PASS_MAXNAME); - } - return outname; + BLI_strncpy(outname, name, EXR_PASS_MAXNAME); + if (channel >= 0) { + char token[3] = {'.', chan_id[channel], '\0'}; + strncat(outname, token, EXR_PASS_MAXNAME); + } + return outname; } -static void set_pass_full_name(char *fullname, const char *name, int channel, const char *view, const char *chan_id) +static void set_pass_full_name( + char *fullname, const char *name, int channel, const char *view, const char *chan_id) { - BLI_strncpy(fullname, name, EXR_PASS_MAXNAME); - if (view && view[0]) { - strncat(fullname, ".", EXR_PASS_MAXNAME); - strncat(fullname, view, EXR_PASS_MAXNAME); - } - if (channel >= 0) { - char token[3] = {'.', chan_id[channel], '\0'}; - strncat(fullname, token, EXR_PASS_MAXNAME); - } + BLI_strncpy(fullname, name, EXR_PASS_MAXNAME); + if (view && view[0]) { + strncat(fullname, ".", EXR_PASS_MAXNAME); + strncat(fullname, view, EXR_PASS_MAXNAME); + } + if (channel >= 0) { + char token[3] = {'.', chan_id[channel], '\0'}; + strncat(fullname, token, EXR_PASS_MAXNAME); + } } /********************************** New **************************************/ -static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, const char *name, const char *viewname, const char *chan_id) +static RenderPass *render_layer_add_pass(RenderResult *rr, + RenderLayer *rl, + int channels, + const char *name, + const char *viewname, + const char *chan_id) { - const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); - RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name); - size_t rectsize = ((size_t)rr->rectx) * rr->recty * channels; - - rpass->channels = channels; - rpass->rectx = rl->rectx; - rpass->recty = rl->recty; - rpass->view_id = view_id; - - BLI_strncpy(rpass->name, name, sizeof(rpass->name)); - BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id)); - BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); - set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); - - if (rl->exrhandle) { - int a; - for (a = 0; a < channels; a++) { - char passname[EXR_PASS_MAXNAME]; - IMB_exr_add_channel(rl->exrhandle, rl->name, set_pass_name(passname, rpass->name, a, rpass->chan_id), viewname, 0, 0, NULL, false); - } - } - else { - float *rect; - int x; - - rpass->rect = MEM_mapallocN(sizeof(float) * rectsize, name); - if (rpass->rect == NULL) { - MEM_freeN(rpass); - return NULL; - } - - if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { - /* initialize to max speed */ - rect = rpass->rect; - for (x = rectsize - 1; x >= 0; x--) - rect[x] = PASS_VECTOR_MAX; - } - else if (STREQ(rpass->name, RE_PASSNAME_Z)) { - rect = rpass->rect; - for (x = rectsize - 1; x >= 0; x--) - rect[x] = 10e10; - } - } - - BLI_addtail(&rl->passes, rpass); - - return rpass; + const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); + RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name); + size_t rectsize = ((size_t)rr->rectx) * rr->recty * channels; + + rpass->channels = channels; + rpass->rectx = rl->rectx; + rpass->recty = rl->recty; + rpass->view_id = view_id; + + BLI_strncpy(rpass->name, name, sizeof(rpass->name)); + BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id)); + BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); + set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); + + if (rl->exrhandle) { + int a; + for (a = 0; a < channels; a++) { + char passname[EXR_PASS_MAXNAME]; + IMB_exr_add_channel(rl->exrhandle, + rl->name, + set_pass_name(passname, rpass->name, a, rpass->chan_id), + viewname, + 0, + 0, + NULL, + false); + } + } + else { + float *rect; + int x; + + rpass->rect = MEM_mapallocN(sizeof(float) * rectsize, name); + if (rpass->rect == NULL) { + MEM_freeN(rpass); + return NULL; + } + + if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) { + /* initialize to max speed */ + rect = rpass->rect; + for (x = rectsize - 1; x >= 0; x--) + rect[x] = PASS_VECTOR_MAX; + } + else if (STREQ(rpass->name, RE_PASSNAME_Z)) { + rect = rpass->rect; + for (x = rectsize - 1; x >= 0; x--) + rect[x] = 10e10; + } + } + + BLI_addtail(&rl->passes, rpass); + + return rpass; } /* wrapper called from render_opengl */ -RenderPass *gp_add_pass(RenderResult *rr, RenderLayer *rl, int channels, const char *name, const char *viewname) +RenderPass *gp_add_pass( + RenderResult *rr, RenderLayer *rl, int channels, const char *name, const char *viewname) { - return render_layer_add_pass(rr, rl, channels, name, viewname, "RGBA"); + return render_layer_add_pass(rr, rl, channels, name, viewname, "RGBA"); } /* called by main render as well for parts */ /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname) +RenderResult *render_result_new(Render *re, + rcti *partrct, + int crop, + int savebuffers, + const char *layername, + const char *viewname) { - RenderResult *rr; - RenderLayer *rl; - RenderView *rv; - int rectx, recty; - - rectx = BLI_rcti_size_x(partrct); - recty = BLI_rcti_size_y(partrct); - - if (rectx <= 0 || recty <= 0) - return NULL; - - rr = MEM_callocN(sizeof(RenderResult), "new render result"); - rr->rectx = rectx; - rr->recty = recty; - rr->renrect.xmin = 0; rr->renrect.xmax = rectx - 2 * crop; - /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */ - rr->crop = crop; - - /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ - rr->tilerect.xmin = partrct->xmin - re->disprect.xmin; - rr->tilerect.xmax = partrct->xmax - re->disprect.xmin; - rr->tilerect.ymin = partrct->ymin - re->disprect.ymin; - rr->tilerect.ymax = partrct->ymax - re->disprect.ymin; - - if (savebuffers) { - rr->do_exr_tile = true; - } - - render_result_views_new(rr, &re->r); - - /* check renderdata for amount of layers */ - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re, view_layer) - { - if (layername && layername[0]) { - if (!STREQ(view_layer->name, layername)) { - continue; - } - } - - rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); - BLI_addtail(&rr->layers, rl); - - BLI_strncpy(rl->name, view_layer->name, sizeof(rl->name)); - rl->layflag = view_layer->layflag; - rl->passflag = view_layer->passflag; /* for debugging: view_layer->passflag | SCE_PASS_RAYHITS; */ - rl->rectx = rectx; - rl->recty = recty; - - if (rr->do_exr_tile) { - rl->display_buffer = MEM_mapallocN((size_t)rectx * recty * sizeof(unsigned int), - "Combined display space rgba"); - if (rl->display_buffer == NULL) { - render_result_free(rr); - return NULL; - } - rl->exrhandle = IMB_exr_get_handle(); - } - - for (rv = rr->views.first; rv; rv = rv->next) { - const char *view = rv->name; - - if (viewname && viewname[0]) - if (!STREQ(view, viewname)) - continue; - - if (rr->do_exr_tile) - IMB_exr_add_view(rl->exrhandle, view); + RenderResult *rr; + RenderLayer *rl; + RenderView *rv; + int rectx, recty; + + rectx = BLI_rcti_size_x(partrct); + recty = BLI_rcti_size_y(partrct); + + if (rectx <= 0 || recty <= 0) + return NULL; + + rr = MEM_callocN(sizeof(RenderResult), "new render result"); + rr->rectx = rectx; + rr->recty = recty; + rr->renrect.xmin = 0; + rr->renrect.xmax = rectx - 2 * crop; + /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */ + rr->crop = crop; + + /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ + rr->tilerect.xmin = partrct->xmin - re->disprect.xmin; + rr->tilerect.xmax = partrct->xmax - re->disprect.xmin; + rr->tilerect.ymin = partrct->ymin - re->disprect.ymin; + rr->tilerect.ymax = partrct->ymax - re->disprect.ymin; + + if (savebuffers) { + rr->do_exr_tile = true; + } + + render_result_views_new(rr, &re->r); + + /* check renderdata for amount of layers */ + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer) { + if (layername && layername[0]) { + if (!STREQ(view_layer->name, layername)) { + continue; + } + } + + rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); + BLI_addtail(&rr->layers, rl); + + BLI_strncpy(rl->name, view_layer->name, sizeof(rl->name)); + rl->layflag = view_layer->layflag; + rl->passflag = + view_layer->passflag; /* for debugging: view_layer->passflag | SCE_PASS_RAYHITS; */ + rl->rectx = rectx; + rl->recty = recty; + + if (rr->do_exr_tile) { + rl->display_buffer = MEM_mapallocN((size_t)rectx * recty * sizeof(unsigned int), + "Combined display space rgba"); + if (rl->display_buffer == NULL) { + render_result_free(rr); + return NULL; + } + rl->exrhandle = IMB_exr_get_handle(); + } + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (!STREQ(view, viewname)) + continue; + + if (rr->do_exr_tile) + IMB_exr_add_view(rl->exrhandle, view); #define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \ - do { \ - if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \ - render_result_free(rr); \ - return NULL; \ - } \ - } while (false) - - /* a renderlayer should always have a Combined pass*/ - render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA"); - - if (view_layer->passflag & SCE_PASS_Z) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z"); - if (view_layer->passflag & SCE_PASS_VECTOR) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_VECTOR, view, "XYZW"); - if (view_layer->passflag & SCE_PASS_NORMAL) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_NORMAL, view, "XYZ"); - if (view_layer->passflag & SCE_PASS_UV) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_UV, view, "UVA"); - if (view_layer->passflag & SCE_PASS_EMIT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_EMIT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_AO) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_AO, view, "RGB"); - if (view_layer->passflag & SCE_PASS_ENVIRONMENT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_ENVIRONMENT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_SHADOW) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SHADOW, view, "RGB"); - if (view_layer->passflag & SCE_PASS_INDEXOB) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXOB, view, "X"); - if (view_layer->passflag & SCE_PASS_INDEXMA) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXMA, view, "X"); - if (view_layer->passflag & SCE_PASS_MIST) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_MIST, view, "Z"); - if (rl->passflag & SCE_PASS_RAYHITS) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_RAYHITS, view, "RGB"); - if (view_layer->passflag & SCE_PASS_DIFFUSE_DIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_DIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_DIFFUSE_INDIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_INDIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_DIFFUSE_COLOR) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_COLOR, view, "RGB"); - if (view_layer->passflag & SCE_PASS_GLOSSY_DIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_DIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_GLOSSY_INDIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_INDIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_GLOSSY_COLOR) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_COLOR, view, "RGB"); - if (view_layer->passflag & SCE_PASS_TRANSM_DIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_DIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_TRANSM_INDIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_INDIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_TRANSM_COLOR) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_COLOR, view, "RGB"); - if (view_layer->passflag & SCE_PASS_SUBSURFACE_DIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_DIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_SUBSURFACE_INDIRECT) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_INDIRECT, view, "RGB"); - if (view_layer->passflag & SCE_PASS_SUBSURFACE_COLOR) - RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_COLOR, view, "RGB"); + do { \ + if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \ + render_result_free(rr); \ + return NULL; \ + } \ + } while (false) + + /* a renderlayer should always have a Combined pass*/ + render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA"); + + if (view_layer->passflag & SCE_PASS_Z) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z"); + if (view_layer->passflag & SCE_PASS_VECTOR) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_VECTOR, view, "XYZW"); + if (view_layer->passflag & SCE_PASS_NORMAL) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_NORMAL, view, "XYZ"); + if (view_layer->passflag & SCE_PASS_UV) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_UV, view, "UVA"); + if (view_layer->passflag & SCE_PASS_EMIT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_EMIT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_AO) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_AO, view, "RGB"); + if (view_layer->passflag & SCE_PASS_ENVIRONMENT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_ENVIRONMENT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_SHADOW) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SHADOW, view, "RGB"); + if (view_layer->passflag & SCE_PASS_INDEXOB) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXOB, view, "X"); + if (view_layer->passflag & SCE_PASS_INDEXMA) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_INDEXMA, view, "X"); + if (view_layer->passflag & SCE_PASS_MIST) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_MIST, view, "Z"); + if (rl->passflag & SCE_PASS_RAYHITS) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 4, RE_PASSNAME_RAYHITS, view, "RGB"); + if (view_layer->passflag & SCE_PASS_DIFFUSE_DIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_DIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_DIFFUSE_INDIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_INDIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_DIFFUSE_COLOR) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_DIFFUSE_COLOR, view, "RGB"); + if (view_layer->passflag & SCE_PASS_GLOSSY_DIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_DIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_GLOSSY_INDIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_INDIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_GLOSSY_COLOR) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_GLOSSY_COLOR, view, "RGB"); + if (view_layer->passflag & SCE_PASS_TRANSM_DIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_DIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_TRANSM_INDIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_INDIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_TRANSM_COLOR) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_TRANSM_COLOR, view, "RGB"); + if (view_layer->passflag & SCE_PASS_SUBSURFACE_DIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_DIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_SUBSURFACE_INDIRECT) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_INDIRECT, view, "RGB"); + if (view_layer->passflag & SCE_PASS_SUBSURFACE_COLOR) + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_SUBSURFACE_COLOR, view, "RGB"); #undef RENDER_LAYER_ADD_PASS_SAFE - } - } - FOREACH_VIEW_LAYER_TO_RENDER_END; + } + } + FOREACH_VIEW_LAYER_TO_RENDER_END; - /* previewrender doesn't do layers, so we make a default one */ - if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) { - rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); - BLI_addtail(&rr->layers, rl); + /* previewrender doesn't do layers, so we make a default one */ + if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) { + rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); + BLI_addtail(&rr->layers, rl); - rl->rectx = rectx; - rl->recty = recty; + rl->rectx = rectx; + rl->recty = recty; - /* duplicate code... */ - if (rr->do_exr_tile) { - rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - } + /* duplicate code... */ + if (rr->do_exr_tile) { + rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), + "Combined display space rgba"); + rl->exrhandle = IMB_exr_get_handle(); + } - for (rv = rr->views.first; rv; rv = rv->next) { - const char *view = rv->name; + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; - if (viewname && viewname[0]) - if (strcmp(view, viewname) != 0) - continue; + if (viewname && viewname[0]) + if (strcmp(view, viewname) != 0) + continue; - if (rr->do_exr_tile) - IMB_exr_add_view(rl->exrhandle, view); + if (rr->do_exr_tile) + IMB_exr_add_view(rl->exrhandle, view); - /* a renderlayer should always have a Combined pass */ - render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA"); - } + /* a renderlayer should always have a Combined pass */ + render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA"); + } - /* note, this has to be in sync with scene.c */ - rl->layflag = 0x7FFF; /* solid ztra halo strand */ - rl->passflag = SCE_PASS_COMBINED; + /* note, this has to be in sync with scene.c */ + rl->layflag = 0x7FFF; /* solid ztra halo strand */ + rl->passflag = SCE_PASS_COMBINED; - re->active_view_layer = 0; - } + re->active_view_layer = 0; + } - /* border render; calculate offset for use in compositor. compo is centralized coords */ - /* XXX obsolete? I now use it for drawing border render offset (ton) */ - rr->xof = re->disprect.xmin + BLI_rcti_cent_x(&re->disprect) - (re->winx / 2); - rr->yof = re->disprect.ymin + BLI_rcti_cent_y(&re->disprect) - (re->winy / 2); + /* border render; calculate offset for use in compositor. compo is centralized coords */ + /* XXX obsolete? I now use it for drawing border render offset (ton) */ + rr->xof = re->disprect.xmin + BLI_rcti_cent_x(&re->disprect) - (re->winx / 2); + rr->yof = re->disprect.ymin + BLI_rcti_cent_y(&re->disprect) - (re->winy / 2); - return rr; + return rr; } void render_result_clone_passes(Render *re, RenderResult *rr, const char *viewname) { - RenderLayer *rl; - RenderPass *main_rp; - - for (rl = rr->layers.first; rl; rl = rl->next) { - RenderLayer *main_rl = BLI_findstring(&re->result->layers, rl->name, offsetof(RenderLayer, name)); - if (!main_rl) { - continue; - } - - for (main_rp = main_rl->passes.first; main_rp; main_rp = main_rp->next) { - if (viewname && viewname[0] && !STREQ(main_rp->view, viewname)) { - continue; - } - - /* Compare fullname to make sure that the view also is equal. */ - RenderPass *rp = BLI_findstring(&rl->passes, main_rp->fullname, offsetof(RenderPass, fullname)); - if (!rp) { - render_layer_add_pass(rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id); - } - } - } + RenderLayer *rl; + RenderPass *main_rp; + + for (rl = rr->layers.first; rl; rl = rl->next) { + RenderLayer *main_rl = BLI_findstring( + &re->result->layers, rl->name, offsetof(RenderLayer, name)); + if (!main_rl) { + continue; + } + + for (main_rp = main_rl->passes.first; main_rp; main_rp = main_rp->next) { + if (viewname && viewname[0] && !STREQ(main_rp->view, viewname)) { + continue; + } + + /* Compare fullname to make sure that the view also is equal. */ + RenderPass *rp = BLI_findstring( + &rl->passes, main_rp->fullname, offsetof(RenderPass, fullname)); + if (!rp) { + render_layer_add_pass( + rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id); + } + } + } } -void render_result_add_pass(RenderResult *rr, const char *name, int channels, const char *chan_id, const char *layername, const char *viewname) +void render_result_add_pass(RenderResult *rr, + const char *name, + int channels, + const char *chan_id, + const char *layername, + const char *viewname) { - RenderLayer *rl; - RenderPass *rp; - RenderView *rv; - - for (rl = rr->layers.first; rl; rl = rl->next) { - if (layername && layername[0] && !STREQ(rl->name, layername)) { - continue; - } - - for (rv = rr->views.first; rv; rv = rv->next) { - const char *view = rv->name; - - if (viewname && viewname[0] && !STREQ(view, viewname)) continue; - - /* Ensure that the pass doesn't exist yet. */ - for (rp = rl->passes.first; rp; rp = rp->next) { - if (!STREQ(rp->name, name)) continue; - if (!STREQ(rp->view, view)) continue; - break; - } - - if (!rp) { - render_layer_add_pass(rr, rl, channels, name, view, chan_id); - } - } - } + RenderLayer *rl; + RenderPass *rp; + RenderView *rv; + + for (rl = rr->layers.first; rl; rl = rl->next) { + if (layername && layername[0] && !STREQ(rl->name, layername)) { + continue; + } + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0] && !STREQ(view, viewname)) + continue; + + /* Ensure that the pass doesn't exist yet. */ + for (rp = rl->passes.first; rp; rp = rp->next) { + if (!STREQ(rp->name, name)) + continue; + if (!STREQ(rp->view, view)) + continue; + break; + } + + if (!rp) { + render_layer_add_pass(rr, rl, channels, name, view, chan_id); + } + } + } } static int passtype_from_name(const char *name) { - const char delim[] = {'.', '\0'}; - const char *sep, *suf; - int len = BLI_str_partition(name, delim, &sep, &suf); - -#define CHECK_PASS(NAME) if (STREQLEN(name, RE_PASSNAME_ ## NAME, len)) return SCE_PASS_ ## NAME - - CHECK_PASS(COMBINED); - CHECK_PASS(Z); - CHECK_PASS(VECTOR); - CHECK_PASS(NORMAL); - CHECK_PASS(UV); - CHECK_PASS(EMIT); - CHECK_PASS(SHADOW); - CHECK_PASS(AO); - CHECK_PASS(ENVIRONMENT); - CHECK_PASS(INDEXOB); - CHECK_PASS(INDEXMA); - CHECK_PASS(MIST); - CHECK_PASS(RAYHITS); - CHECK_PASS(DIFFUSE_DIRECT); - CHECK_PASS(DIFFUSE_INDIRECT); - CHECK_PASS(DIFFUSE_COLOR); - CHECK_PASS(GLOSSY_DIRECT); - CHECK_PASS(GLOSSY_INDIRECT); - CHECK_PASS(GLOSSY_COLOR); - CHECK_PASS(TRANSM_DIRECT); - CHECK_PASS(TRANSM_INDIRECT); - CHECK_PASS(TRANSM_COLOR); - CHECK_PASS(SUBSURFACE_DIRECT); - CHECK_PASS(SUBSURFACE_INDIRECT); - CHECK_PASS(SUBSURFACE_COLOR); + const char delim[] = {'.', '\0'}; + const char *sep, *suf; + int len = BLI_str_partition(name, delim, &sep, &suf); + +#define CHECK_PASS(NAME) \ + if (STREQLEN(name, RE_PASSNAME_##NAME, len)) \ + return SCE_PASS_##NAME + + CHECK_PASS(COMBINED); + CHECK_PASS(Z); + CHECK_PASS(VECTOR); + CHECK_PASS(NORMAL); + CHECK_PASS(UV); + CHECK_PASS(EMIT); + CHECK_PASS(SHADOW); + CHECK_PASS(AO); + CHECK_PASS(ENVIRONMENT); + CHECK_PASS(INDEXOB); + CHECK_PASS(INDEXMA); + CHECK_PASS(MIST); + CHECK_PASS(RAYHITS); + CHECK_PASS(DIFFUSE_DIRECT); + CHECK_PASS(DIFFUSE_INDIRECT); + CHECK_PASS(DIFFUSE_COLOR); + CHECK_PASS(GLOSSY_DIRECT); + CHECK_PASS(GLOSSY_INDIRECT); + CHECK_PASS(GLOSSY_COLOR); + CHECK_PASS(TRANSM_DIRECT); + CHECK_PASS(TRANSM_INDIRECT); + CHECK_PASS(TRANSM_COLOR); + CHECK_PASS(SUBSURFACE_DIRECT); + CHECK_PASS(SUBSURFACE_INDIRECT); + CHECK_PASS(SUBSURFACE_COLOR); #undef CHECK_PASS - return 0; + return 0; } /* callbacks for render_result_new_from_exr */ static void *ml_addlayer_cb(void *base, const char *str) { - RenderResult *rr = base; - RenderLayer *rl; + RenderResult *rr = base; + RenderLayer *rl; - rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); - BLI_addtail(&rr->layers, rl); + rl = MEM_callocN(sizeof(RenderLayer), "new render layer"); + BLI_addtail(&rr->layers, rl); - BLI_strncpy(rl->name, str, EXR_LAY_MAXNAME); - return rl; + BLI_strncpy(rl->name, str, EXR_LAY_MAXNAME); + return rl; } -static void ml_addpass_cb(void *base, void *lay, const char *name, float *rect, int totchan, const char *chan_id, const char *view) +static void ml_addpass_cb(void *base, + void *lay, + const char *name, + float *rect, + int totchan, + const char *chan_id, + const char *view) { - RenderResult *rr = base; - RenderLayer *rl = lay; - RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass"); - - BLI_addtail(&rl->passes, rpass); - rpass->channels = totchan; - rl->passflag |= passtype_from_name(name); - - /* channel id chars */ - BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id)); - - rpass->rect = rect; - BLI_strncpy(rpass->name, name, EXR_PASS_MAXNAME); - BLI_strncpy(rpass->view, view, sizeof(rpass->view)); - set_pass_full_name(rpass->fullname, name, -1, view, rpass->chan_id); - - if (view[0] != '\0') { - rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); - } - else { - rpass->view_id = 0; - } + RenderResult *rr = base; + RenderLayer *rl = lay; + RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass"); + + BLI_addtail(&rl->passes, rpass); + rpass->channels = totchan; + rl->passflag |= passtype_from_name(name); + + /* channel id chars */ + BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id)); + + rpass->rect = rect; + BLI_strncpy(rpass->name, name, EXR_PASS_MAXNAME); + BLI_strncpy(rpass->view, view, sizeof(rpass->view)); + set_pass_full_name(rpass->fullname, name, -1, view, rpass->chan_id); + + if (view[0] != '\0') { + rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); + } + else { + rpass->view_id = 0; + } } static void *ml_addview_cb(void *base, const char *str) { - RenderResult *rr = base; - RenderView *rv; - - rv = MEM_callocN(sizeof(RenderView), "new render view"); - BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME); - - /* For stereo drawing we need to ensure: - * STEREO_LEFT_NAME == STEREO_LEFT_ID and - * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ - - if (STREQ(str, STEREO_LEFT_NAME)) { - BLI_addhead(&rr->views, rv); - } - else if (STREQ(str, STEREO_RIGHT_NAME)) { - RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)); - - if (left_rv == NULL) { - BLI_addhead(&rr->views, rv); - } - else { - BLI_insertlinkafter(&rr->views, left_rv, rv); - } - } - else { - BLI_addtail(&rr->views, rv); - } - - return rv; + RenderResult *rr = base; + RenderView *rv; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(str, STEREO_LEFT_NAME)) { + BLI_addhead(&rr->views, rv); + } + else if (STREQ(str, STEREO_RIGHT_NAME)) { + RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)); + + if (left_rv == NULL) { + BLI_addhead(&rr->views, rv); + } + else { + BLI_insertlinkafter(&rr->views, left_rv, rv); + } + } + else { + BLI_addtail(&rr->views, rv); + } + + return rv; } static int order_render_passes(const void *a, const void *b) { - // 1 if a is after b - RenderPass *rpa = (RenderPass *)a; - RenderPass *rpb = (RenderPass *)b; - unsigned int passtype_a = passtype_from_name(rpa->name); - unsigned int passtype_b = passtype_from_name(rpb->name); - - /* Render passes with default type always go first. */ - if (passtype_b && !passtype_a) - return 1; - if (passtype_a && !passtype_b) - return 0; - - if (passtype_a && passtype_b) { - if (passtype_a > passtype_b) - return 1; - else if (passtype_a < passtype_b) - return 0; - } - else { - int cmp = strncmp(rpa->name, rpb->name, EXR_PASS_MAXNAME); - if (cmp > 0) - return 1; - if (cmp < 0) - return 0; - } - - - /* they have the same type */ - /* left first */ - if (STREQ(rpa->view, STEREO_LEFT_NAME)) - return 0; - else if (STREQ(rpb->view, STEREO_LEFT_NAME)) - return 1; - - /* right second */ - if (STREQ(rpa->view, STEREO_RIGHT_NAME)) - return 0; - else if (STREQ(rpb->view, STEREO_RIGHT_NAME)) - return 1; - - /* remaining in ascending id order */ - return (rpa->view_id < rpb->view_id); + // 1 if a is after b + RenderPass *rpa = (RenderPass *)a; + RenderPass *rpb = (RenderPass *)b; + unsigned int passtype_a = passtype_from_name(rpa->name); + unsigned int passtype_b = passtype_from_name(rpb->name); + + /* Render passes with default type always go first. */ + if (passtype_b && !passtype_a) + return 1; + if (passtype_a && !passtype_b) + return 0; + + if (passtype_a && passtype_b) { + if (passtype_a > passtype_b) + return 1; + else if (passtype_a < passtype_b) + return 0; + } + else { + int cmp = strncmp(rpa->name, rpb->name, EXR_PASS_MAXNAME); + if (cmp > 0) + return 1; + if (cmp < 0) + return 0; + } + + /* they have the same type */ + /* left first */ + if (STREQ(rpa->view, STEREO_LEFT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_LEFT_NAME)) + return 1; + + /* right second */ + if (STREQ(rpa->view, STEREO_RIGHT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_RIGHT_NAME)) + return 1; + + /* remaining in ascending id order */ + return (rpa->view_id < rpb->view_id); } /* from imbuf, if a handle was returned and it's not a singlelayer multiview we convert this to render result */ -RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) +RenderResult *render_result_new_from_exr( + void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) { - RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); - RenderLayer *rl; - RenderPass *rpass; - const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); - - rr->rectx = rectx; - rr->recty = recty; - - IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); - - for (rl = rr->layers.first; rl; rl = rl->next) { - rl->rectx = rectx; - rl->recty = recty; - - BLI_listbase_sort(&rl->passes, order_render_passes); - - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - rpass->rectx = rectx; - rpass->recty = recty; - - if (rpass->channels >= 3) { - IMB_colormanagement_transform(rpass->rect, rpass->rectx, rpass->recty, rpass->channels, - colorspace, to_colorspace, predivide); - } - } - } - - return rr; + RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); + RenderLayer *rl; + RenderPass *rpass; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + + rr->rectx = rectx; + rr->recty = recty; + + IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); + + for (rl = rr->layers.first; rl; rl = rl->next) { + rl->rectx = rectx; + rl->recty = recty; + + BLI_listbase_sort(&rl->passes, order_render_passes); + + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + rpass->rectx = rectx; + rpass->recty = recty; + + if (rpass->channels >= 3) { + IMB_colormanagement_transform(rpass->rect, + rpass->rectx, + rpass->recty, + rpass->channels, + colorspace, + to_colorspace, + predivide); + } + } + } + + return rr; } void render_result_view_new(RenderResult *rr, const char *viewname) { - RenderView *rv = MEM_callocN(sizeof(RenderView), "new render view"); - BLI_addtail(&rr->views, rv); - BLI_strncpy(rv->name, viewname, sizeof(rv->name)); + RenderView *rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + BLI_strncpy(rv->name, viewname, sizeof(rv->name)); } void render_result_views_new(RenderResult *rr, RenderData *rd) { - SceneRenderView *srv; - - /* clear previously existing views - for sequencer */ - render_result_views_free(rr); - - /* check renderdata for amount of views */ - if ((rd->scemode & R_MULTIVIEW)) { - for (srv = rd->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) - continue; - render_result_view_new(rr, srv->name); - } - } - - /* we always need at least one view */ - if (BLI_listbase_count_at_most(&rr->views, 1) == 0) { - render_result_view_new(rr, ""); - } + SceneRenderView *srv; + + /* clear previously existing views - for sequencer */ + render_result_views_free(rr); + + /* check renderdata for amount of views */ + if ((rd->scemode & R_MULTIVIEW)) { + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + render_result_view_new(rr, srv->name); + } + } + + /* we always need at least one view */ + if (BLI_listbase_count_at_most(&rr->views, 1) == 0) { + render_result_view_new(rr, ""); + } } bool render_result_has_views(RenderResult *rr) { - RenderView *rv = rr->views.first; - return (rv && (rv->next || rv->name[0])); + RenderView *rv = rr->views.first; + return (rv && (rv->next || rv->name[0])); } /*********************************** Merge ***********************************/ -static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) +static void do_merge_tile( + RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) { - int y, tilex, tiley; - size_t ofs, copylen; - - copylen = tilex = rrpart->rectx; - tiley = rrpart->recty; - - if (rrpart->crop) { /* filters add pixel extra */ - tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex); - - copylen = tilex - 2 * rrpart->crop; - tiley -= 2 * rrpart->crop; - - ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + (rrpart->tilerect.xmin + rrpart->crop); - target += pixsize * ofs; - } - else { - ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); - target += pixsize * ofs; - } - - copylen *= sizeof(float) * pixsize; - tilex *= pixsize; - ofs = pixsize * rr->rectx; - - for (y = 0; y < tiley; y++) { - memcpy(target, tile, copylen); - target += ofs; - tile += tilex; - } + int y, tilex, tiley; + size_t ofs, copylen; + + copylen = tilex = rrpart->rectx; + tiley = rrpart->recty; + + if (rrpart->crop) { /* filters add pixel extra */ + tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex); + + copylen = tilex - 2 * rrpart->crop; + tiley -= 2 * rrpart->crop; + + ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + + (rrpart->tilerect.xmin + rrpart->crop); + target += pixsize * ofs; + } + else { + ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin); + target += pixsize * ofs; + } + + copylen *= sizeof(float) * pixsize; + tilex *= pixsize; + ofs = pixsize * rr->rectx; + + for (y = 0; y < tiley; y++) { + memcpy(target, tile, copylen); + target += ofs; + tile += tilex; + } } /* used when rendering to a full buffer, or when reading the exr part-layer-pass file */ @@ -749,787 +801,831 @@ static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, /* is used within threads */ void render_result_merge(RenderResult *rr, RenderResult *rrpart) { - RenderLayer *rl, *rlp; - RenderPass *rpass, *rpassp; - - for (rl = rr->layers.first; rl; rl = rl->next) { - rlp = RE_GetRenderLayer(rrpart, rl->name); - if (rlp) { - /* passes are allocated in sync */ - for (rpass = rl->passes.first, rpassp = rlp->passes.first; - rpass && rpassp; - rpass = rpass->next) - { - /* renderresult have all passes, renderpart only the active view's passes */ - if (strcmp(rpassp->fullname, rpass->fullname) != 0) - continue; - - do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); - - /* manually get next render pass */ - rpassp = rpassp->next; - } - } - } + RenderLayer *rl, *rlp; + RenderPass *rpass, *rpassp; + + for (rl = rr->layers.first; rl; rl = rl->next) { + rlp = RE_GetRenderLayer(rrpart, rl->name); + if (rlp) { + /* passes are allocated in sync */ + for (rpass = rl->passes.first, rpassp = rlp->passes.first; rpass && rpassp; + rpass = rpass->next) { + /* renderresult have all passes, renderpart only the active view's passes */ + if (strcmp(rpassp->fullname, rpass->fullname) != 0) + continue; + + do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); + + /* manually get next render pass */ + rpassp = rpassp->next; + } + } + } } /* Called from the UI and render pipeline, to save multilayer and multiview * images, optionally isolating a specific, view, layer or RGBA/Z pass. */ -bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const char *view, int layer) +bool RE_WriteRenderResult(ReportList *reports, + RenderResult *rr, + const char *filename, + ImageFormatData *imf, + const char *view, + int layer) { - void *exrhandle = IMB_exr_get_handle(); - const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16); - const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR); - const bool write_z = !multi_layer && (imf && (imf->flag & R_IMF_FLAG_ZBUF)); - - /* Write first layer if not multilayer and no layer was specified. */ - if (!multi_layer && layer == -1) { - layer = 0; - } - - /* First add views since IMB_exr_add_channel checks number of views. */ - if (render_result_has_views(rr)) { - for (RenderView *rview = rr->views.first; rview; rview = rview->next) { - if (!view || STREQ(view, rview->name)) { - IMB_exr_add_view(exrhandle, rview->name); - } - } - } - - /* Compositing result. */ - if (rr->have_combined) { - for (RenderView *rview = rr->views.first; rview; rview = rview->next) { - if (!rview->rectf) { - continue; - } - - const char *viewname = rview->name; - if (view) { - if (!STREQ(view, viewname)) { - continue; - } - else { - viewname = ""; - } - } - - /* Skip compositing if only a single other layer is requested. */ - if (!multi_layer && layer != 0) { - continue; - } - - for (int a = 0; a < 4; a++) { - char passname[EXR_PASS_MAXNAME]; - char layname[EXR_PASS_MAXNAME]; - const char *chan_id = "RGBA"; - - if (multi_layer) { - set_pass_name(passname, "Combined", a, chan_id); - BLI_strncpy(layname, "Composite", sizeof(layname)); - } - else { - passname[0] = chan_id[a]; - passname[1] = '\0'; - layname[0] = '\0'; - } - - IMB_exr_add_channel(exrhandle, layname, passname, viewname, - 4, 4 * rr->rectx, rview->rectf + a, half_float); - } - - if (write_z && rview->rectz) { - const char *layname = (multi_layer) ? "Composite" : ""; - IMB_exr_add_channel(exrhandle, layname, "Z", viewname, - 1, rr->rectx, rview->rectz, false); - } - } - } - - /* Other render layers. */ - int nr = (rr->have_combined) ? 1 : 0; - for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next, nr++) { - /* Skip other render layers if requested. */ - if (!multi_layer && nr != layer) { - continue; - } - - for (RenderPass *rp = rl->passes.first; rp; rp = rp->next) { - /* Skip non-RGBA and Z passes if not using multi layer. */ - if (!multi_layer && !(STREQ(rp->name, RE_PASSNAME_COMBINED) || - STREQ(rp->name, "") || - (STREQ(rp->name, RE_PASSNAME_Z) && write_z))) - { - continue; - } - - /* Skip pass if it does not match the requested view(s). */ - const char *viewname = rp->view; - if (view) { - if (!STREQ(view, viewname)) { - continue; - } - else { - viewname = ""; - } - } - - /* We only store RGBA passes as half float, for - * others precision loss can be problematic. */ - bool pass_half_float = half_float && - (STREQ(rp->chan_id, "RGB") || - STREQ(rp->chan_id, "RGBA") || - STREQ(rp->chan_id, "R") || - STREQ(rp->chan_id, "G") || - STREQ(rp->chan_id, "B") || - STREQ(rp->chan_id, "A")); - - - for (int a = 0; a < rp->channels; a++) { - /* Save Combined as RGBA if single layer save. */ - char passname[EXR_PASS_MAXNAME]; - char layname[EXR_PASS_MAXNAME]; - - if (multi_layer) { - set_pass_name(passname, rp->name, a, rp->chan_id); - BLI_strncpy(layname, rl->name, sizeof(layname)); - } - else { - passname[0] = rp->chan_id[a]; - passname[1] = '\0'; - layname[0] = '\0'; - } - - IMB_exr_add_channel(exrhandle, layname, passname, viewname, - rp->channels, rp->channels * rr->rectx, rp->rect + a, - pass_half_float); - } - } - } - - errno = 0; - - BLI_make_existing_file(filename); - - int compress = (imf ? imf->exr_codec : 0); - bool success = IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress, rr->stamp_data); - if (success) { - IMB_exr_write_channels(exrhandle); - } - else { - /* TODO, get the error from openexr's exception */ - BKE_reportf(reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno)); - } - - IMB_exr_close(exrhandle); - return success; + void *exrhandle = IMB_exr_get_handle(); + const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16); + const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR); + const bool write_z = !multi_layer && (imf && (imf->flag & R_IMF_FLAG_ZBUF)); + + /* Write first layer if not multilayer and no layer was specified. */ + if (!multi_layer && layer == -1) { + layer = 0; + } + + /* First add views since IMB_exr_add_channel checks number of views. */ + if (render_result_has_views(rr)) { + for (RenderView *rview = rr->views.first; rview; rview = rview->next) { + if (!view || STREQ(view, rview->name)) { + IMB_exr_add_view(exrhandle, rview->name); + } + } + } + + /* Compositing result. */ + if (rr->have_combined) { + for (RenderView *rview = rr->views.first; rview; rview = rview->next) { + if (!rview->rectf) { + continue; + } + + const char *viewname = rview->name; + if (view) { + if (!STREQ(view, viewname)) { + continue; + } + else { + viewname = ""; + } + } + + /* Skip compositing if only a single other layer is requested. */ + if (!multi_layer && layer != 0) { + continue; + } + + for (int a = 0; a < 4; a++) { + char passname[EXR_PASS_MAXNAME]; + char layname[EXR_PASS_MAXNAME]; + const char *chan_id = "RGBA"; + + if (multi_layer) { + set_pass_name(passname, "Combined", a, chan_id); + BLI_strncpy(layname, "Composite", sizeof(layname)); + } + else { + passname[0] = chan_id[a]; + passname[1] = '\0'; + layname[0] = '\0'; + } + + IMB_exr_add_channel(exrhandle, + layname, + passname, + viewname, + 4, + 4 * rr->rectx, + rview->rectf + a, + half_float); + } + + if (write_z && rview->rectz) { + const char *layname = (multi_layer) ? "Composite" : ""; + IMB_exr_add_channel(exrhandle, layname, "Z", viewname, 1, rr->rectx, rview->rectz, false); + } + } + } + + /* Other render layers. */ + int nr = (rr->have_combined) ? 1 : 0; + for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next, nr++) { + /* Skip other render layers if requested. */ + if (!multi_layer && nr != layer) { + continue; + } + + for (RenderPass *rp = rl->passes.first; rp; rp = rp->next) { + /* Skip non-RGBA and Z passes if not using multi layer. */ + if (!multi_layer && !(STREQ(rp->name, RE_PASSNAME_COMBINED) || STREQ(rp->name, "") || + (STREQ(rp->name, RE_PASSNAME_Z) && write_z))) { + continue; + } + + /* Skip pass if it does not match the requested view(s). */ + const char *viewname = rp->view; + if (view) { + if (!STREQ(view, viewname)) { + continue; + } + else { + viewname = ""; + } + } + + /* We only store RGBA passes as half float, for + * others precision loss can be problematic. */ + bool pass_half_float = half_float && + (STREQ(rp->chan_id, "RGB") || STREQ(rp->chan_id, "RGBA") || + STREQ(rp->chan_id, "R") || STREQ(rp->chan_id, "G") || + STREQ(rp->chan_id, "B") || STREQ(rp->chan_id, "A")); + + for (int a = 0; a < rp->channels; a++) { + /* Save Combined as RGBA if single layer save. */ + char passname[EXR_PASS_MAXNAME]; + char layname[EXR_PASS_MAXNAME]; + + if (multi_layer) { + set_pass_name(passname, rp->name, a, rp->chan_id); + BLI_strncpy(layname, rl->name, sizeof(layname)); + } + else { + passname[0] = rp->chan_id[a]; + passname[1] = '\0'; + layname[0] = '\0'; + } + + IMB_exr_add_channel(exrhandle, + layname, + passname, + viewname, + rp->channels, + rp->channels * rr->rectx, + rp->rect + a, + pass_half_float); + } + } + } + + errno = 0; + + BLI_make_existing_file(filename); + + int compress = (imf ? imf->exr_codec : 0); + bool success = IMB_exr_begin_write( + exrhandle, filename, rr->rectx, rr->recty, compress, rr->stamp_data); + if (success) { + IMB_exr_write_channels(exrhandle); + } + else { + /* TODO, get the error from openexr's exception */ + BKE_reportf( + reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno)); + } + + IMB_exr_close(exrhandle); + return success; } /**************************** Single Layer Rendering *************************/ void render_result_single_layer_begin(Render *re) { - /* all layers except the active one get temporally pushed away */ + /* all layers except the active one get temporally pushed away */ - /* officially pushed result should be NULL... error can happen with do_seq */ - RE_FreeRenderResult(re->pushedresult); + /* officially pushed result should be NULL... error can happen with do_seq */ + RE_FreeRenderResult(re->pushedresult); - re->pushedresult = re->result; - re->result = NULL; + re->pushedresult = re->result; + re->result = NULL; } /* if scemode is R_SINGLE_LAYER, at end of rendering, merge the both render results */ void render_result_single_layer_end(Render *re) { - ViewLayer *view_layer; - RenderLayer *rlpush; - RenderLayer *rl; - int nr; - - if (re->result == NULL) { - printf("pop render result error; no current result!\n"); - return; - } - - if (!re->pushedresult) - return; - - if (re->pushedresult->rectx == re->result->rectx && re->pushedresult->recty == re->result->recty) { - /* find which layer in re->pushedresult should be replaced */ - rl = re->result->layers.first; - - /* render result should be empty after this */ - BLI_remlink(&re->result->layers, rl); - - /* reconstruct render result layers */ - for (nr = 0, view_layer = re->view_layers.first; view_layer; view_layer = view_layer->next, nr++) { - if (nr == re->active_view_layer) { - BLI_addtail(&re->result->layers, rl); - } - else { - rlpush = RE_GetRenderLayer(re->pushedresult, view_layer->name); - if (rlpush) { - BLI_remlink(&re->pushedresult->layers, rlpush); - BLI_addtail(&re->result->layers, rlpush); - } - } - } - } - - RE_FreeRenderResult(re->pushedresult); - re->pushedresult = NULL; + ViewLayer *view_layer; + RenderLayer *rlpush; + RenderLayer *rl; + int nr; + + if (re->result == NULL) { + printf("pop render result error; no current result!\n"); + return; + } + + if (!re->pushedresult) + return; + + if (re->pushedresult->rectx == re->result->rectx && + re->pushedresult->recty == re->result->recty) { + /* find which layer in re->pushedresult should be replaced */ + rl = re->result->layers.first; + + /* render result should be empty after this */ + BLI_remlink(&re->result->layers, rl); + + /* reconstruct render result layers */ + for (nr = 0, view_layer = re->view_layers.first; view_layer; + view_layer = view_layer->next, nr++) { + if (nr == re->active_view_layer) { + BLI_addtail(&re->result->layers, rl); + } + else { + rlpush = RE_GetRenderLayer(re->pushedresult, view_layer->name); + if (rlpush) { + BLI_remlink(&re->pushedresult->layers, rlpush); + BLI_addtail(&re->result->layers, rlpush); + } + } + } + } + + RE_FreeRenderResult(re->pushedresult); + re->pushedresult = NULL; } /************************* EXR Tile File Rendering ***************************/ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) { - RenderLayer *rlp, *rl; - RenderPass *rpassp; - int offs, partx, party; - - BLI_thread_lock(LOCK_IMAGE); - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - if (rrpart->crop) { /* filters add pixel extra */ - offs = (rrpart->crop + rrpart->crop * rrpart->rectx); - } - else { - offs = 0; - } - - /* passes are allocated in sync */ - for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - const int xstride = rpassp->channels; - int a; - char fullname[EXR_PASS_MAXNAME]; - - for (a = 0; a < xstride; a++) { - set_pass_full_name(fullname, rpassp->name, a, viewname, rpassp->chan_id); - - IMB_exr_set_channel(rl->exrhandle, rlp->name, fullname, - xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs); - } - } - - } - - party = rrpart->tilerect.ymin + rrpart->crop; - partx = rrpart->tilerect.xmin + rrpart->crop; - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname, false); - } - - BLI_thread_unlock(LOCK_IMAGE); + RenderLayer *rlp, *rl; + RenderPass *rpassp; + int offs, partx, party; + + BLI_thread_lock(LOCK_IMAGE); + + for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { + rl = RE_GetRenderLayer(rr, rlp->name); + + /* should never happen but prevents crash if it does */ + BLI_assert(rl); + if (UNLIKELY(rl == NULL)) { + continue; + } + + if (rrpart->crop) { /* filters add pixel extra */ + offs = (rrpart->crop + rrpart->crop * rrpart->rectx); + } + else { + offs = 0; + } + + /* passes are allocated in sync */ + for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { + const int xstride = rpassp->channels; + int a; + char fullname[EXR_PASS_MAXNAME]; + + for (a = 0; a < xstride; a++) { + set_pass_full_name(fullname, rpassp->name, a, viewname, rpassp->chan_id); + + IMB_exr_set_channel(rl->exrhandle, + rlp->name, + fullname, + xstride, + xstride * rrpart->rectx, + rpassp->rect + a + xstride * offs); + } + } + } + + party = rrpart->tilerect.ymin + rrpart->crop; + partx = rrpart->tilerect.xmin + rrpart->crop; + + for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { + rl = RE_GetRenderLayer(rr, rlp->name); + + /* should never happen but prevents crash if it does */ + BLI_assert(rl); + if (UNLIKELY(rl == NULL)) { + continue; + } + + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname, false); + } + + BLI_thread_unlock(LOCK_IMAGE); } void render_result_save_empty_result_tiles(Render *re) { - RenderPart *pa; - RenderResult *rr; - RenderLayer *rl; - - for (rr = re->result; rr; rr = rr->next) { - for (rl = rr->layers.first; rl; rl = rl->next) { - for (pa = re->parts.first; pa; pa = pa->next) { - if (pa->status != PART_STATUS_MERGED) { - int party = pa->disprect.ymin - re->disprect.ymin; - int partx = pa->disprect.xmin - re->disprect.xmin; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname, true); - } - } - } - } + RenderPart *pa; + RenderResult *rr; + RenderLayer *rl; + + for (rr = re->result; rr; rr = rr->next) { + for (rl = rr->layers.first; rl; rl = rl->next) { + for (pa = re->parts.first; pa; pa = pa->next) { + if (pa->status != PART_STATUS_MERGED) { + int party = pa->disprect.ymin - re->disprect.ymin; + int partx = pa->disprect.xmin - re->disprect.xmin; + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname, true); + } + } + } + } } /* Compute list of passes needed by render engine. */ -static void templates_register_pass_cb(void *userdata, Scene *UNUSED(scene), ViewLayer *UNUSED(view_layer), - const char *name, int channels, const char *chan_id, int UNUSED(type)) +static void templates_register_pass_cb(void *userdata, + Scene *UNUSED(scene), + ViewLayer *UNUSED(view_layer), + const char *name, + int channels, + const char *chan_id, + int UNUSED(type)) { - ListBase *templates = userdata; - RenderPass *pass = MEM_callocN(sizeof(RenderPass), "RenderPassTemplate"); + ListBase *templates = userdata; + RenderPass *pass = MEM_callocN(sizeof(RenderPass), "RenderPassTemplate"); - pass->channels = channels; - BLI_strncpy(pass->name, name, sizeof(pass->name)); - BLI_strncpy(pass->chan_id, chan_id, sizeof(pass->chan_id)); + pass->channels = channels; + BLI_strncpy(pass->name, name, sizeof(pass->name)); + BLI_strncpy(pass->chan_id, chan_id, sizeof(pass->chan_id)); - BLI_addtail(templates, pass); + BLI_addtail(templates, pass); } -static void render_result_get_pass_templates(RenderEngine *engine, Render *re, RenderLayer *rl, ListBase *templates) +static void render_result_get_pass_templates(RenderEngine *engine, + Render *re, + RenderLayer *rl, + ListBase *templates) { - BLI_listbase_clear(templates); - - if (engine && engine->type->update_render_passes) { - ViewLayer *view_layer = BLI_findstring(&re->view_layers, rl->name, offsetof(ViewLayer, name)); - if (view_layer) { - RE_engine_update_render_passes(engine, re->scene, view_layer, templates_register_pass_cb, templates); - } - } + BLI_listbase_clear(templates); + + if (engine && engine->type->update_render_passes) { + ViewLayer *view_layer = BLI_findstring(&re->view_layers, rl->name, offsetof(ViewLayer, name)); + if (view_layer) { + RE_engine_update_render_passes( + engine, re->scene, view_layer, templates_register_pass_cb, templates); + } + } } /* begin write of exr tile file */ void render_result_exr_file_begin(Render *re, RenderEngine *engine) { - char str[FILE_MAX]; - - for (RenderResult *rr = re->result; rr; rr = rr->next) { - for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next) { - /* Get passes needed by engine. Normally we would wait for the - * engine to create them, but for EXR file we need to know in - * advance. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - for (RenderPass *pass = templates.first; pass; pass = pass->next) { - render_result_add_pass(re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - BLI_rw_mutex_unlock(&re->resultmutex); - - BLI_freelistN(&templates); - - /* Open EXR file for writing. */ - render_result_exr_file_path(re->scene, rl->name, rr->sample_nr, str); - printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); - IMB_exrtile_begin_write(rl->exrhandle, str, 0, rr->rectx, rr->recty, re->partx, re->party); - } - } + char str[FILE_MAX]; + + for (RenderResult *rr = re->result; rr; rr = rr->next) { + for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next) { + /* Get passes needed by engine. Normally we would wait for the + * engine to create them, but for EXR file we need to know in + * advance. */ + ListBase templates; + render_result_get_pass_templates(engine, re, rl, &templates); + + /* Create render passes requested by engine. Only this part is + * mutex locked to avoid deadlock with Python GIL. */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + for (RenderPass *pass = templates.first; pass; pass = pass->next) { + render_result_add_pass( + re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); + } + BLI_rw_mutex_unlock(&re->resultmutex); + + BLI_freelistN(&templates); + + /* Open EXR file for writing. */ + render_result_exr_file_path(re->scene, rl->name, rr->sample_nr, str); + printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); + IMB_exrtile_begin_write(rl->exrhandle, str, 0, rr->rectx, rr->recty, re->partx, re->party); + } + } } /* end write of exr tile file, read back first sample */ void render_result_exr_file_end(Render *re, RenderEngine *engine) { - /* Close EXR files. */ - for (RenderResult *rr = re->result; rr; rr = rr->next) { - for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next) { - IMB_exr_close(rl->exrhandle); - rl->exrhandle = NULL; - } - - rr->do_exr_tile = false; - } - - /* Create new render result in memory instead of on disk. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_free_list(&re->fullresult, re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - BLI_rw_mutex_unlock(&re->resultmutex); - - for (RenderLayer *rl = re->result->layers.first; rl; rl = rl->next) { - /* Get passes needed by engine. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - for (RenderPass *pass = templates.first; pass; pass = pass->next) { - render_result_add_pass(re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - - BLI_freelistN(&templates); - - /* Render passes contents from file. */ - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; - render_result_exr_file_path(re->scene, rl->name, 0, str); - printf("read exr tmp file: %s\n", str); - - if (!render_result_exr_file_read_path(re->result, rl, str)) { - printf("cannot read: %s\n", str); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } + /* Close EXR files. */ + for (RenderResult *rr = re->result; rr; rr = rr->next) { + for (RenderLayer *rl = rr->layers.first; rl; rl = rl->next) { + IMB_exr_close(rl->exrhandle); + rl->exrhandle = NULL; + } + + rr->do_exr_tile = false; + } + + /* Create new render result in memory instead of on disk. */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_free_list(&re->fullresult, re->result); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + BLI_rw_mutex_unlock(&re->resultmutex); + + for (RenderLayer *rl = re->result->layers.first; rl; rl = rl->next) { + /* Get passes needed by engine. */ + ListBase templates; + render_result_get_pass_templates(engine, re, rl, &templates); + + /* Create render passes requested by engine. Only this part is + * mutex locked to avoid deadlock with Python GIL. */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + for (RenderPass *pass = templates.first; pass; pass = pass->next) { + render_result_add_pass( + re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); + } + + BLI_freelistN(&templates); + + /* Render passes contents from file. */ + char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; + render_result_exr_file_path(re->scene, rl->name, 0, str); + printf("read exr tmp file: %s\n", str); + + if (!render_result_exr_file_read_path(re->result, rl, str)) { + printf("cannot read: %s\n", str); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } } /* save part into exr file */ void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) { - for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) - save_render_result_tile(rr, rrpart, viewname); + for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) + save_render_result_tile(rr, rrpart, viewname); } /* path to temporary exr file */ void render_result_exr_file_path(Scene *scene, const char *layname, int sample, char *filepath) { - char name[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100]; - const char *fi = BLI_path_basename(BKE_main_blendfile_path_from_global()); + char name[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100]; + const char *fi = BLI_path_basename(BKE_main_blendfile_path_from_global()); - if (sample == 0) { - BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname); - } - else { - BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample); - } + if (sample == 0) { + BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname); + } + else { + BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample); + } - /* Make name safe for paths, see T43275. */ - BLI_filename_make_safe(name); + /* Make name safe for paths, see T43275. */ + BLI_filename_make_safe(name); - BLI_make_file_string("/", filepath, BKE_tempdir_session(), name); + BLI_make_file_string("/", filepath, BKE_tempdir_session(), name); } /* called for reading temp files, and for external engines */ -int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, const char *filepath) +int render_result_exr_file_read_path(RenderResult *rr, + RenderLayer *rl_single, + const char *filepath) { - RenderLayer *rl; - RenderPass *rpass; - void *exrhandle = IMB_exr_get_handle(); - int rectx, recty; - - if (IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty) == 0) { - printf("failed being read %s\n", filepath); - IMB_exr_close(exrhandle); - return 0; - } - - if (rr == NULL || rectx != rr->rectx || recty != rr->recty) { - if (rr) - printf("error in reading render result: dimensions don't match\n"); - else - printf("error in reading render result: NULL result pointer\n"); - IMB_exr_close(exrhandle); - return 0; - } - - for (rl = rr->layers.first; rl; rl = rl->next) { - if (rl_single && rl_single != rl) - continue; - - /* passes are allocated in sync */ - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - const int xstride = rpass->channels; - int a; - char fullname[EXR_PASS_MAXNAME]; - - for (a = 0; a < xstride; a++) { - set_pass_full_name(fullname, rpass->name, a, rpass->view, rpass->chan_id); - IMB_exr_set_channel(exrhandle, rl->name, fullname, - xstride, xstride * rectx, rpass->rect + a); - } - - set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); - } - } - - IMB_exr_read_channels(exrhandle); - IMB_exr_close(exrhandle); - - return 1; + RenderLayer *rl; + RenderPass *rpass; + void *exrhandle = IMB_exr_get_handle(); + int rectx, recty; + + if (IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty) == 0) { + printf("failed being read %s\n", filepath); + IMB_exr_close(exrhandle); + return 0; + } + + if (rr == NULL || rectx != rr->rectx || recty != rr->recty) { + if (rr) + printf("error in reading render result: dimensions don't match\n"); + else + printf("error in reading render result: NULL result pointer\n"); + IMB_exr_close(exrhandle); + return 0; + } + + for (rl = rr->layers.first; rl; rl = rl->next) { + if (rl_single && rl_single != rl) + continue; + + /* passes are allocated in sync */ + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + const int xstride = rpass->channels; + int a; + char fullname[EXR_PASS_MAXNAME]; + + for (a = 0; a < xstride; a++) { + set_pass_full_name(fullname, rpass->name, a, rpass->view, rpass->chan_id); + IMB_exr_set_channel( + exrhandle, rl->name, fullname, xstride, xstride * rectx, rpass->rect + a); + } + + set_pass_full_name(rpass->fullname, rpass->name, -1, rpass->view, rpass->chan_id); + } + } + + IMB_exr_read_channels(exrhandle); + IMB_exr_close(exrhandle); + + return 1; } static void render_result_exr_file_cache_path(Scene *sce, const char *root, char *r_path) { - char filename_full[FILE_MAX + MAX_ID_NAME + 100], filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; - char path_digest[16] = {0}; - char path_hexdigest[33]; - - /* If root is relative, use either current .blend file dir, or temp one if not saved. */ - const char *blendfile_path = BKE_main_blendfile_path_from_global(); - if (blendfile_path[0] != '\0') { - BLI_split_dirfile(blendfile_path, dirname, filename, sizeof(dirname), sizeof(filename)); - BLI_path_extension_replace(filename, sizeof(filename), ""); /* strip '.blend' */ - BLI_hash_md5_buffer(blendfile_path, strlen(blendfile_path), path_digest); - } - else { - BLI_strncpy(dirname, BKE_tempdir_base(), sizeof(dirname)); - BLI_strncpy(filename, "UNSAVED", sizeof(filename)); - } - BLI_hash_md5_to_hexdigest(path_digest, path_hexdigest); - - /* Default to *non-volatile* tmp dir. */ - if (*root == '\0') { - root = BKE_tempdir_base(); - } - - BLI_snprintf(filename_full, sizeof(filename_full), "cached_RR_%s_%s_%s.exr", - filename, sce->id.name + 2, path_hexdigest); - BLI_make_file_string(dirname, r_path, root, filename_full); + char filename_full[FILE_MAX + MAX_ID_NAME + 100], filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; + char path_digest[16] = {0}; + char path_hexdigest[33]; + + /* If root is relative, use either current .blend file dir, or temp one if not saved. */ + const char *blendfile_path = BKE_main_blendfile_path_from_global(); + if (blendfile_path[0] != '\0') { + BLI_split_dirfile(blendfile_path, dirname, filename, sizeof(dirname), sizeof(filename)); + BLI_path_extension_replace(filename, sizeof(filename), ""); /* strip '.blend' */ + BLI_hash_md5_buffer(blendfile_path, strlen(blendfile_path), path_digest); + } + else { + BLI_strncpy(dirname, BKE_tempdir_base(), sizeof(dirname)); + BLI_strncpy(filename, "UNSAVED", sizeof(filename)); + } + BLI_hash_md5_to_hexdigest(path_digest, path_hexdigest); + + /* Default to *non-volatile* tmp dir. */ + if (*root == '\0') { + root = BKE_tempdir_base(); + } + + BLI_snprintf(filename_full, + sizeof(filename_full), + "cached_RR_%s_%s_%s.exr", + filename, + sce->id.name + 2, + path_hexdigest); + BLI_make_file_string(dirname, r_path, root, filename_full); } void render_result_exr_file_cache_write(Render *re) { - RenderResult *rr = re->result; - char str[FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100]; - char *root = U.render_cachedir; + RenderResult *rr = re->result; + char str[FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100]; + char *root = U.render_cachedir; - render_result_exr_file_cache_path(re->scene, root, str); - printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); + render_result_exr_file_cache_path(re->scene, root, str); + printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); - RE_WriteRenderResult(NULL, rr, str, NULL, NULL, -1); + RE_WriteRenderResult(NULL, rr, str, NULL, NULL, -1); } /* For cache, makes exact copy of render result */ bool render_result_exr_file_cache_read(Render *re) { - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; - char *root = U.render_cachedir; + char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; + char *root = U.render_cachedir; - RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + RE_FreeRenderResult(re->result); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - /* First try cache. */ - render_result_exr_file_cache_path(re->scene, root, str); + /* First try cache. */ + render_result_exr_file_cache_path(re->scene, root, str); - printf("read exr cache file: %s\n", str); - if (!render_result_exr_file_read_path(re->result, NULL, str)) { - printf("cannot read: %s\n", str); - return false; - } - return true; + printf("read exr cache file: %s\n", str); + if (!render_result_exr_file_read_path(re->result, NULL, str)) { + printf("cannot read: %s\n", str); + return false; + } + return true; } /*************************** Combined Pixel Rect *****************************/ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int view_id) { - ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); - RenderView *rv = RE_RenderViewGetById(rr, view_id); - - /* if not exists, BKE_imbuf_write makes one */ - ibuf->rect = (unsigned int *)rv->rect32; - ibuf->rect_float = rv->rectf; - ibuf->zbuf_float = rv->rectz; - - /* float factor for random dither, imbuf takes care of it */ - ibuf->dither = rd->dither_intensity; - - /* prepare to gamma correct to sRGB color space - * note that sequence editor can generate 8bpc render buffers - */ - if (ibuf->rect) { - if (BKE_imtype_valid_depths(rd->im_format.imtype) & (R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) { - if (rd->im_format.depth == R_IMF_CHAN_DEPTH_8) { - /* Higher depth bits are supported but not needed for current file output. */ - ibuf->rect_float = NULL; - } - else { - IMB_float_from_rect(ibuf); - } - } - else { - /* ensure no float buffer remained from previous frame */ - ibuf->rect_float = NULL; - } - } - - /* color -> grayscale */ - /* editing directly would alter the render view */ - if (rd->im_format.planes == R_IMF_PLANES_BW) { - ImBuf *ibuf_bw = IMB_dupImBuf(ibuf); - IMB_color_to_bw(ibuf_bw); - IMB_freeImBuf(ibuf); - ibuf = ibuf_bw; - } - - return ibuf; + ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); + RenderView *rv = RE_RenderViewGetById(rr, view_id); + + /* if not exists, BKE_imbuf_write makes one */ + ibuf->rect = (unsigned int *)rv->rect32; + ibuf->rect_float = rv->rectf; + ibuf->zbuf_float = rv->rectz; + + /* float factor for random dither, imbuf takes care of it */ + ibuf->dither = rd->dither_intensity; + + /* prepare to gamma correct to sRGB color space + * note that sequence editor can generate 8bpc render buffers + */ + if (ibuf->rect) { + if (BKE_imtype_valid_depths(rd->im_format.imtype) & + (R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) { + if (rd->im_format.depth == R_IMF_CHAN_DEPTH_8) { + /* Higher depth bits are supported but not needed for current file output. */ + ibuf->rect_float = NULL; + } + else { + IMB_float_from_rect(ibuf); + } + } + else { + /* ensure no float buffer remained from previous frame */ + ibuf->rect_float = NULL; + } + } + + /* color -> grayscale */ + /* editing directly would alter the render view */ + if (rd->im_format.planes == R_IMF_PLANES_BW) { + ImBuf *ibuf_bw = IMB_dupImBuf(ibuf); + IMB_color_to_bw(ibuf_bw); + IMB_freeImBuf(ibuf); + ibuf = ibuf_bw; + } + + return ibuf; } -void RE_render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf, const int view_id) +void RE_render_result_rect_from_ibuf(RenderResult *rr, + RenderData *UNUSED(rd), + ImBuf *ibuf, + const int view_id) { - RenderView *rv = RE_RenderViewGetById(rr, view_id); + RenderView *rv = RE_RenderViewGetById(rr, view_id); - if (ibuf->rect_float) { - rr->have_combined = true; + if (ibuf->rect_float) { + rr->have_combined = true; - if (!rv->rectf) - rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); + if (!rv->rectf) + rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); - memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); + memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); - /* TSK! Since sequence render doesn't free the *rr render result, the old rect32 - * can hang around when sequence render has rendered a 32 bits one before */ - MEM_SAFE_FREE(rv->rect32); - } - else if (ibuf->rect) { - rr->have_combined = true; + /* TSK! Since sequence render doesn't free the *rr render result, the old rect32 + * can hang around when sequence render has rendered a 32 bits one before */ + MEM_SAFE_FREE(rv->rect32); + } + else if (ibuf->rect) { + rr->have_combined = true; - if (!rv->rect32) - rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + if (!rv->rect32) + rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); - memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); + memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); - /* Same things as above, old rectf can hang around from previous render. */ - MEM_SAFE_FREE(rv->rectf); - } + /* Same things as above, old rectf can hang around from previous render. */ + MEM_SAFE_FREE(rv->rectf); + } } void render_result_rect_fill_zero(RenderResult *rr, const int view_id) { - RenderView *rv = RE_RenderViewGetById(rr, view_id); - - if (rv->rectf) - memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); - else if (rv->rect32) - memset(rv->rect32, 0, 4 * rr->rectx * rr->recty); - else - rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + RenderView *rv = RE_RenderViewGetById(rr, view_id); + + if (rv->rectf) + memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); + else if (rv->rect32) + memset(rv->rect32, 0, 4 * rr->rectx * rr->recty); + else + rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); } -void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rectx, int recty, - const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, +void render_result_rect_get_pixels(RenderResult *rr, + unsigned int *rect, + int rectx, + int recty, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, const int view_id) { - RenderView *rv = RE_RenderViewGetById(rr, view_id); - - if (rv->rect32) - memcpy(rect, rv->rect32, sizeof(int) * rr->rectx * rr->recty); - else if (rv->rectf) - IMB_display_buffer_transform_apply((unsigned char *)rect, rv->rectf, rr->rectx, rr->recty, 4, - view_settings, display_settings, true); - else - /* else fill with black */ - memset(rect, 0, sizeof(int) * rectx * recty); + RenderView *rv = RE_RenderViewGetById(rr, view_id); + + if (rv->rect32) + memcpy(rect, rv->rect32, sizeof(int) * rr->rectx * rr->recty); + else if (rv->rectf) + IMB_display_buffer_transform_apply((unsigned char *)rect, + rv->rectf, + rr->rectx, + rr->recty, + 4, + view_settings, + display_settings, + true); + else + /* else fill with black */ + memset(rect, 0, sizeof(int) * rectx * recty); } - /*************************** multiview functions *****************************/ bool RE_HasCombinedLayer(RenderResult *res) { - RenderView *rv; + RenderView *rv; - if (res == NULL) - return false; + if (res == NULL) + return false; - rv = res->views.first; - if (rv == NULL) - return false; + rv = res->views.first; + if (rv == NULL) + return false; - return (rv->rect32 || rv->rectf); + return (rv->rect32 || rv->rectf); } bool RE_HasFloatPixels(RenderResult *res) { - RenderView *rview; + RenderView *rview; - for (rview = res->views.first; rview; rview = rview->next) { - if (rview->rect32 && !rview->rectf) { - return false; - } - } + for (rview = res->views.first; rview; rview = rview->next) { + if (rview->rect32 && !rview->rectf) { + return false; + } + } - return true; + return true; } bool RE_RenderResult_is_stereo(RenderResult *res) { - if (!BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) { - return false; - } + if (!BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) { + return false; + } - if (!BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) { - return false; - } + if (!BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) { + return false; + } - return true; + return true; } RenderView *RE_RenderViewGetById(RenderResult *res, const int view_id) { - RenderView *rv = BLI_findlink(&res->views, view_id); - BLI_assert(res->views.first); - return rv ? rv : res->views.first; + RenderView *rv = BLI_findlink(&res->views, view_id); + BLI_assert(res->views.first); + return rv ? rv : res->views.first; } RenderView *RE_RenderViewGetByName(RenderResult *res, const char *viewname) { - RenderView *rv = BLI_findstring(&res->views, viewname, offsetof(RenderView, name)); - BLI_assert(res->views.first); - return rv ? rv : res->views.first; + RenderView *rv = BLI_findstring(&res->views, viewname, offsetof(RenderView, name)); + BLI_assert(res->views.first); + return rv ? rv : res->views.first; } static RenderPass *duplicate_render_pass(RenderPass *rpass) { - RenderPass *new_rpass = MEM_mallocN(sizeof(RenderPass), "new render pass"); - *new_rpass = *rpass; - new_rpass->next = new_rpass->prev = NULL; - if (new_rpass->rect != NULL) { - new_rpass->rect = MEM_dupallocN(new_rpass->rect); - } - return new_rpass; + RenderPass *new_rpass = MEM_mallocN(sizeof(RenderPass), "new render pass"); + *new_rpass = *rpass; + new_rpass->next = new_rpass->prev = NULL; + if (new_rpass->rect != NULL) { + new_rpass->rect = MEM_dupallocN(new_rpass->rect); + } + return new_rpass; } static RenderLayer *duplicate_render_layer(RenderLayer *rl) { - RenderLayer *new_rl = MEM_mallocN(sizeof(RenderLayer), "new render layer"); - *new_rl = *rl; - new_rl->next = new_rl->prev = NULL; - new_rl->passes.first = new_rl->passes.last = NULL; - new_rl->exrhandle = NULL; - if (new_rl->acolrect != NULL) { - new_rl->acolrect = MEM_dupallocN(new_rl->acolrect); - } - if (new_rl->scolrect != NULL) { - new_rl->scolrect = MEM_dupallocN(new_rl->scolrect); - } - if (new_rl->display_buffer != NULL) { - new_rl->display_buffer = MEM_dupallocN(new_rl->display_buffer); - } - for (RenderPass *rpass = rl->passes.first; rpass != NULL; rpass = rpass->next) { - RenderPass *new_rpass = duplicate_render_pass(rpass); - BLI_addtail(&new_rl->passes, new_rpass); - } - return new_rl; + RenderLayer *new_rl = MEM_mallocN(sizeof(RenderLayer), "new render layer"); + *new_rl = *rl; + new_rl->next = new_rl->prev = NULL; + new_rl->passes.first = new_rl->passes.last = NULL; + new_rl->exrhandle = NULL; + if (new_rl->acolrect != NULL) { + new_rl->acolrect = MEM_dupallocN(new_rl->acolrect); + } + if (new_rl->scolrect != NULL) { + new_rl->scolrect = MEM_dupallocN(new_rl->scolrect); + } + if (new_rl->display_buffer != NULL) { + new_rl->display_buffer = MEM_dupallocN(new_rl->display_buffer); + } + for (RenderPass *rpass = rl->passes.first; rpass != NULL; rpass = rpass->next) { + RenderPass *new_rpass = duplicate_render_pass(rpass); + BLI_addtail(&new_rl->passes, new_rpass); + } + return new_rl; } static RenderView *duplicate_render_view(RenderView *rview) { - RenderView *new_rview = MEM_mallocN(sizeof(RenderView), "new render view"); - *new_rview = *rview; - if (new_rview->rectf != NULL) { - new_rview->rectf = MEM_dupallocN(new_rview->rectf); - } - if (new_rview->rectz != NULL) { - new_rview->rectz = MEM_dupallocN(new_rview->rectz); - } - if (new_rview->rect32 != NULL) { - new_rview->rect32 = MEM_dupallocN(new_rview->rect32); - } - return new_rview; + RenderView *new_rview = MEM_mallocN(sizeof(RenderView), "new render view"); + *new_rview = *rview; + if (new_rview->rectf != NULL) { + new_rview->rectf = MEM_dupallocN(new_rview->rectf); + } + if (new_rview->rectz != NULL) { + new_rview->rectz = MEM_dupallocN(new_rview->rectz); + } + if (new_rview->rect32 != NULL) { + new_rview->rect32 = MEM_dupallocN(new_rview->rect32); + } + return new_rview; } RenderResult *RE_DuplicateRenderResult(RenderResult *rr) { - RenderResult *new_rr = MEM_mallocN(sizeof(RenderResult), "new duplicated render result"); - *new_rr = *rr; - new_rr->next = new_rr->prev = NULL; - new_rr->layers.first = new_rr->layers.last = NULL; - new_rr->views.first = new_rr->views.last = NULL; - for (RenderLayer *rl = rr->layers.first; rl != NULL; rl = rl->next) { - RenderLayer *new_rl = duplicate_render_layer(rl); - BLI_addtail(&new_rr->layers, new_rl); - } - for (RenderView *rview = rr->views.first; rview != NULL; rview = rview->next) { - RenderView *new_rview = duplicate_render_view(rview); - BLI_addtail(&new_rr->views, new_rview); - } - if (new_rr->rect32 != NULL) { - new_rr->rect32 = MEM_dupallocN(new_rr->rect32); - } - if (new_rr->rectf != NULL) { - new_rr->rectf = MEM_dupallocN(new_rr->rectf); - } - if (new_rr->rectz != NULL) { - new_rr->rectz = MEM_dupallocN(new_rr->rectz); - } - new_rr->stamp_data = BKE_stamp_data_copy(new_rr->stamp_data); - return new_rr; + RenderResult *new_rr = MEM_mallocN(sizeof(RenderResult), "new duplicated render result"); + *new_rr = *rr; + new_rr->next = new_rr->prev = NULL; + new_rr->layers.first = new_rr->layers.last = NULL; + new_rr->views.first = new_rr->views.last = NULL; + for (RenderLayer *rl = rr->layers.first; rl != NULL; rl = rl->next) { + RenderLayer *new_rl = duplicate_render_layer(rl); + BLI_addtail(&new_rr->layers, new_rl); + } + for (RenderView *rview = rr->views.first; rview != NULL; rview = rview->next) { + RenderView *new_rview = duplicate_render_view(rview); + BLI_addtail(&new_rr->views, new_rview); + } + if (new_rr->rect32 != NULL) { + new_rr->rect32 = MEM_dupallocN(new_rr->rect32); + } + if (new_rr->rectf != NULL) { + new_rr->rectf = MEM_dupallocN(new_rr->rectf); + } + if (new_rr->rectz != NULL) { + new_rr->rectz = MEM_dupallocN(new_rr->rectz); + } + new_rr->stamp_data = BKE_stamp_data_copy(new_rr->stamp_data); + return new_rr; } diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index 7678e69caad..0400ad7ee95 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -21,7 +21,6 @@ * \ingroup render */ - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -64,96 +63,97 @@ static RNG_THREAD_ARRAY *random_tex_array; - void RE_texture_rng_init(void) { - random_tex_array = BLI_rng_threaded_new(); + random_tex_array = BLI_rng_threaded_new(); } void RE_texture_rng_exit(void) { - BLI_rng_threaded_free(random_tex_array); + BLI_rng_threaded_free(random_tex_array); } - /* ------------------------------------------------------------------------- */ - /* this allows colorbanded textures to control normals as well */ static void tex_normal_derivate(Tex *tex, TexResult *texres) { - if (tex->flag & TEX_COLORBAND) { - float col[4]; - if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) { - float fac0, fac1, fac2, fac3; - - fac0 = (col[0] + col[1] + col[2]); - BKE_colorband_evaluate(tex->coba, texres->nor[0], col); - fac1 = (col[0] + col[1] + col[2]); - BKE_colorband_evaluate(tex->coba, texres->nor[1], col); - fac2 = (col[0] + col[1] + col[2]); - BKE_colorband_evaluate(tex->coba, texres->nor[2], col); - fac3 = (col[0] + col[1] + col[2]); - - texres->nor[0] = (fac0 - fac1) / 3.0f; - texres->nor[1] = (fac0 - fac2) / 3.0f; - texres->nor[2] = (fac0 - fac3) / 3.0f; - - return; - } - } - texres->nor[0] = texres->tin - texres->nor[0]; - texres->nor[1] = texres->tin - texres->nor[1]; - texres->nor[2] = texres->tin - texres->nor[2]; + if (tex->flag & TEX_COLORBAND) { + float col[4]; + if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) { + float fac0, fac1, fac2, fac3; + + fac0 = (col[0] + col[1] + col[2]); + BKE_colorband_evaluate(tex->coba, texres->nor[0], col); + fac1 = (col[0] + col[1] + col[2]); + BKE_colorband_evaluate(tex->coba, texres->nor[1], col); + fac2 = (col[0] + col[1] + col[2]); + BKE_colorband_evaluate(tex->coba, texres->nor[2], col); + fac3 = (col[0] + col[1] + col[2]); + + texres->nor[0] = (fac0 - fac1) / 3.0f; + texres->nor[1] = (fac0 - fac2) / 3.0f; + texres->nor[2] = (fac0 - fac3) / 3.0f; + + return; + } + } + texres->nor[0] = texres->tin - texres->nor[0]; + texres->nor[1] = texres->tin - texres->nor[1]; + texres->nor[2] = texres->tin - texres->nor[2]; } - - static int blend(Tex *tex, const float texvec[3], TexResult *texres) { - float x, y, t; - - if (tex->flag & TEX_FLIPBLEND) { - x = texvec[1]; - y = texvec[0]; - } - else { - x = texvec[0]; - y = texvec[1]; - } - - if (tex->stype == TEX_LIN) { /* lin */ - texres->tin = (1.0f + x) / 2.0f; - } - else if (tex->stype == TEX_QUAD) { /* quad */ - texres->tin = (1.0f + x) / 2.0f; - if (texres->tin < 0.0f) texres->tin = 0.0f; - else texres->tin *= texres->tin; - } - else if (tex->stype == TEX_EASE) { /* ease */ - texres->tin = (1.0f + x) / 2.0f; - if (texres->tin <= 0.0f) texres->tin = 0.0f; - else if (texres->tin >= 1.0f) texres->tin = 1.0f; - else { - t = texres->tin * texres->tin; - texres->tin = (3.0f * t - 2.0f * t * texres->tin); - } - } - else if (tex->stype == TEX_DIAG) { /* diag */ - texres->tin = (2.0f + x + y) / 4.0f; - } - else if (tex->stype == TEX_RAD) { /* radial */ - texres->tin = (atan2f(y, x) / (float)(2 * M_PI) + 0.5f); - } - else { /* sphere TEX_SPHERE */ - texres->tin = 1.0f - sqrtf(x * x + y * y + texvec[2] * texvec[2]); - if (texres->tin < 0.0f) texres->tin = 0.0f; - if (tex->stype == TEX_HALO) texres->tin *= texres->tin; /* halo */ - } - - BRICONT; - - return TEX_INT; + float x, y, t; + + if (tex->flag & TEX_FLIPBLEND) { + x = texvec[1]; + y = texvec[0]; + } + else { + x = texvec[0]; + y = texvec[1]; + } + + if (tex->stype == TEX_LIN) { /* lin */ + texres->tin = (1.0f + x) / 2.0f; + } + else if (tex->stype == TEX_QUAD) { /* quad */ + texres->tin = (1.0f + x) / 2.0f; + if (texres->tin < 0.0f) + texres->tin = 0.0f; + else + texres->tin *= texres->tin; + } + else if (tex->stype == TEX_EASE) { /* ease */ + texres->tin = (1.0f + x) / 2.0f; + if (texres->tin <= 0.0f) + texres->tin = 0.0f; + else if (texres->tin >= 1.0f) + texres->tin = 1.0f; + else { + t = texres->tin * texres->tin; + texres->tin = (3.0f * t - 2.0f * t * texres->tin); + } + } + else if (tex->stype == TEX_DIAG) { /* diag */ + texres->tin = (2.0f + x + y) / 4.0f; + } + else if (tex->stype == TEX_RAD) { /* radial */ + texres->tin = (atan2f(y, x) / (float)(2 * M_PI) + 0.5f); + } + else { /* sphere TEX_SPHERE */ + texres->tin = 1.0f - sqrtf(x * x + y * y + texvec[2] * texvec[2]); + if (texres->tin < 0.0f) + texres->tin = 0.0f; + if (tex->stype == TEX_HALO) + texres->tin *= texres->tin; /* halo */ + } + + BRICONT; + + return TEX_INT; } /* ------------------------------------------------------------------------- */ @@ -163,245 +163,293 @@ static int blend(Tex *tex, const float texvec[3], TexResult *texres) static int clouds(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - - texres->tin = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1], texvec[2], tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - - if (texres->nor != NULL) { - /* calculate bumpnormal */ - texres->nor[0] = BLI_gTurbulence(tex->noisesize, texvec[0] + tex->nabla, texvec[1], texvec[2], tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - texres->nor[1] = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1] + tex->nabla, texvec[2], tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - texres->nor[2] = BLI_gTurbulence(tex->noisesize, texvec[0], texvec[1], texvec[2] + tex->nabla, tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - if (tex->stype == TEX_COLOR) { - /* in this case, int. value should really be computed from color, - * and bumpnormal from that, would be too slow, looks ok as is */ - texres->tr = texres->tin; - texres->tg = BLI_gTurbulence(tex->noisesize, texvec[1], texvec[0], texvec[2], tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - texres->tb = BLI_gTurbulence(tex->noisesize, texvec[1], texvec[2], texvec[0], tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - BRICONTRGB; - texres->ta = 1.0; - return (rv | TEX_RGB); - } - - BRICONT; - - return rv; - + int rv = TEX_INT; + + texres->tin = BLI_gTurbulence(tex->noisesize, + texvec[0], + texvec[1], + texvec[2], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + + if (texres->nor != NULL) { + /* calculate bumpnormal */ + texres->nor[0] = BLI_gTurbulence(tex->noisesize, + texvec[0] + tex->nabla, + texvec[1], + texvec[2], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + texres->nor[1] = BLI_gTurbulence(tex->noisesize, + texvec[0], + texvec[1] + tex->nabla, + texvec[2], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + texres->nor[2] = BLI_gTurbulence(tex->noisesize, + texvec[0], + texvec[1], + texvec[2] + tex->nabla, + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + if (tex->stype == TEX_COLOR) { + /* in this case, int. value should really be computed from color, + * and bumpnormal from that, would be too slow, looks ok as is */ + texres->tr = texres->tin; + texres->tg = BLI_gTurbulence(tex->noisesize, + texvec[1], + texvec[0], + texvec[2], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + texres->tb = BLI_gTurbulence(tex->noisesize, + texvec[1], + texvec[2], + texvec[0], + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + BRICONTRGB; + texres->ta = 1.0; + return (rv | TEX_RGB); + } + + BRICONT; + + return rv; } /* creates a sine wave */ static float tex_sin(float a) { - a = 0.5f + 0.5f * sinf(a); + a = 0.5f + 0.5f * sinf(a); - return a; + return a; } /* creates a saw wave */ static float tex_saw(float a) { - const float b = 2 * M_PI; + const float b = 2 * M_PI; - int n = (int)(a / b); - a -= n * b; - if (a < 0) a += b; - return a / b; + int n = (int)(a / b); + a -= n * b; + if (a < 0) + a += b; + return a / b; } /* creates a triangle wave */ static float tex_tri(float a) { - const float b = 2 * M_PI; - const float rmax = 1.0; + const float b = 2 * M_PI; + const float rmax = 1.0; - a = rmax - 2.0f * fabsf(floorf((a * (1.0f / b)) + 0.5f) - (a * (1.0f / b))); + a = rmax - 2.0f * fabsf(floorf((a * (1.0f / b)) + 0.5f) - (a * (1.0f / b))); - return a; + return a; } /* computes basic wood intensity value at x,y,z */ static float wood_int(Tex *tex, float x, float y, float z) { - float wi = 0; - short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ - short wt = tex->stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */ - - float (*waveform[3])(float); /* create array of pointers to waveform functions */ - waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */ - waveform[1] = tex_saw; - waveform[2] = tex_tri; - - if ((wf > TEX_TRI) || (wf < TEX_SIN)) wf = 0; /* check to be sure noisebasis2 is initialized ahead of time */ - - if (wt == TEX_BAND) { - wi = waveform[wf]((x + y + z) * 10.0f); - } - else if (wt == TEX_RING) { - wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f); - } - else if (wt == TEX_BANDNOISE) { - wi = tex->turbul * BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - wi = waveform[wf]((x + y + z) * 10.0f + wi); - } - else if (wt == TEX_RINGNOISE) { - wi = tex->turbul * BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f + wi); - } - - return wi; + float wi = 0; + short wf = + tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ + short wt = + tex->stype; /* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */ + + float (*waveform[3])(float); /* create array of pointers to waveform functions */ + waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */ + waveform[1] = tex_saw; + waveform[2] = tex_tri; + + if ((wf > TEX_TRI) || (wf < TEX_SIN)) + wf = 0; /* check to be sure noisebasis2 is initialized ahead of time */ + + if (wt == TEX_BAND) { + wi = waveform[wf]((x + y + z) * 10.0f); + } + else if (wt == TEX_RING) { + wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f); + } + else if (wt == TEX_BANDNOISE) { + wi = tex->turbul * + BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); + wi = waveform[wf]((x + y + z) * 10.0f + wi); + } + else if (wt == TEX_RINGNOISE) { + wi = tex->turbul * + BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); + wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f + wi); + } + + return wi; } static int wood(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; + int rv = TEX_INT; - texres->tin = wood_int(tex, texvec[0], texvec[1], texvec[2]); - if (texres->nor != NULL) { - /* calculate bumpnormal */ - texres->nor[0] = wood_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]); - texres->nor[1] = wood_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]); - texres->nor[2] = wood_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla); + texres->tin = wood_int(tex, texvec[0], texvec[1], texvec[2]); + if (texres->nor != NULL) { + /* calculate bumpnormal */ + texres->nor[0] = wood_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]); + texres->nor[1] = wood_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]); + texres->nor[2] = wood_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla); - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } - BRICONT; + BRICONT; - return rv; + return rv; } /* computes basic marble intensity at x,y,z */ static float marble_int(Tex *tex, float x, float y, float z) { - float n, mi; - short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ - short mt = tex->stype; /* marble type: TEX_SOFT=0, TEX_SHARP=1, TEX_SHAPER=2 */ - - float (*waveform[3])(float); /* create array of pointers to waveform functions */ - waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */ - waveform[1] = tex_saw; - waveform[2] = tex_tri; - - if ((wf > TEX_TRI) || (wf < TEX_SIN)) wf = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */ - - n = 5.0f * (x + y + z); - - mi = n + tex->turbul * BLI_gTurbulence(tex->noisesize, x, y, z, tex->noisedepth, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - - if (mt >= TEX_SOFT) { /* TEX_SOFT always true */ - mi = waveform[wf](mi); - if (mt == TEX_SHARP) { - mi = sqrtf(mi); - } - else if (mt == TEX_SHARPER) { - mi = sqrtf(sqrtf(mi)); - } - } - - return mi; + float n, mi; + short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ + short mt = tex->stype; /* marble type: TEX_SOFT=0, TEX_SHARP=1, TEX_SHAPER=2 */ + + float (*waveform[3])(float); /* create array of pointers to waveform functions */ + waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */ + waveform[1] = tex_saw; + waveform[2] = tex_tri; + + if ((wf > TEX_TRI) || (wf < TEX_SIN)) + wf = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */ + + n = 5.0f * (x + y + z); + + mi = n + tex->turbul * BLI_gTurbulence(tex->noisesize, + x, + y, + z, + tex->noisedepth, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + + if (mt >= TEX_SOFT) { /* TEX_SOFT always true */ + mi = waveform[wf](mi); + if (mt == TEX_SHARP) { + mi = sqrtf(mi); + } + else if (mt == TEX_SHARPER) { + mi = sqrtf(sqrtf(mi)); + } + } + + return mi; } static int marble(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; + int rv = TEX_INT; - texres->tin = marble_int(tex, texvec[0], texvec[1], texvec[2]); + texres->tin = marble_int(tex, texvec[0], texvec[1], texvec[2]); - if (texres->nor != NULL) { - /* calculate bumpnormal */ - texres->nor[0] = marble_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]); - texres->nor[1] = marble_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]); - texres->nor[2] = marble_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla); + if (texres->nor != NULL) { + /* calculate bumpnormal */ + texres->nor[0] = marble_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]); + texres->nor[1] = marble_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]); + texres->nor[2] = marble_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla); - tex_normal_derivate(tex, texres); + tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } + rv |= TEX_NOR; + } - BRICONT; + BRICONT; - return rv; + return rv; } /* ------------------------------------------------------------------------- */ static int magic(Tex *tex, const float texvec[3], TexResult *texres) { - float x, y, z, turb; - int n; - - n = tex->noisedepth; - turb = tex->turbul / 5.0f; - - x = sinf((texvec[0] + texvec[1] + texvec[2]) * 5.0f); - y = cosf((-texvec[0] + texvec[1] - texvec[2]) * 5.0f); - z = -cosf((-texvec[0] - texvec[1] + texvec[2]) * 5.0f); - if (n > 0) { - x *= turb; - y *= turb; - z *= turb; - y = -cosf(x - y + z); - y *= turb; - if (n > 1) { - x = cosf(x - y - z); - x *= turb; - if (n > 2) { - z = sinf(-x - y - z); - z *= turb; - if (n > 3) { - x = -cosf(-x + y - z); - x *= turb; - if (n > 4) { - y = -sinf(-x + y + z); - y *= turb; - if (n > 5) { - y = -cosf(-x + y + z); - y *= turb; - if (n > 6) { - x = cosf(x + y + z); - x *= turb; - if (n > 7) { - z = sinf(x + y - z); - z *= turb; - if (n > 8) { - x = -cosf(-x - y + z); - x *= turb; - if (n > 9) { - y = -sinf(x - y + z); - y *= turb; - } - } - } - } - } - } - } - } - } - } - - if (turb != 0.0f) { - turb *= 2.0f; - x /= turb; - y /= turb; - z /= turb; - } - texres->tr = 0.5f - x; - texres->tg = 0.5f - y; - texres->tb = 0.5f - z; - - texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb); - - BRICONTRGB; - texres->ta = 1.0f; - - return TEX_RGB; + float x, y, z, turb; + int n; + + n = tex->noisedepth; + turb = tex->turbul / 5.0f; + + x = sinf((texvec[0] + texvec[1] + texvec[2]) * 5.0f); + y = cosf((-texvec[0] + texvec[1] - texvec[2]) * 5.0f); + z = -cosf((-texvec[0] - texvec[1] + texvec[2]) * 5.0f); + if (n > 0) { + x *= turb; + y *= turb; + z *= turb; + y = -cosf(x - y + z); + y *= turb; + if (n > 1) { + x = cosf(x - y - z); + x *= turb; + if (n > 2) { + z = sinf(-x - y - z); + z *= turb; + if (n > 3) { + x = -cosf(-x + y - z); + x *= turb; + if (n > 4) { + y = -sinf(-x + y + z); + y *= turb; + if (n > 5) { + y = -cosf(-x + y + z); + y *= turb; + if (n > 6) { + x = cosf(x + y + z); + x *= turb; + if (n > 7) { + z = sinf(x + y - z); + z *= turb; + if (n > 8) { + x = -cosf(-x - y + z); + x *= turb; + if (n > 9) { + y = -sinf(x - y + z); + y *= turb; + } + } + } + } + } + } + } + } + } + } + + if (turb != 0.0f) { + turb *= 2.0f; + x /= turb; + y /= turb; + z /= turb; + } + texres->tr = 0.5f - x; + texres->tg = 0.5f - y; + texres->tb = 0.5f - z; + + texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb); + + BRICONTRGB; + texres->ta = 1.0f; + + return TEX_RGB; } /* ------------------------------------------------------------------------- */ @@ -409,41 +457,62 @@ static int magic(Tex *tex, const float texvec[3], TexResult *texres) /* newnoise: stucci also modified to use different noisebasis */ static int stucci(Tex *tex, const float texvec[3], TexResult *texres) { - float nor[3], b2, ofs; - int retval = TEX_INT; - - b2 = BLI_gNoise(tex->noisesize, texvec[0], texvec[1], texvec[2], (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - - ofs = tex->turbul / 200.0f; - - if (tex->stype) ofs *= (b2 * b2); - nor[0] = BLI_gNoise(tex->noisesize, texvec[0] + ofs, texvec[1], texvec[2], (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - nor[1] = BLI_gNoise(tex->noisesize, texvec[0], texvec[1] + ofs, texvec[2], (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - nor[2] = BLI_gNoise(tex->noisesize, texvec[0], texvec[1], texvec[2] + ofs, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis); - - texres->tin = nor[2]; - - if (texres->nor) { - - copy_v3_v3(texres->nor, nor); - tex_normal_derivate(tex, texres); - - if (tex->stype == TEX_WALLOUT) { - texres->nor[0] = -texres->nor[0]; - texres->nor[1] = -texres->nor[1]; - texres->nor[2] = -texres->nor[2]; - } - - retval |= TEX_NOR; - } - - if (tex->stype == TEX_WALLOUT) - texres->tin = 1.0f - texres->tin; - - if (texres->tin < 0.0f) - texres->tin = 0.0f; - - return retval; + float nor[3], b2, ofs; + int retval = TEX_INT; + + b2 = BLI_gNoise(tex->noisesize, + texvec[0], + texvec[1], + texvec[2], + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + + ofs = tex->turbul / 200.0f; + + if (tex->stype) + ofs *= (b2 * b2); + nor[0] = BLI_gNoise(tex->noisesize, + texvec[0] + ofs, + texvec[1], + texvec[2], + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + nor[1] = BLI_gNoise(tex->noisesize, + texvec[0], + texvec[1] + ofs, + texvec[2], + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + nor[2] = BLI_gNoise(tex->noisesize, + texvec[0], + texvec[1], + texvec[2] + ofs, + (tex->noisetype != TEX_NOISESOFT), + tex->noisebasis); + + texres->tin = nor[2]; + + if (texres->nor) { + + copy_v3_v3(texres->nor, nor); + tex_normal_derivate(tex, texres); + + if (tex->stype == TEX_WALLOUT) { + texres->nor[0] = -texres->nor[0]; + texres->nor[1] = -texres->nor[1]; + texres->nor[2] = -texres->nor[2]; + } + + retval |= TEX_NOR; + } + + if (tex->stype == TEX_WALLOUT) + texres->tin = 1.0f - texres->tin; + + if (texres->tin < 0.0f) + texres->tin = 0.0f; + + return retval; } /* ------------------------------------------------------------------------- */ @@ -451,253 +520,355 @@ static int stucci(Tex *tex, const float texvec[3], TexResult *texres) static float mg_mFractalOrfBmTex(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - float (*mgravefunc)(float, float, float, float, float, float, int); - - if (tex->stype == TEX_MFRACTAL) - mgravefunc = mg_MultiFractal; - else - mgravefunc = mg_fBm; - - texres->tin = tex->ns_outscale * mgravefunc(texvec[0], texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis); - - if (texres->nor != NULL) { - float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ - - /* calculate bumpnormal */ - texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis); - texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis); - texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->noisebasis); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - BRICONT; - - return rv; - + int rv = TEX_INT; + float (*mgravefunc)(float, float, float, float, float, float, int); + + if (tex->stype == TEX_MFRACTAL) + mgravefunc = mg_MultiFractal; + else + mgravefunc = mg_fBm; + + texres->tin = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->noisebasis); + + if (texres->nor != NULL) { + float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ + + /* calculate bumpnormal */ + texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs, + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->noisebasis); + texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1] + offs, + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->noisebasis); + texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1], + texvec[2] + offs, + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->noisebasis); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + BRICONT; + + return rv; } static float mg_ridgedOrHybridMFTex(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - float (*mgravefunc)(float, float, float, float, float, float, float, float, int); - - if (tex->stype == TEX_RIDGEDMF) - mgravefunc = mg_RidgedMultiFractal; - else - mgravefunc = mg_HybridMultiFractal; - - texres->tin = tex->ns_outscale * mgravefunc(texvec[0], texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis); - - if (texres->nor != NULL) { - float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ - - /* calculate bumpnormal */ - texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis); - texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis); - texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->mg_gain, tex->noisebasis); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - BRICONT; - - return rv; - + int rv = TEX_INT; + float (*mgravefunc)(float, float, float, float, float, float, float, float, int); + + if (tex->stype == TEX_RIDGEDMF) + mgravefunc = mg_RidgedMultiFractal; + else + mgravefunc = mg_HybridMultiFractal; + + texres->tin = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->mg_gain, + tex->noisebasis); + + if (texres->nor != NULL) { + float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ + + /* calculate bumpnormal */ + texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs, + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->mg_gain, + tex->noisebasis); + texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1] + offs, + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->mg_gain, + tex->noisebasis); + texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0], + texvec[1], + texvec[2] + offs, + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->mg_gain, + tex->noisebasis); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + BRICONT; + + return rv; } - static float mg_HTerrainTex(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - - texres->tin = tex->ns_outscale * mg_HeteroTerrain(texvec[0], texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis); - - if (texres->nor != NULL) { - float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ - - /* calculate bumpnormal */ - texres->nor[0] = tex->ns_outscale * mg_HeteroTerrain(texvec[0] + offs, texvec[1], texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis); - texres->nor[1] = tex->ns_outscale * mg_HeteroTerrain(texvec[0], texvec[1] + offs, texvec[2], tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis); - texres->nor[2] = tex->ns_outscale * mg_HeteroTerrain(texvec[0], texvec[1], texvec[2] + offs, tex->mg_H, tex->mg_lacunarity, tex->mg_octaves, tex->mg_offset, tex->noisebasis); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - BRICONT; - - return rv; - + int rv = TEX_INT; + + texres->tin = tex->ns_outscale * mg_HeteroTerrain(texvec[0], + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->noisebasis); + + if (texres->nor != NULL) { + float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ + + /* calculate bumpnormal */ + texres->nor[0] = tex->ns_outscale * mg_HeteroTerrain(texvec[0] + offs, + texvec[1], + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->noisebasis); + texres->nor[1] = tex->ns_outscale * mg_HeteroTerrain(texvec[0], + texvec[1] + offs, + texvec[2], + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->noisebasis); + texres->nor[2] = tex->ns_outscale * mg_HeteroTerrain(texvec[0], + texvec[1], + texvec[2] + offs, + tex->mg_H, + tex->mg_lacunarity, + tex->mg_octaves, + tex->mg_offset, + tex->noisebasis); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + BRICONT; + + return rv; } - static float mg_distNoiseTex(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - - texres->tin = mg_VLNoise(texvec[0], texvec[1], texvec[2], tex->dist_amount, tex->noisebasis, tex->noisebasis2); - - if (texres->nor != NULL) { - float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ - - /* calculate bumpnormal */ - texres->nor[0] = mg_VLNoise(texvec[0] + offs, texvec[1], texvec[2], tex->dist_amount, tex->noisebasis, tex->noisebasis2); - texres->nor[1] = mg_VLNoise(texvec[0], texvec[1] + offs, texvec[2], tex->dist_amount, tex->noisebasis, tex->noisebasis2); - texres->nor[2] = mg_VLNoise(texvec[0], texvec[1], texvec[2] + offs, tex->dist_amount, tex->noisebasis, tex->noisebasis2); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - BRICONT; - - - return rv; - + int rv = TEX_INT; + + texres->tin = mg_VLNoise( + texvec[0], texvec[1], texvec[2], tex->dist_amount, tex->noisebasis, tex->noisebasis2); + + if (texres->nor != NULL) { + float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ + + /* calculate bumpnormal */ + texres->nor[0] = mg_VLNoise(texvec[0] + offs, + texvec[1], + texvec[2], + tex->dist_amount, + tex->noisebasis, + tex->noisebasis2); + texres->nor[1] = mg_VLNoise(texvec[0], + texvec[1] + offs, + texvec[2], + tex->dist_amount, + tex->noisebasis, + tex->noisebasis2); + texres->nor[2] = mg_VLNoise(texvec[0], + texvec[1], + texvec[2] + offs, + tex->dist_amount, + tex->noisebasis, + tex->noisebasis2); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + BRICONT; + + return rv; } - /* ------------------------------------------------------------------------- */ /* newnoise: Voronoi texture type, probably the slowest, especially with minkovsky, bumpmapping, could be done another way */ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres) { - int rv = TEX_INT; - float da[4], pa[12]; /* distance and point coordinate arrays of 4 nearest neighbors */ - float aw1 = fabsf(tex->vn_w1); - float aw2 = fabsf(tex->vn_w2); - float aw3 = fabsf(tex->vn_w3); - float aw4 = fabsf(tex->vn_w4); - float sc = (aw1 + aw2 + aw3 + aw4); - if (sc != 0.f) sc = tex->ns_outscale / sc; - - voronoi(texvec[0], texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->tin = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + tex->vn_w4 * da[3]); - - if (tex->vn_coltype) { - float ca[3]; /* cell color */ - cellNoiseV(pa[0], pa[1], pa[2], ca); - texres->tr = aw1 * ca[0]; - texres->tg = aw1 * ca[1]; - texres->tb = aw1 * ca[2]; - cellNoiseV(pa[3], pa[4], pa[5], ca); - texres->tr += aw2 * ca[0]; - texres->tg += aw2 * ca[1]; - texres->tb += aw2 * ca[2]; - cellNoiseV(pa[6], pa[7], pa[8], ca); - texres->tr += aw3 * ca[0]; - texres->tg += aw3 * ca[1]; - texres->tb += aw3 * ca[2]; - cellNoiseV(pa[9], pa[10], pa[11], ca); - texres->tr += aw4 * ca[0]; - texres->tg += aw4 * ca[1]; - texres->tb += aw4 * ca[2]; - if (tex->vn_coltype >= 2) { - float t1 = (da[1] - da[0]) * 10; - if (t1 > 1) t1 = 1; - if (tex->vn_coltype == 3) t1 *= texres->tin; else t1 *= sc; - texres->tr *= t1; - texres->tg *= t1; - texres->tb *= t1; - } - else { - texres->tr *= sc; - texres->tg *= sc; - texres->tb *= sc; - } - } - - if (texres->nor != NULL) { - float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ - - /* calculate bumpnormal */ - voronoi(texvec[0] + offs, texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[0] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + tex->vn_w4 * da[3]); - voronoi(texvec[0], texvec[1] + offs, texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[1] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + tex->vn_w4 * da[3]); - voronoi(texvec[0], texvec[1], texvec[2] + offs, da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[2] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + tex->vn_w4 * da[3]); - - tex_normal_derivate(tex, texres); - rv |= TEX_NOR; - } - - if (tex->vn_coltype) { - BRICONTRGB; - texres->ta = 1.0; - return (rv | TEX_RGB); - } - - BRICONT; - - return rv; - + int rv = TEX_INT; + float da[4], pa[12]; /* distance and point coordinate arrays of 4 nearest neighbors */ + float aw1 = fabsf(tex->vn_w1); + float aw2 = fabsf(tex->vn_w2); + float aw3 = fabsf(tex->vn_w3); + float aw4 = fabsf(tex->vn_w4); + float sc = (aw1 + aw2 + aw3 + aw4); + if (sc != 0.f) + sc = tex->ns_outscale / sc; + + voronoi(texvec[0], texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); + texres->tin = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + + tex->vn_w4 * da[3]); + + if (tex->vn_coltype) { + float ca[3]; /* cell color */ + cellNoiseV(pa[0], pa[1], pa[2], ca); + texres->tr = aw1 * ca[0]; + texres->tg = aw1 * ca[1]; + texres->tb = aw1 * ca[2]; + cellNoiseV(pa[3], pa[4], pa[5], ca); + texres->tr += aw2 * ca[0]; + texres->tg += aw2 * ca[1]; + texres->tb += aw2 * ca[2]; + cellNoiseV(pa[6], pa[7], pa[8], ca); + texres->tr += aw3 * ca[0]; + texres->tg += aw3 * ca[1]; + texres->tb += aw3 * ca[2]; + cellNoiseV(pa[9], pa[10], pa[11], ca); + texres->tr += aw4 * ca[0]; + texres->tg += aw4 * ca[1]; + texres->tb += aw4 * ca[2]; + if (tex->vn_coltype >= 2) { + float t1 = (da[1] - da[0]) * 10; + if (t1 > 1) + t1 = 1; + if (tex->vn_coltype == 3) + t1 *= texres->tin; + else + t1 *= sc; + texres->tr *= t1; + texres->tg *= t1; + texres->tb *= t1; + } + else { + texres->tr *= sc; + texres->tg *= sc; + texres->tb *= sc; + } + } + + if (texres->nor != NULL) { + float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */ + + /* calculate bumpnormal */ + voronoi(texvec[0] + offs, texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); + texres->nor[0] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + + tex->vn_w4 * da[3]); + voronoi(texvec[0], texvec[1] + offs, texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); + texres->nor[1] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + + tex->vn_w4 * da[3]); + voronoi(texvec[0], texvec[1], texvec[2] + offs, da, pa, tex->vn_mexp, tex->vn_distm); + texres->nor[2] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + + tex->vn_w4 * da[3]); + + tex_normal_derivate(tex, texres); + rv |= TEX_NOR; + } + + if (tex->vn_coltype) { + BRICONTRGB; + texres->ta = 1.0; + return (rv | TEX_RGB); + } + + BRICONT; + + return rv; } /* ------------------------------------------------------------------------- */ static int texnoise(Tex *tex, TexResult *texres, int thread) { - float div = 3.0; - int val, ran, loop, shift = 29; + float div = 3.0; + int val, ran, loop, shift = 29; - ran = BLI_rng_thread_rand(random_tex_array, thread); + ran = BLI_rng_thread_rand(random_tex_array, thread); - loop = tex->noisedepth; + loop = tex->noisedepth; - /* start from top bits since they have more variance */ - val = ((ran >> shift) & 3); + /* start from top bits since they have more variance */ + val = ((ran >> shift) & 3); - while (loop--) { - shift -= 2; - val *= ((ran >> shift) & 3); - div *= 3.0f; - } + while (loop--) { + shift -= 2; + val *= ((ran >> shift) & 3); + div *= 3.0f; + } - texres->tin = ((float)val) / div; + texres->tin = ((float)val) / div; - BRICONT; - return TEX_INT; + BRICONT; + return TEX_INT; } /* ------------------------------------------------------------------------- */ static int cubemap_glob(const float n[3], float x, float y, float z, float *adr1, float *adr2) { - float x1, y1, z1, nor[3]; - int ret; - - if (n == NULL) { - nor[0] = x; nor[1] = y; nor[2] = z; /* use local render coord */ - } - else { - copy_v3_v3(nor, n); - } - - x1 = fabsf(nor[0]); - y1 = fabsf(nor[1]); - z1 = fabsf(nor[2]); - - if (z1 >= x1 && z1 >= y1) { - *adr1 = (x + 1.0f) / 2.0f; - *adr2 = (y + 1.0f) / 2.0f; - ret = 0; - } - else if (y1 >= x1 && y1 >= z1) { - *adr1 = (x + 1.0f) / 2.0f; - *adr2 = (z + 1.0f) / 2.0f; - ret = 1; - } - else { - *adr1 = (y + 1.0f) / 2.0f; - *adr2 = (z + 1.0f) / 2.0f; - ret = 2; - } - return ret; + float x1, y1, z1, nor[3]; + int ret; + + if (n == NULL) { + nor[0] = x; + nor[1] = y; + nor[2] = z; /* use local render coord */ + } + else { + copy_v3_v3(nor, n); + } + + x1 = fabsf(nor[0]); + y1 = fabsf(nor[1]); + z1 = fabsf(nor[2]); + + if (z1 >= x1 && z1 >= y1) { + *adr1 = (x + 1.0f) / 2.0f; + *adr2 = (y + 1.0f) / 2.0f; + ret = 0; + } + else if (y1 >= x1 && y1 >= z1) { + *adr1 = (x + 1.0f) / 2.0f; + *adr2 = (z + 1.0f) / 2.0f; + ret = 1; + } + else { + *adr1 = (y + 1.0f) / 2.0f; + *adr2 = (z + 1.0f) / 2.0f; + ret = 2; + } + return ret; } /* ------------------------------------------------------------------------- */ @@ -705,232 +876,246 @@ static int cubemap_glob(const float n[3], float x, float y, float z, float *adr1 /* ------------------------------------------------------------------------- */ static void do_2d_mapping( - const MTex *mtex, float texvec[3], const float n[3], float dxt[3], float dyt[3]) + const MTex *mtex, float texvec[3], const float n[3], float dxt[3], float dyt[3]) { - Tex *tex; - float fx, fy, fac1, area[8]; - int ok, proj, areaflag = 0, wrap; - - /* mtex variables localized, only cubemap doesn't cooperate yet... */ - wrap = mtex->mapping; - tex = mtex->tex; - - if (!(dxt && dyt)) { - - if (wrap == MTEX_FLAT) { - fx = (texvec[0] + 1.0f) / 2.0f; - fy = (texvec[1] + 1.0f) / 2.0f; - } - else if (wrap == MTEX_TUBE) map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]); - else if (wrap == MTEX_SPHERE) map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]); - else { - cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy); - } - - /* repeat */ - if (tex->extend == TEX_REPEAT) { - if (tex->xrepeat > 1) { - float origf = fx *= tex->xrepeat; - - if (fx > 1.0f) fx -= (int)(fx); - else if (fx < 0.0f) fx += 1 - (int)(fx); - - if (tex->flag & TEX_REPEAT_XMIR) { - int orig = (int)floor(origf); - if (orig & 1) - fx = 1.0f - fx; - } - } - if (tex->yrepeat > 1) { - float origf = fy *= tex->yrepeat; - - if (fy > 1.0f) fy -= (int)(fy); - else if (fy < 0.0f) fy += 1 - (int)(fy); - - if (tex->flag & TEX_REPEAT_YMIR) { - int orig = (int)floor(origf); - if (orig & 1) - fy = 1.0f - fy; - } - } - } - /* crop */ - if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) { - fac1 = tex->cropxmax - tex->cropxmin; - fx = tex->cropxmin + fx * fac1; - } - if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) { - fac1 = tex->cropymax - tex->cropymin; - fy = tex->cropymin + fy * fac1; - } - - texvec[0] = fx; - texvec[1] = fy; - } - else { - - if (wrap == MTEX_FLAT) { - fx = (texvec[0] + 1.0f) / 2.0f; - fy = (texvec[1] + 1.0f) / 2.0f; - dxt[0] /= 2.0f; - dxt[1] /= 2.0f; - dxt[2] /= 2.0f; - dyt[0] /= 2.0f; - dyt[1] /= 2.0f; - dyt[2] /= 2.0f; - } - else if (ELEM(wrap, MTEX_TUBE, MTEX_SPHERE)) { - /* exception: the seam behind (y<0.0) */ - ok = 1; - if (texvec[1] <= 0.0f) { - fx = texvec[0] + dxt[0]; - fy = texvec[0] + dyt[0]; - if (fx >= 0.0f && fy >= 0.0f && texvec[0] >= 0.0f) { - /* pass */ - } - else if (fx <= 0.0f && fy <= 0.0f && texvec[0] <= 0.0f) { - /* pass */ - } - else { - ok = 0; - } - } - - if (ok) { - if (wrap == MTEX_TUBE) { - map_to_tube(area, area + 1, texvec[0], texvec[1], texvec[2]); - map_to_tube(area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]); - map_to_tube(area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]); - } - else { - map_to_sphere(area, area + 1, texvec[0], texvec[1], texvec[2]); - map_to_sphere(area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]); - map_to_sphere(area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]); - } - areaflag = 1; - } - else { - if (wrap == MTEX_TUBE) map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]); - else map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]); - dxt[0] /= 2.0f; - dxt[1] /= 2.0f; - dyt[0] /= 2.0f; - dyt[1] /= 2.0f; - } - } - else { - - proj = cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy); - - if (proj == 1) { - SWAP(float, dxt[1], dxt[2]); - SWAP(float, dyt[1], dyt[2]); - } - else if (proj == 2) { - float f1 = dxt[0], f2 = dyt[0]; - dxt[0] = dxt[1]; - dyt[0] = dyt[1]; - dxt[1] = dxt[2]; - dyt[1] = dyt[2]; - dxt[2] = f1; - dyt[2] = f2; - } - - dxt[0] *= 0.5f; - dxt[1] *= 0.5f; - dxt[2] *= 0.5f; - - dyt[0] *= 0.5f; - dyt[1] *= 0.5f; - dyt[2] *= 0.5f; - - } - - /* if area, then reacalculate dxt[] and dyt[] */ - if (areaflag) { - fx = area[0]; - fy = area[1]; - dxt[0] = area[2] - fx; - dxt[1] = area[3] - fy; - dyt[0] = area[4] - fx; - dyt[1] = area[5] - fy; - } - - /* repeat */ - if (tex->extend == TEX_REPEAT) { - float max = 1.0f; - if (tex->xrepeat > 1) { - float origf = fx *= tex->xrepeat; - - /* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */ - if (tex->texfilter == TXF_BOX) { - if (fx > 1.0f) fx -= (int)(fx); - else if (fx < 0.0f) fx += 1 - (int)(fx); - - if (tex->flag & TEX_REPEAT_XMIR) { - int orig = (int)floor(origf); - if (orig & 1) - fx = 1.0f - fx; - } - } - - max = tex->xrepeat; - - dxt[0] *= tex->xrepeat; - dyt[0] *= tex->xrepeat; - } - if (tex->yrepeat > 1) { - float origf = fy *= tex->yrepeat; - - /* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */ - if (tex->texfilter == TXF_BOX) { - if (fy > 1.0f) fy -= (int)(fy); - else if (fy < 0.0f) fy += 1 - (int)(fy); - - if (tex->flag & TEX_REPEAT_YMIR) { - int orig = (int)floor(origf); - if (orig & 1) - fy = 1.0f - fy; - } - } - - if (max < tex->yrepeat) - max = tex->yrepeat; - - dxt[1] *= tex->yrepeat; - dyt[1] *= tex->yrepeat; - } - if (max != 1.0f) { - dxt[2] *= max; - dyt[2] *= max; - } - - } - /* crop */ - if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) { - fac1 = tex->cropxmax - tex->cropxmin; - fx = tex->cropxmin + fx * fac1; - dxt[0] *= fac1; - dyt[0] *= fac1; - } - if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) { - fac1 = tex->cropymax - tex->cropymin; - fy = tex->cropymin + fy * fac1; - dxt[1] *= fac1; - dyt[1] *= fac1; - } - - texvec[0] = fx; - texvec[1] = fy; - - } + Tex *tex; + float fx, fy, fac1, area[8]; + int ok, proj, areaflag = 0, wrap; + + /* mtex variables localized, only cubemap doesn't cooperate yet... */ + wrap = mtex->mapping; + tex = mtex->tex; + + if (!(dxt && dyt)) { + + if (wrap == MTEX_FLAT) { + fx = (texvec[0] + 1.0f) / 2.0f; + fy = (texvec[1] + 1.0f) / 2.0f; + } + else if (wrap == MTEX_TUBE) + map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]); + else if (wrap == MTEX_SPHERE) + map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]); + else { + cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy); + } + + /* repeat */ + if (tex->extend == TEX_REPEAT) { + if (tex->xrepeat > 1) { + float origf = fx *= tex->xrepeat; + + if (fx > 1.0f) + fx -= (int)(fx); + else if (fx < 0.0f) + fx += 1 - (int)(fx); + + if (tex->flag & TEX_REPEAT_XMIR) { + int orig = (int)floor(origf); + if (orig & 1) + fx = 1.0f - fx; + } + } + if (tex->yrepeat > 1) { + float origf = fy *= tex->yrepeat; + + if (fy > 1.0f) + fy -= (int)(fy); + else if (fy < 0.0f) + fy += 1 - (int)(fy); + + if (tex->flag & TEX_REPEAT_YMIR) { + int orig = (int)floor(origf); + if (orig & 1) + fy = 1.0f - fy; + } + } + } + /* crop */ + if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) { + fac1 = tex->cropxmax - tex->cropxmin; + fx = tex->cropxmin + fx * fac1; + } + if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) { + fac1 = tex->cropymax - tex->cropymin; + fy = tex->cropymin + fy * fac1; + } + + texvec[0] = fx; + texvec[1] = fy; + } + else { + + if (wrap == MTEX_FLAT) { + fx = (texvec[0] + 1.0f) / 2.0f; + fy = (texvec[1] + 1.0f) / 2.0f; + dxt[0] /= 2.0f; + dxt[1] /= 2.0f; + dxt[2] /= 2.0f; + dyt[0] /= 2.0f; + dyt[1] /= 2.0f; + dyt[2] /= 2.0f; + } + else if (ELEM(wrap, MTEX_TUBE, MTEX_SPHERE)) { + /* exception: the seam behind (y<0.0) */ + ok = 1; + if (texvec[1] <= 0.0f) { + fx = texvec[0] + dxt[0]; + fy = texvec[0] + dyt[0]; + if (fx >= 0.0f && fy >= 0.0f && texvec[0] >= 0.0f) { + /* pass */ + } + else if (fx <= 0.0f && fy <= 0.0f && texvec[0] <= 0.0f) { + /* pass */ + } + else { + ok = 0; + } + } + + if (ok) { + if (wrap == MTEX_TUBE) { + map_to_tube(area, area + 1, texvec[0], texvec[1], texvec[2]); + map_to_tube( + area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]); + map_to_tube( + area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]); + } + else { + map_to_sphere(area, area + 1, texvec[0], texvec[1], texvec[2]); + map_to_sphere( + area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]); + map_to_sphere( + area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]); + } + areaflag = 1; + } + else { + if (wrap == MTEX_TUBE) + map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]); + else + map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]); + dxt[0] /= 2.0f; + dxt[1] /= 2.0f; + dyt[0] /= 2.0f; + dyt[1] /= 2.0f; + } + } + else { + + proj = cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy); + + if (proj == 1) { + SWAP(float, dxt[1], dxt[2]); + SWAP(float, dyt[1], dyt[2]); + } + else if (proj == 2) { + float f1 = dxt[0], f2 = dyt[0]; + dxt[0] = dxt[1]; + dyt[0] = dyt[1]; + dxt[1] = dxt[2]; + dyt[1] = dyt[2]; + dxt[2] = f1; + dyt[2] = f2; + } + + dxt[0] *= 0.5f; + dxt[1] *= 0.5f; + dxt[2] *= 0.5f; + + dyt[0] *= 0.5f; + dyt[1] *= 0.5f; + dyt[2] *= 0.5f; + } + + /* if area, then reacalculate dxt[] and dyt[] */ + if (areaflag) { + fx = area[0]; + fy = area[1]; + dxt[0] = area[2] - fx; + dxt[1] = area[3] - fy; + dyt[0] = area[4] - fx; + dyt[1] = area[5] - fy; + } + + /* repeat */ + if (tex->extend == TEX_REPEAT) { + float max = 1.0f; + if (tex->xrepeat > 1) { + float origf = fx *= tex->xrepeat; + + /* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */ + if (tex->texfilter == TXF_BOX) { + if (fx > 1.0f) + fx -= (int)(fx); + else if (fx < 0.0f) + fx += 1 - (int)(fx); + + if (tex->flag & TEX_REPEAT_XMIR) { + int orig = (int)floor(origf); + if (orig & 1) + fx = 1.0f - fx; + } + } + + max = tex->xrepeat; + + dxt[0] *= tex->xrepeat; + dyt[0] *= tex->xrepeat; + } + if (tex->yrepeat > 1) { + float origf = fy *= tex->yrepeat; + + /* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */ + if (tex->texfilter == TXF_BOX) { + if (fy > 1.0f) + fy -= (int)(fy); + else if (fy < 0.0f) + fy += 1 - (int)(fy); + + if (tex->flag & TEX_REPEAT_YMIR) { + int orig = (int)floor(origf); + if (orig & 1) + fy = 1.0f - fy; + } + } + + if (max < tex->yrepeat) + max = tex->yrepeat; + + dxt[1] *= tex->yrepeat; + dyt[1] *= tex->yrepeat; + } + if (max != 1.0f) { + dxt[2] *= max; + dyt[2] *= max; + } + } + /* crop */ + if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) { + fac1 = tex->cropxmax - tex->cropxmin; + fx = tex->cropxmin + fx * fac1; + dxt[0] *= fac1; + dyt[0] *= fac1; + } + if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) { + fac1 = tex->cropymax - tex->cropymin; + fy = tex->cropymin + fy * fac1; + dxt[1] *= fac1; + dyt[1] *= fac1; + } + + texvec[0] = fx; + texvec[1] = fy; + } } /* ************************************** */ static int multitex(Tex *tex, float texvec[3], - float dxt[3], float dyt[3], + float dxt[3], + float dyt[3], int osatex, TexResult *texres, const short thread, @@ -940,240 +1125,266 @@ static int multitex(Tex *tex, const bool texnode_preview, const bool use_nodes) { - float tmpvec[3]; - int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */ - - texres->talpha = false; /* is set when image texture returns alpha (considered premul) */ - - if (use_nodes && tex->use_nodes && tex->nodetree) { - const float cfra = 1.0f; /* This was only set for Blender Internal render before. */ - retval = ntreeTexExecTree(tex->nodetree, texres, texvec, dxt, dyt, osatex, thread, - tex, which_output, cfra, texnode_preview, NULL); - } - else { - switch (tex->type) { - case 0: - texres->tin = 0.0f; - return 0; - case TEX_CLOUDS: - retval = clouds(tex, texvec, texres); - break; - case TEX_WOOD: - retval = wood(tex, texvec, texres); - break; - case TEX_MARBLE: - retval = marble(tex, texvec, texres); - break; - case TEX_MAGIC: - retval = magic(tex, texvec, texres); - break; - case TEX_BLEND: - retval = blend(tex, texvec, texres); - break; - case TEX_STUCCI: - retval = stucci(tex, texvec, texres); - break; - case TEX_NOISE: - retval = texnoise(tex, texres, thread); - break; - case TEX_IMAGE: - if (osatex) retval = imagewraposa(tex, tex->ima, NULL, texvec, dxt, dyt, texres, pool, skip_load_image); - else retval = imagewrap(tex, tex->ima, NULL, texvec, texres, pool, skip_load_image); - if (tex->ima) { - BKE_image_tag_time(tex->ima); - } - break; - case TEX_MUSGRAVE: - /* newnoise: musgrave types */ - - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ - copy_v3_v3(tmpvec, texvec); - mul_v3_fl(tmpvec, 1.0f / tex->noisesize); - - switch (tex->stype) { - case TEX_MFRACTAL: - case TEX_FBM: - retval = mg_mFractalOrfBmTex(tex, tmpvec, texres); - break; - case TEX_RIDGEDMF: - case TEX_HYBRIDMF: - retval = mg_ridgedOrHybridMFTex(tex, tmpvec, texres); - break; - case TEX_HTERRAIN: - retval = mg_HTerrainTex(tex, tmpvec, texres); - break; - } - break; - /* newnoise: voronoi type */ - case TEX_VORONOI: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ - copy_v3_v3(tmpvec, texvec); - mul_v3_fl(tmpvec, 1.0f / tex->noisesize); - - retval = voronoiTex(tex, tmpvec, texres); - break; - case TEX_DISTNOISE: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ - copy_v3_v3(tmpvec, texvec); - mul_v3_fl(tmpvec, 1.0f / tex->noisesize); - - retval = mg_distNoiseTex(tex, tmpvec, texres); - break; - } - } - - if (tex->flag & TEX_COLORBAND) { - float col[4]; - if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) { - texres->talpha = true; - texres->tr = col[0]; - texres->tg = col[1]; - texres->tb = col[2]; - texres->ta = col[3]; - retval |= TEX_RGB; - } - } - return retval; + float tmpvec[3]; + int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */ + + texres->talpha = false; /* is set when image texture returns alpha (considered premul) */ + + if (use_nodes && tex->use_nodes && tex->nodetree) { + const float cfra = 1.0f; /* This was only set for Blender Internal render before. */ + retval = ntreeTexExecTree(tex->nodetree, + texres, + texvec, + dxt, + dyt, + osatex, + thread, + tex, + which_output, + cfra, + texnode_preview, + NULL); + } + else { + switch (tex->type) { + case 0: + texres->tin = 0.0f; + return 0; + case TEX_CLOUDS: + retval = clouds(tex, texvec, texres); + break; + case TEX_WOOD: + retval = wood(tex, texvec, texres); + break; + case TEX_MARBLE: + retval = marble(tex, texvec, texres); + break; + case TEX_MAGIC: + retval = magic(tex, texvec, texres); + break; + case TEX_BLEND: + retval = blend(tex, texvec, texres); + break; + case TEX_STUCCI: + retval = stucci(tex, texvec, texres); + break; + case TEX_NOISE: + retval = texnoise(tex, texres, thread); + break; + case TEX_IMAGE: + if (osatex) + retval = imagewraposa( + tex, tex->ima, NULL, texvec, dxt, dyt, texres, pool, skip_load_image); + else + retval = imagewrap(tex, tex->ima, NULL, texvec, texres, pool, skip_load_image); + if (tex->ima) { + BKE_image_tag_time(tex->ima); + } + break; + case TEX_MUSGRAVE: + /* newnoise: musgrave types */ + + /* ton: added this, for Blender convention reason. + * artificer: added the use of tmpvec to avoid scaling texvec + */ + copy_v3_v3(tmpvec, texvec); + mul_v3_fl(tmpvec, 1.0f / tex->noisesize); + + switch (tex->stype) { + case TEX_MFRACTAL: + case TEX_FBM: + retval = mg_mFractalOrfBmTex(tex, tmpvec, texres); + break; + case TEX_RIDGEDMF: + case TEX_HYBRIDMF: + retval = mg_ridgedOrHybridMFTex(tex, tmpvec, texres); + break; + case TEX_HTERRAIN: + retval = mg_HTerrainTex(tex, tmpvec, texres); + break; + } + break; + /* newnoise: voronoi type */ + case TEX_VORONOI: + /* ton: added this, for Blender convention reason. + * artificer: added the use of tmpvec to avoid scaling texvec + */ + copy_v3_v3(tmpvec, texvec); + mul_v3_fl(tmpvec, 1.0f / tex->noisesize); + + retval = voronoiTex(tex, tmpvec, texres); + break; + case TEX_DISTNOISE: + /* ton: added this, for Blender convention reason. + * artificer: added the use of tmpvec to avoid scaling texvec + */ + copy_v3_v3(tmpvec, texvec); + mul_v3_fl(tmpvec, 1.0f / tex->noisesize); + + retval = mg_distNoiseTex(tex, tmpvec, texres); + break; + } + } + + if (tex->flag & TEX_COLORBAND) { + float col[4]; + if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) { + texres->talpha = true; + texres->tr = col[0]; + texres->tg = col[1]; + texres->tb = col[2]; + texres->ta = col[3]; + retval |= TEX_RGB; + } + } + return retval; } static int multitex_nodes_intern(Tex *tex, float texvec[3], - float dxt[3], float dyt[3], + float dxt[3], + float dyt[3], int osatex, TexResult *texres, const short thread, short which_output, - MTex *mtex, struct - ImagePool *pool, + MTex *mtex, + struct ImagePool *pool, const bool scene_color_manage, const bool skip_load_image, const bool texnode_preview, const bool use_nodes) { - if (tex == NULL) { - memset(texres, 0, sizeof(TexResult)); - return 0; - } - - if (mtex) - which_output = mtex->which_output; - - if (tex->type == TEX_IMAGE) { - int rgbnor; - - if (mtex) { - /* we have mtex, use it for 2d mapping images only */ - do_2d_mapping(mtex, texvec, NULL, dxt, dyt); - rgbnor = multitex(tex, - texvec, - dxt, dyt, - osatex, - texres, - thread, - which_output, - pool, - skip_load_image, - texnode_preview, - use_nodes); - - if (mtex->mapto & (MAP_COL)) { - ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); - - /* don't linearize float buffers, assumed to be linear */ - if (ibuf != NULL && - ibuf->rect_float == NULL && - (rgbnor & TEX_RGB) && - scene_color_manage) - { - IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); - } - - BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); - } - } - else { - /* we don't have mtex, do default flat 2d projection */ - MTex localmtex; - float texvec_l[3], dxt_l[3], dyt_l[3]; - - localmtex.mapping = MTEX_FLAT; - localmtex.tex = tex; - localmtex.object = NULL; - localmtex.texco = TEXCO_ORCO; - - copy_v3_v3(texvec_l, texvec); - if (dxt && dyt) { - copy_v3_v3(dxt_l, dxt); - copy_v3_v3(dyt_l, dyt); - } - else { - zero_v3(dxt_l); - zero_v3(dyt_l); - } - - do_2d_mapping(&localmtex, texvec_l, NULL, dxt_l, dyt_l); - rgbnor = multitex(tex, - texvec_l, - dxt_l, dyt_l, - osatex, - texres, - thread, - which_output, - pool, - skip_load_image, - texnode_preview, - use_nodes); - - { - ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); - - /* don't linearize float buffers, assumed to be linear */ - if (ibuf != NULL && - ibuf->rect_float == NULL && - (rgbnor & TEX_RGB) && - scene_color_manage) - { - IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); - } - - BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); - } - } - - return rgbnor; - } - else { - return multitex(tex, - texvec, - dxt, dyt, - osatex, - texres, - thread, - which_output, - pool, - skip_load_image, - texnode_preview, - use_nodes); - } + if (tex == NULL) { + memset(texres, 0, sizeof(TexResult)); + return 0; + } + + if (mtex) + which_output = mtex->which_output; + + if (tex->type == TEX_IMAGE) { + int rgbnor; + + if (mtex) { + /* we have mtex, use it for 2d mapping images only */ + do_2d_mapping(mtex, texvec, NULL, dxt, dyt); + rgbnor = multitex(tex, + texvec, + dxt, + dyt, + osatex, + texres, + thread, + which_output, + pool, + skip_load_image, + texnode_preview, + use_nodes); + + if (mtex->mapto & (MAP_COL)) { + ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); + + /* don't linearize float buffers, assumed to be linear */ + if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) { + IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); + } + + BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); + } + } + else { + /* we don't have mtex, do default flat 2d projection */ + MTex localmtex; + float texvec_l[3], dxt_l[3], dyt_l[3]; + + localmtex.mapping = MTEX_FLAT; + localmtex.tex = tex; + localmtex.object = NULL; + localmtex.texco = TEXCO_ORCO; + + copy_v3_v3(texvec_l, texvec); + if (dxt && dyt) { + copy_v3_v3(dxt_l, dxt); + copy_v3_v3(dyt_l, dyt); + } + else { + zero_v3(dxt_l); + zero_v3(dyt_l); + } + + do_2d_mapping(&localmtex, texvec_l, NULL, dxt_l, dyt_l); + rgbnor = multitex(tex, + texvec_l, + dxt_l, + dyt_l, + osatex, + texres, + thread, + which_output, + pool, + skip_load_image, + texnode_preview, + use_nodes); + + { + ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); + + /* don't linearize float buffers, assumed to be linear */ + if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) { + IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace); + } + + BKE_image_pool_release_ibuf(tex->ima, ibuf, pool); + } + } + + return rgbnor; + } + else { + return multitex(tex, + texvec, + dxt, + dyt, + osatex, + texres, + thread, + which_output, + pool, + skip_load_image, + texnode_preview, + use_nodes); + } } /* this is called from the shader and texture nodes * Use it from render pipeline only! */ -int multitex_nodes(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, - const short thread, short which_output, MTex *mtex, struct ImagePool *pool) +int multitex_nodes(Tex *tex, + float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + TexResult *texres, + const short thread, + short which_output, + MTex *mtex, + struct ImagePool *pool) { - return multitex_nodes_intern(tex, texvec, dxt, dyt, osatex, texres, - thread, which_output, mtex, pool, true, - false, - false, - true); + return multitex_nodes_intern(tex, + texvec, + dxt, + dyt, + osatex, + texres, + thread, + which_output, + mtex, + pool, + true, + false, + false, + true); } /* Warning, if the texres's values are not declared zero, check the return value to be sure @@ -1183,7 +1394,8 @@ int multitex_nodes(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int os */ int multitex_ext(Tex *tex, float texvec[3], - float dxt[3], float dyt[3], + float dxt[3], + float dyt[3], int osatex, TexResult *texres, const short thread, @@ -1191,310 +1403,334 @@ int multitex_ext(Tex *tex, bool scene_color_manage, const bool skip_load_image) { - return multitex_nodes_intern(tex, - texvec, - dxt, dyt, - osatex, - texres, - thread, - 0, - NULL, - pool, - scene_color_manage, - skip_load_image, - false, - true); + return multitex_nodes_intern(tex, + texvec, + dxt, + dyt, + osatex, + texres, + thread, + 0, + NULL, + pool, + scene_color_manage, + skip_load_image, + false, + true); } /* extern-tex doesn't support nodes (ntreeBeginExec() can't be called when rendering is going on)\ * * Use it for stuff which is out of render pipeline. */ -int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) +int multitex_ext_safe(Tex *tex, + float texvec[3], + TexResult *texres, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image) { - return multitex_nodes_intern(tex, - texvec, - NULL, NULL, - 0, - texres, - 0, - 0, - NULL, - pool, - scene_color_manage, - skip_load_image, - false, - false); + return multitex_nodes_intern(tex, + texvec, + NULL, + NULL, + 0, + texres, + 0, + 0, + NULL, + pool, + scene_color_manage, + skip_load_image, + false, + false); } - /* ------------------------------------------------------------------------- */ /* in = destination, tex = texture, out = previous color */ /* fact = texture strength, facg = button strength value */ -void texture_rgb_blend(float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype) +void texture_rgb_blend( + float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype) { - float facm; - - switch (blendtype) { - case MTEX_BLEND: - fact *= facg; - facm = 1.0f - fact; - - in[0] = (fact * tex[0] + facm * out[0]); - in[1] = (fact * tex[1] + facm * out[1]); - in[2] = (fact * tex[2] + facm * out[2]); - break; - - case MTEX_MUL: - fact *= facg; - facm = 1.0f - fact; - in[0] = (facm + fact * tex[0]) * out[0]; - in[1] = (facm + fact * tex[1]) * out[1]; - in[2] = (facm + fact * tex[2]) * out[2]; - break; - - case MTEX_SCREEN: - fact *= facg; - facm = 1.0f - fact; - in[0] = 1.0f - (facm + fact * (1.0f - tex[0])) * (1.0f - out[0]); - in[1] = 1.0f - (facm + fact * (1.0f - tex[1])) * (1.0f - out[1]); - in[2] = 1.0f - (facm + fact * (1.0f - tex[2])) * (1.0f - out[2]); - break; - - case MTEX_OVERLAY: - fact *= facg; - facm = 1.0f - fact; - - if (out[0] < 0.5f) - in[0] = out[0] * (facm + 2.0f * fact * tex[0]); - else - in[0] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[0])) * (1.0f - out[0]); - if (out[1] < 0.5f) - in[1] = out[1] * (facm + 2.0f * fact * tex[1]); - else - in[1] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[1])) * (1.0f - out[1]); - if (out[2] < 0.5f) - in[2] = out[2] * (facm + 2.0f * fact * tex[2]); - else - in[2] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[2])) * (1.0f - out[2]); - break; - - case MTEX_SUB: - fact = -fact; - ATTR_FALLTHROUGH; - case MTEX_ADD: - fact *= facg; - in[0] = (fact * tex[0] + out[0]); - in[1] = (fact * tex[1] + out[1]); - in[2] = (fact * tex[2] + out[2]); - break; - - case MTEX_DIV: - fact *= facg; - facm = 1.0f - fact; - - if (tex[0] != 0.0f) - in[0] = facm * out[0] + fact * out[0] / tex[0]; - if (tex[1] != 0.0f) - in[1] = facm * out[1] + fact * out[1] / tex[1]; - if (tex[2] != 0.0f) - in[2] = facm * out[2] + fact * out[2] / tex[2]; - - break; - - case MTEX_DIFF: - fact *= facg; - facm = 1.0f - fact; - in[0] = facm * out[0] + fact * fabsf(tex[0] - out[0]); - in[1] = facm * out[1] + fact * fabsf(tex[1] - out[1]); - in[2] = facm * out[2] + fact * fabsf(tex[2] - out[2]); - break; - - case MTEX_DARK: - fact *= facg; - facm = 1.0f - fact; - - in[0] = min_ff(out[0], tex[0]) * fact + out[0] * facm; - in[1] = min_ff(out[1], tex[1]) * fact + out[1] * facm; - in[2] = min_ff(out[2], tex[2]) * fact + out[2] * facm; - break; - - case MTEX_LIGHT: - fact *= facg; - - in[0] = max_ff(fact * tex[0], out[0]); - in[1] = max_ff(fact * tex[1], out[1]); - in[2] = max_ff(fact * tex[2], out[2]); - break; - - case MTEX_BLEND_HUE: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_HUE, in, fact, tex); - break; - case MTEX_BLEND_SAT: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_SAT, in, fact, tex); - break; - case MTEX_BLEND_VAL: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_VAL, in, fact, tex); - break; - case MTEX_BLEND_COLOR: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_COLOR, in, fact, tex); - break; - case MTEX_SOFT_LIGHT: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_SOFT, in, fact, tex); - break; - case MTEX_LIN_LIGHT: - fact *= facg; - copy_v3_v3(in, out); - ramp_blend(MA_RAMP_LINEAR, in, fact, tex); - break; - } + float facm; + + switch (blendtype) { + case MTEX_BLEND: + fact *= facg; + facm = 1.0f - fact; + + in[0] = (fact * tex[0] + facm * out[0]); + in[1] = (fact * tex[1] + facm * out[1]); + in[2] = (fact * tex[2] + facm * out[2]); + break; + + case MTEX_MUL: + fact *= facg; + facm = 1.0f - fact; + in[0] = (facm + fact * tex[0]) * out[0]; + in[1] = (facm + fact * tex[1]) * out[1]; + in[2] = (facm + fact * tex[2]) * out[2]; + break; + + case MTEX_SCREEN: + fact *= facg; + facm = 1.0f - fact; + in[0] = 1.0f - (facm + fact * (1.0f - tex[0])) * (1.0f - out[0]); + in[1] = 1.0f - (facm + fact * (1.0f - tex[1])) * (1.0f - out[1]); + in[2] = 1.0f - (facm + fact * (1.0f - tex[2])) * (1.0f - out[2]); + break; + + case MTEX_OVERLAY: + fact *= facg; + facm = 1.0f - fact; + + if (out[0] < 0.5f) + in[0] = out[0] * (facm + 2.0f * fact * tex[0]); + else + in[0] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[0])) * (1.0f - out[0]); + if (out[1] < 0.5f) + in[1] = out[1] * (facm + 2.0f * fact * tex[1]); + else + in[1] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[1])) * (1.0f - out[1]); + if (out[2] < 0.5f) + in[2] = out[2] * (facm + 2.0f * fact * tex[2]); + else + in[2] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[2])) * (1.0f - out[2]); + break; + + case MTEX_SUB: + fact = -fact; + ATTR_FALLTHROUGH; + case MTEX_ADD: + fact *= facg; + in[0] = (fact * tex[0] + out[0]); + in[1] = (fact * tex[1] + out[1]); + in[2] = (fact * tex[2] + out[2]); + break; + + case MTEX_DIV: + fact *= facg; + facm = 1.0f - fact; + + if (tex[0] != 0.0f) + in[0] = facm * out[0] + fact * out[0] / tex[0]; + if (tex[1] != 0.0f) + in[1] = facm * out[1] + fact * out[1] / tex[1]; + if (tex[2] != 0.0f) + in[2] = facm * out[2] + fact * out[2] / tex[2]; + + break; + + case MTEX_DIFF: + fact *= facg; + facm = 1.0f - fact; + in[0] = facm * out[0] + fact * fabsf(tex[0] - out[0]); + in[1] = facm * out[1] + fact * fabsf(tex[1] - out[1]); + in[2] = facm * out[2] + fact * fabsf(tex[2] - out[2]); + break; + + case MTEX_DARK: + fact *= facg; + facm = 1.0f - fact; + + in[0] = min_ff(out[0], tex[0]) * fact + out[0] * facm; + in[1] = min_ff(out[1], tex[1]) * fact + out[1] * facm; + in[2] = min_ff(out[2], tex[2]) * fact + out[2] * facm; + break; + + case MTEX_LIGHT: + fact *= facg; + + in[0] = max_ff(fact * tex[0], out[0]); + in[1] = max_ff(fact * tex[1], out[1]); + in[2] = max_ff(fact * tex[2], out[2]); + break; + + case MTEX_BLEND_HUE: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_HUE, in, fact, tex); + break; + case MTEX_BLEND_SAT: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_SAT, in, fact, tex); + break; + case MTEX_BLEND_VAL: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_VAL, in, fact, tex); + break; + case MTEX_BLEND_COLOR: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_COLOR, in, fact, tex); + break; + case MTEX_SOFT_LIGHT: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_SOFT, in, fact, tex); + break; + case MTEX_LIN_LIGHT: + fact *= facg; + copy_v3_v3(in, out); + ramp_blend(MA_RAMP_LINEAR, in, fact, tex); + break; + } } float texture_value_blend(float tex, float out, float fact, float facg, int blendtype) { - float in = 0.0, facm, col, scf; - int flip = (facg < 0.0f); - - facg = fabsf(facg); - - fact *= facg; - facm = 1.0f - fact; - if (flip) SWAP(float, fact, facm); - - switch (blendtype) { - case MTEX_BLEND: - in = fact * tex + facm * out; - break; - - case MTEX_MUL: - facm = 1.0f - facg; - in = (facm + fact * tex) * out; - break; - - case MTEX_SCREEN: - facm = 1.0f - facg; - in = 1.0f - (facm + fact * (1.0f - tex)) * (1.0f - out); - break; - - case MTEX_OVERLAY: - facm = 1.0f - facg; - if (out < 0.5f) - in = out * (facm + 2.0f * fact * tex); - else - in = 1.0f - (facm + 2.0f * fact * (1.0f - tex)) * (1.0f - out); - break; - - case MTEX_SUB: - fact = -fact; - ATTR_FALLTHROUGH; - case MTEX_ADD: - in = fact * tex + out; - break; - - case MTEX_DIV: - if (tex != 0.0f) - in = facm * out + fact * out / tex; - break; - - case MTEX_DIFF: - in = facm * out + fact * fabsf(tex - out); - break; - - case MTEX_DARK: - in = min_ff(out, tex) * fact + out * facm; - break; - - case MTEX_LIGHT: - col = fact * tex; - if (col > out) in = col; else in = out; - break; - - case MTEX_SOFT_LIGHT: - scf = 1.0f - (1.0f - tex) * (1.0f - out); - in = facm * out + fact * ((1.0f - out) * tex * out) + (out * scf); - break; - - case MTEX_LIN_LIGHT: - if (tex > 0.5f) - in = out + fact * (2.0f * (tex - 0.5f)); - else - in = out + fact * (2.0f * tex - 1.0f); - break; - } - - return in; + float in = 0.0, facm, col, scf; + int flip = (facg < 0.0f); + + facg = fabsf(facg); + + fact *= facg; + facm = 1.0f - fact; + if (flip) + SWAP(float, fact, facm); + + switch (blendtype) { + case MTEX_BLEND: + in = fact * tex + facm * out; + break; + + case MTEX_MUL: + facm = 1.0f - facg; + in = (facm + fact * tex) * out; + break; + + case MTEX_SCREEN: + facm = 1.0f - facg; + in = 1.0f - (facm + fact * (1.0f - tex)) * (1.0f - out); + break; + + case MTEX_OVERLAY: + facm = 1.0f - facg; + if (out < 0.5f) + in = out * (facm + 2.0f * fact * tex); + else + in = 1.0f - (facm + 2.0f * fact * (1.0f - tex)) * (1.0f - out); + break; + + case MTEX_SUB: + fact = -fact; + ATTR_FALLTHROUGH; + case MTEX_ADD: + in = fact * tex + out; + break; + + case MTEX_DIV: + if (tex != 0.0f) + in = facm * out + fact * out / tex; + break; + + case MTEX_DIFF: + in = facm * out + fact * fabsf(tex - out); + break; + + case MTEX_DARK: + in = min_ff(out, tex) * fact + out * facm; + break; + + case MTEX_LIGHT: + col = fact * tex; + if (col > out) + in = col; + else + in = out; + break; + + case MTEX_SOFT_LIGHT: + scf = 1.0f - (1.0f - tex) * (1.0f - out); + in = facm * out + fact * ((1.0f - out) * tex * out) + (out * scf); + break; + + case MTEX_LIN_LIGHT: + if (tex > 0.5f) + in = out + fact * (2.0f * (tex - 0.5f)); + else + in = out + fact * (2.0f * tex - 1.0f); + break; + } + + return in; } /* ------------------------------------------------------------------------- */ int externtex(const MTex *mtex, const float vec[3], - float *tin, float *tr, float *tg, float *tb, float *ta, + float *tin, + float *tr, + float *tg, + float *tb, + float *ta, const int thread, struct ImagePool *pool, const bool skip_load_image, const bool texnode_preview) { - Tex *tex; - TexResult texr; - float dxt[3], dyt[3], texvec[3]; - int rgb; - - tex = mtex->tex; - if (tex == NULL) return 0; - texr.nor = NULL; - - /* placement */ - if (mtex->projx) texvec[0] = mtex->size[0] * (vec[mtex->projx - 1] + mtex->ofs[0]); - else texvec[0] = mtex->size[0] * (mtex->ofs[0]); - - if (mtex->projy) texvec[1] = mtex->size[1] * (vec[mtex->projy - 1] + mtex->ofs[1]); - else texvec[1] = mtex->size[1] * (mtex->ofs[1]); - - if (mtex->projz) texvec[2] = mtex->size[2] * (vec[mtex->projz - 1] + mtex->ofs[2]); - else texvec[2] = mtex->size[2] * (mtex->ofs[2]); - - /* texture */ - if (tex->type == TEX_IMAGE) { - do_2d_mapping(mtex, texvec, NULL, dxt, dyt); - } - - rgb = multitex(tex, - texvec, - dxt, dyt, - 0, &texr, - thread, - mtex->which_output, - pool, - skip_load_image, - texnode_preview, - true); - - if (rgb) { - texr.tin = IMB_colormanagement_get_luminance(&texr.tr); - } - else { - texr.tr = mtex->r; - texr.tg = mtex->g; - texr.tb = mtex->b; - } - - *tin = texr.tin; - *tr = texr.tr; - *tg = texr.tg; - *tb = texr.tb; - *ta = texr.ta; - - return (rgb != 0); + Tex *tex; + TexResult texr; + float dxt[3], dyt[3], texvec[3]; + int rgb; + + tex = mtex->tex; + if (tex == NULL) + return 0; + texr.nor = NULL; + + /* placement */ + if (mtex->projx) + texvec[0] = mtex->size[0] * (vec[mtex->projx - 1] + mtex->ofs[0]); + else + texvec[0] = mtex->size[0] * (mtex->ofs[0]); + + if (mtex->projy) + texvec[1] = mtex->size[1] * (vec[mtex->projy - 1] + mtex->ofs[1]); + else + texvec[1] = mtex->size[1] * (mtex->ofs[1]); + + if (mtex->projz) + texvec[2] = mtex->size[2] * (vec[mtex->projz - 1] + mtex->ofs[2]); + else + texvec[2] = mtex->size[2] * (mtex->ofs[2]); + + /* texture */ + if (tex->type == TEX_IMAGE) { + do_2d_mapping(mtex, texvec, NULL, dxt, dyt); + } + + rgb = multitex(tex, + texvec, + dxt, + dyt, + 0, + &texr, + thread, + mtex->which_output, + pool, + skip_load_image, + texnode_preview, + true); + + if (rgb) { + texr.tin = IMB_colormanagement_get_luminance(&texr.tr); + } + else { + texr.tr = mtex->r; + texr.tg = mtex->g; + texr.tb = mtex->b; + } + + *tin = texr.tin; + *tr = texr.tr; + *tg = texr.tg; + *tb = texr.tb; + *ta = texr.ta; + + return (rgb != 0); } diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 05944df770a..e8c97661547 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -21,8 +21,6 @@ * \ingroup render */ - - /*---------------------------------------------------------------------------*/ /* Common includes */ /*---------------------------------------------------------------------------*/ @@ -46,109 +44,121 @@ /* each zbuffer has coordinates transformed to local rect coordinates, so we can simply clip */ void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty) { - memset(zspan, 0, sizeof(ZSpan)); + memset(zspan, 0, sizeof(ZSpan)); - zspan->rectx = rectx; - zspan->recty = recty; + zspan->rectx = rectx; + zspan->recty = recty; - zspan->span1 = MEM_mallocN(recty * sizeof(float), "zspan"); - zspan->span2 = MEM_mallocN(recty * sizeof(float), "zspan"); + zspan->span1 = MEM_mallocN(recty * sizeof(float), "zspan"); + zspan->span2 = MEM_mallocN(recty * sizeof(float), "zspan"); } void zbuf_free_span(ZSpan *zspan) { - if (zspan) { - if (zspan->span1) MEM_freeN(zspan->span1); - if (zspan->span2) MEM_freeN(zspan->span2); - zspan->span1 = zspan->span2 = NULL; - } + if (zspan) { + if (zspan->span1) + MEM_freeN(zspan->span1); + if (zspan->span2) + MEM_freeN(zspan->span2); + zspan->span1 = zspan->span2 = NULL; + } } /* reset range for clipping */ static void zbuf_init_span(ZSpan *zspan) { - zspan->miny1 = zspan->miny2 = zspan->recty + 1; - zspan->maxy1 = zspan->maxy2 = -1; - zspan->minp1 = zspan->maxp1 = zspan->minp2 = zspan->maxp2 = NULL; + zspan->miny1 = zspan->miny2 = zspan->recty + 1; + zspan->maxy1 = zspan->maxy2 = -1; + zspan->minp1 = zspan->maxp1 = zspan->minp2 = zspan->maxp2 = NULL; } static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2]) { - const float *minv, *maxv; - float *span; - float xx1, dx0, xs0; - int y, my0, my2; - - if (v1[1] < v2[1]) { - minv = v1; maxv = v2; - } - else { - minv = v2; maxv = v1; - } - - my0 = ceil(minv[1]); - my2 = floor(maxv[1]); - - if (my2 < 0 || my0 >= zspan->recty) return; - - /* clip top */ - if (my2 >= zspan->recty) my2 = zspan->recty - 1; - /* clip bottom */ - if (my0 < 0) my0 = 0; - - if (my0 > my2) return; - /* if (my0>my2) should still fill in, that way we get spans that skip nicely */ - - xx1 = maxv[1] - minv[1]; - if (xx1 > FLT_EPSILON) { - dx0 = (minv[0] - maxv[0]) / xx1; - xs0 = dx0 * (minv[1] - my2) + minv[0]; - } - else { - dx0 = 0.0f; - xs0 = min_ff(minv[0], maxv[0]); - } - - /* empty span */ - if (zspan->maxp1 == NULL) { - span = zspan->span1; - } - else { /* does it complete left span? */ - if (maxv == zspan->minp1 || minv == zspan->maxp1) { - span = zspan->span1; - } - else { - span = zspan->span2; - } - } - - if (span == zspan->span1) { -// printf("left span my0 %d my2 %d\n", my0, my2); - if (zspan->minp1 == NULL || zspan->minp1[1] > minv[1]) { - zspan->minp1 = minv; - } - if (zspan->maxp1 == NULL || zspan->maxp1[1] < maxv[1]) { - zspan->maxp1 = maxv; - } - if (my0 < zspan->miny1) zspan->miny1 = my0; - if (my2 > zspan->maxy1) zspan->maxy1 = my2; - } - else { -// printf("right span my0 %d my2 %d\n", my0, my2); - if (zspan->minp2 == NULL || zspan->minp2[1] > minv[1]) { - zspan->minp2 = minv; - } - if (zspan->maxp2 == NULL || zspan->maxp2[1] < maxv[1]) { - zspan->maxp2 = maxv; - } - if (my0 < zspan->miny2) zspan->miny2 = my0; - if (my2 > zspan->maxy2) zspan->maxy2 = my2; - } - - for (y = my2; y >= my0; y--, xs0 += dx0) { - /* xs0 is the xcoord! */ - span[y] = xs0; - } + const float *minv, *maxv; + float *span; + float xx1, dx0, xs0; + int y, my0, my2; + + if (v1[1] < v2[1]) { + minv = v1; + maxv = v2; + } + else { + minv = v2; + maxv = v1; + } + + my0 = ceil(minv[1]); + my2 = floor(maxv[1]); + + if (my2 < 0 || my0 >= zspan->recty) + return; + + /* clip top */ + if (my2 >= zspan->recty) + my2 = zspan->recty - 1; + /* clip bottom */ + if (my0 < 0) + my0 = 0; + + if (my0 > my2) + return; + /* if (my0>my2) should still fill in, that way we get spans that skip nicely */ + + xx1 = maxv[1] - minv[1]; + if (xx1 > FLT_EPSILON) { + dx0 = (minv[0] - maxv[0]) / xx1; + xs0 = dx0 * (minv[1] - my2) + minv[0]; + } + else { + dx0 = 0.0f; + xs0 = min_ff(minv[0], maxv[0]); + } + + /* empty span */ + if (zspan->maxp1 == NULL) { + span = zspan->span1; + } + else { /* does it complete left span? */ + if (maxv == zspan->minp1 || minv == zspan->maxp1) { + span = zspan->span1; + } + else { + span = zspan->span2; + } + } + + if (span == zspan->span1) { + // printf("left span my0 %d my2 %d\n", my0, my2); + if (zspan->minp1 == NULL || zspan->minp1[1] > minv[1]) { + zspan->minp1 = minv; + } + if (zspan->maxp1 == NULL || zspan->maxp1[1] < maxv[1]) { + zspan->maxp1 = maxv; + } + if (my0 < zspan->miny1) + zspan->miny1 = my0; + if (my2 > zspan->maxy1) + zspan->maxy1 = my2; + } + else { + // printf("right span my0 %d my2 %d\n", my0, my2); + if (zspan->minp2 == NULL || zspan->minp2[1] > minv[1]) { + zspan->minp2 = minv; + } + if (zspan->maxp2 == NULL || zspan->maxp2[1] < maxv[1]) { + zspan->maxp2 = maxv; + } + if (my0 < zspan->miny2) + zspan->miny2 = my0; + if (my2 > zspan->maxy2) + zspan->maxy2 = my2; + } + + for (y = my2; y >= my0; y--, xs0 += dx0) { + /* xs0 is the xcoord! */ + span[y] = xs0; + } } /*-----------------------------------------------------------*/ @@ -157,81 +167,91 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2]) /* scanconvert for strand triangles, calls func for each x, y coordinate and gives UV barycentrics and z */ -void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float) ) +void zspan_scanconvert(ZSpan *zspan, + void *handle, + float *v1, + float *v2, + float *v3, + void (*func)(void *, int, int, float, float)) { - float x0, y0, x1, y1, x2, y2, z0, z1, z2; - float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1; - const float *span1, *span2; - int i, j, x, y, sn1, sn2, rectx = zspan->rectx, my0, my2; + float x0, y0, x1, y1, x2, y2, z0, z1, z2; + float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1; + const float *span1, *span2; + int i, j, x, y, sn1, sn2, rectx = zspan->rectx, my0, my2; - /* init */ - zbuf_init_span(zspan); + /* init */ + zbuf_init_span(zspan); - /* set spans */ - zbuf_add_to_span(zspan, v1, v2); - zbuf_add_to_span(zspan, v2, v3); - zbuf_add_to_span(zspan, v3, v1); + /* set spans */ + zbuf_add_to_span(zspan, v1, v2); + zbuf_add_to_span(zspan, v2, v3); + zbuf_add_to_span(zspan, v3, v1); - /* clipped */ - if (zspan->minp2 == NULL || zspan->maxp2 == NULL) return; + /* clipped */ + if (zspan->minp2 == NULL || zspan->maxp2 == NULL) + return; - my0 = max_ii(zspan->miny1, zspan->miny2); - my2 = min_ii(zspan->maxy1, zspan->maxy2); + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); - // printf("my %d %d\n", my0, my2); - if (my2 < my0) return; + // printf("my %d %d\n", my0, my2); + if (my2 < my0) + return; - /* ZBUF DX DY, in floats still */ - x1 = v1[0] - v2[0]; - x2 = v2[0] - v3[0]; - y1 = v1[1] - v2[1]; - y2 = v2[1] - v3[1]; + /* ZBUF DX DY, in floats still */ + x1 = v1[0] - v2[0]; + x2 = v2[0] - v3[0]; + y1 = v1[1] - v2[1]; + y2 = v2[1] - v3[1]; - z1 = 1.0f; /* (u1 - u2) */ - z2 = 0.0f; /* (u2 - u3) */ + z1 = 1.0f; /* (u1 - u2) */ + z2 = 0.0f; /* (u2 - u3) */ - x0 = y1 * z2 - z1 * y2; - y0 = z1 * x2 - x1 * z2; - z0 = x1 * y2 - y1 * x2; + x0 = y1 * z2 - z1 * y2; + y0 = z1 * x2 - x1 * z2; + z0 = x1 * y2 - y1 * x2; - if (z0 == 0.0f) return; + if (z0 == 0.0f) + return; - xx1 = (x0 * v1[0] + y0 * v1[1]) / z0 + 1.0f; - uxd = -(double)x0 / (double)z0; - uyd = -(double)y0 / (double)z0; - uy0 = ((double)my2) * uyd + (double)xx1; + xx1 = (x0 * v1[0] + y0 * v1[1]) / z0 + 1.0f; + uxd = -(double)x0 / (double)z0; + uyd = -(double)y0 / (double)z0; + uy0 = ((double)my2) * uyd + (double)xx1; - z1 = -1.0f; /* (v1 - v2) */ - z2 = 1.0f; /* (v2 - v3) */ + z1 = -1.0f; /* (v1 - v2) */ + z2 = 1.0f; /* (v2 - v3) */ - x0 = y1 * z2 - z1 * y2; - y0 = z1 * x2 - x1 * z2; + x0 = y1 * z2 - z1 * y2; + y0 = z1 * x2 - x1 * z2; - xx1 = (x0 * v1[0] + y0 * v1[1]) / z0; - vxd = -(double)x0 / (double)z0; - vyd = -(double)y0 / (double)z0; - vy0 = ((double)my2) * vyd + (double)xx1; + xx1 = (x0 * v1[0] + y0 * v1[1]) / z0; + vxd = -(double)x0 / (double)z0; + vyd = -(double)y0 / (double)z0; + vy0 = ((double)my2) * vyd + (double)xx1; - /* correct span */ - span1 = zspan->span1 + my2; - span2 = zspan->span2 + my2; + /* correct span */ + span1 = zspan->span1 + my2; + span2 = zspan->span2 + my2; - for (i = 0, y = my2; y >= my0; i++, y--, span1--, span2--) { + for (i = 0, y = my2; y >= my0; i++, y--, span1--, span2--) { - sn1 = floor(min_ff(*span1, *span2)); - sn2 = floor(max_ff(*span1, *span2)); - sn1++; + sn1 = floor(min_ff(*span1, *span2)); + sn2 = floor(max_ff(*span1, *span2)); + sn1++; - if (sn2 >= rectx) sn2 = rectx - 1; - if (sn1 < 0) sn1 = 0; + if (sn2 >= rectx) + sn2 = rectx - 1; + if (sn1 < 0) + sn1 = 0; - u = (((double)sn1 * uxd) + uy0) - (i * uyd); - v = (((double)sn1 * vxd) + vy0) - (i * vyd); + u = (((double)sn1 * uxd) + uy0) - (i * uyd); + v = (((double)sn1 * vxd) + vy0) - (i * vyd); - for (j = 0, x = sn1; x <= sn2; j++, x++) { - func(handle, x, y, u + (j * uxd), v + (j * vxd)); - } - } + for (j = 0, x = sn1; x <= sn2; j++, x++) { + func(handle, x, y, u + (j * uxd), v + (j * vxd)); + } + } } /* end of zbuf.c */ |