Grab a vertex under mouse position in GLScene

╄→гoц情女王★ 提交于 2019-12-24 03:24:54

问题


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.

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


回答1:


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

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

The intersection point is then

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

  • The normal n is the camera's forward direction, GLCamera.Direction.

  • The point on the plane, p_0 is LocalToAbsolute(Vertex)

  • 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).

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

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

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.



来源:https://stackoverflow.com/questions/30505029/grab-a-vertex-under-mouse-position-in-glscene

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!