This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/space_view3d/view3d_camera_control.c

338 lines
9.2 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.
*/
/** \file
* \ingroup spview3d
*
* The purpose of View3DCameraControl is to allow editing \a rv3d manipulation
* (mainly \a ofs and \a viewquat) for the purpose of view navigation
* without having to worry about positioning the camera, its parent...
* or other details.
* Typical view-control usage:
*
* - acquire a view-control (#ED_view3d_cameracontrol_acquire).
* - modify ``rv3d->ofs``, ``rv3d->viewquat``.
* - update the view data (#ED_view3d_cameracontrol_acquire) - within a loop which draws the viewport.
* - finish and release the view-control (#ED_view3d_cameracontrol_release),
* either keeping the current view or restoring the initial view.
*
* Notes:
*
* - when acquiring ``rv3d->dist`` is set to zero
* (so ``rv3d->ofs`` is always the view-point)
* - updating can optionally keyframe the camera object.
*/
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
#include "DNA_camera_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_object.h"
#include "DEG_depsgraph.h"
#include "ED_screen.h"
#include "view3d_intern.h" /* own include */
#include "BLI_strict_flags.h"
typedef struct View3DCameraControl {
/* -------------------------------------------------------------------- */
/* Context (assign these to vars before use) */
Scene *ctx_scene;
View3D *ctx_v3d;
RegionView3D *ctx_rv3d;
/* -------------------------------------------------------------------- */
/* internal vars */
/* for parenting calculation */
float view_mat_prev[4][4];
/* -------------------------------------------------------------------- */
/* optional capabilities */
bool use_parent_root;
/* -------------------------------------------------------------------- */
/* initial values */
/* root most parent */
Object *root_parent;
/* backup values */
float dist_backup;
/* backup the views distance since we use a zero dist for fly mode */
float ofs_backup[3];
/* backup the views offset in case the user cancels flying in non camera mode */
/* backup the views quat in case the user cancels flying in non camera mode.
* (quat for view, eul for camera) */
float rot_backup[4];
/* remember if were ortho or not, only used for restoring the view if it was a ortho view */
char persp_backup;
/* are we flying an ortho camera in perspective view,
* which was originally in ortho view?
* could probably figure it out but better be explicit */
bool is_ortho_cam;
/* backup the objects transform */
void *obtfm;
} View3DCameraControl;
BLI_INLINE Object *view3d_cameracontrol_object(View3DCameraControl *vctrl)
{
return vctrl->root_parent ? vctrl->root_parent : vctrl->ctx_v3d->camera;
}
/**
* Returns the object which is being manipulated or NULL.
*/
Object *ED_view3d_cameracontrol_object_get(View3DCameraControl *vctrl)
{
RegionView3D *rv3d = vctrl->ctx_rv3d;
if (rv3d->persp == RV3D_CAMOB) {
return view3d_cameracontrol_object(vctrl);
}
else {
return NULL;
}
}
/**
* Creates a #View3DCameraControl handle and sets up
* the view for first-person style navigation.
*/
struct View3DCameraControl *ED_view3d_cameracontrol_acquire(
Depsgraph *depsgraph, Scene *scene, View3D *v3d, RegionView3D *rv3d,
const bool use_parent_root)
{
View3DCameraControl *vctrl;
vctrl = MEM_callocN(sizeof(View3DCameraControl), __func__);
/* Store context */
vctrl->ctx_scene = scene;
vctrl->ctx_v3d = v3d;
vctrl->ctx_rv3d = rv3d;
vctrl->use_parent_root = use_parent_root;
vctrl->persp_backup = rv3d->persp;
vctrl->dist_backup = rv3d->dist;
/* check for flying ortho camera - which we cant support well
* we _could_ also check for an ortho camera but this is easier */
if ((rv3d->persp == RV3D_CAMOB) &&
(rv3d->is_persp == false))
{
((Camera *)v3d->camera->data)->type = CAM_PERSP;
vctrl->is_ortho_cam = true;
}
if (rv3d->persp == RV3D_CAMOB) {
Object *ob_back;
if (use_parent_root && (vctrl->root_parent = v3d->camera->parent)) {
while (vctrl->root_parent->parent) {
vctrl->root_parent = vctrl->root_parent->parent;
}
ob_back = vctrl->root_parent;
}
else {
ob_back = v3d->camera;
}
/* store the original camera loc and rot */
vctrl->obtfm = BKE_object_tfm_backup(ob_back);
BKE_object_where_is_calc(depsgraph, scene, v3d->camera);
negate_v3_v3(rv3d->ofs, v3d->camera->obmat[3]);
rv3d->dist = 0.0;
}
else {
/* perspective or ortho */
if (rv3d->persp == RV3D_ORTHO) {
/* if ortho projection, make perspective */
rv3d->persp = RV3D_PERSP;
}
copy_qt_qt(vctrl->rot_backup, rv3d->viewquat);
copy_v3_v3(vctrl->ofs_backup, rv3d->ofs);
/* the dist defines a vector that is infront of the offset
* to rotate the view about.
* this is no good for fly mode because we
* want to rotate about the viewers center.
* but to correct the dist removal we must
* alter offset so the view doesn't jump. */
ED_view3d_distance_set(rv3d, 0.0f);
/* Done with correcting for the dist */
}
ED_view3d_to_m4(vctrl->view_mat_prev, rv3d->ofs, rv3d->viewquat, rv3d->dist);
return vctrl;
}
/**
* Updates cameras from the ``rv3d`` values, optionally auto-keyframing.
*/
void ED_view3d_cameracontrol_update(
View3DCameraControl *vctrl,
/* args for keyframing */
const bool use_autokey,
struct bContext *C, const bool do_rotate, const bool do_translate)
{
/* we are in camera view so apply the view ofs and quat to the view matrix and set the camera
* to the view */
Scene *scene = vctrl->ctx_scene;
View3D *v3d = vctrl->ctx_v3d;
RegionView3D *rv3d = vctrl->ctx_rv3d;
ID *id_key;
/* transform the parent or the camera? */
if (vctrl->root_parent) {
Object *ob_update;
float view_mat[4][4];
float prev_view_imat[4][4];
float diff_mat[4][4];
float parent_mat[4][4];
invert_m4_m4(prev_view_imat, vctrl->view_mat_prev);
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
mul_m4_m4m4(diff_mat, view_mat, prev_view_imat);
mul_m4_m4m4(parent_mat, diff_mat, vctrl->root_parent->obmat);
BKE_object_apply_mat4(vctrl->root_parent, parent_mat, true, false);
ob_update = v3d->camera->parent;
while (ob_update) {
DEG_id_tag_update(&ob_update->id, ID_RECALC_TRANSFORM);
ob_update = ob_update->parent;
}
copy_m4_m4(vctrl->view_mat_prev, view_mat);
id_key = &vctrl->root_parent->id;
}
else {
float view_mat[4][4];
float scale_mat[4][4];
float scale_back[3];
/* even though we handle the scale matrix, this still changes over time */
copy_v3_v3(scale_back, v3d->camera->scale);
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
size_to_mat4(scale_mat, v3d->camera->scale);
mul_m4_m4m4(view_mat, view_mat, scale_mat);
BKE_object_apply_mat4(v3d->camera, view_mat, true, true);
DEG_id_tag_update(&v3d->camera->id, ID_RECALC_TRANSFORM);
copy_v3_v3(v3d->camera->scale, scale_back);
id_key = &v3d->camera->id;
}
/* record the motion */
if (use_autokey) {
ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate);
}
}
/**
* Release view control.
*
* \param restore: Sets the view state to the values that were set
* before #ED_view3d_control_acquire was called.
*/
void ED_view3d_cameracontrol_release(
View3DCameraControl *vctrl,
const bool restore)
{
View3D *v3d = vctrl->ctx_v3d;
RegionView3D *rv3d = vctrl->ctx_rv3d;
if (restore) {
/* Revert to original view? */
if (vctrl->persp_backup == RV3D_CAMOB) { /* a camera view */
Object *ob_back = view3d_cameracontrol_object(vctrl);
/* store the original camera loc and rot */
BKE_object_tfm_restore(ob_back, vctrl->obtfm);
DEG_id_tag_update(&ob_back->id, ID_RECALC_TRANSFORM);
}
else {
/* Non Camera we need to reset the view back
* to the original location because the user canceled. */
copy_qt_qt(rv3d->viewquat, vctrl->rot_backup);
rv3d->persp = vctrl->persp_backup;
}
/* always, is set to zero otherwise */
copy_v3_v3(rv3d->ofs, vctrl->ofs_backup);
rv3d->dist = vctrl->dist_backup;
}
else if (vctrl->persp_backup == RV3D_CAMOB) { /* camera */
DEG_id_tag_update((ID *)view3d_cameracontrol_object(vctrl), ID_RECALC_TRANSFORM);
/* always, is set to zero otherwise */
copy_v3_v3(rv3d->ofs, vctrl->ofs_backup);
rv3d->dist = vctrl->dist_backup;
}
else { /* not camera */
/* Apply the fly mode view */
/* restore the dist */
ED_view3d_distance_set(rv3d, vctrl->dist_backup);
/* Done with correcting for the dist */
}
if (vctrl->is_ortho_cam) {
((Camera *)v3d->camera->data)->type = CAM_ORTHO;
}
if (vctrl->obtfm) {
MEM_freeN(vctrl->obtfm);
}
MEM_freeN(vctrl);
}