在GLScene中的鼠标位置下抓取一个顶点 [英] Grab a vertex under mouse position in GLScene

查看:112
本文介绍了在GLScene中的鼠标位置下抓取一个顶点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用GLScene编写3D建模应用程序,因此我需要通过抓住顶点将鼠标位置下的某些顶点移动到新位置. 首先,我加载一个球体并用鼠标拾取一个顶点,然后尝试拖动它,但我意识到该顶点在鼠标插入符下无法正确移动.

I'm writing a 3D modeling application using GLScene , so I need to move some vertices under mouse position to new position by grabbing the vertex. First of all, I load a sphere and pick up a vertex by mouse,then I try to drag it but I realize that the vertex doesn't move under mouse caret correctly.

    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

what is my wrong? I want to write a tools like stephaneginier.com/sculptgl

推荐答案

一种想到此的方法是解决以下

One way to think of this is solving the following line-plane intersection:

沿着 l d 的距离可以这样找到:

The distance along l, d can be found like so:

交点是

您现在所需要的就是输入.我从未使用过GLScene,所以这只是为您指明正确的方向:

All you need now are the inputs. I've never used GLScene, so this is just to point you in the right direction:

平面上的点p_0LocalToAbsolute(Vertex)

射线起点l_0是相机的位置GLCamera.Position或近平面上的点GLSceneViewer.Buffer.ScreenToWorld(x, y).

The ray start, l_0, is either the camera's position, GLCamera.Position or a point on the near plane, GLSceneViewer.Buffer.ScreenToWorld(x, y).

射线方向l是投影的鼠标方向GLSceneViewer.Buffer.ScreenToVector(x, y)

The ray direction, l, is the projected mouse direction, GLSceneViewer.Buffer.ScreenToVector(x, y)

最后,您需要使用AbsoluteToLocal将交点放回对象空间.

Finally, you'll want to put the intersection point back into object space with AbsoluteToLocal.

看起来这种交叉测试可能会有捷径(例如,使用PixelRayToWorld中的射线),但是就像我说的我不知道GLScene.

It looks like there may be shortcuts for this intersection test (e.g. using the ray from PixelRayToWorld), but like I said I don't know GLScene.

这篇关于在GLScene中的鼠标位置下抓取一个顶点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆