Ich mache gerade ein Päuschen vom nächtlichem kompelieren und habe daher einige minutem zum "Klugscheißen".
Wenn Ihr oder Du dich mit die Maus über eine 3D Scene bewegst dann gehe einfach mal davon aus
das Du for dem Monitor die aktuelle Kammera bist (gedacht an Position 0.,0,0)
und die x,und y Koordinate der Maus ist ein Strahl (Ray) der in die Scene zeigt.
RayPosition = 0,0,0
RayDirection = mausx,mausy,1
Da aber Fensterkoordinaten von Oben nach unten laufen (top to botton)
und die y Achse der 3D Scene von unten nach oben (bottom to top)
muss man noch die Maus Y Koordinate umkeheren
RayDirection = mausx,Fensterhöhe-mausy,1
Vereinfacht ausgedrückt schiest man nun einen Strahl in die Scene und ermittelt erst grob ob eine
Boundingbox oder Sphere (die das komplette Objekt umschliesen) getroffen wird.
Ist dies der Fall dann geht man eine Ebene tiefer und ermittelt welche Polygone (oft Dreiecke) from Ray getroffen wurden.
Das ganze nennt man dann RayPicking.
Werden mehrer Polygone getroffen nimmt das welches am nahesten zur Kammerea ist.
Die Sache hat nur einen
kleinen Haken.
In der Regel sitzt Du noch immer an der gedachten Position 0,0,0 vor deinem Monitor
aber die virtuelle Kamera wurde verschoben und mit Sicherheit auch noch rotiert.
Daher muss vor der Kollisionsberechnung dein Sitzplatzposition und Blickrichtung
in die 3D Scene rotiert und verschoben werden so das deine Position und Richtung der virtuellen Kamera entspricht.
Mathematisch multipliziert man den gedachten "Sehstrahl" = Vektor mit der Model- und Projections Matrix der 3D Engine.
Da verbirgt sich aber schon der nächste
kleine Haken.
Denn eigentlich tranformiert man garnicht den Sehstrahl in die 3D Welt
sondern man tranformiert damit die 3D Welt (in umgkehrter Reihenfolge wieder vor deinem Monitor)
so das die gedachte Sitzposition auch wirklich an Position 0,0,0 ist.
Wenn vorher also die Kamera rotiert und dann verschoben wurde
verschiebt man nun zuert die 3D Scene und rotiert man sie wieder zurück.
Da währen wier beim dritten "Häkchen".
Die nennen wir es mal rückwerts laufende Koordinatentransformation erreicht man dadurch
das man den gedachten Sehstrahl mit der
inversen Matrix multipliziert.
Jetzt muss man aber keine Mathebücher pauken wenn man nicht weiss warum es geht
denn das haben andere schon für uns gemacht.
Zum Beispiel in der Form von gluProject() und gluUnproject() da die Engine aber auch im Direct3D Modus
sich befinden kann nützen einem die netten OpenGL tools in diesem Fall nicht sonderlich viel.
Also müste man dieses funktion als Software haben und welch ein Zufall ich hab den "Krämpel"
noch gestern für FreeBASIC portiert.
Code: Alles auswählen
type float as double ' oder single
sub __gluMultMatrixVecd(m as const float ptr, _
i as const float ptr, _
o as float ptr)
for j as integer=0 to 3
o[j] = i[0] * m[0*4+j] + _
i[1] * m[1*4+j] + _
i[2] * m[2*4+j] + _
i[3] * m[3*4+j]
next
end sub
sub __gluMakeIdentityd(m as float ptr)
m[0+4*0] = 1: m[0+4*1] = 0: m[0+4*2] = 0: m[0+4*3] = 0
m[1+4*0] = 0: m[1+4*1] = 1: m[1+4*2] = 0: m[1+4*3] = 0
m[2+4*0] = 0: m[2+4*1] = 0: m[2+4*2] = 1: m[2+4*3] = 0
m[3+4*0] = 0: m[3+4*1] = 0: m[3+4*2] = 0: m[3+4*3] = 1
end sub
sub __gluMultMatricesd(a as const float ptr, _
b as const float ptr, _
r as float ptr)
for i as integer = 0 to 3
dim as integer ii=i*4
for j as integer = 0 to 3
r[ii+j] = a[ii]*b[j] + a[ii+1]*b[4+j] + a[ii+2]*b[8+j] + a[ii+3]*b[12+j]
next
next
end sub
function __gluInvertMatrixd(src as const float ptr, _
inverse as float ptr) as integer
dim as float t
dim as float temp(3,3)
for i as integer=0 to 3
for j as integer=0 to 3
temp(i,j) = src[i*4+j]
next
next
__gluMakeIdentityd(inverse)
dim as integer j
for i as integer = 0 to 3
if (temp(i,i) = 0.0) then
for j = i + 1 to 3
if (temp(j,i) <> 0.0) then exit for
next
if (j <> 4) then
for k as integer= 0 to 3
t = temp(i,k)
temp(i,k) = temp(j,k)
temp(j,k) = t
t = inverse[i*4+k]
inverse[i*4+k] = inverse[j*4+k]
inverse[j*4+k] = t
next
else
return 0
end if
end if
t = 1.0 / temp(i,i)
for k as integer = 0 to 3
temp(i,k) *= t
inverse[i*4+k] *= t
next
for j as integer = 0 to 3
if (j <> i) then
t = temp(j,i)
for k as integer = 0 to 3
temp(j,k) -= temp(i,k)*t
inverse[j*4+k] -= inverse[i*4+k]*t
next
end if
next
next
return 1
end function
function __gluUnProject(winx as float, _
winy as float, _
winz as float, _
modelMatrix as const float ptr, _
projMatrix as const float ptr, _
viewport as const integer ptr, _
objx as float ptr, _
objy as float ptr, _
objz as float ptr) as integer
dim as float finalMatrix(15)
dim as float i(3)
dim as float o(3)
__gluMultMatricesd(modelMatrix, projMatrix, @finalMatrix(0))
if (__gluInvertMatrixd(@finalMatrix(0), @finalMatrix(0))=0) then
return 0
end if
i(0)=winx:i(1)=winy:i(2)=winz:i(3)=1.0
' Map x and y from window coordinates
i(0) = (i(0) - viewport[0]) / viewport[2]
i(1) = (i(1) - viewport[1]) / viewport[3]
' Map to range -1 to 1
i(0) = i(0) * 2 - 1
i(1) = i(1) * 2 - 1
i(2) = i(2) * 2 - 1
__gluMultMatrixVecd(@finalMatrix(0), @i(0), @o(0))
if (o(3) = 0.0) then return 0
o(0) /= o(3)
o(1) /= o(3)
o(2) /= o(3)
*objx = o(0)
*objy = o(1)
*objz = o(2)
return 1
end function
function __gluProject(objx as float, _
objy as float, _
objz as float, _
modelMatrix as const float ptr, _
projMatrix as const float ptr, _
viewport as const integer ptr, _
winx as float ptr, _
winy as float ptr, _
winz as float ptr) as integer
dim as float i(3),o(3)
i(0)=objx:i(1)=objy:i(2)=objz:i(3)=1.0
__gluMultMatrixVecd(modelMatrix,@i(0), @o(0))
__gluMultMatrixVecd(projMatrix, @o(0), @i(0))
if (i(3) = 0.0) then return 0
i(0) /= i(3)
i(1) /= i(3)
i(2) /= i(3)
' Map x, y and z to range 0-1
i(0) = i(0) * 0.5 + 0.5
i(1) = i(1) * 0.5 + 0.5
i(2) = i(2) * 0.5 + 0.5
' Map x,y to viewport
i(0) = i(0) * viewport[2] + viewport[0]
i(1) = i(1) * viewport[3] + viewport[1]
*winx=i(0)
*winy=i(1)
*winz=i(2)
return 1
end function
Da ganze hatte ich vor zwei Jahren für Basic4GL schon mal näher beleuchtet gehabt
weil ich damals mathematisch gesehen noch im Dunklem stand.
Die Purebasic 3D Engine stellt meiner bescheidenen Meinung nach nicht mal 30% der Orge Engine dem Käufer/Nutzer zur Verfügung.
Wo ist da z.B. GetCameraMatrix() oder GetModelViewMatrix() ?
Egal ob DirectX oder OpenGL man brauch diese Funktionen einfach nur zu Exportieren und Das wäre es dann auch schon.
Also muss man einen Umweg gehen und via Kamera und Node Pitch, Yaw und Roll und Position sich die Matrizen selber zusammen "basten".
Ich bin gerade mit meine Softwarefirma bis ende des Monates ausgebucht und in meiner Freizeit quäle ich gerne
die neuen OOP fähigkeiten von FreeBASIC und beruflich die von C++ und JAVA aber zum 1. könnte ich wenn dann
noch Bedarf bestehen sollte eine PureBasic Version für das "Mouse World Picking" zur Verfügung stellen.
Aber hier sind ja auch einige 3D "Freggels" welche manchmal nicht wissen was sie nun schon wieder anstellen können.
Vielleicht hat ja Jemand Lust und Zeit gluUnprojekt für die PureBasic 3D Engine zu verwirklichen
Code: Alles auswählen
PureUnProject(MouseX,MouseY,MouseZ, ViewMatrix, ProjMatrix, WindowLeft, WindowTop, WindowRight, WindowBottom, ResultRayDirectionX, ResultRayDirectionY, ResultRayDirectionZ)
So dann mal ran an die Tastaturen.
Liebe Grüße ich glaube mein Bett ruft gerade.
DJ
PS. Wer mal sehen möchte wie genau die Kollisionskoordinaten sind kann ja mal hier die win32 exe testen.
download:
MouseRayPicking.zip