This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/windowmanager/manipulators/intern/wm_manipulator.c
Campbell Barton dfb890947d Manipulator: store initial-final matrix for reuse
Also take matrix_space into account when calculating final pixel size.
2017-08-15 13:46:09 +10:00

699 lines
18 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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) 2014 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/windowmanager/manipulators/intern/wm_manipulator.c
* \ingroup wm
*/
#include "BKE_context.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "GPU_batch.h"
#include "GPU_glew.h"
#include "GPU_immediate.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_idprop.h"
#include "WM_api.h"
#include "WM_types.h"
#ifdef WITH_PYTHON
#include "BPY_extern.h"
#endif
/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
#include "wm.h"
/* own includes */
#include "wm_manipulator_wmapi.h"
#include "wm_manipulator_intern.h"
static void wm_manipulator_register(
wmManipulatorGroup *mgroup, wmManipulator *mpr);
/**
* \note Follow #wm_operator_create convention.
*/
static wmManipulator *wm_manipulator_create(
const wmManipulatorType *wt,
PointerRNA *properties)
{
BLI_assert(wt != NULL);
BLI_assert(wt->struct_size >= sizeof(wmManipulator));
wmManipulator *mpr = MEM_callocN(
wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__);
mpr->type = wt;
/* initialize properties, either copy or create */
mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA");
if (properties && properties->data) {
mpr->properties = IDP_CopyProperty(properties->data);
}
else {
IDPropertyTemplate val = {0};
mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties");
}
RNA_pointer_create(G.main->wm.first, wt->srna, mpr->properties, mpr->ptr);
WM_manipulator_properties_sanitize(mpr->ptr, 0);
unit_m4(mpr->matrix_space);
unit_m4(mpr->matrix_basis);
unit_m4(mpr->matrix_offset);
return mpr;
}
wmManipulator *WM_manipulator_new_ptr(
const wmManipulatorType *wt, wmManipulatorGroup *mgroup,
PointerRNA *properties)
{
wmManipulator *mpr = wm_manipulator_create(wt, properties);
wm_manipulator_register(mgroup, mpr);
if (mpr->type->setup != NULL) {
mpr->type->setup(mpr);
}
return mpr;
}
/**
* \param wt: Must be valid,
* if you need to check it exists use #WM_manipulator_new_ptr
* because callers of this function don't NULL check the return value.
*/
wmManipulator *WM_manipulator_new(
const char *idname, wmManipulatorGroup *mgroup,
PointerRNA *properties)
{
const wmManipulatorType *wt = WM_manipulatortype_find(idname, false);
return WM_manipulator_new_ptr(wt, mgroup, properties);
}
/**
* Initialize default values and allocate needed memory for members.
*/
static void manipulator_init(wmManipulator *mpr)
{
const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
mpr->scale_basis = 1.0f;
mpr->line_width = 1.0f;
/* defaults */
copy_v4_v4(mpr->color, color_default);
copy_v4_v4(mpr->color_hi, color_default);
}
/**
* Register \a manipulator.
*
* \param name: name used to create a unique idname for \a manipulator in \a mgroup
*
* \note Not to be confused with type registration from RNA.
*/
static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
{
manipulator_init(mpr);
wm_manipulatorgroup_manipulator_register(mgroup, mpr);
}
/**
* \warning this doesn't check #wmManipulatorMap (highlight, selection etc).
* Typical use is when freeing the windowing data,
* where caller can manage clearing selection, highlight... etc.
*/
void WM_manipulator_free(wmManipulator *mpr)
{
#ifdef WITH_PYTHON
if (mpr->py_instance) {
/* do this first in case there are any __del__ functions or
* similar that use properties */
BPY_DECREF_RNA_INVALIDATE(mpr->py_instance);
}
#endif
if (mpr->op_data.ptr.data) {
WM_operator_properties_free(&mpr->op_data.ptr);
}
if (mpr->ptr != NULL) {
WM_manipulator_properties_free(mpr->ptr);
MEM_freeN(mpr->ptr);
}
if (mpr->type->target_property_defs_len != 0) {
wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
if (mpr_prop->custom_func.free_fn) {
mpr_prop->custom_func.free_fn(mpr, mpr_prop);
}
}
}
MEM_freeN(mpr);
}
/**
* Free \a manipulator and unlink from \a manipulatorlist.
* \a manipulatorlist is allowed to be NULL.
*/
void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C)
{
if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) {
wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
}
if (mpr->state & WM_MANIPULATOR_STATE_MODAL) {
wm_manipulatormap_modal_set(mmap, C, NULL, NULL);
}
/* Unlink instead of setting so we don't run callbacks. */
if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
WM_manipulator_select_unlink(mmap, mpr);
}
if (manipulatorlist) {
BLI_remlink(manipulatorlist, mpr);
}
BLI_assert(mmap->mmap_context.highlight != mpr);
BLI_assert(mmap->mmap_context.modal != mpr);
WM_manipulator_free(mpr);
}
/* -------------------------------------------------------------------- */
/** \name Manipulator Creation API
*
* API for defining data on manipulator creation.
*
* \{ */
PointerRNA *WM_manipulator_set_operator(
wmManipulator *mpr, wmOperatorType *ot, IDProperty *properties)
{
mpr->op_data.type = ot;
if (mpr->op_data.ptr.data) {
WM_operator_properties_free(&mpr->op_data.ptr);
}
WM_operator_properties_create_ptr(&mpr->op_data.ptr, ot);
if (properties) {
mpr->op_data.ptr.data = properties;
}
return &mpr->op_data.ptr;
}
static void wm_manipulator_set_matrix_rotation_from_z_axis__internal(
float matrix[4][4], const float z_axis[3])
{
/* old code, seems we can use simpler method */
#if 0
const float z_global[3] = {0.0f, 0.0f, 1.0f};
float rot[3][3];
rotation_between_vecs_to_mat3(rot, z_global, z_axis);
copy_v3_v3(matrix[0], rot[0]);
copy_v3_v3(matrix[1], rot[1]);
copy_v3_v3(matrix[2], rot[2]);
#else
normalize_v3_v3(matrix[2], z_axis);
ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
#endif
}
static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal(
float matrix[4][4], const float y_axis[3], const float z_axis[3])
{
normalize_v3_v3(matrix[1], y_axis);
normalize_v3_v3(matrix[2], z_axis);
cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
normalize_v3(matrix[0]);
}
/**
* wmManipulator.matrix utils.
*/
void WM_manipulator_set_matrix_rotation_from_z_axis(
wmManipulator *mpr, const float z_axis[3])
{
wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis);
}
void WM_manipulator_set_matrix_rotation_from_yz_axis(
wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
{
wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis);
}
void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3])
{
copy_v3_v3(mpr->matrix_basis[3], origin);
}
/**
* wmManipulator.matrix_offset utils.
*/
void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
wmManipulator *mpr, const float z_axis[3])
{
wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis);
}
void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
{
wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis);
}
void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3])
{
copy_v3_v3(mpr->matrix_offset[3], offset);
}
void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable)
{
if (enable) {
mpr->flag |= flag;
}
else {
mpr->flag &= ~flag;
}
}
void WM_manipulator_set_scale(wmManipulator *mpr, const float scale)
{
mpr->scale_basis = scale;
}
void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width)
{
mpr->line_width = line_width;
}
/**
* Set manipulator rgba colors.
*
* \param col Normal state color.
* \param col_hi Highlighted state color.
*/
void WM_manipulator_get_color(const wmManipulator *mpr, float color[4])
{
copy_v4_v4(color, mpr->color);
}
void WM_manipulator_set_color(wmManipulator *mpr, const float color[4])
{
copy_v4_v4(mpr->color, color);
}
void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4])
{
copy_v4_v4(color_hi, mpr->color_hi);
}
void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4])
{
copy_v4_v4(mpr->color_hi, color_hi);
}
/** \} */ // Manipulator Creation API
/* -------------------------------------------------------------------- */
/** \name Manipulator Callback Assignment
*
* \{ */
void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn)
{
mpr->custom_modal = fn;
}
/** \} */
/* -------------------------------------------------------------------- */
/**
* Add/Remove \a manipulator to selection.
* Reallocates memory for selected manipulators so better not call for selecting multiple ones.
*
* \return if the selection has changed.
*/
bool wm_manipulator_select_set_ex(
wmManipulatorMap *mmap, wmManipulator *mpr, bool select,
bool use_array, bool use_callback)
{
bool changed = false;
if (select) {
if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
if (use_array) {
wm_manipulatormap_select_array_push_back(mmap, mpr);
}
mpr->state |= WM_MANIPULATOR_STATE_SELECT;
changed = true;
}
}
else {
if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
if (use_array) {
wm_manipulatormap_select_array_remove(mmap, mpr);
}
mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
changed = true;
}
}
/* In the case of unlinking we only want to remove from the array
* and not write to the external state */
if (use_callback && changed) {
if (mpr->type->select_refresh) {
mpr->type->select_refresh(mpr);
}
}
return changed;
}
/* Remove from selection array without running callbacks. */
bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr)
{
return wm_manipulator_select_set_ex(mmap, mpr, false, true, false);
}
bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select)
{
return wm_manipulator_select_set_ex(mmap, mpr, select, true, true);
}
bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
{
if (WM_manipulator_select_set(mmap, mpr, true)) {
wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
return true;
}
else {
return false;
}
}
void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C)
{
const RegionView3D *rv3d = CTX_wm_region_view3d(C);
float scale = U.ui_scale;
if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) {
scale *= U.manipulator_size;
if (rv3d) {
/* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
float matrix_world[4][4];
if (mpr->type->matrix_basis_get) {
float matrix_basis[4][4];
mpr->type->matrix_basis_get(mpr, matrix_basis);
mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis);
}
else {
mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis);
}
/* Exclude matrix_offset from scale. */
scale *= ED_view3d_pixel_size(rv3d, matrix_world[3]) / U.pixelsize;
}
else {
scale *= 0.02f;
}
}
mpr->scale_final = mpr->scale_basis * scale;
}
static void manipulator_update_prop_data(wmManipulator *mpr)
{
/* manipulator property might have been changed, so update manipulator */
if (mpr->type->property_update) {
wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
if (WM_manipulator_target_property_is_valid(mpr_prop)) {
mpr->type->property_update(mpr, mpr_prop);
}
}
}
}
void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map)
{
if (refresh_map) {
manipulator_update_prop_data(mpr);
}
wm_manipulator_calculate_scale(mpr, C);
}
int wm_manipulator_is_visible(wmManipulator *mpr)
{
if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
return 0;
}
if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) &&
!(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE)))
{
/* don't draw while modal (dragging) */
return 0;
}
if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) &&
!(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) &&
!(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */
{
/* update but don't draw */
return WM_MANIPULATOR_IS_VISIBLE_UPDATE;
}
return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW;
}
void WM_manipulator_calc_matrix_final_params(
const wmManipulator *mpr,
const struct WM_ManipulatorMatrixParams *params,
float r_mat[4][4])
{
const float (* const matrix_space)[4] = params->matrix_space ? params->matrix_space : mpr->matrix_space;
const float (* const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis : mpr->matrix_basis;
const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset;
const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final;
float final_matrix[4][4];
copy_m4_m4(final_matrix, matrix_basis);
if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) {
mul_mat3_m4_fl(final_matrix, *scale_final);
mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
}
else {
mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
mul_mat3_m4_fl(final_matrix, *scale_final);
}
mul_m4_m4m4(r_mat, matrix_space, final_matrix);
}
void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4])
{
WM_manipulator_calc_matrix_final_params(
mpr,
&((struct WM_ManipulatorMatrixParams) {
.matrix_space = mpr->matrix_space,
.matrix_basis = mpr->matrix_basis,
.matrix_offset = mpr->matrix_offset,
.scale_final = &mpr->scale_final,
}), r_mat
);
}
/** \name Manipulator Propery Access
*
* Matches `WM_operator_properties` conventions.
*
* \{ */
void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt)
{
RNA_pointer_create(NULL, wt->srna, NULL, ptr);
}
void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring)
{
const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false);
if (wt)
WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt);
else
RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr);
}
/* similar to the function above except its uses ID properties
* used for keymaps and macros */
void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring)
{
if (*properties == NULL) {
IDPropertyTemplate val = {0};
*properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
}
if (*ptr == NULL) {
*ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
WM_manipulator_properties_create(*ptr, wtstring);
}
(*ptr)->data = *properties;
}
void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context)
{
RNA_STRUCT_BEGIN (ptr, prop)
{
switch (RNA_property_type(prop)) {
case PROP_ENUM:
if (no_context)
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
else
RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
break;
case PROP_POINTER:
{
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
/* recurse into manipulator properties */
if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) {
PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
WM_manipulator_properties_sanitize(&opptr, no_context);
}
break;
}
default:
break;
}
}
RNA_STRUCT_END;
}
/** set all props to their default,
* \param do_update Only update un-initialized props.
*
* \note, theres nothing specific to manipulators here.
* this could be made a general function.
*/
bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update)
{
bool changed = false;
RNA_STRUCT_BEGIN (ptr, prop)
{
switch (RNA_property_type(prop)) {
case PROP_POINTER:
{
StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
if (ptype != &RNA_Struct) {
PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
changed |= WM_manipulator_properties_default(&opptr, do_update);
}
break;
}
default:
if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
if (RNA_property_reset(ptr, prop, -1)) {
changed = true;
}
}
break;
}
}
RNA_STRUCT_END;
return changed;
}
/* remove all props without PROP_SKIP_SAVE */
void WM_manipulator_properties_reset(wmManipulator *mpr)
{
if (mpr->ptr->data) {
PropertyRNA *iterprop;
iterprop = RNA_struct_iterator_property(mpr->type->srna);
RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop)
{
PropertyRNA *prop = itemptr.data;
if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
const char *identifier = RNA_property_identifier(prop);
RNA_struct_idprops_unset(mpr->ptr, identifier);
}
}
RNA_PROP_END;
}
}
void WM_manipulator_properties_clear(PointerRNA *ptr)
{
IDProperty *properties = ptr->data;
if (properties) {
IDP_ClearProperty(properties);
}
}
void WM_manipulator_properties_free(PointerRNA *ptr)
{
IDProperty *properties = ptr->data;
if (properties) {
IDP_FreeProperty(properties);
MEM_freeN(properties);
ptr->data = NULL; /* just in case */
}
}
/** \} */