338 lines
9.2 KiB
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);
|
|
}
|