Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   2D-Pixel eines Control3D? (https://www.delphipraxis.net/176820-2d-pixel-eines-control3d.html)

laube53 28. Sep 2013 18:23

2D-Pixel eines Control3D?
 
Aus den 3D-Koordinaten (Position und RotationAngle) einer Camera in Firemonkey müsste man doch die Bildschirm-Pixel eines Punkts (Point3D) berechnen können.
Weiß jemand, wie das geht?

Thom 1. Okt 2013 16:48

AW: 2D-Pixel eines Control3D?
 
[gelöscht]

Sorry - Unfug geschrieben... :oops:

Man sollte nicht immer der Onlinehilfe vertrauen. Dort steht zwar zur Funktion
Delphi-Quellcode:
TContext3D.WorldToScreen()
:
Zitat:

Wandelt einen 2D-Punkt (z.B. Mauskoordinaten) in einen 3D-Punkt in einer Szene um.
Das ist aber falsch: Diese Methode macht, was sie dem Namen nach soll - sie wandelt einen Punkt aus der 3D-Welt in Bildschirmkoordinaten um. :thumb:

Die Anwendung ist einfach:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  P: TPoint3D;
begin
  P:=Context.WorldToScreen(Cube.Projection,Cube.AbsolutePosition);
  [...]
end;

laube53 15. Okt 2013 18:01

AW: 2D-Pixel eines Control3D?
 
Vielen Dank (nachträglich; war verreist).

Ich versuche, 3D-Objekte mit der Maus zu verschieben.
Dazu muss die Zugrichtung der Maus analysiert werden und herausgefunden werden, ob sie in eine Achsenrichtung zieht.
Mit WorldToScreen geht das schon. Aber die Ereignisbehandlung (MouseDown etc.) von 3D-Objekten scheint ziemlich langsam zu sein (oder ich mache etwas falsch);
jedenfalls kann ich nur sehr langsam ziehen.

Allerdings wurmt es mich (als Mathematiker), dass ich die Mathematik hinter der Projektion noch nicht genau verstehe.
Gibt es schon etwas gründliches zu FireMonkey-3D, oder muss man sich durch die Quellen arbeiten?

Thom 15. Okt 2013 18:20

AW: 2D-Pixel eines Control3D?
 
Ich habe auch etliche Semester Mathematik hinter mir - aber wenn ich mir die FMX-Quelltexte anschaue, bin ich ständig am Überlegen, ob ich nur zu dumm dafür bin, die Genialität dahinter zu verstehen oder ob da einfach (Entschuldigung) nur Stümper am Werk waren/sind. Ich hatte bisher etliche Wochen damit zugebracht, Teile von FMX (u.a. 3D und Filter) zu verstehen und muß gestehen, daß ich an vielen Stellen noch ratlos bin.

Die Bewegung von Objekten ist ebenfalls ein Thema für mich, da ich die dumme Idee hatte, einen brauchbaren Editor für FMX 3D zu programmieren. Die IDE ignoriert ja bekanntlich in 3D-Formularen die zugewiesenen Kamera zur Design-Time und in TViewport3D ist die Nutzung auch nur unsagbar mühsam. Aber es freut mich sehr, daß es noch mehr Leute gibt, die genügend Optimismus aufbringen, es mit dem Feueraffen aufzunehmen. Vielleicht bekommen wir es gemeinsam hin! :-D

P.S.: Ja - ohne die Quellen geht meiner Meinung nach gar nichts (außer dem Zusammenklicken der Oberfläche). Sobald eine halbwegs ernsthafte Anwendung daraus werden soll, führt kein Weg daran vorbei.

laube53 20. Okt 2013 19:22

AW: 2D-Pixel eines Control3D?
 
Meine Erkenntnisse bis jetzt:
Ich habe begonnen mit einer FireMonkey3D-Demo namens Arrows3D, siehe http://cc.embarcadero.com/item/29148.

Bei einer Projektion vom 3-dimensionalen Raum auf einen Bildschirm kann man sich zwischen einr Parallelprojektion
und einer Zentralprojektion entscheiden.

FireMonkey3D benutzt eine Zentralprojektion: Der Bildpunkt P' von P ist der Schnittpunkt der Gerade OZ mit der
Bildebene E: P' = OZ geschnitten E.

Z ist das Projektionszentrum, bei Firemonkey die (absolute) Position der Camera.

Die Bildebene E ist die Senkrechte zur Camera-Richtung und geht durch den Nullpunkt.
In der Bildebene E legt man einen "Ursprung" VP und zwei Basisvektoren VH (horizontal) und VV (vertikal) fest.
Die Koordinaten des Bildpunkts P' sind dann s und t, wenn der Ortsvektor von P' gleich dem von VP + r*VH + s*VV ist.

Die Umwandlung von P' in Bildschirmpixel geschieht dann anhand einer "Karte":
Sie legt ein reelles Intervall [a,b] für die s-Koordinate und [c,d] für die t-Koordinate fest,
Randpixel pa (linker Rand) und Pd (oberer Rand) und es bzw. es (Pixel pro s-Einheit bzw. Pixel pro t-Einheit).

Sei [XVon, XBis] x [YVon, YBis] x [ZVon, ZBis] der sichtbare Bereich der x- y- und z-Achse.

Das kann man folgendermaßen in Delphi aufschreiben:

type
TKarte = record {Daten zur Transf.von xy-Koordinaten in Pixel}
a,b,c,d :Single; {sichtbares Rechteck}
pa,pd :integer; {Pixel von (a,d) = linke obere Ecke}
es,et :LongInt; {Pixel pro Einheit}
end;

TProjTyp=(parallel,zentral); {Parallel- oder Zentralprojektion }
TProjDaten=record {Proj.ebene: VX = VP+tVH+sVV }
PT:TProjTyp;
VS,VP,VH,VV:TVector3D; {VS: Proj.richtung bzw. -zentrum }
end;

var
Projektion: TProjDaten;
Karte:TKarte;

function XPix(s:Single):Single; {Umrechnung von s,t in Pixel}
begin
with Karte do
XPix:=pa+ex*(s-a);
end;

function YPix(t:Single):Single;
begin
with Karte do
YPix:=pd+ey*(d-t);
end;

procedure normiere(var v:TVector3D);
var t:Single;
begin
t:=sqrt(v.X*v.X+v.Y*v.Y+v.Z*v.Z);
if t<>0 then
begin
v.X:=v.X/t;
v.Y:=v.Y/t;
v.Z:=v.Z/t;
end else
raise Exception.Create('Versuch,Nullvektor zu normieren!');
end;

procedure SetPDC(Camera:TCamera; w,h:Single); {setzt Projektion gemäß Camera}
var
N,V : TVector3D;
PD : TProjDaten;
begin
Karte.a:=XVon*1.25; // ist der tatsächlich in Viewport3D sichtbare Bereich
Karte.b:=XBis*1.25;
Karte.c:=YVon*1.1;
Karte.d:=YBis*1.1;
Karte.pa:=0;
Karte.pd:=0;
Karte.ex:=Round(w/(Karte.b-Karte.a)); //w=Viewport3D1.Width);
Karte.ey:=Round(h/(1*(YBis-YVon))); //h=Viewport3D1.Height);

PD.PT:=zentral;
PD.VS:=Camera.AbsolutePosition; {Projektions-Zentrum Z}

PD.VP.X:=0; {Ursprung des Koordinatensystems der Bildebene}
PD.VP.Y:=0;
PD.VP.Z:=0;

N:=Camera.AbsoluteDirection;
Normiere(N);
V.X:=-N.Y*N.X;
V.Y:=1-N.Y*N.Y;
V.Z:=-N.Y*N.Z;
Normiere(V);
PD.VV:=V;
PD.VH:=N.CrossProduct(V);

Projektion:=PD;
end;

procedure project(x,y,z:Single; var s,t:Single);
var
d,dx,dy:Single;
U1,U2,VH0,VV0:TVector3D;
k:integer;
begin
with Projektion do
begin {Berechnung d.Schnittpunkts}
if PT=zentral then
begin
U1.X:=VS.X-w.X;
U1.Y:=VS.Y-w.Y;
U1.Z:=VS.Z-w.Z;
end else
begin
U1.X:=VS.X; {U1=RichtungsTVector3D}
U1.Y:=VS.Y;
U1.Z:=VS.Z;
end;
U2.X:=w.X-VP.X; {U2=rechte Seite des LGS}
U2.Y:=w.Y-VP.Y;
U2.Z:=w.Z-VP.Z;

VH0:=VH;
VV0:=VV;
normiere(VH0);
normiere(VV0);

d:=det(U1,VH0,VV0); {Determinate}
dx:=det(U1,U2,VV0);
dy:=det(U1,VH0,U2);
if d=0 then
raise Exception.Create('Projektionsgerade parallel zur Ebene! Prozedur abgebrochen')
else begin
s:=dx/d;
t:=dy/d;
end;
end;
end;

Der letzte Schritt (Umrechnung von s,t in Pixel) ist aber noch verbesserungsbedürftig.
WorldToScreen ist da genauer.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:38 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz