This patch implements part of what was stated in {T66484}, with respect to `Base Point`.
## Introduction
The snapping feature of the transform tools has a variety of applications:
- Organization of nodes.
- Positioning of frames in precise time units.
- Retopology with snap to face
- Creation of armatures with bone positioning through the snap to volume
- Precise positioning of 3D or 2D objects in the surrounding geometry (CAD modeling)
The goal of this document is to make it more powerful for precision modeling and still supporting the old use cases without extra complexity.
The main topic addressed here is the introduction of a **interactive mode for setting a snap source** (See terminology).
## Terminology
* **Snap Source**: 3d coordinate * we want to snap from. (Currently defined by the `Snap With` options: `Closest`, `Center`, `Median` and `Active`).
* **Snap Target**: 3d coordinate* we want to snap to. (Vertices, Edges, Faces, Grid...)
## Interactive Mode for Editing a Snap Source
Currently the fixed snap point can only be obtained through the `Snap With` options. So it's a little tricky for the user to define a snap source point having so much geometry on an object.
Because of this, the user needs to resort to impractical solutions to get a point in the geometry.
See example of an impractical use:
{F11714181, layout=left, width=960, alt="The user used the cursor (which can be snapped) to choose the snap origin point."}
The user used the cursor (which can be snapped) to choose the snap source point.
While it is possible to work around this current limitation, it is important to reduce the number of steps and allow the user to set a snap source point through an optional interactive mode during a transformation.
The proposed solution is to be able to move the current snap source point through a modal modifier activated with a key (eg. B).
The snap source point can thus "snap" to the elements in the scene (vertex, mid-edge, Lamp, …) during this mode.
{F9122814, layout=left, width=960, alt="Base Point Snap, example of transform operation via the shortcut (not the tool). After pressing g and the snap base change shortcut (e.g., shift + ctrl) the user set the base point. The base point is then visible until the end of the operation. The z axis constrains the final position."}
## Implementation Details
- The feature will only be available in 3D View.
- The feature will only be available for `Move`, `Rotate` and `Scale` transform modes.
- The snap source editing will be enabled with a single click on the modifier key (B).
- Having a snap point indicated, the new snap origin point will be confirmed with the same buttons that confirms the transformation (but the transformation will not be concluded).
- The snap source editing can be canceled with the same key that activated it (B).
- If the transformation is done with "release_confirm" (common for gizmos), the new feature cannot be enabled.
- During the transformation, when enabling the feature, if the snap option is turned off in the scene, the snap will be forced on throughout the rest of the transformation (unless interactive mode is canceled).
- During a transformation, if no snap target is set for an element in the scene (Vertex, Grid...), the snap targets to geometry Vertex, Edge, Face, Center of Edge and Perpendicular of Edge will be set automatically.
- Snap cannot be turned off during the snap source editing.
- Constraint or similar modification features will not be available during the snap source editing.
- Text input will not be available during the snap source editing.
- When adding multiple snap points (A) the new prone snap source point will be indicated with an "X" drawing.
{F11817267}
Maniphest Tasks: T66484
Differential Revision: https://developer.blender.org/D9415
686 lines
22 KiB
C
686 lines
22 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 gpu
|
|
*
|
|
* GPU immediate mode drawing utilities
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_immediate_util.h"
|
|
|
|
#include "UI_resources.h"
|
|
|
|
static const float cube_coords[8][3] = {
|
|
{-1, -1, -1},
|
|
{-1, -1, +1},
|
|
{-1, +1, -1},
|
|
{-1, +1, +1},
|
|
{+1, -1, -1},
|
|
{+1, -1, +1},
|
|
{+1, +1, -1},
|
|
{+1, +1, +1},
|
|
};
|
|
static const int cube_quad_index[6][4] = {
|
|
{0, 1, 3, 2},
|
|
{0, 2, 6, 4},
|
|
{0, 4, 5, 1},
|
|
{1, 5, 7, 3},
|
|
{2, 3, 7, 6},
|
|
{4, 6, 7, 5},
|
|
};
|
|
static const int cube_line_index[12][2] = {
|
|
{0, 1},
|
|
{0, 2},
|
|
{0, 4},
|
|
{1, 3},
|
|
{1, 5},
|
|
{2, 3},
|
|
{2, 6},
|
|
{3, 7},
|
|
{4, 5},
|
|
{4, 6},
|
|
{5, 7},
|
|
{6, 7},
|
|
};
|
|
|
|
void immRectf(uint pos, float x1, float y1, float x2, float y2)
|
|
{
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
immVertex2f(pos, x1, y1);
|
|
immVertex2f(pos, x2, y1);
|
|
immVertex2f(pos, x2, y2);
|
|
immVertex2f(pos, x1, y2);
|
|
immEnd();
|
|
}
|
|
|
|
void immRecti(uint pos, int x1, int y1, int x2, int y2)
|
|
{
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
immVertex2i(pos, x1, y1);
|
|
immVertex2i(pos, x2, y1);
|
|
immVertex2i(pos, x2, y2);
|
|
immVertex2i(pos, x1, y2);
|
|
immEnd();
|
|
}
|
|
|
|
void immRectf_fast(uint pos, float x1, float y1, float x2, float y2)
|
|
{
|
|
immVertex2f(pos, x1, y1);
|
|
immVertex2f(pos, x2, y1);
|
|
immVertex2f(pos, x2, y2);
|
|
|
|
immVertex2f(pos, x1, y1);
|
|
immVertex2f(pos, x2, y2);
|
|
immVertex2f(pos, x1, y2);
|
|
}
|
|
|
|
void immRectf_fast_with_color(
|
|
uint pos, uint col, float x1, float y1, float x2, float y2, const float color[4])
|
|
{
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x1, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x2, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x2, y2);
|
|
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x1, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x2, y2);
|
|
immAttr4fv(col, color);
|
|
immVertex2f(pos, x1, y2);
|
|
}
|
|
|
|
void immRecti_fast_with_color(
|
|
uint pos, uint col, int x1, int y1, int x2, int y2, const float color[4])
|
|
{
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x1, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x2, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x2, y2);
|
|
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x1, y1);
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x2, y2);
|
|
immAttr4fv(col, color);
|
|
immVertex2i(pos, x1, y2);
|
|
}
|
|
|
|
#if 0 /* more complete version in case we want that */
|
|
void immRecti_complete(int x1, int y1, int x2, int y2, const float color[4])
|
|
{
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = add_attr(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
immUniformColor4fv(color);
|
|
immRecti(pos, x1, y1, x2, y2);
|
|
immUnbindProgram();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Pack color into 3 bytes
|
|
*
|
|
* This define converts a numerical value to the equivalent 24-bit
|
|
* color, while not being endian-sensitive. On little-endian, this
|
|
* is the same as doing a 'naive' indexing, on big-endian, it is not!
|
|
*
|
|
* \note BGR format (i.e. 0xBBGGRR)...
|
|
*
|
|
* \param x: color.
|
|
*/
|
|
void imm_cpack(uint x)
|
|
{
|
|
immUniformColor3ub(((x)&0xFF), (((x) >> 8) & 0xFF), (((x) >> 16) & 0xFF));
|
|
}
|
|
|
|
static void imm_draw_circle(GPUPrimType prim_type,
|
|
const uint shdr_pos,
|
|
float x,
|
|
float y,
|
|
float rad_x,
|
|
float rad_y,
|
|
int nsegments)
|
|
{
|
|
immBegin(prim_type, nsegments);
|
|
for (int i = 0; i < nsegments; i++) {
|
|
const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
|
|
immVertex2f(shdr_pos, x + (rad_x * cosf(angle)), y + (rad_y * sinf(angle)));
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
/**
|
|
* Draw a circle outline with the given \a radius.
|
|
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
|
*
|
|
* \param shdr_pos: The vertex attribute number for position.
|
|
* \param x: Horizontal center.
|
|
* \param y: Vertical center.
|
|
* \param rad: The circle's radius.
|
|
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
|
*/
|
|
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, rad, rad, nsegments);
|
|
}
|
|
|
|
/**
|
|
* Draw a filled circle with the given \a radius.
|
|
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
|
*
|
|
* \param shdr_pos: The vertex attribute number for position.
|
|
* \param x: Horizontal center.
|
|
* \param y: Vertical center.
|
|
* \param rad: The circle's radius.
|
|
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
|
*/
|
|
void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, rad, rad, nsegments);
|
|
}
|
|
|
|
void imm_draw_circle_wire_aspect_2d(
|
|
uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments)
|
|
{
|
|
imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, rad_x, rad_y, nsegments);
|
|
}
|
|
void imm_draw_circle_fill_aspect_2d(
|
|
uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments)
|
|
{
|
|
imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, rad_x, rad_y, nsegments);
|
|
}
|
|
|
|
static void imm_draw_circle_partial(GPUPrimType prim_type,
|
|
uint pos,
|
|
float x,
|
|
float y,
|
|
float rad,
|
|
int nsegments,
|
|
float start,
|
|
float sweep)
|
|
{
|
|
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
|
const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
|
|
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
|
nsegments += 1;
|
|
immBegin(prim_type, nsegments);
|
|
for (int i = 0; i < nsegments; i++) {
|
|
const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
|
|
const float angle_sin = sinf(angle);
|
|
const float angle_cos = cosf(angle);
|
|
immVertex2f(pos, x + rad * angle_cos, y + rad * angle_sin);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_circle_partial_wire_2d(
|
|
uint pos, float x, float y, float rad, int nsegments, float start, float sweep)
|
|
{
|
|
imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, rad, nsegments, start, sweep);
|
|
}
|
|
|
|
static void imm_draw_disk_partial(GPUPrimType prim_type,
|
|
uint pos,
|
|
float x,
|
|
float y,
|
|
float rad_inner,
|
|
float rad_outer,
|
|
int nsegments,
|
|
float start,
|
|
float sweep)
|
|
{
|
|
/* to avoid artifacts */
|
|
const float max_angle = 3 * 360;
|
|
CLAMP(sweep, -max_angle, max_angle);
|
|
|
|
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
|
const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
|
|
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
|
nsegments += 1;
|
|
immBegin(prim_type, nsegments * 2);
|
|
for (int i = 0; i < nsegments; i++) {
|
|
const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
|
|
const float angle_sin = sinf(angle);
|
|
const float angle_cos = cosf(angle);
|
|
immVertex2f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin);
|
|
immVertex2f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
/**
|
|
* Draw a filled arc with the given inner and outer radius.
|
|
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
|
*
|
|
* \note Arguments are `gluPartialDisk` compatible.
|
|
*
|
|
* \param pos: The vertex attribute number for position.
|
|
* \param x: Horizontal center.
|
|
* \param y: Vertical center.
|
|
* \param rad_inner: The inner circle's radius.
|
|
* \param rad_outer: The outer circle's radius (can be zero).
|
|
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
|
* \param start: Specifies the starting angle, in degrees, of the disk portion.
|
|
* \param sweep: Specifies the sweep angle, in degrees, of the disk portion.
|
|
*/
|
|
void imm_draw_disk_partial_fill_2d(uint pos,
|
|
float x,
|
|
float y,
|
|
float rad_inner,
|
|
float rad_outer,
|
|
int nsegments,
|
|
float start,
|
|
float sweep)
|
|
{
|
|
imm_draw_disk_partial(
|
|
GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep);
|
|
}
|
|
|
|
static void imm_draw_circle_3D(
|
|
GPUPrimType prim_type, uint pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
immBegin(prim_type, nsegments);
|
|
for (int i = 0; i < nsegments; i++) {
|
|
float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
|
|
immVertex3f(pos, x + rad * cosf(angle), y + rad * sinf(angle), 0.0f);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_circle_wire_3d(uint pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
imm_draw_circle_3D(GPU_PRIM_LINE_LOOP, pos, x, y, rad, nsegments);
|
|
}
|
|
|
|
void imm_draw_circle_dashed_3d(uint pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
imm_draw_circle_3D(GPU_PRIM_LINES, pos, x, y, rad, nsegments / 2);
|
|
}
|
|
|
|
void imm_draw_circle_fill_3d(uint pos, float x, float y, float rad, int nsegments)
|
|
{
|
|
imm_draw_circle_3D(GPU_PRIM_TRI_FAN, pos, x, y, rad, nsegments);
|
|
}
|
|
|
|
/**
|
|
* Draw a lined box.
|
|
*
|
|
* \param pos: The vertex attribute number for position.
|
|
* \param x1: left.
|
|
* \param y1: bottom.
|
|
* \param x2: right.
|
|
* \param y2: top.
|
|
*/
|
|
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
|
|
{
|
|
immBegin(GPU_PRIM_LINE_LOOP, 4);
|
|
immVertex2f(pos, x1, y1);
|
|
immVertex2f(pos, x1, y2);
|
|
immVertex2f(pos, x2, y2);
|
|
immVertex2f(pos, x2, y1);
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
|
|
{
|
|
/* use this version when GPUVertFormat has a vec3 position */
|
|
immBegin(GPU_PRIM_LINE_LOOP, 4);
|
|
immVertex3f(pos, x1, y1, 0.0f);
|
|
immVertex3f(pos, x1, y2, 0.0f);
|
|
immVertex3f(pos, x2, y2, 0.0f);
|
|
immVertex3f(pos, x2, y1, 0.0f);
|
|
immEnd();
|
|
}
|
|
|
|
/**
|
|
* Draw a standard checkerboard to indicate transparent backgrounds.
|
|
*/
|
|
void imm_draw_box_checker_2d_ex(float x1,
|
|
float y1,
|
|
float x2,
|
|
float y2,
|
|
const float color_primary[4],
|
|
const float color_secondary[4],
|
|
int checker_size)
|
|
{
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
|
|
|
|
immUniform4fv("color1", color_primary);
|
|
immUniform4fv("color2", color_secondary);
|
|
immUniform1i("size", checker_size);
|
|
|
|
immRectf(pos, x1, y1, x2, y2);
|
|
|
|
immUnbindProgram();
|
|
}
|
|
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2)
|
|
{
|
|
float checker_primary[4];
|
|
float checker_secondary[4];
|
|
UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_PRIMARY, checker_primary);
|
|
UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_SECONDARY, checker_secondary);
|
|
int checker_size = UI_GetThemeValue(TH_TRANSPARENT_CHECKER_SIZE);
|
|
imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size);
|
|
}
|
|
|
|
void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3])
|
|
{
|
|
float coords[ARRAY_SIZE(cube_coords)][3];
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
|
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
|
}
|
|
|
|
immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2);
|
|
for (int i = 0; i < ARRAY_SIZE(cube_quad_index); i++) {
|
|
immVertex3fv(pos, coords[cube_quad_index[i][0]]);
|
|
immVertex3fv(pos, coords[cube_quad_index[i][1]]);
|
|
immVertex3fv(pos, coords[cube_quad_index[i][2]]);
|
|
|
|
immVertex3fv(pos, coords[cube_quad_index[i][0]]);
|
|
immVertex3fv(pos, coords[cube_quad_index[i][2]]);
|
|
immVertex3fv(pos, coords[cube_quad_index[i][3]]);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3])
|
|
{
|
|
float coords[ARRAY_SIZE(cube_coords)][3];
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
|
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
|
}
|
|
|
|
immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2);
|
|
for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
|
|
immVertex3fv(pos, coords[cube_line_index[i][0]]);
|
|
immVertex3fv(pos, coords[cube_line_index[i][1]]);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_cube_corners_3d(uint pos,
|
|
const float center[3],
|
|
const float aspect[3],
|
|
const float factor)
|
|
{
|
|
float coords[ARRAY_SIZE(cube_coords)][3];
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
|
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
|
}
|
|
|
|
immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4);
|
|
for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
|
|
float vec[3], co[3];
|
|
sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]);
|
|
mul_v3_fl(vec, factor);
|
|
|
|
copy_v3_v3(co, coords[cube_line_index[i][0]]);
|
|
immVertex3fv(pos, co);
|
|
add_v3_v3(co, vec);
|
|
immVertex3fv(pos, co);
|
|
|
|
copy_v3_v3(co, coords[cube_line_index[i][1]]);
|
|
immVertex3fv(pos, co);
|
|
sub_v3_v3(co, vec);
|
|
immVertex3fv(pos, co);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
/**
|
|
* Draw a cylinder. Replacement for gluCylinder.
|
|
* _warning_ : Slow, better use it only if you no other choices.
|
|
*
|
|
* \param pos: The vertex attribute number for position.
|
|
* \param nor: The vertex attribute number for normal.
|
|
* \param base: Specifies the radius of the cylinder at z = 0.
|
|
* \param top: Specifies the radius of the cylinder at z = height.
|
|
* \param height: Specifies the height of the cylinder.
|
|
* \param slices: Specifies the number of subdivisions around the z axis.
|
|
* \param stacks: Specifies the number of subdivisions along the z axis.
|
|
*/
|
|
void imm_draw_cylinder_fill_normal_3d(
|
|
uint pos, uint nor, float base, float top, float height, int slices, int stacks)
|
|
{
|
|
immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
|
|
for (int i = 0; i < slices; i++) {
|
|
const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
|
|
const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
|
|
const float cos1 = cosf(angle1);
|
|
const float sin1 = sinf(angle1);
|
|
const float cos2 = cosf(angle2);
|
|
const float sin2 = sinf(angle2);
|
|
|
|
for (int j = 0; j < stacks; j++) {
|
|
float fac1 = (float)j / (float)stacks;
|
|
float fac2 = (float)(j + 1) / (float)stacks;
|
|
float r1 = base * (1.0f - fac1) + top * fac1;
|
|
float r2 = base * (1.0f - fac2) + top * fac2;
|
|
float h1 = height * ((float)j / (float)stacks);
|
|
float h2 = height * ((float)(j + 1) / (float)stacks);
|
|
|
|
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
|
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
|
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
|
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
|
float n1[3], n2[3];
|
|
|
|
/* calc normals */
|
|
sub_v3_v3v3(n1, v2, v1);
|
|
normalize_v3(n1);
|
|
n1[0] = cos1;
|
|
n1[1] = sin1;
|
|
n1[2] = 1 - n1[2];
|
|
|
|
sub_v3_v3v3(n2, v3, v4);
|
|
normalize_v3(n2);
|
|
n2[0] = cos2;
|
|
n2[1] = sin2;
|
|
n2[2] = 1 - n2[2];
|
|
|
|
/* first tri */
|
|
immAttr3fv(nor, n2);
|
|
immVertex3fv(pos, v1);
|
|
immVertex3fv(pos, v2);
|
|
immAttr3fv(nor, n1);
|
|
immVertex3fv(pos, v3);
|
|
|
|
/* second tri */
|
|
immVertex3fv(pos, v3);
|
|
immVertex3fv(pos, v4);
|
|
immAttr3fv(nor, n2);
|
|
immVertex3fv(pos, v1);
|
|
}
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_cylinder_wire_3d(
|
|
uint pos, float base, float top, float height, int slices, int stacks)
|
|
{
|
|
immBegin(GPU_PRIM_LINES, 6 * slices * stacks);
|
|
for (int i = 0; i < slices; i++) {
|
|
const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
|
|
const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
|
|
const float cos1 = cosf(angle1);
|
|
const float sin1 = sinf(angle1);
|
|
const float cos2 = cosf(angle2);
|
|
const float sin2 = sinf(angle2);
|
|
|
|
for (int j = 0; j < stacks; j++) {
|
|
float fac1 = (float)j / (float)stacks;
|
|
float fac2 = (float)(j + 1) / (float)stacks;
|
|
float r1 = base * (1.0f - fac1) + top * fac1;
|
|
float r2 = base * (1.0f - fac2) + top * fac2;
|
|
float h1 = height * ((float)j / (float)stacks);
|
|
float h2 = height * ((float)(j + 1) / (float)stacks);
|
|
|
|
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
|
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
|
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
|
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
|
|
|
immVertex3fv(pos, v1);
|
|
immVertex3fv(pos, v2);
|
|
|
|
immVertex3fv(pos, v2);
|
|
immVertex3fv(pos, v3);
|
|
|
|
immVertex3fv(pos, v1);
|
|
immVertex3fv(pos, v4);
|
|
}
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_draw_cylinder_fill_3d(
|
|
uint pos, float base, float top, float height, int slices, int stacks)
|
|
{
|
|
immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
|
|
for (int i = 0; i < slices; i++) {
|
|
const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
|
|
const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
|
|
const float cos1 = cosf(angle1);
|
|
const float sin1 = sinf(angle1);
|
|
const float cos2 = cosf(angle2);
|
|
const float sin2 = sinf(angle2);
|
|
|
|
for (int j = 0; j < stacks; j++) {
|
|
float fac1 = (float)j / (float)stacks;
|
|
float fac2 = (float)(j + 1) / (float)stacks;
|
|
float r1 = base * (1.0f - fac1) + top * fac1;
|
|
float r2 = base * (1.0f - fac2) + top * fac2;
|
|
float h1 = height * ((float)j / (float)stacks);
|
|
float h2 = height * ((float)(j + 1) / (float)stacks);
|
|
|
|
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
|
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
|
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
|
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
|
|
|
/* first tri */
|
|
immVertex3fv(pos, v1);
|
|
immVertex3fv(pos, v2);
|
|
immVertex3fv(pos, v3);
|
|
|
|
/* second tri */
|
|
immVertex3fv(pos, v3);
|
|
immVertex3fv(pos, v4);
|
|
immVertex3fv(pos, v1);
|
|
}
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
/* Circle Drawing - Tables for Optimized Drawing Speed */
|
|
#define CIRCLE_RESOL 32
|
|
|
|
static void circball_array_fill(const float verts[CIRCLE_RESOL][3],
|
|
const float cent[3],
|
|
float rad,
|
|
const float tmat[4][4])
|
|
{
|
|
/* 32 values of sin function (still same result!) */
|
|
const float sinval[CIRCLE_RESOL] = {
|
|
0.00000000, 0.20129852, 0.39435585, 0.57126821, 0.72479278, 0.84864425, 0.93775213,
|
|
0.98846832, 0.99871650, 0.96807711, 0.89780453, 0.79077573, 0.65137248, 0.48530196,
|
|
0.29936312, 0.10116832, -0.10116832, -0.29936312, -0.48530196, -0.65137248, -0.79077573,
|
|
-0.89780453, -0.96807711, -0.99871650, -0.98846832, -0.93775213, -0.84864425, -0.72479278,
|
|
-0.57126821, -0.39435585, -0.20129852, 0.00000000,
|
|
};
|
|
|
|
/* 32 values of cos function (still same result!) */
|
|
const float cosval[CIRCLE_RESOL] = {
|
|
1.00000000, 0.97952994, 0.91895781, 0.82076344, 0.68896691, 0.52896401, 0.34730525,
|
|
0.15142777, -0.05064916, -0.25065253, -0.44039415, -0.61210598, -0.75875812, -0.87434661,
|
|
-0.95413925, -0.99486932, -0.99486932, -0.95413925, -0.87434661, -0.75875812, -0.61210598,
|
|
-0.44039415, -0.25065253, -0.05064916, 0.15142777, 0.34730525, 0.52896401, 0.68896691,
|
|
0.82076344, 0.91895781, 0.97952994, 1.00000000,
|
|
};
|
|
|
|
float vx[3], vy[3];
|
|
float *viter = (float *)verts;
|
|
|
|
mul_v3_v3fl(vx, tmat[0], rad);
|
|
mul_v3_v3fl(vy, tmat[1], rad);
|
|
|
|
for (uint a = 0; a < CIRCLE_RESOL; a++, viter += 3) {
|
|
viter[0] = cent[0] + sinval[a] * vx[0] + cosval[a] * vy[0];
|
|
viter[1] = cent[1] + sinval[a] * vx[1] + cosval[a] * vy[1];
|
|
viter[2] = cent[2] + sinval[a] * vx[2] + cosval[a] * vy[2];
|
|
}
|
|
}
|
|
|
|
void imm_drawcircball(const float cent[3], float rad, const float tmat[4][4], uint pos)
|
|
{
|
|
float verts[CIRCLE_RESOL][3];
|
|
|
|
circball_array_fill(verts, cent, rad, tmat);
|
|
|
|
immBegin(GPU_PRIM_LINE_LOOP, CIRCLE_RESOL);
|
|
for (int i = 0; i < CIRCLE_RESOL; i++) {
|
|
immVertex3fv(pos, verts[i]);
|
|
}
|
|
immEnd();
|
|
}
|
|
|
|
void imm_drawX(const float cent[3], float size, const float tmat[4][4], uint pos)
|
|
{
|
|
/* Draw an "X" indicating where the previous snap point is.
|
|
* This is useful for indicating perpendicular snap. */
|
|
|
|
/* v1, v2, v3 and v4 indicate the coordinates of the ends of the "X". */
|
|
float vx[3], vy[3], v1[3], v2[3], v3[3], v4[4];
|
|
|
|
mul_v3_v3fl(vx, tmat[0], size);
|
|
mul_v3_v3fl(vy, tmat[1], size);
|
|
|
|
add_v3_v3v3(v1, vx, vy);
|
|
sub_v3_v3v3(v2, vx, vy);
|
|
negate_v3_v3(v3, v1);
|
|
negate_v3_v3(v4, v2);
|
|
|
|
add_v3_v3(v1, cent);
|
|
add_v3_v3(v2, cent);
|
|
add_v3_v3(v3, cent);
|
|
add_v3_v3(v4, cent);
|
|
|
|
immBegin(GPU_PRIM_LINES, 4);
|
|
immVertex3fv(pos, v3);
|
|
immVertex3fv(pos, v1);
|
|
immVertex3fv(pos, v4);
|
|
immVertex3fv(pos, v2);
|
|
immEnd();
|
|
}
|