diff options
Diffstat (limited to 'source/blender/editors/manipulator_library/dial3d_manipulator.c')
-rw-r--r-- | source/blender/editors/manipulator_library/dial3d_manipulator.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/source/blender/editors/manipulator_library/dial3d_manipulator.c b/source/blender/editors/manipulator_library/dial3d_manipulator.c new file mode 100644 index 00000000000..5097c8cab6f --- /dev/null +++ b/source/blender/editors/manipulator_library/dial3d_manipulator.c @@ -0,0 +1,376 @@ +/* + * ***** 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 dial3d_manipulator.c + * \ingroup wm + * + * \name Dial Manipulator + * + * 3D Manipulator + * + * \brief Circle shaped manipulator for circular interaction. + * Currently no own handling, use with operator only. + */ + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "BKE_context.h" + +#include "BLI_math.h" + +#include "DNA_manipulator_types.h" + +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_manipulator_library.h" + +#include "GPU_select.h" + +#include "GPU_matrix.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* own includes */ +#include "manipulator_geometry.h" +#include "manipulator_library_intern.h" + +/* to use custom dials exported to geom_dial_manipulator.c */ +// #define USE_MANIPULATOR_CUSTOM_DIAL + +typedef struct DialManipulator { + wmManipulator manipulator; + int style; + float direction[3]; +} DialManipulator; + +typedef struct DialInteraction { + float init_mval[2]; + + /* cache the last angle to detect rotations bigger than -/+ PI */ + float last_angle; + /* number of full rotations */ + int rotations; +} DialInteraction; + +#define DIAL_WIDTH 1.0f +#define DIAL_RESOLUTION 32 + +/* -------------------------------------------------------------------- */ + +static void dial_geom_draw( + const DialManipulator *dial, const float col[4], const bool select, + float axis_modal_mat[4][4], float clip_plane[4]) +{ +#ifdef USE_MANIPULATOR_CUSTOM_DIAL + UNUSED_VARS(dial, col, axis_modal_mat, clip_plane); + wm_manipulator_geometryinfo_draw(&wm_manipulator_geom_data_dial, select); +#else + const bool filled = (dial->style == ED_MANIPULATOR_DIAL_STYLE_RING_FILLED); + + glLineWidth(dial->manipulator.line_width); + + VertexFormat *format = immVertexFormat(); + unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT); + + if (clip_plane) { + immBindBuiltinProgram(GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR); + float clip_plane_f[4] = {clip_plane[0], clip_plane[1], clip_plane[2], clip_plane[3]}; + immUniform4fv("ClipPlane", clip_plane_f); + immUniformMatrix4fv("ModelMatrix", axis_modal_mat); + } + else { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + } + + immUniformColor4fv(col); + + if (filled) { + imm_draw_circle_fill(pos, 0, 0, 1.0, DIAL_RESOLUTION); + } + else { + imm_draw_circle_wire(pos, 0, 0, 1.0, DIAL_RESOLUTION); + } + + immUnbindProgram(); + + UNUSED_VARS(select); +#endif +} + +/** + * Draws a line from (0, 0, 0) to \a co_outer, at \a angle. + */ +static void dial_ghostarc_draw_helpline(const float angle, const float co_outer[3], const float col[4]) +{ + glLineWidth(1.0f); + + gpuPushMatrix(); + gpuRotate3f(RAD2DEGF(angle), 0.0f, 0.0f, -1.0f); + + unsigned int pos = VertexFormat_add_attrib(immVertexFormat(), "pos", COMP_F32, 3, KEEP_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + immUniformColor4fv(col); + + immBegin(PRIM_LINE_STRIP, 2); + immVertex3f(pos, 0.0f, 0.0f, 0.0f); + immVertex3fv(pos, co_outer); + immEnd(); + + immUnbindProgram(); + + gpuPopMatrix(); +} + +static void dial_ghostarc_draw( + const DialManipulator *dial, const float angle_ofs, const float angle_delta, const float color[4]) +{ + const float width_inner = DIAL_WIDTH - dial->manipulator.line_width * 0.5f / U.manipulator_scale; + + VertexFormat *format = immVertexFormat(); + unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4fv(color); + imm_draw_disk_partial_fill( + pos, 0, 0, 0.0, width_inner, DIAL_RESOLUTION, RAD2DEGF(angle_ofs), RAD2DEGF(angle_delta)); + immUnbindProgram(); +} + +static void dial_ghostarc_get_angles( + const DialManipulator *dial, const wmEvent *event, const ARegion *ar, + float mat[4][4], const float co_outer[3], + float *r_start, float *r_delta) +{ + DialInteraction *inter = dial->manipulator.interaction_data; + const RegionView3D *rv3d = ar->regiondata; + const float mval[2] = {event->x - ar->winrct.xmin, event->y - ar->winrct.ymin}; + bool inv = false; + + /* we might need to invert the direction of the angles */ + float view_vec[3], axis_vec[3]; + ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec); + normalize_v3_v3(axis_vec, dial->direction); + if (dot_v3v3(view_vec, axis_vec) < 0.0f) { + inv = true; + } + + float co[3], origin2d[2], co2d[2]; + mul_v3_project_m4_v3(co, mat, co_outer); + /* project 3d coordinats to 2d viewplane */ + ED_view3d_project_float_global(ar, dial->manipulator.origin, origin2d, V3D_PROJ_TEST_NOP); + ED_view3d_project_float_global(ar, co, co2d, V3D_PROJ_TEST_NOP); + + /* convert to manipulator relative space */ + float rel_initmval[2], rel_mval[2], rel_co[2]; + sub_v2_v2v2(rel_initmval, inter->init_mval, origin2d); + sub_v2_v2v2(rel_mval, mval, origin2d); + sub_v2_v2v2(rel_co, co2d, origin2d); + + /* return angles */ + const float start = angle_signed_v2v2(rel_co, rel_initmval) * (inv ? -1 : 1); + const float delta = angle_signed_v2v2(rel_initmval, rel_mval) * (inv ? -1 : 1); + + /* Change of sign, we passed the 180 degree threshold. This means we need to add a turn + * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2. + * Logic taken from BLI_dial_angle */ + if ((delta * inter->last_angle < 0.0f) && + (fabsf(inter->last_angle) > (float)M_PI_2)) + { + if (inter->last_angle < 0.0f) + inter->rotations--; + else + inter->rotations++; + } + inter->last_angle = delta; + + *r_start = start; + *r_delta = fmod(delta + 2.0f * (float)M_PI * inter->rotations, 2 * (float)M_PI); +} + +static void dial_draw_intern( + const bContext *C, DialManipulator *dial, + const bool select, const bool highlight, float clip_plane[4]) +{ + float rot[3][3]; + float mat[4][4]; + const float up[3] = {0.0f, 0.0f, 1.0f}; + float col[4]; + + BLI_assert(CTX_wm_area(C)->spacetype == SPACE_VIEW3D); + + manipulator_color_get(&dial->manipulator, highlight, col); + + rotation_between_vecs_to_mat3(rot, up, dial->direction); + copy_m4_m3(mat, rot); + copy_v3_v3(mat[3], dial->manipulator.origin); + mul_mat3_m4_fl(mat, dial->manipulator.scale); + + gpuPushMatrix(); + gpuMultMatrix(mat); + gpuTranslate3fv(dial->manipulator.offset); + + /* draw rotation indicator arc first */ + if ((dial->manipulator.flag & WM_MANIPULATOR_DRAW_VALUE) && (dial->manipulator.state & WM_MANIPULATOR_STATE_ACTIVE)) { + wmWindow *win = CTX_wm_window(C); + const float co_outer[4] = {0.0f, DIAL_WIDTH, 0.0f}; /* coordinate at which the arc drawing will be started */ + float angle_ofs, angle_delta; + + dial_ghostarc_get_angles(dial, win->eventstate, CTX_wm_region(C), mat, co_outer, &angle_ofs, &angle_delta); + /* draw! */ + dial_ghostarc_draw(dial, angle_ofs, angle_delta, (const float [4]){0.8f, 0.8f, 0.8f, 0.4f}); + + dial_ghostarc_draw_helpline(angle_ofs, co_outer, col); /* starting position */ + dial_ghostarc_draw_helpline(angle_ofs + angle_delta, co_outer, col); /* starting position + current value */ + } + + /* draw actual dial manipulator */ + dial_geom_draw(dial, col, select, mat, clip_plane); + + gpuPopMatrix(); +} + +static void manipulator_dial_render_3d_intersect(const bContext *C, wmManipulator *manipulator, int selectionbase) +{ + DialManipulator *dial = (DialManipulator *)manipulator; + float clip_plane_buf[4]; + float *clip_plane = (dial->style == ED_MANIPULATOR_DIAL_STYLE_RING_CLIPPED) ? clip_plane_buf : NULL; + + /* enable clipping if needed */ + if (clip_plane) { + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + copy_v3_v3(clip_plane, rv3d->viewinv[2]); + clip_plane[3] = -dot_v3v3(rv3d->viewinv[2], manipulator->origin); + glEnable(GL_CLIP_DISTANCE0); + } + + GPU_select_load_id(selectionbase); + dial_draw_intern(C, dial, true, false, clip_plane); + + if (clip_plane) { + glDisable(GL_CLIP_DISTANCE0); + } +} + +static void manipulator_dial_draw(const bContext *C, wmManipulator *manipulator) +{ + DialManipulator *dial = (DialManipulator *)manipulator; + const bool active = manipulator->state & WM_MANIPULATOR_STATE_ACTIVE; + const bool highlight = (manipulator->state & WM_MANIPULATOR_STATE_HIGHLIGHT) != 0; + float clip_plane_buf[4]; + float *clip_plane = (!active && dial->style == ED_MANIPULATOR_DIAL_STYLE_RING_CLIPPED) ? clip_plane_buf : NULL; + + /* enable clipping if needed */ + if (clip_plane) { + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + copy_v3_v3(clip_plane, rv3d->viewinv[2]); + clip_plane[3] = -dot_v3v3(rv3d->viewinv[2], manipulator->origin); + clip_plane[3] -= 0.02 * dial->manipulator.scale; + + glEnable(GL_CLIP_DISTANCE0); + } + + glEnable(GL_BLEND); + dial_draw_intern(C, dial, false, highlight, clip_plane); + glDisable(GL_BLEND); + + if (clip_plane) { + glDisable(GL_CLIP_DISTANCE0); + } +} + +static void manipulator_dial_invoke( + bContext *UNUSED(C), wmManipulator *manipulator, const wmEvent *event) +{ + DialInteraction *inter = MEM_callocN(sizeof(DialInteraction), __func__); + + inter->init_mval[0] = event->mval[0]; + inter->init_mval[1] = event->mval[1]; + + manipulator->interaction_data = inter; +} + + +/* -------------------------------------------------------------------- */ +/** \name Dial Manipulator API + * + * \{ */ + +wmManipulator *ED_manipulator_dial3d_new(wmManipulatorGroup *mgroup, const char *name, const int style) +{ + const wmManipulatorType *mpt = WM_manipulatortype_find("MANIPULATOR_WT_dial", false); + DialManipulator *dial = (DialManipulator *)WM_manipulator_new(mpt, mgroup, name); + + const float dir_default[3] = {0.0f, 0.0f, 1.0f}; + + dial->style = style; + + /* defaults */ + copy_v3_v3(dial->direction, dir_default); + + return (wmManipulator *)dial; +} + +/** + * Define up-direction of the dial manipulator + */ +void ED_manipulator_dial3d_set_up_vector(wmManipulator *manipulator, const float direction[3]) +{ + DialManipulator *dial = (DialManipulator *)manipulator; + + copy_v3_v3(dial->direction, direction); + normalize_v3(dial->direction); +} + +static void MANIPULATOR_WT_dial_3d(wmManipulatorType *wt) +{ + /* identifiers */ + wt->idname = "MANIPULATOR_WT_dial"; + + /* api callbacks */ + wt->draw = manipulator_dial_draw; + wt->draw_select = manipulator_dial_render_3d_intersect; + wt->invoke = manipulator_dial_invoke; + + wt->size = sizeof(DialManipulator); +} + +void ED_manipulatortypes_dial_3d(void) +{ + WM_manipulatortype_append(MANIPULATOR_WT_dial_3d); +} + +/** \} */ // Dial Manipulator API |