2920 lines
		
	
	
		
			82 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2920 lines
		
	
	
		
			82 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This program is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU General Public License
 | 
						|
 * as published by the Free Software Foundation; either version 2
 | 
						|
 * of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software Foundation,
 | 
						|
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | 
						|
 *
 | 
						|
 * The Original Code is Copyright (C) 2006 Blender Foundation.
 | 
						|
 * All rights reserved.
 | 
						|
 */
 | 
						|
 | 
						|
/** \file
 | 
						|
 * \ingroup render
 | 
						|
 */
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <math.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include "DNA_anim_types.h"
 | 
						|
#include "DNA_collection_types.h"
 | 
						|
#include "DNA_image_types.h"
 | 
						|
#include "DNA_node_types.h"
 | 
						|
#include "DNA_object_types.h"
 | 
						|
#include "DNA_particle_types.h"
 | 
						|
#include "DNA_scene_types.h"
 | 
						|
#include "DNA_sequence_types.h"
 | 
						|
#include "DNA_space_types.h"
 | 
						|
#include "DNA_userdef_types.h"
 | 
						|
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
#include "BLI_fileops.h"
 | 
						|
#include "BLI_listbase.h"
 | 
						|
#include "BLI_math.h"
 | 
						|
#include "BLI_path_util.h"
 | 
						|
#include "BLI_rect.h"
 | 
						|
#include "BLI_string.h"
 | 
						|
#include "BLI_threads.h"
 | 
						|
#include "BLI_timecode.h"
 | 
						|
 | 
						|
#include "BLT_translation.h"
 | 
						|
 | 
						|
#include "BKE_anim_data.h"
 | 
						|
#include "BKE_animsys.h" /* <------ should this be here?, needed for sequencer update */
 | 
						|
#include "BKE_callbacks.h"
 | 
						|
#include "BKE_camera.h"
 | 
						|
#include "BKE_colortools.h"
 | 
						|
#include "BKE_context.h" /* XXX needed by wm_window.h */
 | 
						|
#include "BKE_global.h"
 | 
						|
#include "BKE_image.h"
 | 
						|
#include "BKE_layer.h"
 | 
						|
#include "BKE_lib_id.h"
 | 
						|
#include "BKE_lib_remap.h"
 | 
						|
#include "BKE_mask.h"
 | 
						|
#include "BKE_modifier.h"
 | 
						|
#include "BKE_node.h"
 | 
						|
#include "BKE_object.h"
 | 
						|
#include "BKE_pointcache.h"
 | 
						|
#include "BKE_report.h"
 | 
						|
#include "BKE_scene.h"
 | 
						|
#include "BKE_sound.h"
 | 
						|
#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */
 | 
						|
 | 
						|
#include "DEG_depsgraph.h"
 | 
						|
#include "DEG_depsgraph_build.h"
 | 
						|
#include "DEG_depsgraph_debug.h"
 | 
						|
#include "DEG_depsgraph_query.h"
 | 
						|
 | 
						|
#include "IMB_colormanagement.h"
 | 
						|
#include "IMB_imbuf.h"
 | 
						|
#include "IMB_imbuf_types.h"
 | 
						|
#include "IMB_metadata.h"
 | 
						|
#include "PIL_time.h"
 | 
						|
 | 
						|
#include "RE_engine.h"
 | 
						|
#include "RE_pipeline.h"
 | 
						|
#include "RE_texture.h"
 | 
						|
 | 
						|
#include "SEQ_relations.h"
 | 
						|
#include "SEQ_render.h"
 | 
						|
 | 
						|
#include "../../windowmanager/WM_api.h"    /* XXX */
 | 
						|
#include "../../windowmanager/wm_window.h" /* XXX */
 | 
						|
#include "GPU_context.h"
 | 
						|
 | 
						|
#ifdef WITH_FREESTYLE
 | 
						|
#  include "FRS_freestyle.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include "DEG_depsgraph.h"
 | 
						|
 | 
						|
/* internal */
 | 
						|
#include "initrender.h"
 | 
						|
#include "pipeline.h"
 | 
						|
#include "render_result.h"
 | 
						|
#include "render_types.h"
 | 
						|
 | 
						|
/* render flow
 | 
						|
 *
 | 
						|
 * 1) Initialize state
 | 
						|
 * - state data, tables
 | 
						|
 * - movie/image file init
 | 
						|
 * - everything that doesn't change during animation
 | 
						|
 *
 | 
						|
 * 2) Initialize data
 | 
						|
 * - camera, world, matrices
 | 
						|
 * - make render verts, faces, halos, strands
 | 
						|
 * - everything can change per frame/field
 | 
						|
 *
 | 
						|
 * 3) Render Processor
 | 
						|
 * - multiple layers
 | 
						|
 * - tiles, rect, baking
 | 
						|
 * - layers/tiles optionally to disk or directly in Render Result
 | 
						|
 *
 | 
						|
 * 4) Composite Render Result
 | 
						|
 * - also read external files etc
 | 
						|
 *
 | 
						|
 * 5) Image Files
 | 
						|
 * - save file or append in movie
 | 
						|
 */
 | 
						|
 | 
						|
/* ********* globals ******** */
 | 
						|
 | 
						|
/* here we store all renders */
 | 
						|
static struct {
 | 
						|
  ListBase renderlist;
 | 
						|
} RenderGlobal = {{NULL, NULL}};
 | 
						|
 | 
						|
/* ********* callbacks ******** */
 | 
						|
 | 
						|
static void render_callback_exec_null(Render *re, Main *bmain, eCbEvent evt)
 | 
						|
{
 | 
						|
  if (re->r.scemode & R_BUTS_PREVIEW) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  BKE_callback_exec_null(bmain, evt);
 | 
						|
}
 | 
						|
 | 
						|
static void render_callback_exec_id(Render *re, Main *bmain, ID *id, eCbEvent evt)
 | 
						|
{
 | 
						|
  if (re->r.scemode & R_BUTS_PREVIEW) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  BKE_callback_exec_id(bmain, id, evt);
 | 
						|
}
 | 
						|
 | 
						|
/* ********* 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);
 | 
						|
 | 
						|
/* 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)
 | 
						|
{
 | 
						|
  if (rs->infostr == NULL) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  uintptr_t mem_in_use, peak_memory;
 | 
						|
  float megs_used_memory, megs_peak_memory;
 | 
						|
  char info_time_str[32];
 | 
						|
 | 
						|
  mem_in_use = MEM_get_memory_in_use();
 | 
						|
  peak_memory = MEM_get_peak_memory();
 | 
						|
 | 
						|
  megs_used_memory = (mem_in_use) / (1024.0 * 1024.0);
 | 
						|
  megs_peak_memory = (peak_memory) / (1024.0 * 1024.0);
 | 
						|
 | 
						|
  fprintf(stdout,
 | 
						|
          TIP_("Fra:%d Mem:%.2fM (Peak %.2fM) "),
 | 
						|
          rs->cfra,
 | 
						|
          megs_used_memory,
 | 
						|
          megs_peak_memory);
 | 
						|
 | 
						|
  BLI_timecode_string_from_time_simple(
 | 
						|
      info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime);
 | 
						|
  fprintf(stdout, TIP_("| Time:%s | "), info_time_str);
 | 
						|
 | 
						|
  fprintf(stdout, "%s", rs->infostr);
 | 
						|
 | 
						|
  /* 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? */
 | 
						|
  BKE_callback_exec_null(G_MAIN, BKE_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 *rr)
 | 
						|
{
 | 
						|
  render_result_free(rr);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name)
 | 
						|
{
 | 
						|
  if (rr == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return BLI_findstring(&rr->layers, name, offsetof(RenderLayer, name));
 | 
						|
}
 | 
						|
 | 
						|
bool RE_HasSingleLayer(Render *re)
 | 
						|
{
 | 
						|
  return (re->r.scemode & R_SINGLE_LAYER);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
RenderLayer *render_get_active_layer(Render *re, RenderResult *rr)
 | 
						|
{
 | 
						|
  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 (rl) {
 | 
						|
      return rl;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return rr->layers.first;
 | 
						|
}
 | 
						|
 | 
						|
static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_layer)
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
/* *************************************************** */
 | 
						|
 | 
						|
Render *RE_GetRender(const char *name)
 | 
						|
{
 | 
						|
  Render *re;
 | 
						|
 | 
						|
  /* search for existing renders */
 | 
						|
  for (re = RenderGlobal.renderlist.first; re; re = re->next) {
 | 
						|
    if (STREQLEN(re->name, name, RE_MAXNAME)) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
RenderResult *RE_AcquireResultWrite(Render *re)
 | 
						|
{
 | 
						|
  if (re) {
 | 
						|
    BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
 | 
						|
    return re->result;
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void RE_ClearResult(Render *re)
 | 
						|
{
 | 
						|
  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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RE_ReleaseResult(Render *re)
 | 
						|
{
 | 
						|
  if (re) {
 | 
						|
    BLI_rw_mutex_unlock(&re->resultmutex);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* displist.c util.... */
 | 
						|
Scene *RE_GetScene(Render *re)
 | 
						|
{
 | 
						|
  if (re) {
 | 
						|
    return re->scene;
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void RE_SetScene(Render *re, Scene *sce)
 | 
						|
{
 | 
						|
  if (re) {
 | 
						|
    re->scene = sce;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Same as #RE_AcquireResultImage but creating the necessary views to store the result
 | 
						|
 * fill provided result struct with a copy of thew views of what is done so far the
 | 
						|
 * #RenderResult.views #ListBase needs to be freed after with #RE_ReleaseResultImageViews
 | 
						|
 */
 | 
						|
void RE_AcquireResultImageViews(Render *re, RenderResult *rr)
 | 
						|
{
 | 
						|
  memset(rr, 0, sizeof(RenderResult));
 | 
						|
 | 
						|
  if (re) {
 | 
						|
    BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ);
 | 
						|
 | 
						|
    if (re->result) {
 | 
						|
      RenderLayer *rl;
 | 
						|
      RenderView *rv, *rview;
 | 
						|
 | 
						|
      rr->rectx = re->result->rectx;
 | 
						|
      rr->recty = re->result->recty;
 | 
						|
 | 
						|
      /* creates a temporary duplication of views */
 | 
						|
      render_result_views_shallowcopy(rr, re->result);
 | 
						|
 | 
						|
      rv = rr->views.first;
 | 
						|
      rr->have_combined = (rv->rectf != NULL);
 | 
						|
 | 
						|
      /* 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 (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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* 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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* fill provided result struct with what's currently active or done */
 | 
						|
/* this RenderResult struct is the only exception to the rule of a RenderResult */
 | 
						|
/* always having at least one RenderView */
 | 
						|
void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
 | 
						|
{
 | 
						|
  memset(rr, 0, sizeof(RenderResult));
 | 
						|
 | 
						|
  if (re) {
 | 
						|
    BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ);
 | 
						|
 | 
						|
    if (re->result) {
 | 
						|
      RenderLayer *rl;
 | 
						|
      RenderView *rv;
 | 
						|
 | 
						|
      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);
 | 
						|
 | 
						|
      rr->rectf = rv->rectf;
 | 
						|
      rr->rectz = rv->rectz;
 | 
						|
      rr->rect32 = rv->rect32;
 | 
						|
 | 
						|
      /* 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 (rv->rectz == NULL) {
 | 
						|
          rr->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rv->name);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      rr->layers = re->result->layers;
 | 
						|
      rr->views = re->result->views;
 | 
						|
 | 
						|
      rr->xof = re->disprect.xmin;
 | 
						|
      rr->yof = re->disprect.ymin;
 | 
						|
 | 
						|
      rr->stamp_data = re->result->stamp_data;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RE_ReleaseResultImage(Render *re)
 | 
						|
{
 | 
						|
  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);
 | 
						|
 | 
						|
  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)
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
Render *RE_NewRender(const char *name)
 | 
						|
{
 | 
						|
  Render *re;
 | 
						|
 | 
						|
  /* 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);
 | 
						|
  }
 | 
						|
 | 
						|
  RE_InitRenderCB(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)
 | 
						|
{
 | 
						|
  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);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
}
 | 
						|
 | 
						|
/* 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);
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_rw_mutex_end(&re->resultmutex);
 | 
						|
  BLI_rw_mutex_end(&re->partsmutex);
 | 
						|
 | 
						|
  BLI_freelistN(&re->view_layers);
 | 
						|
  BLI_freelistN(&re->r.views);
 | 
						|
 | 
						|
  BKE_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;
 | 
						|
 | 
						|
  render_result_free(re->result);
 | 
						|
  render_result_free(re->pushedresult);
 | 
						|
 | 
						|
  BLI_remlink(&RenderGlobal.renderlist, re);
 | 
						|
  MEM_freeN(re);
 | 
						|
}
 | 
						|
 | 
						|
/* exit blender */
 | 
						|
void RE_FreeAllRender(void)
 | 
						|
{
 | 
						|
  while (RenderGlobal.renderlist.first) {
 | 
						|
    RE_FreeRender(RenderGlobal.renderlist.first);
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef WITH_FREESTYLE
 | 
						|
  /* 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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* on file load, free all re */
 | 
						|
void RE_FreeAllRenderResults(void)
 | 
						|
{
 | 
						|
  Render *re;
 | 
						|
 | 
						|
  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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RE_FreePersistentData(void)
 | 
						|
{
 | 
						|
  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);
 | 
						|
      }
 | 
						|
 | 
						|
      re->engine = NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* ********* initialize state ******** */
 | 
						|
 | 
						|
/* clear full sample and tile flags if needed */
 | 
						|
static int check_mode_full_sample(RenderData *rd)
 | 
						|
{
 | 
						|
  int scemode = rd->scemode;
 | 
						|
 | 
						|
  /* 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 */
 | 
						|
  }
 | 
						|
#else
 | 
						|
  /* can't do this without openexr support */
 | 
						|
  scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE);
 | 
						|
#endif
 | 
						|
 | 
						|
  return scemode;
 | 
						|
}
 | 
						|
 | 
						|
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->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->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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void render_copy_renderdata(RenderData *to, RenderData *from)
 | 
						|
{
 | 
						|
  BLI_freelistN(&to->views);
 | 
						|
  BKE_curvemapping_free_data(&to->mblur_shutter_curve);
 | 
						|
 | 
						|
  *to = *from;
 | 
						|
 | 
						|
  BLI_duplicatelist(&to->views, &from->views);
 | 
						|
  BKE_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();
 | 
						|
}
 | 
						|
 | 
						|
/* 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, 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);
 | 
						|
 | 
						|
  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
 | 
						|
 * doing viewport render. Needs some better integration of BI viewport rendering
 | 
						|
 * into the pipeline.
 | 
						|
 */
 | 
						|
void RE_ChangeModeFlag(Render *re, int flag, bool clear)
 | 
						|
{
 | 
						|
  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;
 | 
						|
 | 
						|
  /* 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;
 | 
						|
 | 
						|
  /* 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);
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
}
 | 
						|
void RE_display_clear_cb(Render *re, void *handle, void (*f)(void *handle, RenderResult *rr))
 | 
						|
{
 | 
						|
  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))
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
void RE_stats_draw_cb(Render *re, void *handle, void (*f)(void *handle, RenderStats *rs))
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
void RE_draw_lock_cb(Render *re, void *handle, void (*f)(void *handle, bool lock))
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
/* ********* 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();
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
    }
 | 
						|
 | 
						|
    WM_opengl_context_dispose(re->gl_context);
 | 
						|
    re->gl_context = NULL;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void *RE_gl_context_get(Render *re)
 | 
						|
{
 | 
						|
  return re->gl_context;
 | 
						|
}
 | 
						|
 | 
						|
void *RE_gpu_context_get(Render *re)
 | 
						|
{
 | 
						|
  if (re->gpu_context == NULL) {
 | 
						|
    re->gpu_context = GPU_context_create(NULL);
 | 
						|
  }
 | 
						|
  return re->gpu_context;
 | 
						|
}
 | 
						|
 | 
						|
/* ********* add object data (later) ******** */
 | 
						|
 | 
						|
/* object is considered fully prepared on correct time etc */
 | 
						|
/* includes lights */
 | 
						|
#if 0
 | 
						|
void RE_AddObject(Render *UNUSED(re), Object *UNUSED(ob))
 | 
						|
{
 | 
						|
}
 | 
						|
#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, false);
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
      /* 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);
 | 
						|
 | 
						|
      /* 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);
 | 
						|
 | 
						|
      rres = render_result_new(re, &re->disprect, 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_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);
 | 
						|
 | 
						|
      BLI_rw_mutex_unlock(&re->resultmutex);
 | 
						|
 | 
						|
      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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
  }
 | 
						|
 | 
						|
  /* now use renderdata and camera to set viewplane */
 | 
						|
  RE_SetCamera(re, camera);
 | 
						|
 | 
						|
  do_render_3d(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;
 | 
						|
 | 
						|
  sce->r.cfra = cfra;
 | 
						|
 | 
						|
  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;
 | 
						|
  }
 | 
						|
 | 
						|
  /* 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);
 | 
						|
 | 
						|
  /* 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;
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
/* bad call... need to think over proper method still */
 | 
						|
static void render_composit_stats(void *arg, const char *str)
 | 
						|
{
 | 
						|
  Render *re = (Render *)arg;
 | 
						|
 | 
						|
  RenderStats i;
 | 
						|
  memcpy(&i, &re->i, sizeof(i));
 | 
						|
  i.infostr = str;
 | 
						|
  re->stats_draw(re->sdh, &i);
 | 
						|
}
 | 
						|
 | 
						|
/* returns fully composited render-result on given time step (in RenderData) */
 | 
						|
static void do_render_composite(Render *re)
 | 
						|
{
 | 
						|
  bNodeTree *ntree = re->pipeline_scene_eval->nodetree;
 | 
						|
  int update_newframe = 0;
 | 
						|
 | 
						|
  if (composite_needs_render(re->pipeline_scene_eval, 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, 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->pipeline_scene_eval);
 | 
						|
    }
 | 
						|
 | 
						|
    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->pipeline_scene_eval,
 | 
						|
                                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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* 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);
 | 
						|
 | 
						|
    Object *ob_camera_eval = DEG_get_evaluated_object(re->pipeline_depsgraph, RE_GetCamera(re));
 | 
						|
    BKE_image_stamp_buf(re->scene,
 | 
						|
                        ob_camera_eval,
 | 
						|
                        (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;
 | 
						|
 | 
						|
  ed = scene->ed;
 | 
						|
 | 
						|
  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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  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");
 | 
						|
 | 
						|
  SEQ_render_new_render_data(re->main,
 | 
						|
                             re->pipeline_depsgraph,
 | 
						|
                             re->scene,
 | 
						|
                             re_x,
 | 
						|
                             re_y,
 | 
						|
                             SEQ_RENDER_SIZE_SCENE,
 | 
						|
                             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 = SEQ_render_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);
 | 
						|
      SEQ_render_imbuf_from_sequencer_space(re->pipeline_scene_eval, 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 top-level. */
 | 
						|
        Editing *ed = re->pipeline_scene_eval->ed;
 | 
						|
        if (ed) {
 | 
						|
          SEQ_relations_free_imbuf(re->pipeline_scene_eval, &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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 | 
						|
 | 
						|
/* main loop: doing sequence + 3d render + compositing */
 | 
						|
static void do_render_all_options(Render *re)
 | 
						|
{
 | 
						|
  bool render_seq = false;
 | 
						|
 | 
						|
  re->current_scene_update(re->suh, re->scene);
 | 
						|
 | 
						|
  BKE_scene_camera_switch_update(re->scene);
 | 
						|
 | 
						|
  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);
 | 
						|
  SEQ_relations_free_all_anim_ibufs(re->scene, re->r.cfra);
 | 
						|
 | 
						|
  if (RE_engine_render(re, true)) {
 | 
						|
    /* 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->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
 | 
						|
 | 
						|
  re->stats_draw(re->sdh, &re->i);
 | 
						|
 | 
						|
  /* save render result stamp if needed */
 | 
						|
  if (re->result != NULL) {
 | 
						|
    /* sequence rendering should have taken care of that already */
 | 
						|
    if (!(render_seq && (re->r.stamp & R_STAMP_STRIPMETA))) {
 | 
						|
      Object *ob_camera_eval = DEG_get_evaluated_object(re->pipeline_depsgraph, RE_GetCamera(re));
 | 
						|
      BKE_render_result_stamp_info(re->scene, ob_camera_eval, 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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
      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 (!active_view) {
 | 
						|
    BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
static bool node_tree_has_composite_output(bNodeTree *ntree)
 | 
						|
{
 | 
						|
  bNode *node;
 | 
						|
 | 
						|
  for (node = ntree->nodes.first; node; node = node->next) {
 | 
						|
    if (ELEM(node->type, CMP_NODE_COMPOSITE, CMP_NODE_OUTPUT_FILE)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
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 (RE_seq_render_active(scene, &scene->r)) {
 | 
						|
    /* Sequencer */
 | 
						|
    if (scene->r.mode & R_BORDER) {
 | 
						|
      BKE_report(reports, RPT_ERROR, "Border rendering is not supported by sequencer");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if ((scemode & R_DOCOMP) && scene->use_nodes) {
 | 
						|
    /* Compositor */
 | 
						|
    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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* Regular Render */
 | 
						|
    if (!render_scene_has_layers_to_render(scene, single_layer)) {
 | 
						|
      BKE_report(reports, RPT_ERROR, "All render layers are disabled");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* check valid camera, without camera render is OK (compo, seq) */
 | 
						|
  if (!check_valid_camera(scene, camera_override, reports)) {
 | 
						|
    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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void update_physics_cache(Render *re,
 | 
						|
                                 Scene *scene,
 | 
						|
                                 ViewLayer *view_layer,
 | 
						|
                                 int UNUSED(anim_init))
 | 
						|
{
 | 
						|
  PTCacheBaker baker;
 | 
						|
 | 
						|
  memset(&baker, 0, sizeof(baker));
 | 
						|
  baker.bmain = re->main;
 | 
						|
  baker.scene = scene;
 | 
						|
  baker.view_layer = view_layer;
 | 
						|
  baker.depsgraph = BKE_scene_ensure_depsgraph(re->main, scene, view_layer);
 | 
						|
  baker.bake = 0;
 | 
						|
  baker.render = 1;
 | 
						|
  baker.anim_init = 1;
 | 
						|
  baker.quick_step = 1;
 | 
						|
 | 
						|
  BKE_ptcache_bake(&baker);
 | 
						|
}
 | 
						|
 | 
						|
void RE_SetActiveRenderView(Render *re, const char *viewname)
 | 
						|
{
 | 
						|
  BLI_strncpy(re->viewname, viewname, sizeof(re->viewname));
 | 
						|
}
 | 
						|
 | 
						|
const char *RE_GetActiveRenderView(Render *re)
 | 
						|
{
 | 
						|
  return re->viewname;
 | 
						|
}
 | 
						|
 | 
						|
/* evaluating scene options for general Blender render */
 | 
						|
static int render_init_from_main(Render *re,
 | 
						|
                                 const 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 business,
 | 
						|
   * 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;
 | 
						|
}
 | 
						|
 | 
						|
static void render_update_depsgraph(Render *re)
 | 
						|
{
 | 
						|
  Scene *scene = re->scene;
 | 
						|
  DEG_evaluate_on_framechange(re->pipeline_depsgraph, CFRA);
 | 
						|
  BKE_scene_update_sound(re->pipeline_depsgraph, re->main);
 | 
						|
}
 | 
						|
 | 
						|
static void render_init_depsgraph(Render *re)
 | 
						|
{
 | 
						|
  Scene *scene = re->scene;
 | 
						|
  ViewLayer *view_layer = BKE_view_layer_default_render(re->scene);
 | 
						|
 | 
						|
  re->pipeline_depsgraph = DEG_graph_new(re->main, scene, view_layer, DAG_EVAL_RENDER);
 | 
						|
  DEG_debug_name_set(re->pipeline_depsgraph, "RENDER PIPELINE");
 | 
						|
 | 
						|
  /* Make sure there is a correct evaluated scene pointer. */
 | 
						|
  DEG_graph_build_for_render_pipeline(re->pipeline_depsgraph);
 | 
						|
 | 
						|
  /* Update immediately so we have proper evaluated scene. */
 | 
						|
  render_update_depsgraph(re);
 | 
						|
 | 
						|
  re->pipeline_scene_eval = DEG_get_evaluated_scene(re->pipeline_depsgraph);
 | 
						|
}
 | 
						|
 | 
						|
/* general Blender frame render call */
 | 
						|
void RE_RenderFrame(Render *re,
 | 
						|
                    Main *bmain,
 | 
						|
                    Scene *scene,
 | 
						|
                    ViewLayer *single_layer,
 | 
						|
                    Object *camera_override,
 | 
						|
                    int frame,
 | 
						|
                    const bool write_still)
 | 
						|
{
 | 
						|
  render_callback_exec_id(re, re->main, &scene->id, BKE_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_init_from_main(re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) {
 | 
						|
    const RenderData rd = scene->r;
 | 
						|
    MEM_reset_peak_memory();
 | 
						|
 | 
						|
    render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE);
 | 
						|
 | 
						|
    render_init_depsgraph(re);
 | 
						|
 | 
						|
    do_render_all_options(re);
 | 
						|
 | 
						|
    if (write_still && !G.is_break) {
 | 
						|
      if (BKE_imtype_is_movie(rd.im_format.imtype)) {
 | 
						|
        /* operator checks this but in case its called from elsewhere */
 | 
						|
        printf("Error: can't write single images with a movie format!\n");
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        char name[FILE_MAX];
 | 
						|
        BKE_image_path_from_imformat(name,
 | 
						|
                                     rd.pic,
 | 
						|
                                     BKE_main_blendfile_path(bmain),
 | 
						|
                                     scene->r.cfra,
 | 
						|
                                     &rd.im_format,
 | 
						|
                                     (rd.scemode & R_EXTENSION) != 0,
 | 
						|
                                     false,
 | 
						|
                                     NULL);
 | 
						|
 | 
						|
        /* reports only used for Movie */
 | 
						|
        do_write_image_or_movie(re, bmain, scene, NULL, 0, name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* keep after file save */
 | 
						|
    render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_POST);
 | 
						|
    if (write_still) {
 | 
						|
      render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_WRITE);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  render_callback_exec_id(re,
 | 
						|
                          re->main,
 | 
						|
                          &scene->id,
 | 
						|
                          G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
 | 
						|
 | 
						|
  RE_CleanAfterRender(re);
 | 
						|
 | 
						|
  /* UGLY WARNING */
 | 
						|
  G.is_rendering = false;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef WITH_FREESTYLE
 | 
						|
 | 
						|
/* Not freestyle specific, currently only used by free-style. */
 | 
						|
static void change_renderdata_engine(Render *re, const char *new_engine)
 | 
						|
{
 | 
						|
  if (!STREQ(re->r.engine, new_engine)) {
 | 
						|
    if (re->engine) {
 | 
						|
      RE_engine_free(re->engine);
 | 
						|
      re->engine = NULL;
 | 
						|
    }
 | 
						|
    BLI_strncpy(re->r.engine, new_engine, sizeof(re->r.engine));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool use_eevee_for_freestyle_render(Render *re)
 | 
						|
{
 | 
						|
  RenderEngineType *type = RE_engines_find(re->r.engine);
 | 
						|
  return !(type->flag & RE_USE_CUSTOM_FREESTYLE);
 | 
						|
}
 | 
						|
 | 
						|
void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render)
 | 
						|
{
 | 
						|
  re->result_ok = 0;
 | 
						|
  if (render_init_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) {
 | 
						|
    if (render) {
 | 
						|
      char scene_engine[32];
 | 
						|
      BLI_strncpy(scene_engine, re->r.engine, sizeof(scene_engine));
 | 
						|
      if (use_eevee_for_freestyle_render(re)) {
 | 
						|
        change_renderdata_engine(re, RE_engine_id_BLENDER_EEVEE);
 | 
						|
      }
 | 
						|
 | 
						|
      do_render_3d(re);
 | 
						|
 | 
						|
      change_renderdata_engine(re, scene_engine);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  re->result_ok = 1;
 | 
						|
}
 | 
						|
 | 
						|
void RE_RenderFreestyleExternal(Render *re)
 | 
						|
{
 | 
						|
  if (re->test_break(re->tbh)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  FRS_init_stroke_renderer(re);
 | 
						|
 | 
						|
  LISTBASE_FOREACH (RenderView *, rv, &re->result->views) {
 | 
						|
    RE_SetActiveRenderView(re, rv->name);
 | 
						|
 | 
						|
    ViewLayer *active_view_layer = BLI_findlink(&re->view_layers, re->active_view_layer);
 | 
						|
    FRS_begin_stroke_rendering(re);
 | 
						|
 | 
						|
    LISTBASE_FOREACH (ViewLayer *, view_layer, &re->view_layers) {
 | 
						|
      if ((re->r.scemode & R_SINGLE_LAYER) && view_layer != active_view_layer) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (FRS_is_freestyle_enabled(view_layer)) {
 | 
						|
        FRS_do_stroke_rendering(re, view_layer);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    FRS_end_stroke_rendering(re);
 | 
						|
  }
 | 
						|
}
 | 
						|
#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? */
 | 
						|
  render_callback_exec_null(re, G_MAIN, BKE_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);
 | 
						|
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
static void get_videos_dimensions(const Render *re,
 | 
						|
                                  const 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;
 | 
						|
 | 
						|
  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);
 | 
						|
}
 | 
						|
 | 
						|
/* saves images to disk */
 | 
						|
void RE_RenderAnim(Render *re,
 | 
						|
                   Main *bmain,
 | 
						|
                   Scene *scene,
 | 
						|
                   ViewLayer *single_layer,
 | 
						|
                   Object *camera_override,
 | 
						|
                   int sfra,
 | 
						|
                   int efra,
 | 
						|
                   int tfra)
 | 
						|
{
 | 
						|
  /* Call hooks before taking a copy of scene->r, so user can alter the render settings prior to
 | 
						|
   * copying (e.g. alter the output path). */
 | 
						|
  render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_INIT);
 | 
						|
 | 
						|
  const RenderData rd = scene->r;
 | 
						|
  bMovieHandle *mh = NULL;
 | 
						|
  const int cfrao = rd.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(rd.im_format.imtype);
 | 
						|
  const bool is_multiview_name = ((rd.scemode & R_MULTIVIEW) != 0 &&
 | 
						|
                                  (rd.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL));
 | 
						|
 | 
						|
  /* do not fully call for each frame, it initializes & pops output window */
 | 
						|
  if (!render_init_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  render_init_depsgraph(re);
 | 
						|
 | 
						|
  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(rd.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],
 | 
						|
                           re->pipeline_scene_eval,
 | 
						|
                           &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);
 | 
						|
      RE_CleanAfterRender(re);
 | 
						|
      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];
 | 
						|
 | 
						|
      /* A feedback loop exists here -- 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);
 | 
						|
        const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
 | 
						|
            re->pipeline_depsgraph, ctime);
 | 
						|
        BKE_animsys_evaluate_animdata(&scene->id, adt, &anim_eval_context, ADT_RECALC_ALL, false);
 | 
						|
      }
 | 
						|
 | 
						|
      render_update_depsgraph(re);
 | 
						|
 | 
						|
      /* only border now, todo: camera lens. (ton) */
 | 
						|
      render_init_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;
 | 
						|
      }
 | 
						|
 | 
						|
      nfra += tfra;
 | 
						|
 | 
						|
      /* Touch/NoOverwrite options are only valid for image's */
 | 
						|
      if (is_movie == false) {
 | 
						|
        if (rd.mode & (R_NO_OVERWRITE | R_TOUCH)) {
 | 
						|
          BKE_image_path_from_imformat(name,
 | 
						|
                                       rd.pic,
 | 
						|
                                       BKE_main_blendfile_path(bmain),
 | 
						|
                                       scene->r.cfra,
 | 
						|
                                       &rd.im_format,
 | 
						|
                                       (rd.scemode & R_EXTENSION) != 0,
 | 
						|
                                       true,
 | 
						|
                                       NULL);
 | 
						|
        }
 | 
						|
 | 
						|
        if (rd.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 (rd.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 callbacks before rendering, before the scene is updated */
 | 
						|
      render_callback_exec_id(re, re->main, &scene->id, BKE_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 ((rd.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) {
 | 
						|
        /* keep after file save */
 | 
						|
        render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_POST);
 | 
						|
        render_callback_exec_id(re, re->main, &scene->id, BKE_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;
 | 
						|
 | 
						|
  render_callback_exec_id(re,
 | 
						|
                          re->main,
 | 
						|
                          &scene->id,
 | 
						|
                          G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
 | 
						|
  BKE_sound_reset_scene_specs(re->pipeline_scene_eval);
 | 
						|
 | 
						|
  RE_CleanAfterRender(re);
 | 
						|
 | 
						|
  /* UGLY WARNING */
 | 
						|
  G.is_rendering = false;
 | 
						|
}
 | 
						|
 | 
						|
void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
 | 
						|
{
 | 
						|
  Object *camera;
 | 
						|
  int winx, winy;
 | 
						|
 | 
						|
  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->main = bmain;
 | 
						|
  re->scene = sce;
 | 
						|
 | 
						|
  camera = RE_GetCamera(re);
 | 
						|
  RE_SetCamera(re, camera);
 | 
						|
 | 
						|
  do_render_3d(re);
 | 
						|
}
 | 
						|
 | 
						|
void RE_CleanAfterRender(Render *re)
 | 
						|
{
 | 
						|
  /* Destroy the opengl context in the correct thread. */
 | 
						|
  RE_gl_context_destroy(re);
 | 
						|
  if (re->pipeline_depsgraph != NULL) {
 | 
						|
    DEG_graph_free(re->pipeline_depsgraph);
 | 
						|
  }
 | 
						|
  re->pipeline_depsgraph = NULL;
 | 
						|
  re->pipeline_scene_eval = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* note; repeated win/disprect calc... solve that nicer, also in compo */
 | 
						|
 | 
						|
/* only the temp file! */
 | 
						|
bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
 | 
						|
{
 | 
						|
  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;
 | 
						|
 | 
						|
  /* 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;
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
  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);
 | 
						|
 | 
						|
  return success;
 | 
						|
}
 | 
						|
 | 
						|
void RE_init_threadcount(Render *re)
 | 
						|
{
 | 
						|
  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 default 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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* 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;
 | 
						|
}
 | 
						|
 | 
						|
bool RE_passes_have_name(struct RenderLayer *rl)
 | 
						|
{
 | 
						|
  LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) {
 | 
						|
    if (!STREQ(rp->name, "Combined")) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
RenderPass *RE_pass_find_by_name(volatile RenderLayer *rl, const char *name, const char *viewname)
 | 
						|
{
 | 
						|
  RenderPass *rp = NULL;
 | 
						|
 | 
						|
  for (rp = rl->passes.last; rp; rp = rp->prev) {
 | 
						|
    if (STREQ(rp->name, name)) {
 | 
						|
      if (viewname == NULL || viewname[0] == '\0') {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      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); \
 | 
						|
  } \
 | 
						|
  ((void)0)
 | 
						|
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
/* 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 render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA");
 | 
						|
}
 |