Kollision Maus/Terrain berechnen [gelöst]

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Benutzeravatar
Scarabol
Beiträge: 1427
Registriert: 30.11.2005 21:00

Beitrag von Scarabol »

@NTQ
Hab gesehen das du 2005 mal ein Paar Proceduren zur Vektorrechnung gemacht hast. Kann man das nicht auf mein Problem beziehen?

Gruß
Scarabol

PS
Hier der Link
http://www.purebasic.fr/german/viewtopic.php?t=3452
Abgeschlossen Projekte:
Schreibmaschine, Bildschirmlupe, Wings3DtoOgreMeshConverter
Watch: PureArea

PB-V: 4
WinXP
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Wäre das Terrain eine Ebene, wäre vieles leichter, man könnte dann den Schnittpunkt sogar ohne 'Sehstrahl' direkt berechnen. Für das Terrain mit Sehstrahl bräuchte man eine Funktion, die zu einem x/y-Wert den z-Wert der Terrainoberfläche liefert.

So grob aus Erinnerung:
Wird ein Terrain nicht durch eine Höhenmap geliefert ? D.h. wenn ich die Höhe an einem x/y-Weltkoordinatenpunkt abfrage, dann suche ich die entsprechenden x'/y'-Koordinaten der Höhenmap und interpoliere linear zwischen x'/y' und x'+1/y'+1 für die Höhe an Zwischenpunkten (oder machen das moderne Terrains schon mit 3D-Splines??). Man kann das Interpolieren auch weglassen, dann kann es aber Probleme an scharfen Kanten (hohen Gradienten in der Heightmap) geben. Also ist es für dich möglich eine Funktion folgender Bauart zu basteln ?

Code: Alles auswählen

Procedure.d GetTerrainZ(x.d, y.d) ; returns world-coord-z of terrainsurface for given world-coords x and y
Angenommen eine solche Funktion haben wir. Dann fallen mir ein paar weitere Fragen ein:
1. Wo sich der Spieler befindet
Also x,y,z in Weltkoordinaten?
2. Welchen Winkel die Kamera zu Achse Null (Norden) hat
Sind das zwei Winkel? Einer für horizontale, einer für vertikale Neigung? Welche Drehrichtung ? und im Bereich ]-pi,pi] oder [0,2pi[ ?
Gibt es hierfür nicht eigentlich anstelle der Winkel so einen LookAt-Vektor ? Dann bräuchte man diesen Winkelkram nicht.
3. Wo sich die Kamera befindet
Also x,y,z in Weltkoordinaten?
4. Die Mauskoordinaten hab ich natürlich auch Wink
Also x,y-Koordinaten in Screenpixeln ?

Weitere Frage: Genügt es dir, den Schnittpunkt mit dem Terrain zu haben (also den Zielpunkt) ? Denn danach muss ja noch die Figur in die entsprechende Richtung bewegt werden. Bewegt man diese Figur eigentlich nur in x/y-Richtung und sie stellt sich automatisch auf die richtige Höhe ?

Nach Antworten auf diese Fragen, erscheint mir der Rest zumindest lösbar.
:wink:
!UD2
Benutzeravatar
Zaphod
Beiträge: 2875
Registriert: 29.08.2004 00:40

Beitrag von Zaphod »

Die Höhe läßt sich leicht herausfinden mit TerrainHeight(x,y) das laufen wäre also nicht das Problem.

Ich kann mich irren, aber ich bezweifle dass das finden eines angeklickten Punktes auf dem Ogre Terrain von PB aus möglich ist, mir würde jdenfall keine Lösung einfallen die ohne die konkreten Polygondaten auskommt.
Benutzeravatar
Scarabol
Beiträge: 1427
Registriert: 30.11.2005 21:00

Beitrag von Scarabol »

Hallo Froggerprogger,
Zitat:
1. Wo sich der Spieler befindet

Also x,y,z in Weltkoordinaten?
Ja
Zitat:
2. Welchen Winkel die Kamera zu Achse Null (Norden) hat

Sind das zwei Winkel? Einer für horizontale, einer für vertikale Neigung? Welche Drehrichtung ? und im Bereich ]-pi,pi] oder [0,2pi[ ?
Gibt es hierfür nicht eigentlich anstelle der Winkel so einen LookAt-Vektor ? Dann bräuchte man diesen Winkelkram nicht.
Ich hab den X-Winkel (Norden = 0), den Y Winkel (waagerecht = 0), LookAt-Vektor ist die Position des Entity. Die Winkel sind im Gradmaß aber auch im Bogenmaß verfügbar.
Keine Ahnung was du mit "Bereich ]-pi,pi] oder [0,2pi[ ?" meinst aber falls es ums Bogenmaß geht kann ich die Werte ebenfalls liefern.
Zitat:
3. Wo sich die Kamera befindet

Also x,y,z in Weltkoordinaten?
Ja
Zitat:
4. Die Mauskoordinaten hab ich natürlich auch Wink

Also x,y-Koordinaten in Screenpixeln ?
Ja

Und zum Letzten: Ja Ja

Gruß
Scarabol
Abgeschlossen Projekte:
Schreibmaschine, Bildschirmlupe, Wings3DtoOgreMeshConverter
Watch: PureArea

PB-V: 4
WinXP
Benutzeravatar
Scarabol
Beiträge: 1427
Registriert: 30.11.2005 21:00

Beitrag von Scarabol »

@Zaphod
Die genauen Polygondaten lassen sich zwar nicht auslesen, aber nachrechnen wie in folgendem Thread geschrieben.
http://www.purebasic.fr/german/viewtopic.php?t=11842

Also hat man schon die genauen Daten der Polygone und sollte die Berechnung durchführen können.

Gruß
Scarabol
Abgeschlossen Projekte:
Schreibmaschine, Bildschirmlupe, Wings3DtoOgreMeshConverter
Watch: PureArea

PB-V: 4
WinXP
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Man benötigt noch den Camera-Top-Vektor bzw. den Rotate-Winkel der Cam. Also den Winkel, der angibt mit welchem Winkel die Kamera um die Sichtachse gedreht ist. In PB scheint es keinen Befehl GetCameraTop oder GetCameraRotate zu geben, und der Funktion CameraRotate übergibt man den Wert, um den gedreht wird, nicht den absoluten Wert. Ein Mitspeichern der kumulierten Werte könnte daher irgendwann ungenau werden. Einfachste Lösung wäre, wenn du niemals CameraRotate nutzt, und daher 'oben' im Screen auch 'oben' in der Welt ist. Ist dem so ?
!UD2
Benutzeravatar
Scarabol
Beiträge: 1427
Registriert: 30.11.2005 21:00

Beitrag von Scarabol »

Erstmal nein, aber ich versteh deine Aussage nicht so ganz.

"Man benötigt noch den [...] den Rotate-Winkel der Cam"
Hab ich doch schon dreimal geschrieben das ich den Winkel habe.

"und der Funktion CameraRotate übergibt man den Wert, um den gedreht wird, nicht den absoluten Wert"
???

Gruß
Scarabol
Abgeschlossen Projekte:
Schreibmaschine, Bildschirmlupe, Wings3DtoOgreMeshConverter
Watch: PureArea

PB-V: 4
WinXP
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Die Kamera kann sich ja um 3 Achsen drehen: Einmal links/rechts und hoch/runter. Damit legt man ja bereits die Sichtlinie fest. Alternativ kann man dies durch setzen des LookAt erreichen. Dabei bleibt aber immer oben im Screen oben in der Welt, also Wolken sind oben, der Boden unten.

Nun lässt sich aber die Kamera noch entlang der Sichtlinie herumdrehen, also bspw. so, dass z.B. der linke Bildschirmrand in den Himmel zeigt. Vergleichbar damit, dass man seinen Kopf nach rechts neigt. Dabei bleibt der LookAt unverändert.

Du schreibst:
X-Winkel (Norden = 0), den Y Winkel (waagerecht = 0)
Ich interpretiere das als X-Winkel: In welche Himmelsrichtung wird geschaut, Y-Winkel: Wird nach oben oder unten geschaut. Fehlt: Wie stark ist das Bild nach links oder rechts entlang der Sichtlinie gekippt ?
Bei OpenGL gibt es hierfür noch einen Vektor, der anzeigt, wohin 'oben' im Screen in der Welt zeigt, bei PB habe ich nichts dergleichen finden können.
Wenn du das nirgendwo explizit änderst ist immer oben im Screen gleich oben in der Welt und die Sache ist wieder vereinfacht. Denn dann lässt sich aus "lookatpos - campos" der Sichtlinienvektor berechnen, anhand der x/y-Mausklickkoordinaten und dem FOV-Winkel die Winkeldrehungen in waagerechter und vertikaler Richtung und somit dann sofort die Linie, entlang derer nach Terrainkollisionen gesucht werden soll.
Man spart sich dann das Rechnen, dass bsp. ein Klick "links mittig" im Screen in der Welt nach "oben" geht, wenn die Camera nach rechts geneigt ist.
!UD2
Benutzeravatar
Scarabol
Beiträge: 1427
Registriert: 30.11.2005 21:00

Beitrag von Scarabol »

Jo sorry hatte ein Brett vorm Kopf da es wie du schon sagtest die Funktion zwar gibt aber ich sie nicht eingebaut habe und auch niemals benutzen werde. (Siehe Bild auf der ersten Seite)

Gruß
Scarabol
Abgeschlossen Projekte:
Schreibmaschine, Bildschirmlupe, Wings3DtoOgreMeshConverter
Watch: PureArea

PB-V: 4
WinXP
Benutzeravatar
Froggerprogger
Badmin
Beiträge: 855
Registriert: 08.09.2004 20:02

Beitrag von Froggerprogger »

Hier ist ein lauffähiges Beispiel (basierend auf Terrain.pb aus den PB-Examples).
Kopiere es einfach in den Purebasic\Examples\Sources-Ordner und starte es.
Die Mathematik wurde doch erst recht knifflig, da man Rotationen um beliebige Achsen durchführen musste. Dann aber fiel mir ein viel einfacherer Weg ein um den entsprechenden 'Zielstrahl' (pointerDir) zu berechnen, der dann entlangbeschritten wird um den Schnittpunkt mit dem Terrain zu bestimmen.

Code: Alles auswählen

; TerrainCollision.pb
;
; Example how to translate 2D-screencoordinates into a 3D-worldposition given by the first hit with the terrain
;
; If you want to use RotateCamera, you would have to change this example (setting up the up-vector again
; and recalculate some values supposed to be constant in this examples)
; 
; To start this example copy it to the PureBasic\Examples\Sources - directory to let it reference the requested resources
;
; by Froggerprogger, 01.04.07

#CameraSpeed = 5

#Deg2Rad = #Pi / 180.0
#Rad2Deg = 1.0 / #Deg2Rad

; declared as global to get them from within Screen3DRequester
Global ScreenWidth, ScreenHeight

IncludeFile "Screen3DRequester.pb"

Define.f KeyX, KeyY, MouseX, MouseY

; a structure for a 3D-float-vector
Structure Vec3D
  x.f
  y.f
  z.f
EndStructure

; some vector-math
Procedure.f Vec_ScalarProd(*v1.Vec3D, *v2.Vec3D) ; <v1, v2>
  ProcedureReturn *v1\x * *v2\x + *v1\y * *v2\y + *v1\z * *v2\z  
EndProcedure

Procedure.f Vec_Abs(*v1.Vec3D) ; |v1|
  Protected z.f
  z = Sqr(*v1\x * *v1\x + *v1\y * *v1\y + *v1\z * *v1\z)
  ProcedureReturn z
EndProcedure

Procedure Vec_ScalarMul(*v1.Vec3D, f.f, *v.Vec3D = 0) ; v1 = f * v1 
  If *v = 0
    *v = *v1
  EndIf
  *v\x = *v1\x * f
  *v\y = *v1\y * f
  *v\z = *v1\z * f
EndProcedure

Procedure Vec_VectorProd(*v1.Vec3D, *v2.Vec3D, *v.Vec3D) ; v = v1 x v2
  *v\x = *v1\y * *v2\z - *v1\z * *v2\y
  *v\y = *v1\z * *v2\x - *v1\x * *v2\z
  *v\z = *v1\x * *v2\y - *v1\y * *v2\x
EndProcedure

Procedure Vec_Add(*v1.Vec3D, *v2.Vec3D, *v.Vec3D) ; v = v1 + v2
  *v\x = *v1\x + *v2\x
  *v\y = *v1\y + *v2\y
  *v\z = *v1\z + *v2\z
EndProcedure

Procedure Vec_Sub(*v1.Vec3D, *v2.Vec3D, *v.Vec3D) ; v = v1 - v2
  *v\x = *v1\x - *v2\x
  *v\y = *v1\y - *v2\y
  *v\z = *v1\z - *v2\z
EndProcedure

Procedure Vec_Copy(*v1.Vec3D, *v.Vec3D) ; v = v1
  *v\x = *v1\x
  *v\y = *v1\y
  *v\z = *v1\z
EndProcedure

Procedure Vec_Set(x.f, y.f, z.f, *v.Vec3D) ; v = (x,y,z)
  *v\x = x
  *v\y = y
  *v\z = z
EndProcedure

playerPos.Vec3D   ; player's position (same as lookAt)
startPos.Vec3D    ; player's startposition when automoving
targetPos.Vec3D   ; player' targetposition when automoving
camOffset.Vec3D   ; the camera's offset from which to look at the player
camPos.Vec3D      ; the cameras position (playerPos + camOffset)
camDir.Vec3D      ; the direction of the camera (playerPos - camPos)
up.Vec3D          ; the camera's up-position
Vec_Set(0, 1, 0, @up)
pointerDir.Vec3D  ; the line from camPos through the mousecursor
lVector.Vec3D     ; (normalized) vector showing to the screen's left side orthogonal on camDir
tVector.Vec3D     ; (normalized) vector showing to the screen's top orthogonal on camDir
screenHit.Vec3D   ; the point where the surface given by lVector and tVector through playerPos is hit

scalar_lVectorCamDir.f        ; <lVector, camDir> (precalculated for speed-up)
scalar_tVectorCamDir.f        ; <tVector, camDir> (precalculated for speed-up)
vecprod_lVectorCamDir.Vec3D   ; lVector x camDir (precalculated for speed-up)
vecprod_tVectorCamDir.Vec3D   ; tVector x camDir (precalculated for speed-up)
factorPerPixel.f              ; the factor for lVector and tVector for each screen-pixel
searchDepth.f                 ; searching for terrain-collision up to searchDepth times the player-distance
searchStep.f                  ; searching through depth with this stepsize

Vec_Set(200, 100, 200, @camOffset)
fov.f = 80 * #Deg2Rad         ; the field of view to use
searchDepth = 4.0
searchStep  = 0.01

animationSpeed.l  ; ms for player to reach target
animationSpeed = 2000

targetReachtime.l ; time when to reach target
initialized.l = #False

If InitEngine3D()
  Add3DArchive("Data\"          , #PB_3DArchive_FileSystem)
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()

    AmbientColor(RGB(255,255,255))
    
    CreateMaterial  (0, LoadTexture(0, "Terrain_Texture.jpg"))
    AddMaterialLayer(0, LoadTexture(1, "Terrain_Detail.jpg"), 1)
    
    CreateTerrain("Terrain.png", MaterialID(0), 1, 1, 1, 4)

    CreateCamera(0, 0, 0, 100, 100)
    CameraLocate(0, 128, 25, 128)
    
    CameraFOV(0, fov)
    
    SkyDome("Clouds.jpg",10)
    
    ; load player-entity 
    LoadMesh   (0, "Robot.mesh")
    CreateMaterial(1, LoadTexture(1, "r2skin.jpg"))
    CreateEntity(0, MeshID(0), MaterialID(1), 0, 0, 0)
    ResizeEntity(0, 0.2, 0.2, 0.2)
    
    ; loads another mini-entity as 'bullet' for the animation
    CreateEntity(1, MeshID(0), MaterialID(1), 0, 0, 0)
    ResizeEntity(1, 0.1, 0.1, 0.1)
    
    ; set the initial playerposition
    playerPos\x = 100
    playerPos\z = 100
    playerPos\y = TerrainHeight(playerPos\x, playerPos\z)
     
    ; start
    Repeat
      Screen3DEvents()
            
      If ExamineKeyboard()
        
        If KeyboardPushed(#PB_Key_Left)
          KeyX = -#CameraSpeed 
        ElseIf KeyboardPushed(#PB_Key_Right)
          KeyX = #CameraSpeed 
        Else
          KeyX = 0
        EndIf
                  
        If KeyboardPushed(#PB_Key_Up)
          KeyY = -#CameraSpeed 
        ElseIf KeyboardPushed(#PB_Key_Down)
          KeyY = #CameraSpeed 
        Else
          KeyY = 0
        EndIf

      EndIf
      
      If ExamineMouse()
        MouseX + MouseDeltaX()
        If MouseX > ScreenWidth
          MouseX = ScreenWidth
        ElseIf MouseX < 0
          MouseX = 0
        EndIf
        MouseY + MouseDeltaY()
        If MouseY > ScreenHeight
          MouseY = ScreenHeight
        ElseIf MouseY < 0
          MouseY = 0
        EndIf
      EndIf
      
      If initialized = #False
        ; set up some values that do not change for a fixed cam-offset
        
        Vec_Add(@playerPos, @camOffset, @camPos) ; needed by the following
        
        ; set the cameraDir
        Vec_Sub(@playerPos, @camPos, @camDir)
        
        ; set the lVector = norm(up x camDir)
        Vec_VectorProd(@up, @camDir, @lVector)
        abs.f = Vec_Abs(@lVector)
        Vec_ScalarMul(@lVector, 1.0/abs, @lVector)
        
        ; set the tVector = camDir x lVector
        Vec_VectorProd(@camDir, @lVector, @tVector)
        abs.f = Vec_Abs(@tVector)
        Vec_ScalarMul(@tVector, 1/abs)
        
        ; get the distance of cam to entity
        dist.f = Vec_Abs(@camDir)
        
        ; set the factor per pixel
        factorPerPixel = Tan(fov/2) * dist * 2.0 / ScreenHeight
        
        ; set the horizontal/vertical ranges
        vRange.f = factorPerPixel * ScreenHeight/2.0
        hRange.f = factorPerPixel * ScreenWidth/2.0
        
        initialized = #True
      EndIf 
      
      If MouseButton(#PB_MouseButton_Left)
        
        ; get the horizontal ratio
        hRatio.f = (MouseX - ScreenWidth/2.0) / (ScreenWidth / 2.0)
        
        ; get the vertical ratio
        vRatio.f = (MouseY - ScreenHeight/2.0) / (ScreenHeight / 2.0)
        
        ; build screenHit = playerPos - hRange * hRatio * lVector - vRange * vRatio * tVector
        t1.Vec3D
        Vec_ScalarMul(@lVector, hRange * hRatio, @t1) 
        
        t2.Vec3D
        Vec_ScalarMul(@tVector, vRange * vRatio, @t2)
        
        Vec_Sub(@playerPos, @t1, @screenHit)
        Vec_Sub(@screenHit, @t2, @screenHit)
        
        ; pointerDir = screenHit - camPos
        Vec_Sub(@screenHit, @camPos, @pointerDir)
        
        ; set pointerDir's length to dist
        abs.f = Vec_Abs(@pointerDir)
        Vec_ScalarMul(@pointerDir, dist/abs)
        
        ; scan the line until hit terrain
        t.f = 0
        While t < searchDepth
          ; get x and y along line: camPos + t * pointerDir
          x.f = camPos\x + t*pointerDir\x
          y.f = camPos\y + t*pointerDir\y
          z.f = camPos\z + t*pointerDir\z
           
          If y < TerrainHeight(x, z) Or y < 0
            targetPos\x = x - searchStep * pointerDir\x
            targetPos\y = y - searchStep * pointerDir\y
            targetPos\z = z - searchStep * pointerDir\z
            
            Vec_Copy(@playerPos, @startPos)
            targetReachtime = ElapsedMilliseconds() + animationSpeed
            
            Break
          EndIf
          t + searchStep
        Wend
      EndIf

      If targetReachtime > ElapsedMilliseconds()
        playerPos\x = targetPos\x - (targetReachtime - ElapsedMilliseconds()) * (targetPos\x - startPos\x) / animationSpeed
        playerPos\z = targetPos\z - (targetReachtime - ElapsedMilliseconds()) * (targetPos\z - startPos\z) / animationSpeed
      Else
        playerPos\x + KeyX
        playerPos\z + KeyY
      EndIf
      playerPos\y = TerrainHeight(playerPos\x, playerPos\z)
      EntityLocate(0, playerPos\x, playerPos\y, playerPos\z)
      
      ; place the camera relative to the entity and look at player
      Vec_Add(@playerPos, @camOffset, @camPos)
      CameraLocate(0, camPos\x, camPos\y, camPos\z)
      CameraLookAt(0, playerPos\x, playerPos\y, playerPos\z)
       
      ;EntityLocate(1, camPos\x + animationVal * pointerDir\x, camPos\y + animationVal * pointerDir\y, camPos\z + animationVal * pointerDir\z)
      EntityLocate(1, targetPos\x, TerrainHeight(targetPos\x, targetPos\z), targetPos\z)

      RenderWorld()
      Screen3DStats()
       
      StartDrawing(ScreenOutput())
        Circle(MouseX, MouseY, 5, $0000FF)
        DrawingMode(#PB_2DDrawing_Transparent)
        DrawText(0, 0, "hAngle : " + StrF(hAngle) + "  vAngle : " + StrF(vAngle, 2))
        DrawText(0, 20, "camPos (x y z): " + StrF(camPos\x) + "/" + StrF(camPos\y) + "/" + StrF(camPos\z))
        DrawText(0, 40, "camDir (x y z): " + StrF(camDir\x) + "/" + StrF(camDir\y) + "/" + StrF(camDir\z))
        DrawText(0, 60, "lVector (x y z): " + StrF(lVector\x) + "/" + StrF(lVector\y) + "/" + StrF(lVector\z))
        DrawText(0, 80, "tVector (x y z): " + StrF(tVector\x) + "/" + StrF(tVector\y) + "/" + StrF(tVector\z))
        DrawText(0, 100, "pointerDir (x y z): " + StrF(pointerDir\x) + "/" + StrF(pointerDir\y) + "/" + StrF(pointerDir\z))
        DrawText(0, 120, "t1 (x y z): " + StrF(t1\x) + "/" + StrF(t1\y) + "/" + StrF(t1\z))
        DrawText(0, 140, "screenHit (x y z): " + StrF(screenHit\x) + "/" + StrF(screenHit\y) + "/" + StrF(screenHit\z))
        DrawText(0, 160, "hRange/vRange: " + StrF(hRange) + "/" + StrF(vRange, 2))
        DrawText(0, 180, "EntityPos (x y z) : " + StrF(EntityX(1)) + "/" + StrF(EntityY(1)) + "/" + StrF(EntityZ(1)))
      StopDrawing()
      
      
      FlipBuffers()
    Until KeyboardPushed(#PB_Key_Escape) Or Quit = 1
  EndIf
    
Else
  MessageRequester("Error", "The 3D Engine can't be initialized",0)
EndIf
  
End
!UD2
Antworten