virtualGizmo3D is an 3D GIZMO manipulator: like a trackball it provides a way to rotate, move and scale a model, with mouse, also with dolly and pan features You can also define a way to rotate the model around any single axis. It use mouse movement on screen, mouse buttons and (eventually) key modifiers, like Shift/Ctrl/Alt/Super, that you define
It uses quaternions algebra, internally, to manage rotations, but you can also only pass your model matrix and gat back a transformation matrix with rotation, translation and scale, inside.
virtualGizmo3D is an header only tool (vGizmo.h
) and is not bound to frameworks or render engines, is written in C++ (C++11) and uses vgMath a compact (my single file header only) vectors/matrices/quaternions tool/lib that makes virtualGizmo3D standalone.
No other files or external libraries are required
In this way you can use it with any engine, like: OpenGL, DirectX, Vulkan, Metal, etc. and/or with any framework, like: GLFW, SDL, GLUT, Native O.S. calls, etc.
You can use vgMath also externally, for your purposes: it contains classes to manipulate vectors (with 2/3/4 components), quaternions, square matricies (3x3 and 4x4), both as simple single precision float
classes (Default) or, enabling template classes (simply adding a #define
), as both float
and double
data types. It contains also 4 helper functions to define Model/View matrix: perspective, frustum, lookAt, ortho
If need a larger/complete library, as alternative to virtualGizmo3D, is also possible to interface imGuIZMO.quat with glm mathematics library (simply adding a #define
)
==> *Please, read Configure virtualGizmo3D section.
You can run/test an emscripten WebGL 2 example of virtualGismo3D from following link:
It works only on browsers with WebGl2 and webAssembly support (FireFox/Opera/Chrome and Chromium based): test if your browser supports WebGL2, here: WebGL2 Report
To use virtualGizmo3D need to include virtualGizmo.h
file in your code and declare an object of type vfGizmo3DClass, global or as member of your class
#include "vGizmo.h"
// Global or member class declaration
using namespace vg;
vGizmo3D gizmo;
vGizmo3D &getGizmo() { return gizmo; } //optional helper
In your 3D engine initialization declare (overriding default ones) your preferred controls:
GLFW buttons/keys initialization
void onInit()
{
//If you use a different framework simply associate internal ID with your preferences
//For main manipulator/rotation
getGizmo().setGizmoRotControl( (vgButtons) GLFW_MOUSE_BUTTON_LEFT, (vgModifiers) 0 /* evNoModifier */ );
//for pan and zoom/dolly... you can use also wheel to zoom
getGizmo().setDollyControl((vgButtons) GLFW_MOUSE_BUTTON_RIGHT, (vgModifiers) GLFW_MOD_CONTROL|GLFW_MOD_SHIFT);
getGizmo().setPanControl( (vgButtons) GLFW_MOUSE_BUTTON_RIGHT, (vgModifiers) 0);
// Now call viewportSize with the dimension of window/screen
// It is need to set mouse sensitivity for rotation
// You need to call it also in your "reshape" function: when resize the window (look below)
getGizmo().viewportSize(GetWidth(), GetHeight());
// more...
// if you need to rotate around a single axis have to select your preferences: uncomment below...
//
// getGizmo().setGizmoRotXControl((vgButtons) GLFW_MOUSE_BUTTON_LEFT, (vgModifiers) GLFW_MOD_SHIFT); // around X pressing SHIFT+LButton
// getGizmo().setGizmoRotYControl((vgButtons) GLFW_MOUSE_BUTTON_LEFT, (vgModifiers) GLFW_MOD_CONTROL); // around Y pressing CTRL+LButton
// getGizmo().setGizmoRotZControl((vgButtons) GLFW_MOUSE_BUTTON_LEFT, (vgModifiers) GLFW_MOD_ALT | GLFW_MOD_SUPER); // around Z pressing ALT|SUPER+LButton
}
SDL buttons/keys Initialization
void onInit()
{
//If you use a different framework simply associate internal ID with your preferences
//For main manipulator/rotation
getGizmo().setGizmoRotControl( (vgButtons) SDL_BUTTON_LEFT, (vgModifiers) 0 /* evNoModifier */ );
//for pan and zoom/dolly... you can use also wheel to zoom
getGizmo().setDollyControl((vgButtons) SDL_BUTTON_RIGHT, (vgModifiers) 0);
getGizmo().setPanControl( (vgButtons) SDL_BUTTON_RIGHT, (vgModifiers) KMOD_CTRL|KMOD_SHIFT);
// Now call viewportSize with the dimension of window/screen
// It is need to set mouse sensitivity for rotation
// You need to call it also in your "reshape" function: when resize the window (look below)
getGizmo().viewportSize(GetWidth(), GetHeight());
// more...
// if you need to rotate around a single axis have to select your preferences: uncomment below...
//
// getGizmo().setGizmoRotXControl((vgButtons) SDL_BUTTON_LEFT, (vgModifiers) KMOD_SHIFT); // around X pressing SHIFT+LButton
// getGizmo().setGizmoRotYControl((vgButtons) SDL_BUTTON_LEFT, (vgModifiers) KMOD_CTRL); // around Y pressing CTRL+LButton
// getGizmo().setGizmoRotZControl((vgButtons) SDL_BUTTON_LEFT, (vgModifiers) KMOD_ALT); // around Z pressing ALT+LButton
}
Now you need to add some event funtions:
In your Mouse-Button Event function need to call:
void onMouseButton(int button, int upOrDown, int x, int y)
{
// Call in 'mouse button event' the gizmo.mouse() func with:
// button: your mouse button
// mod: your modifier key -> CTRL, SHIFT, ALT, SUPER
// pressed: if button is pressed (TRUE) or released (FALSE)
// x, y: mouse coordinates
bool isPressed = upOrDown==GLFW_PRESS; // or upOrDown==SDL_MOUSEBUTTONDOWN for SDL
getGizmo().mouse((vgButtons) (button), (vgModifiers) theApp->getModifier(), isPressed, x, y);
}
In your Mouse-Motion Event function need to call:
void onMotion(int x, int y)
{
// Call on motion event to communicate the position
getGizmo().motion(x, y);
}
And in your Resize-Window Event function :
void onReshape(GLint w, GLint h)
{
// call it on resize window to re-align mouse sensitivity
getGizmo().viewportSize(w, h);
}
And finally, in your render function (or where you prefer) you can get the transformations
void onRender() //or when you prefer
{
mat4 model(1.0f); // Identity matrix
// virtualGizmo transformations
getGizmo().applyTransform(model); // apply transform to matrix model
// Now the matrix 'model' has inside all the transformations:
// rotation, pan and dolly translations,
// and you can build MV and MVP matrix
}
If you need to more feeling with the mouse use:
getGizmo().setGizmoFeeling(1.0); // 1.0 default, > 1.0 more sensible, < 1.0 less
Same thing for Dolly and Pan:
getGizmo().setDollyScale(1.0f);
getGizmo().setPanScale(1.0f);
You probably will need to set center of rotation (default: origin), Dolly position (default: 1.0), and Pan position (default: vec2(0.0, 0.0)
getGizmo().setDollyPosition(1.0f);
getGizmo().setPanPosition(vec3(0.0f);
getGizmo().setRotationCenter(vec3(0.0));
If you want a continuous rotation, that you can stop with a click, like in example, need to add the below call in your Idle function, or inside of the main render loop
void onIdle()
{
// call it every rendering if want an continue rotation until you do not click on screen
// look at glApp.cpp : "mainLoop" ("newFrame") functions
getGizmo().idle();
}
Class declaration
The include file vGizmo.h
contains two classes:
virtualGizmoClass
simple rotation manipulator, used mainly for imGuIZMO.quat (a GIZMO widget developed for ImGui, Graphic User Intefrace)virtualGizmo3DClass
manipulator (like above) with dolly/zoom and pan/shift- Template classes are also available if configured. *(read below)
Helper typedef
are also defined:
using vGizmo = virtualGizmoClass;
using vGizmo3D = virtualGizmo3DClass;
virtalGizmo3D uses vgMath tool, it contains a group of vector/matrices/quaternion classes, operators, and principal functions. It uses the "glsl" convention for types and function names so is compatible with glm types and function calls: vgMath is a subset of glm mathematics library and so you can use first or upgrade to second via a simple #define
. However vgMath does not want replicate glm, is only intended to make virtalGizmo3D standalone, and avoid template classes use in the cases of low resources or embedded systems.
The file vgConfig.h
allows to configure internal math used form virtalGizmo3D. In particular is possible select between:
- static float classes (Default) / template classes
- internal vgMath tool (Default) / glm mathematics library
- Right (Default) / Left handed coordinate system (lookAt, perspective, ortho, frustum - functions)
- enable (Default) / disable automatic entry of
using namespace vgm;
at end ofvgMath.h
(it influences only your external use ofvgMath.h
) - Add additional HLSL types name convention
You can do this simply by commenting / uncommenting a line in vgConfig.h
or adding related "define" to your project, as you can see below:
// uncomment to use TEMPLATE internal vgMath classes/types
//
// This is if you need to extend the use of different math types in your code
// or for your purposes, there are predefined alias:
// float ==> vec2 / vec3 / vec4 / quat / mat3|mat3x3 / mat4|mat4x4
// and more TEMPLATE (only!) alias:
// double ==> dvec2 / dvec3 / dvec4 / dquat / dmat3|dmat3x3 / dmat4|dmat4x4
// int ==> ivec2 / ivec3 / ivec4
// uint ==> uvec2 / uvec3 / uvec4
// If you select TEMPLATE classes the widget too will use internally them
// with single precision (float)
//
// Default ==> NO template
//------------------------------------------------------------------------------
//#define VGM_USES_TEMPLATE
// uncomment to use "glm" (0.9.9 or higher) library instead of vgMath
// Need to have "glm" installed and in your INCLUDE research compiler path
//
// vgMath is a subset of "glm" and is compatible with glm types and calls
// change only namespace from "vgm" to "glm". It's automatically set by
// including vGizmo.h or vgMath.h or imGuIZMOquat.h
//
// Default ==> use vgMath
// If you enable GLM use, automatically is enabled also VGM_USES_TEMPLATE
// if you can, I recommend to use GLM
//------------------------------------------------------------------------------
//#define VGIZMO_USES_GLM
// uncomment to use LeftHanded
//
// This is used only in: lookAt / perspective / ortho / frustrum - functions
// DX is LeftHanded, OpenGL is RightHanded
//
// Default ==> RightHanded
//------------------------------------------------------------------------------
//#define VGM_USES_LEFT_HAND_AXES
From v.2.1
// uncomment to avoid vgMath.h add folow line code:
// using namespace vgm | glm; // if (!VGIZMO_USES_GLM | VGIZMO_USES_GLM)
//
// Automatically "using namespace" is added to the end vgMath.h:
// it help to maintain compatibilty between vgMath & glm declaration types,
// but can go in confict with other pre-exist data types in your project
//
// note: this is only if you use vgMath.h in your project, for your data types:
// it have no effect for vGizmo | imGuIZMO internal use
//
// Default ==> vgMath.h add: using namespace vgm | glm;
//------------------------------------------------------------------------------
//#define VGM_DISABLE_AUTO_NAMESPACE
// uncomment to use HLSL name types (in addition!)
//
// It add also the HLSL notation in addition to existing one:
// alias types:
// float ==> float2 / float3 / float4 / quat / float3x3 / float4x4
// and more TEMPLATE (only!) alias:
// double ==> double2 / double3 / double4 / dquat / double3x3 / double4x4
// int ==> int2 / int3 / int4
// uint ==> uint2 / uint3 / uint4
//
// Default ==> NO HLSL alia types defined
//------------------------------------------------------------------------------
//#define VGM_USES_HLSL_TYPES
- If your project grows you can upgrade/pass to glm, in any moment
- My glChAoS.P project can switch from internal vgMath (
VGIZMO_USES_TEMPLATE
) to glm (VGIZMO_USES_GLM
), and vice versa, only changing defines: you can examine it as example
The source code example shown in the animated gif screenshot, is provided.
In example I use GLFW or SDL2 (via #define GLAPP_USE_SDL
) with OpenGL, but it is simple to change if you use Vulkan/DirectX/etc, other frameworks (like GLUT) or native OS access.
To use SDL framework instead of GLFW, uncomment #define GLAPP_USE_SDL
in glApp.h
file, or pass -DGLAPP_USE_SDL
directly to compiler.
CMake users can pass command line -DUSE_SDL:BOOL=TRUE
(or use relative GUI flag) to enable SDL framework instead of GLFW.
To build it you can use CMake (3.10 or higher) or the Visual Studio solution project (for VS 2017) in Windows. You need to have GLFW (or SDL) in your compiler search path (LIB/INCLUDE).
The CMake file is able to build also an EMSCRIPTEN version, obviously you need to have installed EMSCRIPTEN SDK on your computer (1.38.10 or higher): look at or use the helper batch/script files, in main example folder, to pass appropriate defines/parameters to CMake command line.
To build the EMSCRIPTEN version, in Windows, with CMake, need to have mingw32-make.exe in your computer and search PATH (only the make utility is enough): it is a condition of EMSDK tool to build with CMake.
For windows users that use vs2017 project solution:
- To build SDL or GLFW, select appropriate build configuration
- If you have GLFW and/or SDL headers/library directory paths added to
INCLUDE
andLIB
environment vars, the compiler find them. - The current VisualStudio project solution refers to my environment variable RAMDISK (
R:
), and subsequent VS intrinsic variables to generate binary output:$(RAMDISK)\$(MSBuildProjectDirectoryNoRoot)\$(DefaultPlatformToolset)\$(Platform)\$(Configuration)\
, so without a RAMDISK variable, executable and binary files are outputted in base to the values of these VS variables, starting from root of current drive. (you find built binary here... or change it)