Seite 4 von 5

Verfasst: 25.10.2007 14:52
von johnnysnet
Ich hatte PureBasic schon einmal über einen längeren Zeitraum hinweg verwendet und bin damit soweit auch gut klar gekommen. Nun beschäftige ich mich aber erstmal mehr mit GLBasic, weil ich damit für eine spezielle Plattform compilieren kann, die mich interessiert.

Also Strukturen und Listen habe ich in PureBasic natürlich schon verwendet aber Pointer nicht. Nun, dann müsste ich mich wohl erstmal mit Pointern auseinandersetzen, vielleicht verstehe ich dann mehr. Allerdings verwendest du in deinem Code unglaublich viele Variablen und da muss man sich erstmal eine Übersicht verschaffen.

Verfasst: 28.10.2007 21:35
von johnnysnet
Inzwischen habe ich mich dazu entschieden lieber einen eigenen Code, der stark vereinfacht ist, zu schreiben. Diesen hier für GLBasic umzusetzen ist mir doch zu mühevoll. Pointer gibt es dort nämlich nicht. Nun hätte ich aber eine Bitte. Und zwar gibt es doch folgende allgemein bekannte einfache Formel, um 3D-Koordinaten in 2D umzurechnen:

Code: Alles auswählen

x2D = ScreenW / 2 + x3D * Zoom / z3D
y2D = ScreenH / 2 + y3D * Zoom / z3D
Das funktioniert auch alles schon wunderbar aber nun gibt es ein Problem, wenn sich ein Punkt hinter dem Betrachtungspunkt befindet, wenn Z also kleiner 0 ist. Dann kehrt sich die ganze Berechnung um und der Punkt wandert wieder in das Bild hinein.
(Um kurz am Rande noch etwas Wichtiges zu erwähnen: ich mache mir es ganz einfach, indem ich keine virtuelle Kamera erstelle, wo ich ständig aus einem anderen Blickwinkel berechnen muss, sondern die Berechnung für den Blickwinkel bleibt immer die selbe und ich bewege oder rotiere nur die gesamte Welt! Ich rotiere dann entsprechend, so dass es so aussieht, als wenn die Kamera schwenken würde.)
Könntest du mir verraten, wie ich nun einen Punkt "hinter dem Bildschirm" berechnen könnte? Ich habe schon viel rumprobiert aber es klappt einfach nicht.
Nun, warum berechnen, wenn "hinter dem Bildschirm"? Ganz einfach: ich definiere auch Linienverbindungen zwischen den Punkten meines Drahtgittermodells und längere Linien, die entlang der Z-Achse verlaufen, müssen solange korrekt dargestellt werden, bis sich beide Punkte der Linie hinter dem Betrachter befinden. Deshalb brauche ich das. Momentan habe ich es halt so gemacht, dass der Punkt dann eben nicht mehr berechnet wird und nur Linien dargestellt werden, von denen beide Punkte noch im Bereich Z>0 sind.
Könntest du mir da helfen, Nic?

Verfasst: 28.10.2007 22:08
von NicTheQuick
Das ist eigentlich eine ganz einfache Rechnung mit Vektoren. Ich habe aber
leider gerade keine Zeit, sie dir zu berechnen. Vielleicht morgen dann. Es
geht einfach nur um den Schnittpunkt einer Geraden mit einer Ebene.

Also bis morgen dann wahrscheinlich. :)

Verfasst: 28.10.2007 22:13
von johnnysnet
Danke, das wäre sehr freundlich. Eilt ja auch nicht so.

Verfasst: 29.10.2007 10:25
von NicTheQuick
Hier wäre der richtige Code:

Code: Alles auswählen

Structure V3D
  x.d
  y.d
  z.d
EndStructure

Procedure GetEndpoint(*p1.V3D, *p2.V3D, *p_out.V3D)
  Protected u.V3D, mue.d
  
  u\x = *p2\x - *p1\x
  u\y = *p2\y - *p1\y
  u\z = *p2\z - *p1\z
  
  If u\z = 0.0
    mue = 0.0
  Else
    mue = - *p1\z / u\z
  EndIf
  
  *p_out\x = *p1\x + mue * u\x
  *p_out\y = *p1\y + mue * u\y
  *p_out\z = *p1\z + mue * u\z
EndProcedure

Define p1.V3D, p2.V3D
p1\x = 1
p1\y = 2
p1\z = 3
p2\x = -3
p2\y = -2
p2\z = -1

;Da bei p2 Z im Negativen liegt, müssen wir p2 neu berechnen
GetEndpoint(@p1, @p2, @p2)

;Neue Koordinaten von Z
Debug p2\x
Debug p2\y
Debug p2\z

Verfasst: 29.10.2007 11:30
von johnnysnet
Der Code erschlägt mich ja glatt, trotzdem besten Dank für deine Bemühung. Naja im Vergleich jetzt mal zu der Formel, wie ich sie gegeben habe, ist dies hier echt umständlich.
Nun gut, ich kann nur versuchen, es irgendwie in meinen Code zu übertragen. Aber ich bin überzeugt, es ginge sehr viel einfacher. Denn es wiederspricht irgendwie sehr meiner Logik, dass so viel mehr Rechenaufwand nötig ist, nur weil Z negativ wird.

Möglicherweise aber konnte ich nicht genau rüberbringen, was ich gerne wollte oder das Problem liegt ganz einfach daran, dass du in PureBasic denkst und ich in GLBasic. Wenn ich meinen Code mal gebe, würde das sicher wenig nützen, denn der würde in PureBasic ja nicht laufen. Müsste ich vorher umschreiben...

Verfasst: 29.10.2007 14:42
von Scarabol
Dein Problem ist einfach das du die Ergebnisse filtern musst, so oder so musst du erst alle Berechnen und dann filtern, welche sind außerhalb des Bildschirms...

Dieses Filtern kann man nicht einfacher machen...

Gruß
Scarabol

Verfasst: 29.10.2007 15:17
von NicTheQuick
Was ist daran umständlich. Einfacher gehts nicht. Du könntest natürlich die
Procedure ganz weglassen, wenn sie dich stört. Sie ist nur praktisch, weil ich
temporäre Variablen brauche, und dafür kann man die lokalen ganz gut
benutzen.

Verfasst: 29.10.2007 21:30
von johnnysnet
Um vielleicht doch ein eventuelles Missverständnis auszuschließen, habe ich nun meinen Code für PureBasic umgeschrieben:

Code: Alles auswählen

Structure Dots3D
	dx.f
	dy.f
	dz.f
EndStructure

Structure Dots2D
	da.l
	dx.l
	dy.l
EndStructure

Structure Lines
	d1.l
	d2.l
	cl.l
EndStructure

Global Dim Dots3D.Dots3D(0)
Global Dim Dots2D.Dots2D(0)
Global Dim Lines.Lines(0)
Global CamDist, ScreenW, ScreenH, Zoom, NrOfDots, NrOfLines

Procedure Convert3Dto2D()
	For i = 0 To NrOfDots
		Dots2D(i)\da = 0
		If Dots3D(i)\dz - CamDist > -200
			Dots2D(i)\da = 1
			Dots2D(i)\dx = ScreenW / 2 + Dots3D(i)\dx * Zoom / (-Dots3D(i)\dz - CamDist)
			Dots2D(i)\dy = ScreenH / 2 + Dots3D(i)\dy * Zoom / (-Dots3D(i)\dz - CamDist)
		Else
			;Hier muss die Formel für die Berechnung des 2D Punktes hinter dem Betrachter rein!
		EndIf
	Next
EndProcedure

Procedure DrawObject(Mode)
	StartDrawing(ScreenOutput())
	If Mode = 1 Or Mode = 3
		For i = 0 To NrOfLines
			If Dots2D(Lines(i)\d1)\da And Dots2D(Lines(i)\d2)\da
				LineXY(Dots2D(Lines(i)\d1)\dx, Dots2D(Lines(i)\d1)\dy, Dots2D(Lines(i)\d2)\dx, Dots2D(Lines(i)\d2)\dy, Lines(i)\cl)
			EndIf
		Next
	EndIf
	If Mode > 1
		For i = 0 To NrOfDots
			If Dots2D(i)\da
				If Dots2D(i)\dx > 0 And Dots2D(i)\dx < ScreenW And Dots2D(i)\dy > 0 And Dots2D(i)\dy < ScreenH
					Plot(Dots2D(i)\dx, Dots2D(i)\dy, $ffffff)
				EndIf
			EndIf
		Next
	EndIf
	StopDrawing()
EndProcedure

Procedure MoveObject(mx, my, mz)
	For i = 0 To NrOfDots
		With Dots3D(i)
			\dx + mx
			\dy + my
			\dz + mz
		EndWith
	Next
EndProcedure

Procedure RotateObject(rx, ry, rz)
	Protected dist, angle
	For i = 0 To NrOfDots
; 		If rx
; 			dist = Sqr(Pow(Dots3D(i)\dy, 2) + Pow(Dots3D(i)\dz + CamDist, 2))
; 			angle = ATan(Dots3D(i)\dy, Dots3D(i)\dz + CamDist)
; 			angle - rx
; 			Dots3D(i)\dy = Sin(angle) * dist
; 			Dots3D(i)\dz = Cos(angle) * dist - CamDist
; 		EndIf
; 
; 		If ry
; 			dist = Sqr(Pow(Dots3D(i)\dx, 2) + Pow(Dots3D(i)\dz + CamDist, 2))
; 			angle = ATan(Dots3D(i)\dz + CamDist, Dots3D(i)\dx)
; 			EndIf
; 			angle + ry
; 			Dots3D(i)\dx = Sin(angle) * dist
; 			Dots3D(i)\dz = Cos(angle) * dist - CamDist
; 		EndIf
; 
; 		If rz
; 			dist = Sqr(Pow(Dots3D(i)\dx, 2) + Pow(Dots3D(i)\dy, 2))
; 			If Dots3D(i)\dx < Dots3D(i)\dy
; 			angle = ATan(Dots3D(i)\dx, Dots3D(i)\dy)
; 			angle + rz
; 			Dots3D(i)\dx = Sin(angle) * dist
; 			Dots3D(i)\dy = Cos(angle) * dist
; 		EndIf
	Next
EndProcedure

;Set initial values
ScreenW = 640
ScreenH = 480
CamDist = 100
Zoom = 512

;Create object
NrOfDots = 120
NrOfLines = 21
ReDim Dots3D.Dots3D(NrOfDots)
ReDim Dots2D.Dots2D(NrOfDots)
ReDim Lines.Lines(NrOfLines)
i = 0
For z = -50 To 50 Step 10
	For x = -50 To 50 Step 10
		With Dots3D(i)
			\dx = x
			\dy = -20
			\dz = z
		EndWith
		i + 1
	Next
Next
For i = 0 To 10
	Lines(i)\d1 = i
	Lines(i)\d2 = i + 1 * 110
	Lines(i)\cl = $ff0000
	Lines(i + 11)\d1 = i * 11
	Lines(i + 11)\d2 = i * 11 + 10
	Lines(i + 11)\cl = $ff0000
Next

InitKeyboard()
InitSprite()

If OpenWindow(0, 0, 0, ScreenW, ScreenH, "3D Wireframe")
	If OpenWindowedScreen(WindowID(0), 0, 0, ScreenW, ScreenH, 0, 0, 0)

		Repeat
			Event = WindowEvent()
			ClearScreen(0)
			ExamineKeyboard()
			If KeyboardPushed(#PB_Key_W): RotateObject(1, 0, 0): EndIf
			If KeyboardPushed(#PB_Key_S): RotateObject(-1, 0, 0): EndIf
			If KeyboardPushed(#PB_Key_Left): MoveObject(-1, 0, 0): EndIf
			If KeyboardPushed(#PB_Key_Right): MoveObject(1, 0, 0): EndIf
			If KeyboardPushed(#PB_Key_LeftControl) Or KeyboardPushed(#PB_Key_RightControl)
				If KeyboardPushed(#PB_Key_A): RotateObject(0, 0, -1): EndIf
				If KeyboardPushed(#PB_Key_D): RotateObject(0, 0, 1): EndIf
				If KeyboardPushed(#PB_Key_Up): MoveObject(0, -1, 0): EndIf
				If KeyboardPushed(#PB_Key_Down): MoveObject(0, 1, 0): EndIf
			Else
				If KeyboardPushed(#PB_Key_A): RotateObject(0, -1, 0): EndIf
				If KeyboardPushed(#PB_Key_D): RotateObject(0, 1, 0): EndIf
				If KeyboardPushed(#PB_Key_Up): MoveObject(0, 0, -1): EndIf
				If KeyboardPushed(#PB_Key_Down): MoveObject(0, 0, 1): EndIf
			EndIf
			Convert3Dto2D()
			DrawObject(1)
			FlipBuffers()
		Until KeyboardPushed(#PB_Key_Escape)
		
	EndIf
EndIf
Leider funktionieren die Rotationen hier in PureBasic nicht, da die Funktionen ATAN(), SIN() und COS() anders arbeiten als bei GLBasic. Da konnte der Winkel im DEG-Format angegeben werden und ATAN() akzeptierte die Angabe des X- und Y-Anstiegs. Ich habe zwar schon probiert in Radiant umzurechnen aber das klappte dann alles vorne und hinten nicht.

Aber egal, es geht ja hier nur mal darum, wie ich das gemacht habe. Wie schon erwähnt, verschiebe ich immer die 3D-Punkte und erreiche damit denselben Effekt.
So, und jetzt habe ich bei obigem Code die Stelle kommentiert, wo die Formel rein müsste für Z<=0. Da muss jetzt nichts gefiltert werden, es geht immer nur um die Berechnung eines einzigen 2D-Punktes bei jedem Schleifendurchlauf. Es muss ganz einfach sein - irgendwie muss die Formal nur umgekehrt werden aber ich weiß eben nicht, wie. Momentan werden eben die Linien einfach ausgeblendet, so bald einer der beiden Endpunkte nicht mehr korrekt berechnet werden kann.

Verfasst: 24.01.2008 22:34
von Scarabol
Hi Leute,

hier noch eine Ergänzung:

Code: Alles auswählen

Procedure Camera_LookAt(*Camera.Camera, x, y, z)
  Protected temp.V3D, dx, dy, dz, d.f
  temp\x = *Camera\p\x
  temp\y = *Camera\p\y
  temp\z = *Camera\p\z
  dx = *Camera\p\x-x
  dy = *Camera\p\y-y
  dz = *Camera\p\z-z
  d = Sqr(dx*dx+dy*dy+dz*dz)
  ; links rechts
  temp\y = ATan(dx/dz)*#rad2deg
  ; hoch runter
  temp\x = ASin(dy/d)*#rad2deg
  Camera_Set(*Camera, 0, temp)
EndProcedure
Gruß
Scarabol