You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1113 lines
43 KiB
C++
1113 lines
43 KiB
C++
//------------------------------------------------------------------------------
|
|
// Copyright (c) 2018-2025 Michele Morrone
|
|
// All rights reserved.
|
|
//
|
|
// https://michelemorrone.eu - https://brutpitt.com
|
|
//
|
|
// X: https://x.com/BrutPitt - GitHub: https://github.com/BrutPitt
|
|
//
|
|
// direct mail: brutpitt(at)gmail.com - me(at)michelemorrone.eu
|
|
//
|
|
// This software is distributed under the terms of the BSD 2-Clause license
|
|
//------------------------------------------------------------------------------
|
|
#include "imGuIZMOquat.h"
|
|
|
|
ImVector<vec3> imguiGizmo::sphereVtx;
|
|
ImVector<int> imguiGizmo::sphereTess;
|
|
ImVector<vec3> imguiGizmo::arrowVtx[4];
|
|
ImVector<vec3> imguiGizmo::arrowNorm[4];
|
|
ImVector<vec3> imguiGizmo::cubeVtx;
|
|
ImVector<vec3> imguiGizmo::cubeNorm;
|
|
ImVector<vec3> imguiGizmo::planeVtx;
|
|
ImVector<vec3> imguiGizmo::planeNorm;
|
|
bool imguiGizmo::solidAreBuilt = false;
|
|
bool imguiGizmo::dragActivate = false;
|
|
//
|
|
// Settings
|
|
//
|
|
// axes/arrow are composed of cone (or pyramid) and cylinder
|
|
// (or parallelepiped): this solid are builded at first instance
|
|
// and will have same slices/radius/length for all controls in your
|
|
// applications but can be resized proportionally with a reductin
|
|
// factor: solidResizeFactor and axesResizeFactor.
|
|
// Same thing for the colors of sphere tessellation, while color
|
|
// of axes and cube are fixed
|
|
//
|
|
// Solid/axes settings can be set one only one time before your widget
|
|
// while solidResizeFactor and axesResizeFactor settings must
|
|
// be call before and always of your widget, every redraw... and
|
|
// restored after use... like push/pop
|
|
// ... I avoided creating a push/pop mechanism
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// arrow/axes components
|
|
///////////////////////////////////////
|
|
int imguiGizmo::coneSlices = 4;
|
|
float imguiGizmo::coneRadius = 0.07f;
|
|
float imguiGizmo::coneLength = 0.37f;
|
|
|
|
|
|
int imguiGizmo::cylSlices = 7;
|
|
float imguiGizmo::cylRadius = 0.02f; // sizeCylLength = defined in base to control size
|
|
|
|
|
|
// Sphere components
|
|
///////////////////////////////////////
|
|
float imguiGizmo::sphereRadius = .27f;
|
|
int imguiGizmo::sphereTessFactor = imguiGizmo::sphereTess4;
|
|
|
|
// Cube components
|
|
///////////////////////////////////////
|
|
float imguiGizmo::cubeSize = .05f;
|
|
|
|
// Plane components
|
|
///////////////////////////////////////
|
|
float imguiGizmo::planeSize = .33f;
|
|
float imguiGizmo::planeThickness = .015f;
|
|
|
|
// Axes resize
|
|
///////////////////////////////////////
|
|
vec3 imguiGizmo::axesResizeFactor(.95f, 1.0f, 1.0f);
|
|
vec3 imguiGizmo::savedAxesResizeFactor = imguiGizmo::axesResizeFactor;
|
|
|
|
// Solid resize
|
|
///////////////////////////////////////
|
|
float imguiGizmo::solidResizeFactor = 1.0f;
|
|
float imguiGizmo::savedSolidResizeFactor = imguiGizmo::solidResizeFactor;
|
|
|
|
// Direction arrow color
|
|
///////////////////////////////////////
|
|
ImVec4 imguiGizmo::directionColor(1.0f, 1.0f, 0.0f, 1.0f);
|
|
ImVec4 imguiGizmo::savedDirectionColor = imguiGizmo::directionColor;
|
|
|
|
// Plane color
|
|
///////////////////////////////////////
|
|
ImVec4 imguiGizmo::planeColor(0.0f, 0.5f, 1.0f, STARTING_ALPHA_PLANE);
|
|
ImVec4 imguiGizmo::savedPlaneColor = imguiGizmo::planeColor;
|
|
|
|
// Sphere Colors
|
|
///////////////////////////////////////
|
|
ImU32 imguiGizmo::sphereColors[2] = { 0xff401010, 0xffc0a0a0 }; // Tessellation colors
|
|
ImU32 imguiGizmo::savedSphereColors[2] = { 0xff401010, 0xffc0a0a0 };
|
|
//ImU32 spherecolorA=0xff005cc0, spherecolorB=0xffc05c00;
|
|
|
|
// Gizmo mouse settings
|
|
///////////////////////////////////////
|
|
float imguiGizmo::gizmoFeelingRot = 1.f; // >1 more mouse sensibility, <1 less mouse sensibility
|
|
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
float imguiGizmo::dollyScale = 1.f, imguiGizmo::panScale = 1.f, imguiGizmo::dollyWheelScale = 1.f, imguiGizmo::dollyWheelMulFactor = 5.f;
|
|
vgModifiers imguiGizmo::panMod = vg::evControlModifier, imguiGizmo::dollyMod = vg::evShiftModifier;
|
|
#endif
|
|
|
|
#ifdef IMGUIZMO_FLIP_ROT_ON_X
|
|
bool imguiGizmo::rotOnX = true;
|
|
#else
|
|
bool imguiGizmo::rotOnX = false;
|
|
#endif
|
|
#ifdef IMGUIZMO_FLIP_ROT_ON_Y
|
|
bool imguiGizmo::rotOnY = true;
|
|
#else
|
|
bool imguiGizmo::rotOnY = false;
|
|
#endif
|
|
#ifdef IMGUIZMO_FLIP_ROT_ON_Z
|
|
bool imguiGizmo::rotOnZ = true;
|
|
#else
|
|
bool imguiGizmo::rotOnZ = false;
|
|
#endif
|
|
|
|
#ifdef IMGUIZMO_FLIP_PAN_X
|
|
bool imguiGizmo::isFlipPanX = true;
|
|
#else
|
|
bool imguiGizmo::isFlipPanX = false;
|
|
#endif
|
|
#ifdef IMGUIZMO_FLIP_PAN_Y
|
|
bool imguiGizmo::isFlipPanY = true;
|
|
#else
|
|
bool imguiGizmo::isFlipPanY = false;
|
|
#endif
|
|
#ifdef IMGUIZMO_FLIP_DOLLY
|
|
bool imguiGizmo::isFlipDolly = true;
|
|
#else
|
|
bool imguiGizmo::isFlipDolly = false;
|
|
#endif
|
|
|
|
#ifdef IMGUIZMO_REVERSE_AXIS_X
|
|
float imguiGizmo::reverseAxisX = -1.f;
|
|
#else
|
|
float imguiGizmo::reverseAxisX = 1.f;
|
|
#endif
|
|
#ifdef IMGUIZMO_REVERSE_AXIS_Y
|
|
float imguiGizmo::reverseAxisY = -1.f;
|
|
#else
|
|
float imguiGizmo::reverseAxisY = 1.f;
|
|
#endif
|
|
#ifdef IMGUIZMO_REVERSE_AXIS_Z
|
|
float imguiGizmo::reverseAxisZ = -1.f;
|
|
#else
|
|
float imguiGizmo::reverseAxisZ = 1.f;
|
|
#endif
|
|
|
|
|
|
//
|
|
// for all gizmo3D
|
|
//
|
|
// input:
|
|
// size: dimension of the control
|
|
// mode: visualization mode: axis starting from origin, fullAxis
|
|
// (whit or w/o solid at 0,0,0) or only one arrow for direction
|
|
//
|
|
// other settings (to call before and always of your control):
|
|
// dimesion solid, axes, and arrow, slice of poligons end over: view
|
|
// section "settings of class declaration", these these values are valid for
|
|
// ALL controls in your application, because the lists of triangles/quads,
|
|
// which compose the solids, are builded one time with the first
|
|
// instance ... and NOT every redraw
|
|
//
|
|
// solidResizeFactor - axesResizeFactor
|
|
// can resize axes or solid, respectively (helper func)
|
|
////////////////////////////////////////////////////////////////////////////
|
|
namespace ImGui
|
|
{
|
|
// Quaternion control
|
|
// in/out:
|
|
// - quat (quaternion) rotation
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, quat& q, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings(mode & ~g.modeDual);
|
|
|
|
g.qtV = g.checkTowards(q);
|
|
|
|
bool ret = g.drawFunc(label, size);
|
|
if(ret) q = g.checkTowards(g.qtV);
|
|
|
|
return ret;
|
|
}
|
|
// Angle/Axes control
|
|
// in/out:
|
|
// - vec4 - X Y Z vector/axes components - W angle of rotation
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec4& axis_angle, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings(mode & ~g.modeDual);
|
|
|
|
return g.getTransforms(g.qtV, label, axis_angle, size);
|
|
}
|
|
// Direction control
|
|
// in/out:
|
|
// - vec3 - X Y Z vector/axes components
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& dir, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings(mode & (imguiGizmo::modeDirection | imguiGizmo::modeDirPlane) ? mode : imguiGizmo::modeDirection);
|
|
|
|
return g.getTransforms(g.qtV, label, dir, size);
|
|
}
|
|
// 2 Manipulators -> 2 Quaternions
|
|
// in/out:
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (quaternion) for full control - RightClick
|
|
//
|
|
// both pressed buttons... rotate together
|
|
// ctrl-Shift-Alt mods, for X-Y-Z rotations (respectivally)
|
|
// are abilitated on both ... also together!
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, quat& axes, quat& spot, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
|
|
g.qtV = g.checkTowards(axes); g.qtV2 = g.checkTowards(spot);
|
|
|
|
bool ret = g.drawFunc(label, size);
|
|
if(ret) { axes = g.checkTowards(g.qtV); spot = g.checkTowards(g.qtV2); }
|
|
|
|
return ret;
|
|
}
|
|
// 2 Manipulators -> Quaternion and vec3
|
|
// in/out:
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (vec3) for full control - RightClick
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, quat& axes, vec3& spotDir, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
|
|
g.qtV = g.checkTowards(axes);
|
|
|
|
bool ret = g.getTransforms(g.qtV2, label, spotDir, size);
|
|
|
|
if(ret) axes = g.checkTowards(g.qtV);
|
|
return ret;
|
|
}
|
|
// 2 Manipulators -> Quaternion and vec4
|
|
// in/out:
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (vec4) for full control - RightClick
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, quat& axes, vec4& axesAngle, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
|
|
g.qtV = g.checkTowards(axes);
|
|
|
|
bool ret = g.getTransforms(g.qtV2, label, axesAngle, size);
|
|
if(ret) axes = g.checkTowards(g.qtV);
|
|
return ret;
|
|
|
|
}
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
// Quaternion control + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z)
|
|
// - quat (quaternion) rotation
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, quat& q, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings((mode | g.modePanDolly) & ~g.modeDual );
|
|
|
|
g.qtV = g.checkTowards(q);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
bool ret = g.drawFunc(label, size);
|
|
if(ret) {
|
|
q = g.checkTowards(g.qtV);
|
|
vPanDolly = g.posPanDolly;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
// Angle/Axes control + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z)
|
|
// - vec4 - X Y Z vector/axes components - W angle of rotation
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, vec4& axis_angle, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings(mode & ~g.modeDual | g.modePanDolly);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
bool ret = g.getTransforms(g.qtV, label, axis_angle, size);
|
|
|
|
if(ret) vPanDolly = g.posPanDolly;
|
|
return ret;
|
|
}
|
|
// Direction control + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z)
|
|
// - vec3 - X Y Z vector/axes components
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, vec3& dir, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.modeSettings(mode & ((imguiGizmo::modeDirection | imguiGizmo::modeDirPlane) ? mode : imguiGizmo::modeDirection) | g.modePanDolly);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
bool ret = g.getTransforms(g.qtV, label, dir, size);
|
|
|
|
if(ret) vPanDolly = g.posPanDolly;
|
|
return ret;
|
|
|
|
}
|
|
// 2 Manipulators -> 2 Quaternions + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z) - default: Ctrl /Shift
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (quaternion) for full control - RightClick
|
|
//
|
|
// both pressed buttons... rotate together
|
|
// ctrl-Shift-Alt mods, for X-Y-Z rotations (respectivally)
|
|
// are abilitated on both ... also together!
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, quat& axes, quat& spot, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
g.qtV = g.checkTowards(axes); g.qtV2 = g.checkTowards(spot);
|
|
|
|
bool ret = g.drawFunc(label, size);
|
|
if(ret) { vPanDolly = g.posPanDolly; axes = g.checkTowards(g.qtV); spot = g.checkTowards(g.qtV2); }
|
|
|
|
return ret;
|
|
}
|
|
// 2 Manipulators -> Quaternion and vec3 + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z) - default: Ctrl /Shift
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (vec3) for full control - RightClick
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, quat& axes, vec3& spotDir, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
g.qtV = g.checkTowards(axes);
|
|
|
|
bool ret = g.getTransforms(g.qtV2, label, spotDir, size);
|
|
if(ret) { vPanDolly = g.posPanDolly; axes = g.checkTowards(g.qtV); }
|
|
return ret;
|
|
}
|
|
// 2 Manipulators -> Quaternion and vec4 + Pan & Dolly
|
|
// in/out:
|
|
// - vec3 Pan(x,y) Dolly(z) - default: Ctrl /Shift
|
|
// - axes (quaternion) for full control - LeftClick
|
|
// - spot (vec4) for full control - RightClick
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool gizmo3D(const char* label, vec3& vPanDolly, quat& axes, vec4& axesAngle, float size, const uint32_t mode)
|
|
{
|
|
imguiGizmo g;
|
|
g.setDualMode(mode);
|
|
g.posPanDolly = vPanDolly;
|
|
|
|
g.qtV = g.checkTowards(axes);
|
|
|
|
bool ret = g.getTransforms(g.qtV2, label, axesAngle, size);
|
|
if(ret) { vPanDolly = g.posPanDolly; axes = g.checkTowards(g.qtV); }
|
|
return ret;
|
|
|
|
}
|
|
#endif
|
|
|
|
} // namespace ImGui
|
|
|
|
static inline int clamp(int v, int mn, int mx)
|
|
{
|
|
return (v < mn) ? mn : (v > mx) ? mx : v;
|
|
}
|
|
|
|
//
|
|
// LightEffect
|
|
// faster but minus cute/precise.. ok for sphere
|
|
////////////////////////////////////////////////////////////////////////////
|
|
inline ImU32 addLightEffect(ImU32 color, float light)
|
|
{
|
|
float l = ((light<.6f) ? .6f : light) * .8f;
|
|
float lc = light * 80.0f; // ambient component
|
|
return clamp(ImU32((( color & 0xff)*l + lc)),0,255) |
|
|
(clamp(ImU32((((color>>8) & 0xff)*l + lc)),0,255) << 8) |
|
|
(clamp(ImU32((((color>>16) & 0xff)*l + lc)),0,255) << 16) |
|
|
(ImU32(ImGui::GetStyle().Alpha * (color>>24)) << 24);
|
|
}
|
|
//
|
|
// LightEffect
|
|
// with distance attenuatin
|
|
////////////////////////////////////////////////////////////////////////////
|
|
inline ImU32 addLightEffect(const vec4 &color, float light, float atten)
|
|
{
|
|
vec3 l((light<.5) ? .5f : light);
|
|
vec3 a(atten>.25 ? .25f : atten);
|
|
vec3 c(((vec3(color) + l*.5f) * l) *.75f + a*vec3(color)*.45f +a*.25f);
|
|
|
|
const float alpha = color.a * ImGui::GetStyle().Alpha; //ImGui::GetCo(ImGuiCol_FrameBg).w;
|
|
return ImGui::ColorConvertFloat4ToU32(ImVec4(c.x, c.y, c.z, alpha));
|
|
}
|
|
|
|
inline ImU32 addLightEffect(ImU32 color, float light, float atten)
|
|
{
|
|
vec4 c(float(color & 0xff)/255.f,float((color>>8) & 0xff)/255.f,float((color>>16) & 0xff)/255.f, 1.0f);
|
|
return addLightEffect(c, light, atten);
|
|
}
|
|
|
|
// inline helper drawing functions passed as (*ptrFn)()
|
|
////////////////////////////////////////////////////////////////////////////
|
|
typedef vec3 & (*ptrFunc)(vec3 &);
|
|
|
|
|
|
inline vec3 &adjustPlane(vec3 &coord)
|
|
{
|
|
coord.x = (coord.x > 0.0f) ? ( 2.5f * coord.x - 1.6f) : coord.x ;
|
|
coord.x = (coord.x)*.5f+.5f + (coord.x>0 ? -imguiGizmo::planeThickness : imguiGizmo::planeThickness) * imguiGizmo::solidResizeFactor;
|
|
coord *= vec3(1.0f, 2.0f, 2.0f);
|
|
return coord;
|
|
}
|
|
|
|
inline vec3 &adjustDir(vec3 &coord)
|
|
{
|
|
coord.x = (coord.x > 0.0f) ? ( 2.5f * coord.x - 1.6f) : coord.x + 0.1f;
|
|
coord *= vec3(1.0f, 3.0f, 3.0f);
|
|
return coord;
|
|
}
|
|
|
|
inline vec3 &adjustSpotCyl(vec3 &coord)
|
|
{
|
|
const float halfCylMinusCone = 1.0f - imguiGizmo::coneLength;
|
|
coord.x = (coord.x*.075f - 2.0f +( halfCylMinusCone - halfCylMinusCone*.075f)); //cyl begin where cone end
|
|
return coord;
|
|
|
|
}
|
|
inline vec3 &adjustSpotCone(vec3 &coord)
|
|
{
|
|
coord.x-= 2.00f;
|
|
return coord;
|
|
}
|
|
|
|
inline vec3 fastRotate (int axis, vec3 &v)
|
|
{
|
|
return ((axis == imguiGizmo::axisIsY) ? vec3(-v.y, v.x, v.z) : // rotation Z 90'
|
|
((axis == imguiGizmo::axisIsZ) ? vec3(-v.z, v.y, v.x) : // rotation Y 90'
|
|
v));
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Draw imguiGizmo
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool imguiGizmo::drawFunc(const char* label, float size)
|
|
{
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
|
|
|
const float arrowStartingPoint = (axesOriginType & imguiGizmo::sphereAtOrigin) ? sphereRadius * solidResizeFactor:
|
|
((axesOriginType & imguiGizmo::cubeAtOrigin ) ? cubeSize * solidResizeFactor:
|
|
cylRadius * .5);
|
|
// if modeDual... leave space for draw light arrow
|
|
vec3 resizeAxes( ((drawMode&modeDual) && (axesResizeFactor.x>.75f)) ? vec3(.75f,axesResizeFactor.y, axesResizeFactor.z) : axesResizeFactor);
|
|
|
|
// build solids... once!
|
|
///////////////////////////////////////
|
|
if (!solidAreBuilt) {
|
|
const float arrowBgn = -1.0f, arrowEnd = 1.0f;
|
|
|
|
buildCone (arrowEnd - coneLength, arrowEnd, coneRadius, coneSlices);
|
|
buildCylinder(arrowBgn, arrowEnd - coneLength, cylRadius , cylSlices );
|
|
buildSphere(sphereRadius, sphereTessFactor);
|
|
buildCube(cubeSize);
|
|
buildPlane(planeSize);
|
|
solidAreBuilt = true;
|
|
}
|
|
|
|
ImGui::PushID(label);
|
|
ImGui::BeginGroup();
|
|
|
|
bool value_changed = false;
|
|
|
|
ImVec2 controlPos(ImGui::GetCursorScreenPos());
|
|
|
|
const float squareSize = size; //std::min(ImGui::CalcItemWidth(), size);
|
|
const float halfSquareSize = squareSize*.5;
|
|
const ImVec2 innerSize(squareSize,squareSize);
|
|
|
|
bool highlighted = false;
|
|
ImGui::InvisibleButton("imguiGizmo", innerSize);
|
|
|
|
bool vgModsActive = false;
|
|
vgModifiers vgMods = vg::evNoModifier;
|
|
|
|
if(io.KeyCtrl) { vgMods |= vg::evControlModifier; vgModsActive = true; }
|
|
if(io.KeyAlt) { vgMods |= vg::evAltModifier; vgModsActive = true; }
|
|
if(io.KeyShift) { vgMods |= vg::evShiftModifier; vgModsActive = true; }
|
|
if(io.KeySuper) { vgMods |= vg::evSuperModifier; vgModsActive = true; }
|
|
|
|
vg::vImGuIZMO track;
|
|
track.flipRotOnX(getFlipRotOnX());
|
|
track.flipRotOnY(getFlipRotOnY());
|
|
track.flipRotOnZ(getFlipRotOnZ());
|
|
track.setFlipPanX(isFlipPanX);
|
|
track.setFlipPanY(isFlipPanY);
|
|
track.setFlipDolly(isFlipDolly);
|
|
track.setGizmoFeeling(gizmoFeelingRot);
|
|
track.viewportSize(innerSize.x, innerSize.y);
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
float screenFactor = innerSize.x / ((io.DisplaySize.x + io.DisplaySize.y) * .5f);
|
|
track.setPosition(posPanDolly);
|
|
track.setDollyControl(buttonPanDolly, dollyMod);
|
|
track.setPanControl(buttonPanDolly, panMod);
|
|
track.setPanScale(screenFactor*panScale);
|
|
track.setDollyScale(screenFactor*dollyScale);
|
|
track.wheel(0.f, dollyWheelScale*dollyWheelMulFactor*io.MouseWheel);
|
|
#endif
|
|
// getTrackball
|
|
// in : q -> quaternion to which applay rotations
|
|
// out: q -> quaternion with rotations
|
|
////////////////////////////////////////////////////////////////////////////
|
|
auto getTrackball = [&] (quat &q) {
|
|
ImVec2 mouse = ImGui::GetMousePos() - controlPos;
|
|
track.setRotation(q); //quat(-q.w, -q.x, -q.y, -q.z));
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
if(drawMode&modePanDolly || io.MouseWheel!=0) {
|
|
track.motionImmediateMode(mouse.x, mouse.y, io.MouseDelta.x, io.MouseDelta.y, vgMods);
|
|
// get new rotation only if !Pan && ! Dolly
|
|
if((!track.isDollyActive() && !track.isPanActive() && io.MouseWheel==0)) q = track.getRotation();
|
|
else posPanDolly = track.getPosition();
|
|
} else {
|
|
track.imGuIZMO_BASE_CLASS::motionImmediateMode(mouse.x, mouse.y, io.MouseDelta.x, io.MouseDelta.y, vgMods);
|
|
q = track.getRotation();
|
|
//q = quat(-q.w, -q.x, -q.y, -q.z);
|
|
}
|
|
#else
|
|
track.motionImmediateMode(mouse.x, mouse.y, io.MouseDelta.x, io.MouseDelta.y, vgMods);
|
|
q = track.getRotation();
|
|
#endif
|
|
value_changed = true; // if getTrackball() called, value is changed
|
|
};
|
|
|
|
// LeftClick
|
|
if (ImGui::IsItemActive()) {
|
|
highlighted = true;
|
|
if(ImGui::IsMouseDragging(0)) getTrackball(qtV);
|
|
if((drawMode&modeDual) && ImGui::IsMouseDragging(1)) getTrackball(qtV2); // if dual mode... move together
|
|
//if((drawMode&modeDual) && ImGui::IsMouseDragging(2)) { getTrackball(qtV); getTrackball(qtV2); } // middle if dual mode... move together
|
|
|
|
ImColor col(style.Colors[ImGuiCol_FrameBgActive]);
|
|
col.Value.w*=ImGui::GetStyle().Alpha;
|
|
draw_list->AddRectFilled(controlPos, controlPos + innerSize, col, style.FrameRounding);
|
|
} else { // eventual right click... only dualmode
|
|
highlighted = ImGui::IsItemHovered();
|
|
if(highlighted && (drawMode&modeDual) && ImGui::IsMouseDragging(1)) getTrackball(qtV2);
|
|
else if(highlighted && (drawMode&modeDual) && ImGui::IsMouseDragging(2)) { getTrackball(qtV); getTrackball(qtV2); }
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
else if(highlighted && io.MouseWheel!=0) getTrackball(qtV);
|
|
#endif
|
|
|
|
ImColor col(highlighted ? style.Colors[ImGuiCol_FrameBgHovered]: style.Colors[ImGuiCol_FrameBg]);
|
|
col.Value.w*=ImGui::GetStyle().Alpha;
|
|
draw_list->AddRectFilled(controlPos, controlPos + innerSize, col, style.FrameRounding);
|
|
}
|
|
|
|
|
|
draw_list->PushClipRect(controlPos, controlPos + innerSize, true);
|
|
|
|
const ImVec2 wpUV = ImGui::GetFontTexUvWhitePixel(); //culling versus
|
|
ImVec2 uv[4]; ImU32 col[4]; //buffers to storetransformed vtx & col for PrimVtx & PrimQuadUV
|
|
|
|
quat _q(normalize(qtV));
|
|
//_q = quat(_q.w, isFlipRotY ? -_q.x : _q.x, isFlipRotX ? -_q.y : _q.y, _q.z);
|
|
|
|
// Just a "few" lambdas...
|
|
//////////////////////////////////////////////////////////////////
|
|
auto normalizeToControlSize = [&] (float x, float y) {
|
|
return controlPos + ImVec2(x,-y) * halfSquareSize + ImVec2(halfSquareSize,halfSquareSize); //drawing from 0,0 .. no borders
|
|
};
|
|
|
|
auto returnSizeFromRatio = [&] (float ratio) { return squareSize * ratio; };
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto addTriangle = [&] ()
|
|
{ // test cull dir
|
|
if(cross(vec2(uv[1].x-uv[0].x, uv[1].y-uv[0].y),
|
|
vec2(uv[2].x-uv[0].x, uv[2].y-uv[0].y)) > 0) { uv[1] = uv[2] = uv[0]; }
|
|
|
|
for(int i=0; i<3; i++) draw_list->PrimVtx(uv[i], wpUV, col[i]);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto addQuad = [&] (ImU32 colLight)
|
|
{ // test cull dir
|
|
if(cross(vec2(uv[1].x-uv[0].x, uv[1].y-uv[0].y),
|
|
vec2(uv[3].x-uv[0].x, uv[3].y-uv[0].y)) > 0) { uv[3] = uv[1] = uv[2] = uv[0]; }
|
|
|
|
draw_list->PrimQuadUV(uv[0],uv[1],uv[2],uv[3], wpUV, wpUV, wpUV, wpUV, colLight);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawSphere = [&] ()
|
|
{
|
|
draw_list->PrimReserve(sphereVtx.size(), sphereVtx.size()); // num vert/indices
|
|
auto itTess = sphereTess.begin();
|
|
for(auto itVtx = sphereVtx.begin(); itVtx != sphereVtx.end(); ) {
|
|
for(int h=0; h<3; h++, itTess++) {
|
|
vec3 coord = _q * (*itVtx++ * solidResizeFactor); //Rotate
|
|
|
|
uv[h] = normalizeToControlSize(coord.x,coord.y);
|
|
const float drawSize = sphereRadius * solidResizeFactor;
|
|
col[h] = addLightEffect(sphereColors[*itTess], (-drawSize*.5f + (coord.z*coord.z) / (drawSize*drawSize)));
|
|
//col[h] = colorLightedY(sphereCol[i++], (-sizeSphereRadius.5f + (coord.z*coord.z) / (sizeSphereRadius*sizeSphereRadius)), coord.z);
|
|
}
|
|
addTriangle();
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawCube = [&] ()
|
|
{
|
|
draw_list->PrimReserve(cubeNorm.size()*6, cubeNorm.size()*4); // num vert/indices
|
|
for(vec3* itNorm = cubeNorm.begin(), *itVtx = cubeVtx.begin() ; itNorm != cubeNorm.end();) {
|
|
vec3 coord;
|
|
vec3 norm = _q * *itNorm;
|
|
for(int i = 0; i<4; ) {
|
|
coord = _q * (*itVtx++ * solidResizeFactor);
|
|
uv[i++] = normalizeToControlSize(coord.x,coord.y);
|
|
}
|
|
addQuad(addLightEffect(vec4(abs(*itNorm++),1.0f), norm.z, coord.z));
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawPlane = [&] ()
|
|
{
|
|
draw_list->PrimReserve(planeNorm.size()*6, planeNorm.size()*4); // num vert/indices
|
|
for(auto itNorm = planeNorm.begin(), itVtx = planeVtx.begin() ; itNorm != planeNorm.end();) {
|
|
vec3 coord;
|
|
vec3 norm = _q * *itNorm;
|
|
for(int i = 0; i<4; ) {
|
|
coord = _q * (*itVtx++ * solidResizeFactor);
|
|
uv[i++] = normalizeToControlSize(coord.x,coord.y);
|
|
}
|
|
itNorm++;
|
|
addQuad(addLightEffect(vec4(planeColor.x, planeColor.y, planeColor.z, planeColor.w), norm.z, coord.z));
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawAxes = [&] (int side)
|
|
{
|
|
for(int n = 0; n < 4; n++) { //Arrow: 2 Cone -> (Surface + cap) + 2 Cyl -> (Surface + cap)
|
|
for(int arrowAxis = 0; arrowAxis < 3; arrowAxis++) { // draw 3 axes
|
|
vec3 arrowCoord(0.0f, 0.0f, 0.0f); arrowCoord[arrowAxis] = 1.0f; // rotate on 3 axis (arrow -> X, Y, Z ) in base to current arrowAxis
|
|
if(arrowAxis == 2) arrowCoord[arrowAxis] = 1.0f;
|
|
const float arrowCoordZ = vec3(_q*arrowCoord).z; //.Rotate
|
|
|
|
const int i = (arrowCoordZ > 0) ? 3 - n : n; //painter algorithm: before farthest
|
|
|
|
bool skipCone =true;
|
|
|
|
int realSide = axesVecModifier[arrowAxis] > 0 ? side : (side == backSide ? frontSide : backSide);
|
|
if((realSide == backSide && arrowCoordZ > 0) || (realSide == frontSide && arrowCoordZ <= 0)) {
|
|
if (!showFullAxes && (i == CYL_CAP)) continue; // skip if cylCap is hidden
|
|
if (i <= CONE_CAP) continue; // do not draw cone
|
|
else skipCone = false;
|
|
}
|
|
|
|
auto *ptrVtx = arrowVtx+i;
|
|
draw_list->PrimReserve(ptrVtx->size(), ptrVtx->size()); // // reserve vtx
|
|
|
|
for(auto itVtx = ptrVtx->begin(), itNorm = (arrowNorm+i)->begin(); itVtx != ptrVtx->end(); ) { //for all Vtx
|
|
#if !defined(imguiGizmo_INTERPOLATE_NORMALS)
|
|
vec3 norm( _q * fastRotate(arrowAxis, *itNorm++));
|
|
#endif
|
|
for(int h=0; h<3; h++) {
|
|
vec3 coord(*itVtx++ * resizeAxes); // reduction
|
|
// reposition starting point...
|
|
if(!skipCone && coord.x > 0) coord.x = -arrowStartingPoint;
|
|
if((skipCone && coord.x <= 0) ||
|
|
(!showFullAxes && (coord.x < arrowStartingPoint)) ) coord.x = arrowStartingPoint;
|
|
//transform
|
|
coord = _q * fastRotate(arrowAxis, coord);
|
|
uv[h] = normalizeToControlSize(coord.x,coord.y);
|
|
#ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
vec3 norm( _q * fastRotate(arrowAxis, *itNorm++));
|
|
#endif
|
|
col[h] = addLightEffect(vec4(float(arrowAxis==axisIsX),float(arrowAxis==axisIsY),float(arrowAxis==axisIsZ), 1.0), norm.z, coord.z);
|
|
}
|
|
addTriangle();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawComponent = [&] (const int idx, const quat &q, ptrFunc func)
|
|
{
|
|
auto *ptrVtx = arrowVtx+idx;
|
|
draw_list->PrimReserve(ptrVtx->size(), ptrVtx->size()); // reserve vtx
|
|
for(auto itVtx = ptrVtx->begin(), itNorm = (arrowNorm+idx)->begin(); itVtx != ptrVtx->end(); ) {
|
|
#if !defined(imguiGizmo_INTERPOLATE_NORMALS)
|
|
vec3 norm = _q * *itNorm++;
|
|
#endif
|
|
for(int h=0; h<3; h++) {
|
|
vec3 coord = *itVtx++;
|
|
#ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
vec3 norm = q * *itNorm++;
|
|
#endif
|
|
coord = q * (func(coord) * resizeAxes); // remodelling Directional Arrow (func) and transforms;
|
|
|
|
uv[h] = normalizeToControlSize(coord.x,coord.y);
|
|
col[h] = addLightEffect(vec4(directionColor.x, directionColor.y, directionColor.z, 1.0), norm.z, coord.z>0 ? coord.z : coord.z*.5);
|
|
}
|
|
addTriangle();
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto dirArrow = [&] (const quat &qt, int mode)
|
|
{
|
|
quat q (qt.w, qt.x, qt.y, qt.z);
|
|
vec3 arrowCoord(_q * vec3(1.0f, 0.0f, 0.0f));
|
|
|
|
ptrFunc func = (mode & modeDirPlane) ? adjustPlane : adjustDir;
|
|
|
|
if(arrowCoord.z <= 0) { for(int i = 0; i < 4; i++) drawComponent(i, q, func); if(mode & modeDirPlane) drawPlane(); }
|
|
else { if(mode & modeDirPlane) drawPlane(); for(int i = 3; i >= 0; i--) drawComponent(i, q, func); }
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto spotArrow = [&] (const quat &qt, const float arrowCoordZ)
|
|
{
|
|
quat q (qt.w, qt.x, qt.y, qt.z);
|
|
//flipRotation(qt);
|
|
if(arrowCoordZ > 0) {
|
|
drawComponent(CONE_SURF, q, adjustSpotCone); drawComponent(CONE_CAP , q, adjustSpotCone);
|
|
drawComponent(CYL_SURF , q, adjustSpotCyl ); drawComponent(CYL_CAP , q, adjustSpotCyl );
|
|
} else {
|
|
drawComponent(CYL_CAP , q, adjustSpotCyl ); drawComponent(CYL_SURF , q, adjustSpotCyl );
|
|
drawComponent(CONE_CAP , q, adjustSpotCone); drawComponent(CONE_SURF, q, adjustSpotCone);
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto draw3DSystem = [&] ()
|
|
{
|
|
drawAxes(backSide);
|
|
if (axesOriginType & sphereAtOrigin) drawSphere();
|
|
else if(axesOriginType & cubeAtOrigin) drawCube();
|
|
drawAxes(frontSide);
|
|
};
|
|
|
|
#define CENTER_HELPER_X -.85f
|
|
#define CENTER_HELPER_Y -.85f
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawRotationHelper = [&] () {
|
|
const ImVec2 center(normalizeToControlSize(CENTER_HELPER_X, CENTER_HELPER_Y));
|
|
const float radius = returnSizeFromRatio(.05);
|
|
const int nSegments = 12;
|
|
const ImU32 color = (vgMods & vg::evShiftModifier) ? 0xff0000ff :
|
|
(vgMods & vg::evControlModifier) ? 0xff00ff00 : 0xffff0000;
|
|
|
|
if(squareSize<100) { // if too small filled circle
|
|
draw_list->AddCircleFilled(center, radius, color, nSegments);
|
|
} else { // draw arc
|
|
const float thickness = squareSize/100.f;
|
|
const float a_max = (IM_PI * 1.5f) * ((float)nSegments) / (float)nSegments;
|
|
draw_list->PathClear();
|
|
draw_list->PathArcTo(center, radius - 0.5f, 0.0f, a_max, nSegments);
|
|
draw_list->PathStroke(color, false, thickness);
|
|
if(squareSize>150) { // if big enough draw also arrowhead
|
|
const float lenLine = radius*.33f;
|
|
const float thickRadius = radius - thickness*.5;
|
|
draw_list->AddTriangleFilled(ImVec2(center.x-lenLine, center.y-(thickRadius+lenLine)),
|
|
ImVec2(center.x+lenLine, center.y- thickRadius),
|
|
ImVec2(center.x-lenLine, center.y-(thickRadius-lenLine)),
|
|
color);
|
|
|
|
draw_list->AddTriangleFilled(ImVec2(center.x+(thickRadius-lenLine), center.y+lenLine),
|
|
ImVec2(center.x+ thickRadius , center.y-lenLine),
|
|
ImVec2(center.x+(thickRadius+lenLine), center.y+lenLine),
|
|
color);
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawPanHelper = [&] () {
|
|
const ImVec2 center(normalizeToControlSize(CENTER_HELPER_X, CENTER_HELPER_Y));
|
|
const float lenLine = returnSizeFromRatio(.05f);
|
|
const float halfLen = lenLine * .5f;
|
|
const float hhLen = halfLen * .5f;
|
|
const ImU32 color = 0xffffff00;
|
|
draw_list->AddTriangleFilled(ImVec2(center.x , center.y+lenLine+halfLen),
|
|
ImVec2(center.x-halfLen, center.y+lenLine-hhLen ),
|
|
ImVec2(center.x+halfLen, center.y+lenLine-hhLen ),
|
|
color);
|
|
draw_list->AddTriangleFilled(ImVec2(center.x , center.y-lenLine-halfLen),
|
|
ImVec2(center.x-halfLen, center.y-lenLine+hhLen ),
|
|
ImVec2(center.x+halfLen, center.y-lenLine+hhLen ),
|
|
color);
|
|
draw_list->AddTriangleFilled(ImVec2(center.x+lenLine+halfLen, center.y ),
|
|
ImVec2(center.x+lenLine-hhLen , center.y-halfLen),
|
|
ImVec2(center.x+lenLine-hhLen , center.y+halfLen),
|
|
color);
|
|
draw_list->AddTriangleFilled(ImVec2(center.x-lenLine-halfLen, center.y ),
|
|
ImVec2(center.x-lenLine+hhLen , center.y-halfLen),
|
|
ImVec2(center.x-lenLine+hhLen , center.y+halfLen),
|
|
color);
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
auto drawDollyHelper = [&] () {
|
|
const ImVec2 center(normalizeToControlSize(CENTER_HELPER_X, CENTER_HELPER_Y));
|
|
const float lenLine = returnSizeFromRatio(.05f);
|
|
const float halfLen = lenLine * .5f;
|
|
const ImU32 color = 0xff00ffff;
|
|
draw_list->AddTriangleFilled(ImVec2(center.x , center.y+lenLine+halfLen),
|
|
ImVec2(center.x-lenLine, center.y+halfLen ),
|
|
ImVec2(center.x+lenLine, center.y+halfLen ),
|
|
color);
|
|
draw_list->AddTriangleFilled(ImVec2(center.x , center.y-lenLine ),
|
|
ImVec2(center.x-halfLen, center.y-halfLen ),
|
|
ImVec2(center.x+halfLen, center.y-halfLen ),
|
|
color);
|
|
};
|
|
|
|
// ... and now.. draw the widget!!!
|
|
///////////////////////////////////////
|
|
//if((drawMode & modePanDolly) && (ImGui::IsItemHovered() || ImGui::IsMouseDragging(0))) {
|
|
|
|
if(drawMode & (modeDirection | modeDirPlane)) dirArrow(_q, drawMode);
|
|
else { // draw arrows & solid
|
|
if(drawMode & modeDual) {
|
|
#ifdef IMGUIZMO_HAS_NEGATIVE_VEC3_LIGHT
|
|
vec3 spot(qtV2 * vec3(-1.0f, 0.0f, .0f));
|
|
if(spot.z>0) // versus opposite
|
|
#else
|
|
vec3 spot { qtV2 * vec3( 1.0f, 0.0f, .0f) };
|
|
if(spot.z<0)
|
|
#endif
|
|
{ draw3DSystem(); spotArrow(normalize(qtV2),spot.z); }
|
|
else { spotArrow(normalize(qtV2),spot.z); draw3DSystem(); }
|
|
} else draw3DSystem();
|
|
}
|
|
|
|
// Helper on vgModifier active
|
|
if(vgModsActive && (ImGui::IsItemHovered() && (!ImGui::IsMouseDown(0) && !ImGui::IsMouseDown(1)) )) {
|
|
#ifndef IMGUIZMO_USE_ONLY_ROT
|
|
if(drawMode & modePanDolly) {
|
|
if(panMod & vgMods) drawPanHelper();
|
|
else if(dollyMod & vgMods) drawDollyHelper();
|
|
} else {
|
|
drawRotationHelper();
|
|
}
|
|
#else
|
|
drawRotationHelper();
|
|
#endif
|
|
}
|
|
|
|
// Draw text from top left corner
|
|
ImGui::SetCursorScreenPos(controlPos);
|
|
if(label[0]!='#' && label[1]!='#') ImGui::Text("%s", label);
|
|
|
|
draw_list->PopClipRect();
|
|
|
|
ImGui::EndGroup();
|
|
ImGui::PopID();
|
|
|
|
return value_changed;
|
|
}
|
|
|
|
// Polygon
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void imguiGizmo::buildPolygon(const vec3 &size, ImVector<vec3> &vtx, ImVector<vec3> &norm)
|
|
{
|
|
|
|
vtx .clear();
|
|
norm.clear();
|
|
|
|
#define V(a,b,c) vtx.push_back(vec3(a size.x, b size.y, c size.z))
|
|
#define N(x,y,z) norm.push_back(vec3(x, y, z))
|
|
|
|
N( 1.0f, 0.0f, 0.0f); V(+,-,+); V(+,-,-); V(+,+,-); V(+,+,+);
|
|
N( 0.0f, 1.0f, 0.0f); V(+,+,+); V(+,+,-); V(-,+,-); V(-,+,+);
|
|
N( 0.0f, 0.0f, 1.0f); V(+,+,+); V(-,+,+); V(-,-,+); V(+,-,+);
|
|
N(-1.0f, 0.0f, 0.0f); V(-,-,+); V(-,+,+); V(-,+,-); V(-,-,-);
|
|
N( 0.0f,-1.0f, 0.0f); V(-,-,+); V(-,-,-); V(+,-,-); V(+,-,+);
|
|
N( 0.0f, 0.0f,-1.0f); V(-,-,-); V(-,+,-); V(+,+,-); V(+,-,-);
|
|
|
|
#undef V
|
|
#undef N
|
|
}
|
|
// Sphere
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void imguiGizmo::buildSphere(const float radius, const int tessFactor)
|
|
{
|
|
const int div = tessFactor; //tessellation colors: meridians/div x paralles/div
|
|
const int meridians = 32; //64/2;
|
|
const int parallels = meridians/2;
|
|
|
|
sphereVtx .clear();
|
|
sphereTess.clear();
|
|
|
|
# define V(x,y,z) sphereVtx.push_back(vec3(x, y, z))
|
|
# define T(t) sphereTess.push_back(t)
|
|
|
|
const float incAngle = 2.0f*T_PI/(float)( meridians );
|
|
float angle = incAngle;
|
|
|
|
// Adjust z and radius as stacks are drawn.
|
|
float z0, z1 = cosf(angle)*radius;
|
|
float r0, r1 = sinf(angle)*radius;
|
|
float x1 = -1.0f;
|
|
float y1 = 0.0f;
|
|
|
|
// The first pole==>parallel is covered with triangles
|
|
for(int j=0; j<meridians; j++, angle+=incAngle) {
|
|
const float x0 = x1; x1 = cosf(T_PI-angle);
|
|
const float y0 = y1; y1 = sinf(T_PI-angle);
|
|
|
|
const int tType = ((j>>div)&1);
|
|
|
|
V(0.0f, 0.0f, radius); T(tType);
|
|
V(x0*r1,-y0*r1, z1); T(tType);
|
|
V(x1*r1,-y1*r1, z1); T(tType);
|
|
}
|
|
|
|
// Cover each stack with a quad divided in 2 triangles, except the top and bottom stacks
|
|
angle = incAngle+incAngle;
|
|
x1 = 1.f; y1 = 0.f;
|
|
|
|
for(int i=1; i<parallels-1; i++, angle+=incAngle) {
|
|
//int div =8;
|
|
z0 = z1; z1 = cosf(angle)*radius;
|
|
r0 = r1; r1 = sinf(angle)*radius;
|
|
float angleJ = incAngle;
|
|
|
|
for(int j=0; j<meridians; j++, angleJ+=incAngle) {
|
|
const float x0 = x1; x1 = cosf(angleJ);
|
|
const float y0 = y1; y1 = sinf(angleJ);
|
|
|
|
const int tType = ((i>>div)&1) ? ((j>>div)&1) : !((j>>div)&1);
|
|
|
|
V(x0*r1, -y0*r1, z1); T(tType);
|
|
V(x0*r0, -y0*r0, z0); T(tType);
|
|
V(x1*r0, -y1*r0, z0); T(tType);
|
|
V(x0*r1, -y0*r1, z1); T(tType);
|
|
V(x1*r0, -y1*r0, z0); T(tType);
|
|
V(x1*r1, -y1*r1, z1); T(tType);
|
|
}
|
|
}
|
|
|
|
// The last parallel==>pole is covered with triangls
|
|
z0 = z1;
|
|
r0 = r1;
|
|
x1 = -1.0f; y1 = 0.f;
|
|
|
|
angle = incAngle;
|
|
for(int j=0; j<meridians; j++,angle+=incAngle) {
|
|
const float x0 = x1; x1 = cosf(angle+T_PI);
|
|
const float y0 = y1; y1 = sinf(angle+T_PI);
|
|
|
|
const int tType = ((parallels-1)>>div)&1 ? ((j>>div)&1) : !((j>>div)&1);
|
|
|
|
V( 0.0f, 0.0f,-radius); T(tType);
|
|
V(x0*r0, -y0*r0, z0); T(tType);
|
|
V(x1*r0, -y1*r0, z0); T(tType);
|
|
}
|
|
# undef V
|
|
# undef C
|
|
}
|
|
// Cone / Pyramid
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void imguiGizmo::buildCone(const float x0, const float x1, const float radius, const int slices)
|
|
{
|
|
const float height = x1-x0 ;
|
|
|
|
// Scaling factors for vertex normals
|
|
const float sq = sqrtf( height * height + radius * radius );
|
|
const float cosn = height / sq;
|
|
const float sinn = radius / sq;
|
|
|
|
const float incAngle = 2.0f*T_PI/(float)( slices );
|
|
float angle = incAngle;
|
|
|
|
float yt1 = sinn, y1 = radius;// ==> yt1 = cos(0) * sinn, y1 = cos(0) * radius
|
|
float zt1 = 0.0f, z1 = 0.0f; // ==> zt1 = sin(0) * sinn, z1 = sin(0) * radius
|
|
|
|
const float xt0 = x0 * cosn, xt1 = x1 * cosn;
|
|
|
|
arrowVtx[CONE_CAP ].clear(); arrowNorm[CONE_CAP ].clear();
|
|
arrowVtx[CONE_SURF].clear(); arrowNorm[CONE_SURF].clear();
|
|
|
|
# define V(i,x,y,z) arrowVtx [i].push_back(vec3(x, y, z))
|
|
# define N(i,x,y,z) arrowNorm[i].push_back(vec3(x, y, z))
|
|
|
|
for(int j=0; j<slices; j++, angle+=incAngle) {
|
|
const float yt0 = yt1; yt1 = cosf(angle);
|
|
const float y0 = y1; y1 = yt1*radius; yt1*=sinn;
|
|
const float zt0 = zt1; zt1 = sinf(angle);
|
|
const float z0 = z1; z1 = zt1*radius; zt1*=sinn;
|
|
|
|
// Cover the circular base with a triangle fan...
|
|
V(CONE_CAP, x0, 0.f, 0.f);
|
|
V(CONE_CAP, x0, y0, -z0);
|
|
V(CONE_CAP, x0, y1, -z1);
|
|
|
|
N(CONE_CAP,-1.f, 0.f, 0.f);
|
|
# ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
N(CONE_CAP,-1.f, 0.f, 0.f);
|
|
N(CONE_CAP,-1.f, 0.f, 0.f);
|
|
#endif
|
|
V(CONE_SURF, x1, 0.f, 0.f);
|
|
V(CONE_SURF, x0, y0, z0);
|
|
V(CONE_SURF, x0, y1, z1);
|
|
# ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
N(CONE_SURF,xt1, 0.f, 0.f);
|
|
N(CONE_SURF,xt0, yt0, zt0);
|
|
N(CONE_SURF,xt0, yt1, zt1);
|
|
#else
|
|
N(CONE_SURF, xt0, yt0, zt0);
|
|
#endif
|
|
}
|
|
#undef V
|
|
#undef N
|
|
}
|
|
// Cylinder
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void imguiGizmo::buildCylinder(const float x0, const float x1, const float radius, const int slices)
|
|
{
|
|
|
|
float y1 = 1.0f, yr1 = radius;
|
|
float z1 = 0.0f, zr1 = 0.0f; // * radius
|
|
|
|
const float incAngle = 2.0f*T_PI/(float)( slices );
|
|
float angle = incAngle;
|
|
|
|
arrowVtx[CYL_CAP ].clear(); arrowNorm[CYL_CAP ].clear();
|
|
arrowVtx[CYL_SURF].clear(); arrowNorm[CYL_SURF].clear();
|
|
|
|
# define V(i,x,y,z) arrowVtx [i].push_back(vec3(x, y, z))
|
|
# define N(i,x,y,z) arrowNorm[i].push_back(vec3(x, y, z))
|
|
|
|
for(int j=0; j<slices; j++, angle+=incAngle) {
|
|
const float y0 = y1; y1 = cosf(angle);
|
|
const float z0 = z1; z1 = sinf(angle);
|
|
const float yr0 = yr1; yr1 = y1 * radius;
|
|
const float zr0 = zr1; zr1 = z1 * radius;
|
|
|
|
// Cover the base
|
|
V(CYL_CAP, x0, 0.f, 0.f);
|
|
V(CYL_CAP, x0, yr0,-zr0);
|
|
V(CYL_CAP, x0, yr1,-zr1);
|
|
|
|
N(CYL_CAP, -1.f, 0.f, 0.f);
|
|
# ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
N(CYL_CAP, -1.f, 0.f, 0.f);
|
|
N(CYL_CAP, -1.f, 0.f, 0.f);
|
|
#endif
|
|
// Cover surface
|
|
N(CYL_SURF, 0.f, y0, z0);
|
|
N(CYL_SURF, 0.f, y0, z0);
|
|
# ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
N(CYL_SURF, 0.f, y1, z1);
|
|
N(CYL_SURF, 0.f, y0, z0);
|
|
N(CYL_SURF, 0.f, y1, z1);
|
|
N(CYL_SURF, 0.f, y1, z1);
|
|
#endif
|
|
V(CYL_SURF, x1, yr0, zr0);
|
|
V(CYL_SURF, x0, yr0, zr0);
|
|
V(CYL_SURF, x0, yr1, zr1);
|
|
V(CYL_SURF, x1, yr0, zr0);
|
|
V(CYL_SURF, x0, yr1, zr1);
|
|
V(CYL_SURF, x1, yr1, zr1);
|
|
#ifdef SHOW_FULL_CYLINDER
|
|
// Cover the top ..in the arrow this cap is covered from cone/pyramid
|
|
V(CYL_CAP , x1, 0.f, 0.f);
|
|
V(CYL_CAP , x1, yr0, zr0);
|
|
V(CYL_CAP , x1, yr1, zr1);
|
|
N(CYL_CAP , 1.f, 0.f, 0.f);
|
|
# ifdef imguiGizmo_INTERPOLATE_NORMALS
|
|
N(CYL_CAP , 1.f, 0.f, 0.f);
|
|
N(CYL_CAP , 1.f, 0.f, 0.f);
|
|
#endif
|
|
#endif
|
|
}
|
|
#undef V
|
|
#undef N
|
|
}
|
|
|
|
|
|
|
|
|
|
|