在 GLScene 中抓取鼠标位置下的顶点
Grab a vertex under mouse position in GLScene
我正在使用 GLScene 编写 3D 建模应用程序,因此我需要通过抓取顶点将鼠标位置下的一些顶点移动到新位置。
首先,我加载一个球体并用鼠标拾取一个顶点,然后我尝试拖动它,但我意识到该顶点在鼠标插入符下没有正确移动。
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,
//GLScene
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
{ Private declarations }
IsVertexDragging: Boolean;
MouseX, MouseY : Integer;
VertexIndexToDrag: Integer;
CenterPosition, LastPosition: TVector3f;
function FindClosestPointIndex(Point: TVector3f): Integer;
public
{ Public declarations }
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
// Search the nearest point between all vertices in the sphere.
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);
// Find closest point to the mouse click position
VertexIndexToDrag := FindClosestPointIndex( Vertex );
//start of dragging position.
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 // Rotating camera
GLCamera.MoveAroundTarget(0.5*(MouseY-Y), 0.5*(MouseX-X) )
else
if (ssLeft in Shift) and not (ssCtrl in Shift) and IsVertexDragging then
begin // Dragging vertex
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
// Show a circle with mouse position
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.
我哪里错了?我想写一个像 stephaneginier.com/sculptgl
这样的工具
思考这个问题的一种方法是解决以下问题 line-plane intersection:
沿着l,d的距离可以这样求:
那么交点就是
您现在需要的只是输入。我从未使用过 GLScene,所以这只是为您指明正确的方向:
正常的n
就是camera's forward direction, GLCamera.Direction
.
平面上的点,p_0
是LocalToAbsolute(Vertex)
射线起点 l_0
是相机的位置 GLCamera.Position
或近平面上的点 GLSceneViewer.Buffer.ScreenToWorld(x, y)
.
光线方向,l
,是投影的鼠标方向,GLSceneViewer.Buffer.ScreenToVector(x, y)
最后,您需要将交点放回对象 space 和 AbsoluteToLocal
。
看起来这个相交测试可能有捷径(例如使用来自 PixelRayToWorld
的光线),但就像我说的我不知道 GLScene。
我正在使用 GLScene 编写 3D 建模应用程序,因此我需要通过抓取顶点将鼠标位置下的一些顶点移动到新位置。 首先,我加载一个球体并用鼠标拾取一个顶点,然后我尝试拖动它,但我意识到该顶点在鼠标插入符下没有正确移动。
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,
//GLScene
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
{ Private declarations }
IsVertexDragging: Boolean;
MouseX, MouseY : Integer;
VertexIndexToDrag: Integer;
CenterPosition, LastPosition: TVector3f;
function FindClosestPointIndex(Point: TVector3f): Integer;
public
{ Public declarations }
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
// Search the nearest point between all vertices in the sphere.
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);
// Find closest point to the mouse click position
VertexIndexToDrag := FindClosestPointIndex( Vertex );
//start of dragging position.
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 // Rotating camera
GLCamera.MoveAroundTarget(0.5*(MouseY-Y), 0.5*(MouseX-X) )
else
if (ssLeft in Shift) and not (ssCtrl in Shift) and IsVertexDragging then
begin // Dragging vertex
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
// Show a circle with mouse position
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.
我哪里错了?我想写一个像 stephaneginier.com/sculptgl
这样的工具思考这个问题的一种方法是解决以下问题 line-plane intersection:
沿着l,d的距离可以这样求:
那么交点就是
您现在需要的只是输入。我从未使用过 GLScene,所以这只是为您指明正确的方向:
正常的
n
就是camera's forward direction,GLCamera.Direction
.平面上的点,
p_0
是LocalToAbsolute(Vertex)
射线起点
l_0
是相机的位置GLCamera.Position
或近平面上的点GLSceneViewer.Buffer.ScreenToWorld(x, y)
.光线方向,
l
,是投影的鼠标方向,GLSceneViewer.Buffer.ScreenToVector(x, y)
最后,您需要将交点放回对象 space 和 AbsoluteToLocal
。
看起来这个相交测试可能有捷径(例如使用来自 PixelRayToWorld
的光线),但就像我说的我不知道 GLScene。