В статье рассматривается решение проблемы перемещения вершин в приложении 3D-моделирования, созданном с использованием библиотеки GLScene. Пользователь хочет перемещать вершины под курсором мыши, но сталкивается с проблемой, когда вершина не движется правильно.
Для решения этой проблемы автор кода предложил использовать подход, основанный на решении уравнения линии-плоскости. Чтобы найти точку пересечения, необходимо определить нормаль плоскости, точку на плоскости, начало луча и направление луча.
Нормаль плоскости можно получить из направления камеры, а точку на плоскости можно найти, преобразовав вершину из локальных координат в абсолютные. Начало луча может быть положением камеры или точкой на плоскости ближнего обзора, а направление луча может быть проекцией направления мыши.
После нахождения точки пересечения ее необходимо преобразовать обратно в локальные координаты.
Автор кода также отмечает, что возможно существуют более простые способы решения этой задачи в GLScene, но он не знаком с этой библиотекой.
Пример кода на Object Pascal (Delphi) для перемещения вершины под курсором мыши:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Math,
GLWin32Viewer, GLCrossPlatform, GLBaseClasses, GLScene, GLColor, GLCanvas,
GLVectorFileObjects, GLObjects, GLVectorTypes,
GLCoordinates, GLFileObj, GLVectorGeometry;
type
TForm1 = class(TForm)
GLSceneViewer: TGLSceneViewer;
GLScene1: TGLScene;
GLLightSource1: TGLLightSource;
GLCamera: TGLCamera;
GLDummyCube1: TGLDummyCube;
FreeForm: TGLFreeForm;
GLLightSource2: TGLLightSource;
procedure FormCreate(Sender: TObject);
procedure GLSceneViewerMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure GLSceneViewerMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure GLSceneViewerPostRender(Sender: TObject);
private
IsVertexDragging: Boolean;
MouseX, MouseY: Integer;
VertexIndexToDrag: Integer;
CenterPosition, LastPosition: TVector3f;
function FindClosestPointIndex(Point: TVector3f): Integer;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FreeForm.LoadFromFile('E:\test2\Sphere.obj');
end;
function TForm1.FindClosestPointIndex(Point: TVector3f): Integer;
var
i: Integer;
NewPoint: TVector3f;
BestDistance, TempDistance: Single;
begin
BestDistance := 100000000000000;
for i := 0 to FreeForm.MeshObjects[0].Vertices.Count - 1 do
begin
NewPoint := FreeForm.MeshObjects[0].Vertices[i];
TempDistance := VectorDistance(Point, NewPoint);
if TempDistance <= BestDistance then
begin
BestDistance := TempDistance;
Result := i;
end;
end;
end;
procedure TForm1.GLSceneViewerMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
Vertex: TVector3f;
begin
MouseX := X;
MouseY := Y;
if (ssLeft in Shift) and not (ssCtrl in Shift) then
begin
IsVertexDragging := True;
Vertex := GLSceneViewer.Buffer.PixelRayToWorld(X, Y);
Vertex := FreeForm.AbsoluteToLocal(Vertex);
VertexIndexToDrag := FindClosestPointIndex(Vertex);
CenterPosition := GLSceneViewer.Buffer.ScreenToWorld(X, Y);
end;
end;
procedure TForm1.GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
delta: TVector3f;
i: Integer;
begin
if (ssLeft in Shift) and (ssCtrl in Shift) then
GLCamera.MoveAroundTarget(0.5 * (MouseY - Y), 0.5 * (MouseX - X))
else
if (ssLeft in Shift) and not (ssCtrl in Shift) and IsVertexDragging then
begin
LastPosition := GLSceneViewer.Buffer.ScreenToWorld(X, Y);
delta.X := RoundTo(LastPosition.X - CenterPosition.X, -5);
delta.Y := RoundTo(LastPosition.Y - CenterPosition.Y, -5);
delta.Z := RoundTo(LastPosition.Z - CenterPosition.Z, -5);
Caption := delta.Z.ToString;
CenterPosition := LastPosition;
FreeForm.MeshObjects[0].Vertices.TranslateItem(VertexIndexToDrag, Delta);
FreeForm.TransformationChanged;
end;
MouseX := X;
MouseY := Y;
end;
procedure TForm1.GLSceneViewerMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
IsVertexDragging := False;
end;
procedure TForm1.GLSceneViewerPostRender(Sender: TObject);
var
glc: TGLCanvas;
begin
if IsVertexDragging then
begin
glc := TGLCanvas.Create(GLSceneViewer.Width, GLSceneViewer.Height);
glc.PenWidth := 2;
glc.PenColor := clLime;
glc.Ellipse(MouseX, MouseY, 5, 5);
glc.Free;
end;
GLSceneViewer.Invalidate;
end;
end.
В коде используются процедуры обработки событий мыши для начала, движения и окончания перемещения вершины под курсором мыши. В процедуре FindClosestPointIndex находиться индекс ближайшей вершины к текущей позиции курсора мыши. В процедуре GLSceneViewerMouseDown начинается перемещение вершины, а в процедуре GLSceneViewerMouseMove осуществляется само перемещение. В процедуре GLSceneViewerPostRender отображается окружность в точке курсора мыши, если происходит перемещение вершины.
В статье рассматривается решение для перемещения вершин в приложении 3D-моделирования, созданном с использованием библиотеки GLScene, где пользователь хочет перемещать вершины под курсором мыши, но сталкивается с проблемой некорректного движения вершины.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.