Page 1 sur 3

3D vers 2D avec perspective

Publié : jeu. 09/déc./2010 21:47
par Le Soldat Inconnu
Salut,

Je reprend un vieux code que j'essaie d'optimiser pour faire un programme qui affiche une carte du ciel.

Le but de ce code est de passer d'une position dans un repère 3D en rotation vers un repère 2D (votre écran) en ajoutant un effet de perspective

Donc voici un code qui affiche un repère XYZ en rotation avec un effet de perspective.

Si vous appuyer sur "entrée" vous passer en rotation manuelle
Il faut alors appuyer sur X, Y, ou Z pour faire tourner le repère sur l'axe X, Y ou Z
  • La fonction XYZ_Rotate gère uniquement la rotation entre 2 repère 3D
  • La fonction XYFrom3D gère la positon d'un point du repère 3D vers l'écran en y ajoutant la perspective.
Les calculs peuvent sembler barbare mais c'est de la mécanique de base : Le changement de repère. J'ai simplement retranscris mes cours.

Code : Tout sélectionner

; Auteur : Le Soldat Inconnu
; Version de PB :  4.5
;
; Explication du programme :
; Exemple qui montre comment dessiner un repère XYZ en 3D
; Touche d'utilisation :
; Enter : Passage de la rotation automatique à manuelle
; X : En mode de rotation manuelle, change l'angle sur l'axe X
; Y : En mode de rotation manuelle, change l'angle sur l'axe Y
; Z : En mode de rotation manuelle, change l'angle sur l'axe Z


Structure Point3D
	x.d
	y.d
	z.d
EndStructure
Structure Point3DP
	x.d
	y.d
	z.d
	p.d
EndStructure

#Angle_Resolution = 100
Global Dim Calculation_Cos.d(360 * #Angle_Resolution * 2)
Global Dim Calculation_Sin.d(360 * #Angle_Resolution * 2)
For i = -360 * #Angle_Resolution To 360 * #Angle_Resolution
	Calculation_Cos(i + 360 * #Angle_Resolution) = Cos(i * #PI / (180 * #Angle_Resolution))
	Calculation_Sin(i + 360 * #Angle_Resolution) = Sin(i * #PI / (180 * #Angle_Resolution))
Next

Procedure XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3D)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  Protected Calcul.Point3D, Sin.d, Cos.d
  
	; Rotation sur l'axe Z
	If Angle_Z_Axis
		; Cos = Cos(Angle_Z_Axis)
		; Sin = Sin(Angle_Z_Axis)
		Angle.i = Angle_Z_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		Calcul\x = *Origine\x * Cos - *Origine\y * Sin
		Calcul\y = *Origine\x * Sin + *Origine\y * Cos
	Else
		Calcul\x = *Origine\x
		Calcul\y = *Origine\y
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(Calcul\y) + " , " + StrD(*Origine\z)
  
  ; Rotation sur l'axe X
	If Angle_X_Axis
		; Cos = Cos(Angle_X_Axis)
		; Sin = Sin(Angle_X_Axis)
		Angle.i = Angle_X_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\y = Calcul\y * Cos - *Origine\z * Sin
		Calcul\z = Calcul\y * Sin + *Origine\z * Cos
	Else
		*Final\y = Calcul\y
		Calcul\z = *Origine\z
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(*Final\y) + " , " + StrD(Calcul\z)
  
  ; Rotation sur l'axe Y
	If Angle_Y_Axis
		; Cos = Cos(Angle_Y_Axis)
		; Sin = Sin(Angle_Y_Axis)
		Angle.i = Angle_Y_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\z = Calcul\z * Cos - Calcul\x * Sin
		*Final\x = Calcul\z * Sin + Calcul\x * Cos
	Else
		*Final\z = Calcul\z
		*Final\x = Calcul\x
	EndIf
  ; Debug StrD(*Final\x) + " , " + StrD(*Final\y) + " , " + StrD(*Final\z)
EndProcedure

Procedure XYFrom3D(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, Perspective.q, *Final.Point3DP)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3DP)
	
	*Final\p = 1 + Abs(*Final\z) / Perspective
  If *Final\z < 0
    *Final\p = 1 / *Final\p
	EndIf
	*Final\x * *Final\p
	*Final\y * *Final\p
	
EndProcedure


If InitSprite() = 0
  End
EndIf

If OpenWindow(0, 0, 0, 500, 500, "Le Soldat Inconnu", #PB_Window_ScreenCentered | #PB_Window_SystemMenu) = 0
  End
EndIf

If OpenWindowedScreen(WindowID(0), 0, 0, 500, 500, 1, 0, 0) = 0
  End
EndIf

AngleX.d = 0
AngleY.d = 0
AngleZ.d = 0

Rotation_Auto = 1

FontID = LoadFont(0, "Verdana", 8, #PB_Font_HighQuality)

Repeat

  ClearScreen($FFFFFF)
  
  ; On change l'angle d'inclinaison du texte (en fait, on change les angles entre le repère écran et le repère du texte)
  ; Vous pouver mettre les paramètres que vous voulez pour changer la rotation du repère 3D
	If Rotation_Auto
		AngleX + 0.005
		AngleY + 0.003
		AngleZ + 0.002
	EndIf
	
	If AngleX > 2 * #PI
		AngleX - 2 * #PI
	ElseIf AngleX < 0
		AngleX + 2 * #PI
	EndIf
	
	If AngleY > 2 * #PI
		AngleY - 2 * #PI
	ElseIf AngleY < 0
		AngleY + 2 * #PI
	EndIf
	
	If AngleZ > 2 * #PI
		AngleZ - 2 * #PI
	ElseIf AngleZ < 0
		AngleZ + 2 * #PI
	EndIf
	
	X_Origine.Point3D
	X_Origine\x = 200
	Y_Origine.Point3D
	Y_Origine\y = 200
	Z_Origine.Point3D
	Z_Origine\z = 200
  
  StartDrawing(ScreenOutput())
    DrawingFont(FontID)
    DrawingMode(#PB_2DDrawing_Transparent)
		
    ; On affiche le repère
    ; la ligne représentant l'axe X en vert
    XYFrom3D(@X_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    LineXY(250, 250, 250 + Coord\x, 250 - Coord\y, RGB(0, 255, 0)) ; on trace une ligne verte à partir du centre de l'image vers les coordonnées calculées
    Circle(250 + Coord\x, 250 - Coord\y, 5 * Coord\p, RGB(0, 255, 0)) ; on trace un cerle sur le bout de la ligne. La variable Coord\p permet de prendre en compte la perspective sur une forme de taille définie, ici un cercle mais également une image par exemple.
    DrawText(255 + Coord\x, 255 - Coord\y, "X", 0) ;On affiche le label de l'axe
    
    ; la ligne représentant l'axe Y en rouge
    XYFrom3D(@Y_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    LineXY(250, 250, 250 + Coord\x, 250 - Coord\y, RGB(255, 0, 0))
    Circle(250 + Coord\x, 250 - Coord\y, 5 * Coord\p, RGB(255, 0, 0))
    DrawText(255 + Coord\x, 255 - Coord\y, "Y", 0)
    
    ; la ligne représentant l'axe Z en bleu
    XYFrom3D(@Z_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    LineXY(250, 250, 250 + Coord\x, 250 - Coord\y, RGB(0, 0, 255))
    Circle(250 + Coord\x, 250 - Coord\y, 5 * Coord\p, RGB(0, 0, 255))
    DrawText(255 + Coord\x, 255 - Coord\y, "Z", 0)
    
  StopDrawing()
  
  FlipBuffers()

  Repeat
		Event = WindowEvent()
		
		If Event = #WM_KEYUP
			Select EventwParam()
				Case 'X'
					AngleX + #PI/6
				Case 'Y'
					AngleY + #PI/6
				Case 'Z'
					AngleZ + #PI/6
				Case 13
					Rotation_Auto = 1 - Rotation_Auto
					AngleX = 0
					AngleY = 0
					AngleZ = 0
		EndSelect
		EndIf
		
		If Event = #PB_Event_CloseWindow
			End
		EndIf
		
	Until Event = 0
	
ForEver

Si vous avez des idées pour optimiser encore la fonction XYZ_Rotate(), je suis preneur :D

Voici un code pour tester la vitesse. Il regarde en combien de temps je calcul la position de 120000 points (vue que j'ai chopé la position de 118000 étoiles). je suis tombé à 18 ms, ça donne 55 images par seconde sans affichage sur l'écran, alors si il est possible d'avoir mieux... :mrgreen:

Code : Tout sélectionner

Structure Point3D
	x.d
	y.d
	z.d
EndStructure

Procedure XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3D)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  Protected Calcul.Point3D, Sin.d, Cos.d
  
	; Rotation sur l'axe Z
	If Angle_Z_Axis
		Cos = Cos(Angle_Z_Axis)
		Sin = Sin(Angle_Z_Axis)
		Calcul\x = *Origine\x * Cos - *Origine\y * Sin
		Calcul\y = *Origine\x * Sin + *Origine\y * Cos
	Else
		Calcul\x = *Origine\x
		Calcul\y = *Origine\y
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(Calcul\y) + " , " + StrD(*Origine\z)
  
  ; Rotation sur l'axe X
	If Angle_X_Axis
		Cos = Cos(Angle_X_Axis)
		Sin = Sin(Angle_X_Axis)
		*Final\y = Calcul\y * Cos - *Origine\z * Sin
		Calcul\z = Calcul\y * Sin + *Origine\z * Cos
	Else
		*Final\y = Calcul\y
		Calcul\z = *Origine\z
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(*Final\y) + " , " + StrD(Calcul\z)
  
  ; Rotation sur l'axe Y
	If Angle_Y_Axis
		Cos = Cos(Angle_Y_Axis)
		Sin = Sin(Angle_Y_Axis)
		*Final\z = Calcul\z * Cos - Calcul\x * Sin
		*Final\x = Calcul\z * Sin + Calcul\x * Cos
	Else
		*Final\z = Calcul\z
		*Final\x = Calcul\x
	EndIf
  ; Debug StrD(*Final\x) + " , " + StrD(*Final\y) + " , " + StrD(*Final\z)
EndProcedure

Origine.Point3D
Origine\x = 10
Origine\y = 10
Origine\z = 10

Final.Point3D

Temps1 = ElapsedMilliseconds()
For nn = 1 To 30
	For n = 1 To 120000
		XYZ_Rotate(@Origine, 0.1, 0.1, 0, @Final)
	Next
Next
Temps2 = ElapsedMilliseconds()

MessageRequester("Temps", Str((Temps2 - Temps1)/30) + " ms")

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 0:10
par Backup
autour de 600 ms sur mon petit NC10 (processeur ATOM / sans carte 3D) :)

impressionnant ! :)

ps: premier code tres fluide :)

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 1:17
par Warkering
189 ms! :mrgreen:
Je dois dire que c'est cool! :o

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 10:13
par kelebrindae
25 ms sur mon AMD 2.3 Ghz.

Super pratique pour générer les 6 textures d'un skycube à partir d'une liste d'étoiles: on recalcule les coordonnées de chaque étoile par rapport à la position du joueur, on les projette dans les 6 directions voulues (haut, bas, devant, derrière, gauche, droite), on sauvegarde les images et boum: voyage spatial instantané => on peut regarder le ciel tel qu'on le verrait depuis Alpha du Centaure. :D

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 11:06
par kernadec
Bonjour Soldat inconnu
merci,
je peux pas rivaliser avec les 25ms de kelebrindae?

Compaq CQ71 dual-core T4300 2.10ghz - 4Go - Seven 64 Familial-premium
197ms seulement!!!!

j'avais trouvé ici une biblio 3d Sympa
AndyX a mis des bibliothèque.pbi "test.zip" ici : http://www.purebasic.fr/english/viewtop ... 12&t=25845&

Cordialement

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 13:11
par Warkering
La vache! Comment tu peux pogner du 25 ms? 8O

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 14:06
par kelebrindae
En enlevant le debugger ?

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 14:17
par kernadec
merci, 28ms.... aie!!! les quiches c'est ça!!!!! :mrgreen: :mrgreen: :mrgreen:

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 14:47
par Warkering
18 ms! C'est cool! :mrgreen:

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 18:18
par venom
14 ms pour ma part






@++

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 18:47
par Le Soldat Inconnu
Ce qui ralentit le calcul, ce sont les cos et sin.
il faut que j'essaie de passer par des tables précalculées pour voir

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 19:28
par Le Soldat Inconnu
et voilà, je suis tombé à 9ms pour le calcul de 120000 points.
J'utilise un précalcul sur les cos et sin

Code : Tout sélectionner

Structure Point3D
	x.d
	y.d
	z.d
EndStructure

#Angle_Resolution = 100
Global Dim Calculation_Cos.d(360 * #Angle_Resolution * 2)
Global Dim Calculation_Sin.d(360 * #Angle_Resolution * 2)
For i = -360 * #Angle_Resolution To 360 * #Angle_Resolution
	Calculation_Cos(i + 360 * #Angle_Resolution) = Cos(i * #PI / (180 * #Angle_Resolution))
	Calculation_Sin(i + 360 * #Angle_Resolution) = Sin(i * #PI / (180 * #Angle_Resolution))
Next

Procedure XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3D)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  Protected Calcul.Point3D, Sin.d, Cos.d
  
	; Rotation sur l'axe Z
	If Angle_Z_Axis
		; Cos = Cos(Angle_Z_Axis)
		; Sin = Sin(Angle_Z_Axis)
		Angle.i = Angle_Z_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		Calcul\x = *Origine\x * Cos - *Origine\y * Sin
		Calcul\y = *Origine\x * Sin + *Origine\y * Cos
	Else
		Calcul\x = *Origine\x
		Calcul\y = *Origine\y
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(Calcul\y) + " , " + StrD(*Origine\z)
  
  ; Rotation sur l'axe X
	If Angle_X_Axis
		; Cos = Cos(Angle_X_Axis)
		; Sin = Sin(Angle_X_Axis)
		Angle.i = Angle_X_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\y = Calcul\y * Cos - *Origine\z * Sin
		Calcul\z = Calcul\y * Sin + *Origine\z * Cos
	Else
		*Final\y = Calcul\y
		Calcul\z = *Origine\z
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(*Final\y) + " , " + StrD(Calcul\z)
  
  ; Rotation sur l'axe Y
	If Angle_Y_Axis
		; Cos = Cos(Angle_Y_Axis)
		; Sin = Sin(Angle_Y_Axis)
		Angle.i = Angle_Y_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\z = Calcul\z * Cos - Calcul\x * Sin
		*Final\x = Calcul\z * Sin + Calcul\x * Cos
	Else
		*Final\z = Calcul\z
		*Final\x = Calcul\x
	EndIf
  ; Debug StrD(*Final\x) + " , " + StrD(*Final\y) + " , " + StrD(*Final\z)
EndProcedure

Origine.Point3D
Origine\x = 10
Origine\y = 10
Origine\z = 10

Final.Point3D

Temps1 = ElapsedMilliseconds()
For nn = 1 To 40
	For n = 1 To 120000
		XYZ_Rotate(@Origine, 0.1, 0.1, 0, @Final)
	Next
Next
Temps2 = ElapsedMilliseconds()

MessageRequester("Temps", Str((Temps2 - Temps1)/40) + " ms")

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 22:28
par venom
en effet la différence 8O Bravo.
Je suis tomber a 4ms 8)






@++

Re: 3D vers 2D avec perspective

Publié : ven. 10/déc./2010 23:57
par Warkering
Moi 5! 8O :D

Re: 3D vers 2D avec perspective

Publié : sam. 11/déc./2010 15:26
par Le Soldat Inconnu
Version optimiser du repère 3D

Avec cette optimisation, il faut faire attention a toujours donner un angle de rotation compris entre 0 et 2 PI

Code : Tout sélectionner

; Auteur : Le Soldat Inconnu
; Version de PB :  4.5
;
; Explication du programme :
; Exemple qui montre comment dessiner un repère XYZ en 3D

Structure Point3D
	x.d
	y.d
	z.d
EndStructure
Structure Point3DP
	x.d
	y.d
	z.d
	p.d
EndStructure

#Angle_Resolution = 100
Global Dim Calculation_Cos.d(360 * #Angle_Resolution * 2)
Global Dim Calculation_Sin.d(360 * #Angle_Resolution * 2)
For i = -360 * #Angle_Resolution To 360 * #Angle_Resolution
	Calculation_Cos(i + 360 * #Angle_Resolution) = Cos(i * #PI / (180 * #Angle_Resolution))
	Calculation_Sin(i + 360 * #Angle_Resolution) = Sin(i * #PI / (180 * #Angle_Resolution))
Next

Procedure XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3D)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  Protected Calcul.Point3D, Sin.d, Cos.d
  
	; Rotation sur l'axe Z
	If Angle_Z_Axis
		; Cos = Cos(Angle_Z_Axis)
		; Sin = Sin(Angle_Z_Axis)
		Angle.i = Angle_Z_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		Calcul\x = *Origine\x * Cos - *Origine\y * Sin
		Calcul\y = *Origine\x * Sin + *Origine\y * Cos
	Else
		Calcul\x = *Origine\x
		Calcul\y = *Origine\y
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(Calcul\y) + " , " + StrD(*Origine\z)
  
  ; Rotation sur l'axe X
	If Angle_X_Axis
		; Cos = Cos(Angle_X_Axis)
		; Sin = Sin(Angle_X_Axis)
		Angle.i = Angle_X_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\y = Calcul\y * Cos - *Origine\z * Sin
		Calcul\z = Calcul\y * Sin + *Origine\z * Cos
	Else
		*Final\y = Calcul\y
		Calcul\z = *Origine\z
	EndIf
  ; Debug StrD(Calcul\x) + " , " + StrD(*Final\y) + " , " + StrD(Calcul\z)
  
  ; Rotation sur l'axe Y
	If Angle_Y_Axis
		; Cos = Cos(Angle_Y_Axis)
		; Sin = Sin(Angle_Y_Axis)
		Angle.i = Angle_Y_Axis * #Angle_Resolution * 180 / #PI + 360 * #Angle_Resolution
		Cos = Calculation_Cos(Angle)
		Sin = Calculation_Sin(Angle)
		*Final\z = Calcul\z * Cos - Calcul\x * Sin
		*Final\x = Calcul\z * Sin + Calcul\x * Cos
	Else
		*Final\z = Calcul\z
		*Final\x = Calcul\x
	EndIf
  ; Debug StrD(*Final\x) + " , " + StrD(*Final\y) + " , " + StrD(*Final\z)
EndProcedure

Procedure XYFrom3D(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, Perspective.q, *Final.Point3DP)
  ; Angle_X_Axis, ay, Angle_Z_Axis : angle de rotation du point sur l'Angle_X_Axise x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
  
  XYZ_Rotate(*Origine.Point3D, Angle_X_Axis.d, Angle_Y_Axis.d, Angle_Z_Axis.d, *Final.Point3DP)
	
	*Final\p = 1 + Abs(*Final\z) / Perspective
  If *Final\z < 0
    *Final\p = 1 / *Final\p
	EndIf
	*Final\x * *Final\p
	*Final\y * *Final\p
	
EndProcedure


If InitSprite() = 0
  End
EndIf

If OpenWindow(0, 0, 0, 500, 500, "Le Soldat Inconnu", #PB_Window_ScreenCentered | #PB_Window_SystemMenu) = 0
  End
EndIf

If OpenWindowedScreen(WindowID(0), 0, 0, 500, 500, 1, 0, 0) = 0
  End
EndIf

AngleX.f = 0
AngleY.f = 0
AngleZ.f = 0

FontID = LoadFont(0, "tahoma", 8, #PB_Font_HighQuality)

Repeat

  ClearScreen($FFFFFF)
  
  ; On change l'angle d'inclinaison du texte (en fait, on change les angles entre le repère écran et le repère du texte)
  ; Vous pouver mettre les paramètres que vous voulez pour changer la rotation du repère 3D
  AngleX + 0.01
	If AngleX > 2 * #PI
		AngleX - 2 * #PI
	ElseIf AngleX < 0
		AngleX + 2 * #PI
	EndIf
	
  AngleY + 0.005
	If AngleY > 2 * #PI
		AngleY - 2 * #PI
	ElseIf AngleY < 0
		AngleY + 2 * #PI
	EndIf
	
  AngleZ + 0.02
	If AngleZ > 2 * #PI
		AngleZ - 2 * #PI
	ElseIf AngleZ < 0
		AngleZ + 2 * #PI
	EndIf
	
	X_Origine.Point3D
	X_Origine\x = 200
	Y_Origine.Point3D
	Y_Origine\y = 200
	Z_Origine.Point3D
	Z_Origine\z = 200
  
  StartDrawing(ScreenOutput())
    DrawingFont(FontID)
    DrawingMode(#PB_2DDrawing_Transparent)
		
    ; On affiche le repère
    ; la ligne représentant l'axe X en vert
    XYFrom3D(@X_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    Line(250, 250, Coord\x, Coord\y, RGB(0, 255, 0)) ; on trace une ligne verte à partir du centre de l'image vers les coordonnées calculées
    Circle(250 + Coord\x, 250 + Coord\y, 5 * Coord\p, RGB(0, 255, 0)) ; on trace un cerle sur le bout de la ligne. La variable Coord\p permet de prendre en compte la perspective sur une forme de taille définie, ici un cercle mais également une image par exemple.
    DrawText(255 + Coord\x, 255 + Coord\y, "X", 0) ;On affiche le label de l'axe
    
    ; la ligne représentant l'axe Y en rouge
    XYFrom3D(@Y_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    Line(250, 250, Coord\x, Coord\y, RGB(255, 0, 0))
    Circle(250 + Coord\x, 250 + Coord\y, 5 * Coord\p, RGB(255, 0, 0))
    DrawText(255 + Coord\x, 255 + Coord\y, "Y", 0)
    
    ; la ligne représentant l'axe Z en bleu
    XYFrom3D(@Z_Origine, AngleX, AngleY, AngleZ, 400, @Coord.Point3DP) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
    Line(250, 250, Coord\x, Coord\y, RGB(0, 0, 255))
    Circle(250 + Coord\x, 250 + Coord\y, 5 * Coord\p, RGB(0, 0, 255))
    DrawText(255 + Coord\x, 255 + Coord\y, "Z", 0)
    
  StopDrawing()
  
  FlipBuffers()

  Repeat
		Event = WindowEvent()
		
		If Event = #PB_Event_CloseWindow
			End
		EndIf
		
	Until Event = 0
	
ForEver