diff options
-rw-r--r-- | source/blender/blenkernel/BKE_camera.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/camera.c | 213 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_geom.h | 3 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_geom.c | 53 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_view.c | 16 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_object.c | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_object_api.c | 51 |
7 files changed, 268 insertions, 73 deletions
diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 01b401c6bcc..26d4986d93e 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -118,7 +118,9 @@ void BKE_camera_view_frame_ex(struct Scene *scene, struct Camera *camera, float void BKE_camera_view_frame(struct Scene *scene, struct Camera *camera, float r_vec[4][3]); bool BKE_camera_view_frame_fit_to_scene(struct Scene *scene, struct View3D *v3d, struct Object *camera_ob, - float r_co[3]); + float r_co[3], float *r_scale); +bool BKE_camera_view_frame_fit_to_coords(struct Scene *scene, float (*cos)[3], int num_cos, + struct Object *camera_ob, float r_co[3], float *r_scale); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 5454062ad1f..d0470ba6f6a 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -39,6 +39,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rect.h" #include "BKE_animsys.h" #include "BKE_camera.h" @@ -458,13 +459,21 @@ void BKE_camera_view_frame(Scene *scene, Camera *camera, float r_vec[4][3]) dummy_asp, dummy_shift, &dummy_drawsize, r_vec); } +#define CAMERA_VIEWFRAME_NUM_PLANES 4 typedef struct CameraViewFrameData { - float plane_tx[4][4]; /* 4 planes (not 4x4 matrix)*/ - float frame_tx[4][3]; - float normal_tx[4][3]; - float dist_vals_sq[4]; /* distance squared (signed) */ + float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes */ + float normal_tx[CAMERA_VIEWFRAME_NUM_PLANES][3]; + float dist_vals_sq[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance squared (signed) */ unsigned int tot; + + /* Ortho camera only. */ + bool is_ortho; + float camera_no[3]; + float dist_to_cam; + + /* Not used by callbacks... */ + float camera_rotmat[3][3]; } CameraViewFrameData; static void camera_to_frame_view_cb(const float co[3], void *user_data) @@ -472,67 +481,106 @@ static void camera_to_frame_view_cb(const float co[3], void *user_data) CameraViewFrameData *data = (CameraViewFrameData *)user_data; unsigned int i; - for (i = 0; i < 4; i++) { - float nd = dist_signed_squared_to_plane_v3(co, data->plane_tx[i]); - if (nd < data->dist_vals_sq[i]) { - data->dist_vals_sq[i] = nd; - } + for (i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { + const float nd = dist_signed_squared_to_plane_v3(co, data->plane_tx[i]); + CLAMP_MAX(data->dist_vals_sq[i], nd); + } + + if (data->is_ortho) { + const float d = dot_v3v3(data->camera_no, co); + CLAMP_MAX(data->dist_to_cam, d); } data->tot++; } -/* don't move the camera, just yield the fit location */ -/* only valid for perspective cameras */ -bool BKE_camera_view_frame_fit_to_scene(Scene *scene, struct View3D *v3d, Object *camera_ob, float r_co[3]) +static void camera_frame_fit_data_init(Scene *scene, Object *ob, CameraParams *params, CameraViewFrameData *data) { - float shift[2]; - float plane_tx[4][3]; - float rot_obmat[3][3]; - const float zero[3] = {0, 0, 0}; - CameraViewFrameData data_cb; - + float camera_rotmat_transposed_inversed[4][4]; unsigned int i; - BKE_camera_view_frame(scene, camera_ob->data, data_cb.frame_tx); + /* setup parameters */ + BKE_camera_params_init(params); + BKE_camera_params_from_object(params, ob); - copy_m3_m4(rot_obmat, camera_ob->obmat); - normalize_m3(rot_obmat); - - for (i = 0; i < 4; i++) { - /* normalize so Z is always 1.0f*/ - mul_v3_fl(data_cb.frame_tx[i], 1.0f / data_cb.frame_tx[i][2]); + /* compute matrix, viewplane, .. */ + if (scene) { + BKE_camera_params_compute_viewplane(params, scene->r.xsch, scene->r.ysch, scene->r.xasp, scene->r.yasp); + } + else { + BKE_camera_params_compute_viewplane(params, 1, 1, 1.0f, 1.0f); } + BKE_camera_params_compute_matrix(params); - /* get the shift back out of the frame */ - shift[0] = (data_cb.frame_tx[0][0] + - data_cb.frame_tx[1][0] + - data_cb.frame_tx[2][0] + - data_cb.frame_tx[3][0]) / 4.0f; - shift[1] = (data_cb.frame_tx[0][1] + - data_cb.frame_tx[1][1] + - data_cb.frame_tx[2][1] + - data_cb.frame_tx[3][1]) / 4.0f; - - for (i = 0; i < 4; i++) { - mul_m3_v3(rot_obmat, data_cb.frame_tx[i]); + /* initialize callback data */ + copy_m3_m4(data->camera_rotmat, ob->obmat); + normalize_m3(data->camera_rotmat); + /* To transform a plane which is in its homogeneous representation (4d vector), + * we need the inverse of the transpose of the transform matrix... */ + copy_m4_m3(camera_rotmat_transposed_inversed, data->camera_rotmat); + transpose_m4(camera_rotmat_transposed_inversed); + invert_m4(camera_rotmat_transposed_inversed); + + /* Extract frustum planes from projection matrix. */ + planes_from_projmat(params->winmat, + /* left right top bottom near far */ + data->plane_tx[2], data->plane_tx[0], data->plane_tx[3], data->plane_tx[1], NULL, NULL); + + /* Rotate planes and get normals from them */ + for (i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { + mul_m4_v4(camera_rotmat_transposed_inversed, data->plane_tx[i]); + normalize_v3_v3(data->normal_tx[i], data->plane_tx[i]); } - for (i = 0; i < 4; i++) { - normal_tri_v3(data_cb.normal_tx[i], zero, data_cb.frame_tx[i], data_cb.frame_tx[(i + 1) % 4]); - plane_from_point_normal_v3(data_cb.plane_tx[i], data_cb.frame_tx[i], data_cb.normal_tx[i]); + copy_v4_fl(data->dist_vals_sq, FLT_MAX); + data->tot = 0; + data->is_ortho = params->is_ortho; + if (params->is_ortho) { + /* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */ + negate_v3_v3(data->camera_no, data->camera_rotmat[2]); + data->dist_to_cam = FLT_MAX; } +} - /* initialize callback data */ - copy_v4_fl(data_cb.dist_vals_sq, FLT_MAX); - data_cb.tot = 0; - /* run callback on all visible points */ - BKE_scene_foreach_display_point(scene, v3d, BA_SELECT, - camera_to_frame_view_cb, &data_cb); +static bool camera_frame_fit_calc_from_data( + CameraParams *params, CameraViewFrameData *data, float r_co[3], float *r_scale) +{ + float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][3]; + unsigned int i; - if (data_cb.tot <= 1) { + if (data->tot <= 1) { return false; } + + if (params->is_ortho) { + const float *cam_axis_x = data->camera_rotmat[0]; + const float *cam_axis_y = data->camera_rotmat[1]; + const float *cam_axis_z = data->camera_rotmat[2]; + float dists[CAMERA_VIEWFRAME_NUM_PLANES]; + float scale_diff; + + /* apply the dist-from-plane's to the transformed plane points */ + for (i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { + dists[i] = sqrtf_signed(data->dist_vals_sq[i]); + } + + if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { + scale_diff = (dists[1] + dists[3]) * + (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); + } + else { + scale_diff = (dists[0] + dists[2]) * + (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->viewplane)); + } + *r_scale = params->ortho_scale - scale_diff; + + zero_v3(r_co); + madd_v3_v3fl(r_co, cam_axis_x, (dists[2] - dists[0]) * 0.5f + params->shiftx * scale_diff); + madd_v3_v3fl(r_co, cam_axis_y, (dists[1] - dists[3]) * 0.5f + params->shifty * scale_diff); + madd_v3_v3fl(r_co, cam_axis_z, -(data->dist_to_cam - 1.0f - params->clipsta)); + + return true; + } else { float plane_isect_1[3], plane_isect_1_no[3], plane_isect_1_other[3]; float plane_isect_2[3], plane_isect_2_no[3], plane_isect_2_other[3]; @@ -540,16 +588,16 @@ bool BKE_camera_view_frame_fit_to_scene(Scene *scene, struct View3D *v3d, Object float plane_isect_pt_1[3], plane_isect_pt_2[3]; /* apply the dist-from-plane's to the transformed plane points */ - for (i = 0; i < 4; i++) { - mul_v3_v3fl(plane_tx[i], data_cb.normal_tx[i], sqrtf_signed(data_cb.dist_vals_sq[i])); + for (i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { + mul_v3_v3fl(plane_tx[i], data->normal_tx[i], sqrtf_signed(data->dist_vals_sq[i])); } if ((!isect_plane_plane_v3(plane_isect_1, plane_isect_1_no, - plane_tx[0], data_cb.normal_tx[0], - plane_tx[2], data_cb.normal_tx[2])) || + plane_tx[0], data->normal_tx[0], + plane_tx[2], data->normal_tx[2])) || (!isect_plane_plane_v3(plane_isect_2, plane_isect_2_no, - plane_tx[1], data_cb.normal_tx[1], - plane_tx[3], data_cb.normal_tx[3]))) + plane_tx[1], data->normal_tx[1], + plane_tx[3], data->normal_tx[3]))) { return false; } @@ -559,16 +607,17 @@ bool BKE_camera_view_frame_fit_to_scene(Scene *scene, struct View3D *v3d, Object if (isect_line_line_v3(plane_isect_1, plane_isect_1_other, plane_isect_2, plane_isect_2_other, - plane_isect_pt_1, plane_isect_pt_2) == 0) + plane_isect_pt_1, plane_isect_pt_2) != 0) { - return false; - } - else { - float cam_plane_no[3] = {0.0f, 0.0f, -1.0f}; + float cam_plane_no[3]; float plane_isect_delta[3]; float plane_isect_delta_len; - mul_m3_v3(rot_obmat, cam_plane_no); + float shift_fac = BKE_camera_sensor_size(params->sensor_fit, params->sensor_x, params->sensor_y) / + params->lens; + + /* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */ + negate_v3_v3(cam_plane_no, data->camera_rotmat[2]); sub_v3_v3v3(plane_isect_delta, plane_isect_pt_2, plane_isect_pt_1); plane_isect_delta_len = len_v3(plane_isect_delta); @@ -578,18 +627,58 @@ bool BKE_camera_view_frame_fit_to_scene(Scene *scene, struct View3D *v3d, Object /* offset shift */ normalize_v3(plane_isect_1_no); - madd_v3_v3fl(r_co, plane_isect_1_no, shift[1] * -plane_isect_delta_len); + madd_v3_v3fl(r_co, plane_isect_1_no, params->shifty * plane_isect_delta_len * shift_fac); } else { copy_v3_v3(r_co, plane_isect_pt_2); /* offset shift */ normalize_v3(plane_isect_2_no); - madd_v3_v3fl(r_co, plane_isect_2_no, shift[0] * -plane_isect_delta_len); + madd_v3_v3fl(r_co, plane_isect_2_no, params->shiftx * plane_isect_delta_len * shift_fac); } - return true; } } + + return false; +} + +/* don't move the camera, just yield the fit location */ +/* r_scale only valid/useful for ortho cameras */ +bool BKE_camera_view_frame_fit_to_scene( + Scene *scene, struct View3D *v3d, Object *camera_ob, float r_co[3], float *r_scale) +{ + CameraParams params; + CameraViewFrameData data_cb; + + /* just in case */ + *r_scale = 1.0f; + + camera_frame_fit_data_init(scene, camera_ob, ¶ms, &data_cb); + + /* run callback on all visible points */ + BKE_scene_foreach_display_point(scene, v3d, BA_SELECT, camera_to_frame_view_cb, &data_cb); + + return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); +} + +bool BKE_camera_view_frame_fit_to_coords( + Scene *scene, float (*cos)[3], int num_cos, Object *camera_ob, float r_co[3], float *r_scale) +{ + CameraParams params; + CameraViewFrameData data_cb; + + /* just in case */ + *r_scale = 1.0f; + + camera_frame_fit_data_init(scene, camera_ob, ¶ms, &data_cb); + + /* run callback on all given coordinates */ + while (num_cos--) { + camera_to_frame_view_cb(cos[num_cos], &data_cb); + } + + return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); } + diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 95ab3185d1f..ee99f860a97 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -265,6 +265,9 @@ void orthographic_m4(float mat[4][4], const float left, const float right, void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y); +void planes_from_projmat(float mat[4][4], float left[4], float right[4], float top[4], float bottom[4], + float front[4], float back[4]); + int box_clip_bounds_m4(float boundbox[2][3], const float bounds[4], float winmat[4][4]); void box_minmax_bounds_m4(float min[3], float max[3], diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index a360148182d..59d9cf85dec 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -3104,6 +3104,59 @@ void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x } } +/** + * Frustum planes extraction from a projection matrix (homogeneous 4d vector representations of planes). + * + * plane parameters can be NULL if you do not need them. + */ +void planes_from_projmat(float mat[4][4], float left[4], float right[4], float top[4], float bottom[4], + float near[4], float far[4]) +{ + /* References: + * + * https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/ + * http://www8.cs.umu.se/kurser/5DV051/HT12/lab/plane_extraction.pdf + */ + + int i; + + if (left) { + for (i = 4; i--; ) { + left[i] = mat[i][3] + mat[i][0]; + } + } + + if (right) { + for (i = 4; i--; ) { + right[i] = mat[i][3] - mat[i][0]; + } + } + + if (bottom) { + for (i = 4; i--; ) { + bottom[i] = mat[i][3] + mat[i][1]; + } + } + + if (top) { + for (i = 4; i--; ) { + top[i] = mat[i][3] - mat[i][1]; + } + } + + if (near) { + for (i = 4; i--; ) { + near[i] = mat[i][3] + mat[i][2]; + } + } + + if (far) { + for (i = 4; i--; ) { + far[i] = mat[i][3] - mat[i][2]; + } + } +} + static void i_multmatrix(float icand[4][4], float Vm[4][4]) { int row, col; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index c0e26498af6..5a3893f733f 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -496,26 +496,22 @@ static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op) Object *camera_ob = v3d ? v3d->camera : scene->camera; float r_co[3]; /* the new location to apply */ + float r_scale; /* only for ortho cameras */ if (camera_ob == NULL) { BKE_report(op->reports, RPT_ERROR, "No active camera"); return OPERATOR_CANCELLED; } - else if (camera_ob->type != OB_CAMERA) { - BKE_report(op->reports, RPT_ERROR, "Object not a camera"); - return OPERATOR_CANCELLED; - } - else if (((Camera *)camera_ob->data)->type == R_ORTHO) { - BKE_report(op->reports, RPT_ERROR, "Orthographic cameras not supported"); - return OPERATOR_CANCELLED; - } /* this function does all the important stuff */ - if (BKE_camera_view_frame_fit_to_scene(scene, v3d, camera_ob, r_co)) { - + if (BKE_camera_view_frame_fit_to_scene(scene, v3d, camera_ob, r_co, &r_scale)) { ObjectTfmProtectedChannels obtfm; float obmat_new[4][4]; + if ((camera_ob->type == OB_CAMERA) && (((Camera *)camera_ob->data)->type == CAM_ORTHO)) { + ((Camera *)camera_ob->data)->ortho_scale = r_scale; + } + copy_m4_m4(obmat_new, camera_ob->obmat); copy_v3_v3(obmat_new[3], r_co); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 34d05f61561..f9a76005df8 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -42,6 +42,7 @@ #include "BLI_utildefines.h" #include "BLI_listbase.h" +#include "BKE_camera.h" #include "BKE_paint.h" #include "BKE_editmesh.h" #include "BKE_group.h" /* needed for BKE_group_object_exists() */ diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 831e5486236..3dc75ef83f6 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -112,6 +112,28 @@ static void rna_Scene_mat_convert_space(Object *ob, ReportList *reports, bPoseCh BKE_constraint_mat_convertspace(ob, pchan, (float (*)[4])mat_ret, from, to); } +static void rna_Object_calc_matrix_camera( + Object *ob, float mat_ret[16], int width, int height, float scalex, float scaley) +{ + CameraParams params; + + /* setup parameters */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, ob); + + /* compute matrix, viewplane, .. */ + BKE_camera_params_compute_viewplane(¶ms, width, height, scalex, scaley); + BKE_camera_params_compute_matrix(¶ms); + + copy_m4_m4((float (*)[4])mat_ret, params.winmat); +} + +static void rna_Object_camera_fit_coords( + Object *ob, Scene *scene, int num_cos, float *cos, float co_ret[3], float *scale_ret) +{ + BKE_camera_view_frame_fit_to_coords(scene, (float (*)[3])cos, num_cos / 3, ob, co_ret, scale_ret); +} + /* copied from Mesh_getFromObject and adapted to RNA interface */ /* settings: 0 - preview, 1 - render */ static Mesh *rna_Object_to_mesh( @@ -468,6 +490,35 @@ void RNA_api_object(StructRNA *srna) parm = RNA_def_enum(func, "to_space", space_items, CONSTRAINT_SPACE_WORLD, "", "The space to which you want to transform 'matrix'"); + /* Camera-related operations */ + func = RNA_def_function(srna, "calc_matrix_camera", "rna_Object_calc_matrix_camera"); + RNA_def_function_ui_description(func, "Generate the camera projection matrix of this object " + "(mostly useful for Camera and Lamp types)"); + parm = RNA_def_property(func, "result", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); + RNA_def_property_ui_text(parm, "", "The camera projection matrix"); + RNA_def_function_output(func, parm); + parm = RNA_def_int(func, "x", 1, 0, INT_MAX, "", "Width of the render area", 0, 10000); + parm = RNA_def_int(func, "y", 1, 0, INT_MAX, "", "Height of the render area", 0, 10000); + parm = RNA_def_float(func, "scale_x", 1.0f, 1.0e-6f, FLT_MAX, "", "Width scaling factor", 1.0e-2f, 100.0f); + parm = RNA_def_float(func, "scale_y", 1.0f, 1.0e-6f, FLT_MAX, "", "height scaling factor", 1.0e-2f, 100.0f); + + func = RNA_def_function(srna, "camera_fit_coords", "rna_Object_camera_fit_coords"); + RNA_def_function_ui_description(func, "Compute the coordinate (and scale for ortho cameras) " + "given object should be to 'see' all given coordinates"); + parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene to get render size information from, if available"); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_float_array(func, "coordinates", 1, NULL, -FLT_MAX, FLT_MAX, "", "Coordinates to fit in", + -FLT_MAX, FLT_MAX); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_DYNAMIC); + parm = RNA_def_property(func, "co_return", PROP_FLOAT, PROP_XYZ); + RNA_def_property_array(parm, 3); + RNA_def_property_ui_text(parm, "", "The location to aim to be able to see all given points"); + RNA_def_property_flag(parm, PROP_OUTPUT); + parm = RNA_def_property(func, "scale_return", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(parm, "", "The ortho scale to aim to be able to see all given points (if relevant)"); + RNA_def_property_flag(parm, PROP_OUTPUT); + /* mesh */ func = RNA_def_function(srna, "to_mesh", "rna_Object_to_mesh"); RNA_def_function_ui_description(func, "Create a Mesh datablock with modifiers applied"); |