/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation * * SPDX-License-Identifier: Apache-2.0 */ #include "scene/camera.h" #include "scene/bake.h" #include "scene/osl.h" #include "scene/scene.h" #include "blender/sync.h" #include "blender/util.h" #include "util/log.h" #include "BKE_action.hh" #include "BKE_camera.h" #include "BKE_scene.hh" #include "DEG_depsgraph_query.hh" #include "DNA_camera_types.h" #include "DNA_light_types.h" #include "RE_engine.h" CCL_NAMESPACE_BEGIN /* Blender Camera Intermediate: we first convert both the offline and 3d view * render camera to this, and from there convert to our native camera format. */ class BlenderCamera { public: explicit BlenderCamera(const blender::RenderData &b_render) { full_width = render_width = render_resolution_x(b_render); full_height = render_height = render_resolution_y(b_render); }; blender::PointerRNA custom_props; string custom_bytecode; string custom_bytecode_hash; string custom_filepath; float nearclip = 1e-5f; float farclip = 1e5f; CameraType type = CAMERA_PERSPECTIVE; float ortho_scale = 1.0f; float lens = 50.0f; float shuttertime = 1.0f; MotionPosition motion_position = MOTION_POSITION_CENTER; array shutter_curve; Camera::RollingShutterType rolling_shutter_type = Camera::ROLLING_SHUTTER_NONE; float rolling_shutter_duration = 0.1f; float aperturesize = 0.0f; uint apertureblades = 0; float aperturerotation = 0.0f; float focaldistance = 10.0f; float2 shift = zero_float2(); float2 offset = zero_float2(); float zoom = 1.0f; float2 pixelaspect = one_float2(); float aperture_ratio = 1.0f; PanoramaType panorama_type = PANORAMA_EQUIRECTANGULAR; float fisheye_fov = M_PI_F; float fisheye_lens = 10.5f; float latitude_min = -M_PI_2_F; float latitude_max = M_PI_2_F; float longitude_min = -M_PI_F; float longitude_max = M_PI_F; bool use_spherical_stereo = false; float interocular_distance = 0.065f; float convergence_distance = 30.0f * 0.065f; bool use_pole_merge = false; float pole_merge_angle_from = (60.0f * M_PI_F / 180.0f); float pole_merge_angle_to = (75.0f * M_PI_F / 180.0f); float fisheye_polynomial_k0 = 0.0f; float fisheye_polynomial_k1 = 0.0f; float fisheye_polynomial_k2 = 0.0f; float fisheye_polynomial_k3 = 0.0f; float fisheye_polynomial_k4 = 0.0f; float central_cylindrical_range_u_min = -M_PI_F; float central_cylindrical_range_u_max = M_PI_F; float central_cylindrical_range_v_min = -1.0f; float central_cylindrical_range_v_max = 1.0f; float central_cylindrical_radius = 1.0f; enum { AUTO, HORIZONTAL, VERTICAL } sensor_fit = AUTO; float sensor_width = 36.0f; float sensor_height = 24.0f; int full_width = 0; int full_height = 0; int render_width = 0; int render_height = 0; BoundBox2D border = BoundBox2D(); BoundBox2D viewport_camera_border = BoundBox2D(); BoundBox2D pano_viewplane = BoundBox2D(); float pano_aspectratio = 0.0f; float passepartout_alpha = 0.5f; Transform matrix = transform_identity(); float offscreen_dicing_scale = 1.0f; int motion_steps = 0; }; static float blender_camera_focal_distance(blender::RenderEngine &b_engine, blender::Object &b_ob, blender::Camera &b_camera, BlenderCamera *bcam) { blender::Object *b_dof_object = b_camera.dof.focus_object; if (!b_dof_object) { return b_camera.dof.focus_distance; } Transform dofmat = get_transform(b_dof_object->object_to_world()); const string focus_subtarget = b_camera.dof.focus_subtarget; if (b_dof_object->pose && !focus_subtarget.empty()) { if (blender::bPoseChannel *pchan = BKE_pose_channel_find_name(b_dof_object->pose, focus_subtarget.c_str())) { dofmat = dofmat * get_transform(blender::float4x4(pchan->pose_mat)); } } /* for dof object, return distance along camera Z direction */ blender::float4x4 b_ob_matrix; RE_engine_get_camera_model_matrix( &b_engine, &b_ob, bcam->use_spherical_stereo, b_ob_matrix.base_ptr()); const Transform obmat = transform_clear_scale(get_transform(b_ob_matrix)); const float3 view_dir = normalize(transform_get_column(&obmat, 2)); const float3 dof_dir = transform_get_column(&obmat, 3) - transform_get_column(&dofmat, 3); return fabsf(dot(view_dir, dof_dir)); } static PanoramaType blender_panorama_type_to_cycles(const int type) { switch (type) { case blender::CAM_PANORAMA_EQUIRECTANGULAR: return PANORAMA_EQUIRECTANGULAR; case blender::CAM_PANORAMA_EQUIANGULAR_CUBEMAP_FACE: return PANORAMA_EQUIANGULAR_CUBEMAP_FACE; case blender::CAM_PANORAMA_MIRRORBALL: return PANORAMA_MIRRORBALL; case blender::CAM_PANORAMA_FISHEYE_EQUIDISTANT: return PANORAMA_FISHEYE_EQUIDISTANT; case blender::CAM_PANORAMA_FISHEYE_EQUISOLID: return PANORAMA_FISHEYE_EQUISOLID; case blender::CAM_PANORAMA_FISHEYE_LENS_POLYNOMIAL: return PANORAMA_FISHEYE_LENS_POLYNOMIAL; case blender::CAM_PANORAMA_CENTRAL_CYLINDRICAL: return PANORAMA_CENTRAL_CYLINDRICAL; } /* Could happen if loading a newer file that has an unsupported type. */ return PANORAMA_FISHEYE_EQUISOLID; } /* Cycles equivalent of view3d_stereo3d_active(). */ static bool is_viewport_stereo3d_active(const blender::RenderData &b_render, const blender::View3D *b_v3d, const blender::RegionView3D *b_rv3d) { if ((b_render.scemode & blender::R_MULTIVIEW) == 0) { return false; } if (!b_v3d || !b_rv3d) { return false; } if ((b_v3d->camera == nullptr) || (b_v3d->camera->type != blender::OB_CAMERA) || b_rv3d->persp != blender::RV3D_CAMOB) { return false; } switch (b_v3d->stereo3d_camera) { case blender::STEREO_MONO_ID: return false; case blender::STEREO_3D_ID: /* Cycles does not have access to window to check WM_stereo3d_enabled(), so assume it is * enabled. */ if (((b_render.views_format & blender::SCE_VIEWS_FORMAT_MULTIVIEW) != 0) && !BKE_scene_multiview_is_stereo3d(&b_render)) { return false; } return true; case blender::STEREO_LEFT_ID: case blender::STEREO_RIGHT_ID: return true; } return false; } static void blender_camera_from_object(BlenderCamera *bcam, blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::View3D *b_v3d, blender::RegionView3D *b_rv3d, blender::Object &b_ob, blender::Main &b_data, bool skip_panorama = false) { blender::ID &b_ob_data = *static_cast(b_ob.data); if (GS(b_ob_data.name) == blender::ID_CA) { blender::Camera &b_camera = blender::id_cast(b_ob_data); bcam->nearclip = b_camera.clip_start; bcam->farclip = b_camera.clip_end; switch (b_camera.type) { case blender::CAM_ORTHO: bcam->type = CAMERA_ORTHOGRAPHIC; break; case blender::CAM_CUSTOM: bcam->type = skip_panorama ? CAMERA_PERSPECTIVE : CAMERA_CUSTOM; break; case blender::CAM_PANO: bcam->type = skip_panorama ? CAMERA_PERSPECTIVE : CAMERA_PANORAMA; break; case blender::CAM_PERSP: default: bcam->type = CAMERA_PERSPECTIVE; break; } bcam->panorama_type = blender_panorama_type_to_cycles(b_camera.panorama_type); bcam->fisheye_fov = b_camera.fisheye_fov; bcam->fisheye_lens = b_camera.fisheye_lens; bcam->latitude_min = b_camera.latitude_min; bcam->latitude_max = b_camera.latitude_max; bcam->longitude_min = b_camera.longitude_min; bcam->longitude_max = b_camera.longitude_max; bcam->fisheye_polynomial_k0 = b_camera.fisheye_polynomial_k0; bcam->fisheye_polynomial_k1 = b_camera.fisheye_polynomial_k1; bcam->fisheye_polynomial_k2 = b_camera.fisheye_polynomial_k2; bcam->fisheye_polynomial_k3 = b_camera.fisheye_polynomial_k3; bcam->fisheye_polynomial_k4 = b_camera.fisheye_polynomial_k4; bcam->central_cylindrical_range_u_min = b_camera.central_cylindrical_range_u_min; bcam->central_cylindrical_range_u_max = b_camera.central_cylindrical_range_u_max; bcam->central_cylindrical_range_v_min = b_camera.central_cylindrical_range_v_min; bcam->central_cylindrical_range_v_max = b_camera.central_cylindrical_range_v_max; bcam->central_cylindrical_radius = b_camera.central_cylindrical_radius; bcam->interocular_distance = b_camera.stereo.interocular_distance; if (b_camera.stereo.convergence_mode == blender::CAM_S3D_PARALLEL) { bcam->convergence_distance = FLT_MAX; } else { bcam->convergence_distance = b_camera.stereo.convergence_distance; } bcam->use_spherical_stereo = RE_engine_get_spherical_stereo(&b_engine, &b_ob); bcam->use_pole_merge = b_camera.stereo.flag & blender::CAM_S3D_POLE_MERGE; bcam->pole_merge_angle_from = b_camera.stereo.pole_merge_angle_from; bcam->pole_merge_angle_to = b_camera.stereo.pole_merge_angle_to; bcam->ortho_scale = b_camera.ortho_scale; bcam->lens = b_camera.lens; bcam->passepartout_alpha = (b_camera.flag & blender::CAM_SHOWPASSEPARTOUT) != 0 ? b_camera.passepartalpha : 0.0f; if (b_camera.dof.flag & blender::CAM_DOF_ENABLED) { /* allow f/stop number to change aperture_size but still * give manual control over aperture radius */ float fstop = b_camera.dof.aperture_fstop; fstop = max(fstop, 1e-5f); if (bcam->type == CAMERA_ORTHOGRAPHIC || bcam->type == CAMERA_CUSTOM) { bcam->aperturesize = 1.0f / (2.0f * fstop); } else { bcam->aperturesize = (bcam->lens * 1e-3f) / (2.0f * fstop); } bcam->apertureblades = b_camera.dof.aperture_blades; bcam->aperturerotation = b_camera.dof.aperture_rotation; bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera, bcam); bcam->aperture_ratio = b_camera.dof.aperture_ratio; } else { /* DOF is turned of for the camera. */ bcam->aperturesize = 0.0f; bcam->apertureblades = 0; bcam->aperturerotation = 0.0f; bcam->focaldistance = 0.0f; bcam->aperture_ratio = 1.0f; } if (is_viewport_stereo3d_active(b_render, b_v3d, b_rv3d)) { /* Cycles equivalent of view3d_stereo3d_setup(). */ using eStereoViews = blender::eStereoViews; const eStereoViews multiview_eye = b_v3d->stereo3d_camera != blender::STEREO_3D_ID ? eStereoViews(b_v3d->stereo3d_camera) : eStereoViews(b_v3d->multiview_eye); const bool is_left = multiview_eye == blender::STEREO_LEFT_ID; const char *viewname = is_left ? STEREO_LEFT_NAME : STEREO_RIGHT_NAME; bcam->shift.x = BKE_camera_multiview_shift_x(&b_render, &b_ob, viewname); } else { bcam->shift.x = RE_engine_get_camera_shift_x(&b_engine, &b_ob, bcam->use_spherical_stereo); } bcam->shift.y = b_camera.shifty; bcam->sensor_width = b_camera.sensor_x; bcam->sensor_height = b_camera.sensor_y; if (b_camera.sensor_fit == blender::CAMERA_SENSOR_FIT_AUTO) { bcam->sensor_fit = BlenderCamera::AUTO; } else if (b_camera.sensor_fit == blender::CAMERA_SENSOR_FIT_HOR) { bcam->sensor_fit = BlenderCamera::HORIZONTAL; } else { bcam->sensor_fit = BlenderCamera::VERTICAL; } if (bcam->type == CAMERA_CUSTOM) { blender::PointerRNA camera_rna_ptr = RNA_id_pointer_create(&b_camera.id); bcam->custom_props = RNA_pointer_get(&camera_rna_ptr, "cycles_custom"); bcam->custom_bytecode_hash = b_camera.custom_bytecode_hash; if (!bcam->custom_bytecode_hash.empty()) { bcam->custom_bytecode = b_camera.custom_bytecode; } else { bcam->custom_filepath = blender_absolute_path( b_data, &b_camera.id, b_camera.custom_filepath); } } } else if (GS(b_ob_data.name) == blender::ID_LA) { /* Can also look through spot light. */ const blender::Light &b_light = reinterpret_cast(b_ob_data); const float lens = 16.0f / tanf(b_light.spotsize * 0.5f); if (lens > 0.0f) { bcam->lens = lens; } } bcam->motion_steps = object_motion_steps(b_ob, b_ob); } static Transform blender_camera_matrix(const Transform &tfm, const CameraType type, const PanoramaType panorama_type) { Transform result; if (type == CAMERA_PANORAMA) { if (panorama_type == PANORAMA_MIRRORBALL) { /* Mirror ball camera is looking into the negative Y direction * which matches texture mirror ball mapping. */ result = tfm * make_transform( 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); } else { /* Make it so environment camera needs to be pointed in the direction * of the positive x-axis to match an environment texture, this way * it is looking at the center of the texture */ result = tfm * make_transform( 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f); } } else { /* Note the blender camera points along the negative z-axis. */ result = tfm * transform_scale(1.0f, 1.0f, -1.0f); } return transform_clear_scale(result); } static void blender_camera_viewplane(BlenderCamera *bcam, const int width, const int height, BoundBox2D &viewplane, float &aspectratio, float &sensor_size) { /* dimensions */ const float xratio = (float)width * bcam->pixelaspect.x; const float yratio = (float)height * bcam->pixelaspect.y; /* compute x/y aspect and ratio */ float2 aspect; bool horizontal_fit; /* sensor fitting */ if (bcam->sensor_fit == BlenderCamera::AUTO) { horizontal_fit = (xratio > yratio); sensor_size = bcam->sensor_width; } else if (bcam->sensor_fit == BlenderCamera::HORIZONTAL) { horizontal_fit = true; sensor_size = bcam->sensor_width; } else { horizontal_fit = false; sensor_size = bcam->sensor_height; } if (horizontal_fit) { aspectratio = xratio / yratio; aspect = make_float2(aspectratio, 1.0f); } else { aspectratio = yratio / xratio; aspect = make_float2(1.0f, aspectratio); } /* modify aspect for orthographic scale */ if (bcam->type == CAMERA_ORTHOGRAPHIC) { aspect *= bcam->ortho_scale / (aspectratio * 2.0f); aspectratio = bcam->ortho_scale / 2.0f; } if (bcam->type == CAMERA_PANORAMA || bcam->type == CAMERA_CUSTOM) { /* Account for camera shift. */ float2 dv = bcam->shift; if (bcam->pano_aspectratio != 0.0f) { dv *= aspectratio / bcam->pano_aspectratio; } /* Set viewplane for panoramic or custom camera. */ viewplane = bcam->pano_viewplane.offset(dv); } else { /* Account for camera shift and 3d camera view offset. */ const float2 dv = 2.0f * (aspectratio * bcam->shift + bcam->offset * aspect * 2.0f); /* Set viewplane for perspective or orthographic camera. */ viewplane = (BoundBox2D(aspect) * bcam->zoom).offset(dv); } } class BlenderCameraParamQuery : public OSLCameraParamQuery { public: BlenderCameraParamQuery(blender::PointerRNA custom_props) : custom_props(custom_props) {} ~BlenderCameraParamQuery() override = default; bool get_float(ustring name, vector &data) override { blender::PropertyRNA *prop = get_prop(name); if (!prop) { return false; } if (RNA_property_array_check(prop)) { data.resize(RNA_property_array_length(&custom_props, prop)); RNA_property_float_get_array(&custom_props, prop, data.data()); } else { data.resize(1); data[0] = RNA_property_float_get(&custom_props, prop); } return true; } bool get_int(ustring name, vector &data) override { blender::PropertyRNA *prop = get_prop(name); if (!prop) { return false; } int array_len = 0; if (RNA_property_array_check(prop)) { array_len = RNA_property_array_length(&custom_props, prop); } /* OSL represents booleans as integers, but we represent them as boolean-type * properties in RNA, so convert here. */ if (RNA_property_type(prop) == blender::PROP_BOOLEAN) { if (array_len > 0) { /* Can't use std::vector here since it's a weird special case. */ array bool_data(array_len); RNA_property_boolean_get_array(&custom_props, prop, bool_data.data()); std::copy(bool_data.begin(), bool_data.end(), std::back_inserter(data)); } else { data.push_back(RNA_property_boolean_get(&custom_props, prop)); } } else if (RNA_property_type(prop) == blender::PROP_ENUM) { const char *identifier = ""; const int value = RNA_property_enum_get(&custom_props, prop); if (RNA_property_enum_identifier(nullptr, &custom_props, prop, value, &identifier)) { data.push_back(atoi(identifier)); } else { data.push_back(value); } } else { if (array_len > 0) { data.resize(array_len); RNA_property_int_get_array(&custom_props, prop, data.data()); } else { data.push_back(RNA_property_int_get(&custom_props, prop)); } } return true; } bool get_string(ustring name, string &data) override { blender::PropertyRNA *prop = get_prop(name); if (!prop) { return false; } data = RNA_property_string_get(&custom_props, prop); return true; } private: blender::PointerRNA custom_props; blender::PropertyRNA *get_prop(ustring param) { string name = string_printf("[\"%s\"]", param.c_str()); return RNA_struct_find_property(&custom_props, name.c_str()); } }; static void blender_camera_sync(Camera *cam, Scene *scene, BlenderCamera *bcam, const int width, const int height, const char *viewname, blender::PointerRNA *cscene) { float aspectratio; float sensor_size; /* viewplane */ BoundBox2D viewplane; blender_camera_viewplane(bcam, width, height, viewplane, aspectratio, sensor_size); cam->set_viewplane_left(viewplane.left); cam->set_viewplane_right(viewplane.right); cam->set_viewplane_top(viewplane.top); cam->set_viewplane_bottom(viewplane.bottom); cam->set_full_width(width); cam->set_full_height(height); /* Set panorama or custom sensor. */ if ((bcam->type == CAMERA_PANORAMA && (bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID || bcam->panorama_type == PANORAMA_FISHEYE_LENS_POLYNOMIAL)) || bcam->type == CAMERA_CUSTOM) { const float fit_xratio = (float)bcam->render_width * bcam->pixelaspect.x; const float fit_yratio = (float)bcam->render_height * bcam->pixelaspect.y; bool horizontal_fit; float sensor_size; if (bcam->sensor_fit == BlenderCamera::AUTO) { horizontal_fit = (fit_xratio > fit_yratio); sensor_size = bcam->sensor_width; } else if (bcam->sensor_fit == BlenderCamera::HORIZONTAL) { horizontal_fit = true; sensor_size = bcam->sensor_width; } else { /* vertical */ horizontal_fit = false; sensor_size = bcam->sensor_height; } if (horizontal_fit) { cam->set_sensorwidth(sensor_size); cam->set_sensorheight(sensor_size * fit_yratio / fit_xratio); } else { cam->set_sensorwidth(sensor_size * fit_xratio / fit_yratio); cam->set_sensorheight(sensor_size); } } /* Sync custom camera parameters. */ if (scene != nullptr) { if (bcam->type == CAMERA_CUSTOM) { BlenderCameraParamQuery params(bcam->custom_props); cam->set_osl_camera( scene, params, bcam->custom_filepath, bcam->custom_bytecode_hash, bcam->custom_bytecode); } else { cam->clear_osl_camera(scene); } } /* clipping distances */ cam->set_nearclip(bcam->nearclip); cam->set_farclip(bcam->farclip); /* type */ cam->set_camera_type(bcam->type); /* panorama */ cam->set_panorama_type(bcam->panorama_type); cam->set_fisheye_fov(bcam->fisheye_fov); cam->set_fisheye_lens(bcam->fisheye_lens); cam->set_latitude_min(bcam->latitude_min); cam->set_latitude_max(bcam->latitude_max); cam->set_fisheye_polynomial_k0(bcam->fisheye_polynomial_k0); cam->set_fisheye_polynomial_k1(bcam->fisheye_polynomial_k1); cam->set_fisheye_polynomial_k2(bcam->fisheye_polynomial_k2); cam->set_fisheye_polynomial_k3(bcam->fisheye_polynomial_k3); cam->set_fisheye_polynomial_k4(bcam->fisheye_polynomial_k4); cam->set_longitude_min(bcam->longitude_min); cam->set_longitude_max(bcam->longitude_max); cam->set_central_cylindrical_range_u_min(bcam->central_cylindrical_range_u_min); cam->set_central_cylindrical_range_u_max(bcam->central_cylindrical_range_u_max); cam->set_central_cylindrical_range_v_min(bcam->central_cylindrical_range_v_min / bcam->central_cylindrical_radius); cam->set_central_cylindrical_range_v_max(bcam->central_cylindrical_range_v_max / bcam->central_cylindrical_radius); /* panorama stereo */ cam->set_interocular_distance(bcam->interocular_distance); cam->set_convergence_distance(bcam->convergence_distance); cam->set_use_spherical_stereo(bcam->use_spherical_stereo); if (cam->get_use_spherical_stereo()) { if (strcmp(viewname, "left") == 0) { cam->set_stereo_eye(Camera::STEREO_LEFT); } else if (strcmp(viewname, "right") == 0) { cam->set_stereo_eye(Camera::STEREO_RIGHT); } else { cam->set_stereo_eye(Camera::STEREO_NONE); } } cam->set_use_pole_merge(bcam->use_pole_merge); cam->set_pole_merge_angle_from(bcam->pole_merge_angle_from); cam->set_pole_merge_angle_to(bcam->pole_merge_angle_to); /* anamorphic lens bokeh */ cam->set_aperture_ratio(bcam->aperture_ratio); /* perspective */ cam->set_fov(2.0f * atanf((0.5f * sensor_size) / bcam->lens / aspectratio)); cam->set_focaldistance(bcam->focaldistance); cam->set_aperturesize(bcam->aperturesize); cam->set_blades(bcam->apertureblades); cam->set_bladesrotation(bcam->aperturerotation); /* transform */ cam->set_matrix(blender_camera_matrix(bcam->matrix, bcam->type, bcam->panorama_type)); array motion = cam->get_motion(); motion.resize(bcam->motion_steps, cam->get_matrix()); if (bcam->motion_steps != 0) { motion[bcam->motion_steps / 2] = cam->get_matrix(); } cam->set_motion(motion); cam->set_use_perspective_motion(false); cam->set_shuttertime(bcam->shuttertime); cam->set_fov_pre(cam->get_fov()); cam->set_fov_post(cam->get_fov()); cam->set_motion_position(bcam->motion_position); cam->set_rolling_shutter_type(bcam->rolling_shutter_type); cam->set_rolling_shutter_duration(bcam->rolling_shutter_duration); cam->set_shutter_curve(bcam->shutter_curve); /* border */ cam->set_border_left(bcam->border.left); cam->set_border_right(bcam->border.right); cam->set_border_top(bcam->border.top); cam->set_border_bottom(bcam->border.bottom); cam->set_viewport_camera_border_left(bcam->viewport_camera_border.left); cam->set_viewport_camera_border_right(bcam->viewport_camera_border.right); cam->set_viewport_camera_border_top(bcam->viewport_camera_border.top); cam->set_viewport_camera_border_bottom(bcam->viewport_camera_border.bottom); bcam->offscreen_dicing_scale = RNA_float_get(cscene, "offscreen_dicing_scale"); cam->set_offscreen_dicing_scale(bcam->offscreen_dicing_scale); } /* Sync Render Camera */ static MotionPosition blender_motion_blur_position_type_to_cycles(const int type) { switch (type) { case blender::SCE_MB_START: return MOTION_POSITION_START; case blender::SCE_MB_CENTER: return MOTION_POSITION_CENTER; case blender::SCE_MB_END: return MOTION_POSITION_END; } /* Could happen if loading a newer file that has an unsupported type. */ return MOTION_POSITION_CENTER; } void BlenderSync::sync_camera(const blender::RenderData &b_render, const int width, const int height, const char *viewname) { BlenderCamera bcam(b_render); /* pixel aspect */ bcam.pixelaspect.x = b_render.xasp; bcam.pixelaspect.y = b_render.yasp; bcam.shuttertime = b_render.motion_blur_shutter; bcam.motion_position = blender_motion_blur_position_type_to_cycles( b_render.motion_blur_position); const blender::CurveMapping &b_shutter_curve = b_render.mblur_shutter_curve; curvemapping_to_array(b_shutter_curve, bcam.shutter_curve, RAMP_TABLE_SIZE); blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene->id); blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles"); bcam.rolling_shutter_type = (Camera::RollingShutterType)get_enum( cscene, "rolling_shutter_type", Camera::ROLLING_SHUTTER_NUM_TYPES, Camera::ROLLING_SHUTTER_NONE); bcam.rolling_shutter_duration = RNA_float_get(&cscene, "rolling_shutter_duration"); /* border */ if (b_render.mode & blender::R_BORDER) { bcam.border.left = b_render.border.xmin; bcam.border.right = b_render.border.xmax; bcam.border.bottom = b_render.border.ymin; bcam.border.top = b_render.border.ymax; } /* camera object */ blender::Object *b_ob = get_camera_object(nullptr, nullptr); if (b_ob) { blender::float4x4 b_ob_matrix; blender_camera_from_object(&bcam, *b_engine, b_render, nullptr, nullptr, *b_ob, *b_data); RE_engine_get_camera_model_matrix( b_engine, b_ob, bcam.use_spherical_stereo, b_ob_matrix.base_ptr()); bcam.matrix = get_transform(b_ob_matrix); scene->bake_manager->set_use_camera(b_render.bake.view_from == blender::R_BAKE_VIEW_FROM_ACTIVE_CAMERA); } else { scene->bake_manager->set_use_camera(false); } /* sync */ Camera *cam = scene->camera; blender_camera_sync(cam, scene, &bcam, width, height, viewname, &cscene); /* dicing camera */ b_ob = RNA_pointer_get(&cscene, "dicing_camera").data_as(); if (b_ob) { blender::float4x4 b_ob_matrix; blender_camera_from_object(&bcam, *b_engine, b_render, nullptr, nullptr, *b_ob, *b_data); RE_engine_get_camera_model_matrix( b_engine, b_ob, bcam.use_spherical_stereo, b_ob_matrix.base_ptr()); bcam.matrix = get_transform(b_ob_matrix); blender_camera_sync(scene->dicing_camera, nullptr, &bcam, width, height, viewname, &cscene); } else { *scene->dicing_camera = *cam; } } /* See #rna_RenderEngine_camera_override_get. */ static blender::Object *camera_override_get(blender::RenderEngine *engine) { /* TODO(sergey): Shouldn't engine point to an evaluated datablocks already? */ if (engine->re) { blender::Object *cam = RE_GetCamera(engine->re); blender::Object *cam_eval = DEG_get_evaluated(engine->depsgraph, cam); return cam_eval; } return engine->camera_override; } blender::Object *BlenderSync::get_camera_object(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d) { /* TODO(sergey): Shouldn't engine point to an evaluated datablocks already? */ blender::Object *b_camera_override = camera_override_get(b_engine); if (b_camera_override) { return b_camera_override; } if (b_v3d && b_rv3d && b_rv3d->persp == blender::RV3D_CAMOB && !b_v3d->scenelock) { return b_v3d->camera; } return b_scene->camera; } blender::Object *BlenderSync::get_dicing_camera_object(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d) { blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene->id); blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles"); blender::Object *b_ob = RNA_pointer_get(&cscene, "dicing_camera").data_as(); if (b_ob) { return b_ob; } return get_camera_object(b_v3d, b_rv3d); } void BlenderSync::sync_camera_motion(const blender::RenderData &b_render, blender::Object *b_ob, const int width, const int height, const float motion_time) { if (!b_ob) { return; } Camera *cam = scene->camera; blender::float4x4 b_ob_matrix; RE_engine_get_camera_model_matrix( b_engine, b_ob, cam->get_use_spherical_stereo(), b_ob_matrix.base_ptr()); Transform tfm = get_transform(b_ob_matrix); tfm = blender_camera_matrix(tfm, cam->get_camera_type(), cam->get_panorama_type()); if (motion_time == 0.0f) { /* When motion blur is not centered in frame, cam->matrix gets reset. */ cam->set_matrix(tfm); } /* Set transform in motion array. */ const int motion_step = cam->motion_step(motion_time); if (motion_step >= 0) { array motion = cam->get_motion(); motion[motion_step] = tfm; cam->set_motion(motion); } if (cam->get_camera_type() == CAMERA_PERSPECTIVE) { BlenderCamera bcam(b_render); /* TODO(sergey): Consider making it a part of BlenderCamera(). */ bcam.pixelaspect.x = b_render.xasp; bcam.pixelaspect.y = b_render.yasp; blender_camera_from_object(&bcam, *b_engine, b_render, nullptr, nullptr, *b_ob, *b_data); BoundBox2D viewplane; float aspectratio; float sensor_size; blender_camera_viewplane(&bcam, width, height, viewplane, aspectratio, sensor_size); /* TODO(sergey): De-duplicate calculation with camera sync. */ const float fov = 2.0f * atanf((0.5f * sensor_size) / bcam.lens / aspectratio); if (fov != cam->get_fov()) { LOG_DEBUG << "Camera " << BKE_id_name(b_ob->id) << " FOV change detected."; if (motion_time == 0.0f) { cam->set_fov(fov); } else if (motion_time == -1.0f) { cam->set_fov_pre(fov); cam->set_use_perspective_motion(true); } else if (motion_time == 1.0f) { cam->set_fov_post(fov); cam->set_use_perspective_motion(true); } } } } /* Sync 3D View Camera */ static void blender_camera_view_subset(blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::Scene &b_scene, blender::Main &b_data, blender::Object &b_ob, blender::View3D *&b_v3d, blender::RegionView3D *b_rv3d, const int width, const int height, BoundBox2D &view_box, BoundBox2D &cam_box, float &view_aspect); static void blender_camera_from_view(BlenderCamera *bcam, blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::Scene &b_scene, blender::Main &b_data, blender::View3D *&b_v3d, blender::RegionView3D *b_rv3d, const int width, const int height, bool skip_panorama = false) { /* 3d view parameters */ bcam->nearclip = b_v3d->clip_start; bcam->farclip = b_v3d->clip_end; bcam->lens = b_v3d->lens; bcam->shuttertime = b_scene.r.motion_blur_shutter; blender::CurveMapping &b_shutter_curve = b_scene.r.mblur_shutter_curve; curvemapping_to_array(b_shutter_curve, bcam->shutter_curve, RAMP_TABLE_SIZE); if (b_rv3d->persp == blender::RV3D_CAMOB) { /* camera view */ blender::Object *b_ob = (b_v3d->scenelock) ? b_scene.camera : b_v3d->camera; if (b_ob) { blender_camera_from_object( bcam, b_engine, b_render, b_v3d, b_rv3d, *b_ob, b_data, skip_panorama); if (!skip_panorama && (bcam->type == CAMERA_PANORAMA || bcam->type == CAMERA_CUSTOM)) { /* in panorama or custom camera view, we map viewplane to camera border */ BoundBox2D view_box; BoundBox2D cam_box; float view_aspect; blender_camera_view_subset(b_engine, b_scene.r, b_scene, b_data, *b_ob, b_v3d, b_rv3d, width, height, view_box, cam_box, view_aspect); bcam->pano_viewplane = view_box.make_relative_to(cam_box); bcam->pano_aspectratio = view_aspect; } else { /* magic zoom formula */ bcam->zoom = b_rv3d->camzoom; bcam->zoom = (1.41421f + bcam->zoom / 50.0f); bcam->zoom *= bcam->zoom; bcam->zoom = 2.0f / bcam->zoom; /* offset */ bcam->offset = make_float2(b_rv3d->camdx, b_rv3d->camdy); } } } else if (b_rv3d->persp == blender::RV3D_ORTHO) { /* orthographic view */ bcam->farclip *= 0.5f; bcam->nearclip = -bcam->farclip; float sensor_size; if (bcam->sensor_fit == BlenderCamera::VERTICAL) { sensor_size = bcam->sensor_height; } else { sensor_size = bcam->sensor_width; } bcam->type = CAMERA_ORTHOGRAPHIC; bcam->ortho_scale = b_rv3d->dist * sensor_size / b_v3d->lens; } bcam->zoom *= 2.0f; /* 3d view transform */ bcam->matrix = transform_inverse(get_transform(blender::float4x4(b_rv3d->viewmat))); /* dimensions */ bcam->full_width = width; bcam->full_height = height; } static void blender_camera_view_subset(blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::Scene &b_scene, blender::Main &b_data, blender::Object &b_ob, blender::View3D *&b_v3d, blender::RegionView3D *b_rv3d, const int width, const int height, BoundBox2D &view_box, BoundBox2D &cam_box, float &view_aspect) { BoundBox2D cam; BoundBox2D view; float cam_aspect; float sensor_size; /* Get viewport viewplane. */ BlenderCamera view_bcam(b_render); blender_camera_from_view( &view_bcam, b_engine, b_render, b_scene, b_data, b_v3d, b_rv3d, width, height, true); blender_camera_viewplane(&view_bcam, width, height, view, view_aspect, sensor_size); /* Get camera viewplane. */ BlenderCamera cam_bcam(b_render); blender_camera_from_object(&cam_bcam, b_engine, b_render, b_v3d, b_rv3d, b_ob, b_data, true); /* Camera border is affect by aspect, viewport is not. */ cam_bcam.pixelaspect.x = b_render.xasp; cam_bcam.pixelaspect.y = b_render.yasp; blender_camera_viewplane( &cam_bcam, cam_bcam.full_width, cam_bcam.full_height, cam, cam_aspect, sensor_size); /* Return */ view_box = view * (1.0f / view_aspect); cam_box = cam * (1.0f / cam_aspect); } static void blender_camera_border_subset(blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::Scene &b_scene, blender::Main &b_data, blender::View3D *&b_v3d, blender::RegionView3D *b_rv3d, blender::Object &b_ob, const int width, const int height, const BoundBox2D &border, BoundBox2D *result) { /* Determine camera viewport subset. */ BoundBox2D view_box; BoundBox2D cam_box; float view_aspect; blender_camera_view_subset(b_engine, b_render, b_scene, b_data, b_ob, b_v3d, b_rv3d, width, height, view_box, cam_box, view_aspect); /* Determine viewport subset matching given border. */ cam_box = cam_box.make_relative_to(view_box); *result = cam_box.subset(border); } static void blender_camera_border(BlenderCamera *bcam, blender::RenderEngine &b_engine, const blender::RenderData &b_render, blender::Scene &b_scene, blender::Main &b_data, blender::View3D *b_v3d, blender::RegionView3D *b_rv3d, const int width, const int height) { bool is_camera_view; /* camera view? */ is_camera_view = b_rv3d->persp == blender::RV3D_CAMOB; if (!is_camera_view) { /* for non-camera view check whether render border is enabled for viewport * and if so use border from 3d viewport * assume viewport has got correctly clamped border already */ if (b_v3d->flag2 & blender::V3D_RENDER_BORDER) { bcam->border.left = b_v3d->render_border.xmin; bcam->border.right = b_v3d->render_border.xmax; bcam->border.bottom = b_v3d->render_border.ymin; bcam->border.top = b_v3d->render_border.ymax; } return; } blender::Object *b_ob = (b_v3d->scenelock) ? b_scene.camera : b_v3d->camera; if (!b_ob) { return; } /* Determine camera border inside the viewport. */ const BoundBox2D full_border; blender_camera_border_subset(b_engine, b_render, b_scene, b_data, b_v3d, b_rv3d, *b_ob, width, height, full_border, &bcam->viewport_camera_border); if (b_render.mode & blender::R_BORDER) { bcam->border.left = b_render.border.xmin; bcam->border.right = b_render.border.xmax; bcam->border.bottom = b_render.border.ymin; bcam->border.top = b_render.border.ymax; } else if (bcam->passepartout_alpha == 1.0f) { bcam->border = full_border; } else { return; } /* Determine viewport subset matching camera border. */ blender_camera_border_subset(b_engine, b_render, b_scene, b_data, b_v3d, b_rv3d, *b_ob, width, height, bcam->border, &bcam->border); bcam->border = bcam->border.clamp(); } void BlenderSync::sync_view(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d, const int width, const int height) { const blender::RenderData &b_render_settings = b_scene->r; BlenderCamera bcam(b_render_settings); blender_camera_from_view( &bcam, *b_engine, b_render_settings, *b_scene, *b_data, b_v3d, b_rv3d, width, height); blender_camera_border( &bcam, *b_engine, b_render_settings, *b_scene, *b_data, b_v3d, b_rv3d, width, height); bcam.motion_steps = scene->need_motion() == Scene::MOTION_PASS_INTERACTIVE ? 2 : 0; blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene->id); blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles"); blender_camera_sync(scene->camera, scene, &bcam, width, height, "", &cscene); /* dicing camera */ blender::Object *b_ob = RNA_pointer_get(&cscene, "dicing_camera").data_as(); if (b_ob) { blender::float4x4 b_ob_matrix; blender_camera_from_object( &bcam, *b_engine, b_render_settings, nullptr, nullptr, *b_ob, *b_data); RE_engine_get_camera_model_matrix( b_engine, b_ob, bcam.use_spherical_stereo, b_ob_matrix.base_ptr()); bcam.matrix = get_transform(b_ob_matrix); blender_camera_sync(scene->dicing_camera, nullptr, &bcam, width, height, "", &cscene); } else { *scene->dicing_camera = *scene->camera; } } BufferParams BlenderSync::get_buffer_params(blender::View3D *b_v3d, blender::RegionView3D *b_rv3d, Camera *cam, const int width, const int height) { BufferParams params; bool use_border = false; params.full_width = width; params.full_height = height; if (b_v3d && b_rv3d && b_rv3d->persp != blender::RV3D_CAMOB) { use_border = b_v3d->flag2 & blender::V3D_RENDER_BORDER; } else { /* the camera can always have a passepartout */ use_border = true; } if (use_border) { /* border render */ /* the viewport may offset the border outside the view */ const BoundBox2D border = cam->border.clamp(); params.full_x = (int)(border.left * (float)width); params.full_y = (int)(border.bottom * (float)height); params.width = (int)(border.right * (float)width) - params.full_x; params.height = (int)(border.top * (float)height) - params.full_y; /* survive in case border goes out of view or becomes too small */ params.width = max(params.width, 1); params.height = max(params.height, 1); } else { params.width = width; params.height = height; } params.window_width = params.width; params.window_height = params.height; return params; } CCL_NAMESPACE_END