编辑!我已经更新了我的代码,并将完整的源代码粘贴到下面
首先,我必须弄清楚如何获得我最初点击飞机的位置。
我在这里找到了解决方案:http://glscene.sourceforge.net/wikka/StyleIndepenentRaycast
一旦我可以在 3D 空间中读取该点,我就必须映射“鼠标点击点”
在两个平面上。
我称之为“MouseEventPlane”的第一架飞机对用户不可见,
并用于绘制鼠标光标在 3D 世界中的位置。
我称之为“myPlane”的第二个平面是我真正想要移动的平面。
第一个平面总是与 myPlane 的 Z 位置对齐。
以下是我的多层/平面功能等的完整源代码:
unit Unit1;
{Important info about component settings:
engine.objectsorting = none
mouseventplane has to be completely transparent (0 alpha = not visible to the user)
and has to aalways aligned with the picked object's X axis}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, lclintf, Graphics, Dialogs,
StdCtrls, ExtDlgs, ExtCtrls, GLLCLViewer, GLScene, GLObjects, GLMaterial,
GLTexture, GLGraph, GLSLPostBlurShader, GLOutlineShader, GLSmoothNavigator,
GLWindows, GLGui, GLCrossPlatform, GLColor, GLCoordinates, GLTextureFormat, VectorGeometry;
type
TPoint3D = record
X,Y,Z: single;
end;
{ TForm1 }
TForm1 = class(TForm)
addImage: TButton;
Cam: TGLCamera;
bmp: TGLPlane;
engine: TGLScene;
Memo1: TMemo;
mouseEventPlane: TGLPlane;
GLSphere1: TGLSphere;
Panel1: TPanel;
sc6: TScrollBar;
sceneScale: TScrollBar;
world: TGLSceneViewer;
pod: TOpenPictureDialog;
sc1: TScrollBar;
sc2: TScrollBar;
sc3: TScrollBar;
sc4: TScrollBar;
sc5: TScrollBar;
procedure addImageClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure worldMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure worldMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure sc1Change(Sender: TObject);
procedure worldMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
procedure AddBitmap(AFilename: string);
public
{ public declarations }
end;
var
Form1: TForm1;
bitmaps: array of TGLPlane;
layerToMove: TGLCustomSceneObject;
leftDown: boolean;
PointOfClick: TPoint3D;
implementation
{$R *.lfm}
{ TForm1 }
function Point3D_to_Vector(Point3D: TPoint3D): TVector;
begin
result.X := Point3D.X;
result.Y := Point3D.Y;
result.Z := Point3D.Z;
end;
function Point3D_to_GLCoordinates(Point3D: TPoint3D): TGLCoordinates;
begin
result.X := Point3D.X;
result.Y := Point3D.Y;
result.Z := Point3D.Z;
end;
function Point3D_To_Str(Point3D: TPoint3D): string;
begin
result := inttostr(round(Point3D.X*10))+':'+inttostr(round(Point3D.Y*10))+':'+inttostr(round(Point3D.Z*10));
end;
function ScreenToPlaneIntersect(World: TGLSceneViewer; Plane: TGLPlane; X,Y: integer): TPoint3D;
var p0, p1, raystart, rayvector, ipoint: TVector;
begin
{This function will return the coordinates of the point of intersection
occurs within a plane boundaries.
This function will automatically fit the results so that no matter
where in the 3D space the plane is located, the results
will be represented as if the plane's center is 0x0x0. }
//get the point near the camera (near plane)
p0:=World.Buffer.ScreenToWorld(vectormake(x, World.height-y, 0));
//get the point on the far plane
p1 := World.Buffer.ScreenToWorld(vectormake(x, World.height-y, 1));
//Use the values for raycasting
raystart := p0;
rayvector := vectornormalize(vectorsubtract(p1,p0));
if not Plane.RayCastIntersect(raystart, rayvector, @ipoint) then exit;
ipoint.X := ipoint.X-Plane.position.X;
ipoint.Y := ipoint.Y-Plane.position.Y;
ipoint.Z := ipoint.Z-Plane.position.Z;
result.X := ipoint.X;
result.Y := ipoint.Y;
result.Z := ipoint.Z;
end;
procedure TForm1.AddBitmap(AFilename: string);
var ms: integer;
begin
setlength(bitmaps, length(bitmaps)+1);
bitmaps[length(bitmaps)-1] := TGLPlane.Create(nil);
with bitmaps[length(bitmaps)-1] do
begin
ms := gettickcount;
Material.Texture.Image.LoadFromFile(pod.FileName);
ms := gettickcount-ms;
addImage.caption := inttostr(ms);
//basically, we assume that scale value 1 is equal to 1000 pixels,
//so we just divide the two values by 1000
Scale.X := Material.Texture.Image.Width/1000;
Scale.Y := Material.Texture.Image.Height/1000;
with Material do
begin
Texture.Enabled := True;
BlendingMode := bmTransparency;
//Texture.TextureMode:=tmModulate;
FrontProperties.Diffuse.Alpha := 1;
Texture.Compression := tcHighSpeed;
end;
end;
engine.Objects.AddChild(bitmaps[length(bitmaps)-1]);
end;
procedure TForm1.sc1Change(Sender: TObject);
begin
Cam.SceneScale := sceneScale.Position / 100;
Cam.Position.Z := sc1.Position / 100;
Cam.Position.X := sc2.Position / 100;
Cam.Position.Y := sc3.Position / 100;
if length(bitmaps) = 0 then exit;
bitmaps[length(bitmaps)-1].PitchAngle := sc4.Position/100;
bitmaps[length(bitmaps)-1].Material.FrontProperties.Diffuse.Alpha := sc5.Position / 100;
end;
procedure TForm1.worldMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var p3: TPoint3D;
begin
leftDown := false;
end;
procedure TForm1.addImageClick(Sender: TObject);
var i: integer;
begin
pod.execute;
if pod.Files.count > 0 then
for i := 0 to pod.files.Count-1 do AddBitmap(pod.files.Strings[i]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
mouseEventPlane.Material.FrontProperties.Diffuse.Alpha := 0;
end;
procedure TForm1.worldMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
layerToMove := (world.Buffer.GetPickedObject(x, y) as TGLCustomSceneObject);
if (layerToMove.ToString <> 'TGLPlane') or (layerToMove.name = 'mouseEventPlane') then exit;
engine.Objects.MoveChildLast(engine.Objects.IndexOfChild(layerToMove)); //Will move a myPlane to the top of ther paint order
pointofclick := ScreenToPlaneIntersect(world, (layerToMove as TGLPlane), X, Y);
leftDown := true;
end;
procedure TForm1.worldMouseMove(Sender: TObject; Shift: TShiftState;X, Y: Integer);
var p3: tpoint3d;
begin
if leftDown=true then
begin
p3 := ScreenToPlaneIntersect(world, mouseEventPlane, x, y);
layerToMove.Position.X := p3.X - pointofclick.X;
layerToMove.Position.Y := p3.Y - pointofclick.Y;
end;
end;
end.