Passage d'une coordonnée 3D vers un écran 2D, avec prise en charge de la perspective
Code : Tout sélectionner
; Auteur : Le Soldat Inconnu
; Version de PB : 3.90
;
; Explication du programme :
; Dessiner un cube filaire en 3D avec de la perspective
#TailleX = 1024
#TailleY = 768
#Couleur = $E5A974
#Cube = 175 ; Taille du cube
#Echo = 8 ; Nombre d'écho
#Echo_Espacement = 1 ; Espacement entre chaque écho
#VitesseMax = 70
#VitesseMin = 350
#VitesseEvolution = 20
Structure Point3D
x.f ; Coordonnée X
y.f ; Coordonnée Y
z.f ; Coordonnée Z
p.f ; Facteur pour la perspective
EndStructure
#Perspective = 600 ; Intensité de la perspective
Procedure XYFrom3D(x.f, y.f, z.f, ax.f, ay.f, az.f, XYZ)
; x, y, z : position de l'objet
; ax, ay, az : angle de rotation du point sur l'axe x, y et z, pour avoir un repère 3D décalé par rapport au repère de l'écran
Protected x2.f, y2.f, z3.f, Coord.Point3D
; Rotation sur l'axe Z
x2 = x * Cos(az) - y * Sin(az)
y2 = x * Sin(az) + y * Cos(az)
; z2 = z
; Debug StrF(x2) + " , " + StrF(y2) + " , " + StrF(z)
; Rotation sur l'axe X
; x3 = x2
Coord\y = y2 * Cos(ax) - z * Sin(ax)
z3 = y2 * Sin(ax) + z * Cos(ax)
; Debug StrF(x2) + " , " + StrF(Coord\y) + " , " + StrF(z3)
; Rotation sur l'axe Y
Coord\z = z3 * Cos(ay) - x2 * Sin(ay)
Coord\x = z3 * Sin(ay) + x2 * Cos(ay)
; y4 = y3
; Debug StrF(Coord\x) + " , " + StrF(Coord\y) + " , " + StrF(z3)
; Prise en compte de la perspective
Coord\p = 1 + Abs(Coord\z) / #Perspective
If Coord\z < 0
Coord\p = 1 / Coord\p
EndIf
Coord\x = Coord\x * Coord\p
Coord\y = Coord\y * Coord\p
; On copie les données
CopyMemory(@Coord, XYZ, SizeOf(Point3D))
EndProcedure
Procedure Cube3D(AngleX.f, AngleY.f, AngleZ.f, Couleur)
StartDrawing(ScreenOutput())
; On dessine une ligne du cube
XYFrom3D(#Cube, #Cube, #Cube, AngleX, AngleY, AngleZ, Coord1.Point3D) ; on calcul les coordonnées de l'extrémité de la ligne pour l'affichage sur l'écran
XYFrom3D(#Cube, -#Cube, #Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur) ; on trace une ligne à partir des coordonnées calculées
; Et on dessine les autres lignes de la même manière
XYFrom3D(#Cube, -#Cube, -#Cube, AngleX, AngleY, AngleZ, Coord1.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(-#Cube, -#Cube, #Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
XYFrom3D(-#Cube, -#Cube, -#Cube, AngleX, AngleY, AngleZ, Coord1.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(-#Cube, #Cube, #Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
XYFrom3D(-#Cube, #Cube, -#Cube, AngleX, AngleY, AngleZ, Coord1.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(#Cube, #Cube, #Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
XYFrom3D(#Cube, #Cube, -#Cube, AngleX, AngleY, AngleZ, Coord1.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
XYFrom3D(#Cube, -#Cube, -#Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(-#Cube, -#Cube, -#Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(-#Cube, #Cube, -#Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
CopyMemory(@Coord2, @Coord1, SizeOf(Point3D)) ; On copie les valeurs de Coord2 dans Coord1
XYFrom3D(#Cube, #Cube, -#Cube, AngleX, AngleY, AngleZ, Coord2.Point3D)
LineXY(Int(#TailleX / 2 + Coord1\x), Int(#TailleY / 2 + Coord1\y), Int(#TailleX / 2 + Coord2\x), Int(#TailleY / 2 + Coord2\y), Couleur)
StopDrawing()
EndProcedure
Procedure.l Objectif()
ProcedureReturn Int((Random(#VitesseMin - #VitesseMax) + #VitesseMax) / #VitesseEvolution) * #VitesseEvolution
EndProcedure
;- Début du code
; Ecran de veille : Si on veut paramétrer, on ne lance rien
Param.s = Left(ProgramParameter(), 2)
If Param = "/p"
MessageRequester("Information", "Concepteur : Le Soldat Inconnu [Bouguin Régis]" + Chr(10) + "Programmé sur PureBasic" + Chr(10) + Chr(10) + "http://perso.wanadoo.fr/lesoldatinconnu/", 4 * 16)
End
EndIf
If OpenWindow(0, 0, 0, 100, 100, "Cube 3D", #PB_Window_BorderLess | #WS_MAXIMIZE) = 0
End
EndIf
SetWindowPos_(WindowID(0), -1, 0, 0, 0, 0, #SWP_NOSIZE | #SWP_NOMOVE) ; Pour mettre la fenêtre toujours au premier plan
UpdateWindow_(WindowID(0))
If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0
End
EndIf
If OpenScreen(#TailleX, #TailleY, 32, "Cube 3D") = 0
End
EndIf
AngleX.f = 0
AngleY.f = 0
AngleZ.f = 0
; Paramètres des vitesses de rotation du cube
RotationX = Objectif()
RotationY = Objectif()
RotationZ = Objectif()
; Objectif des vitesses de rotation
RotationX_Objectif = Objectif()
RotationY_Objectif = Objectif()
RotationZ_Objectif = Objectif()
Repeat
; On change l'angle d'inclinaison du cube (en fait, on change les angles entre le repère écran et le repère du dessin)
AngleX + #PI / RotationX
AngleY + #PI / RotationY
AngleZ + #PI / RotationZ
; Permet de faire varier la vitesse de rotation du cube
If AngleX >= 2 * #PI Or AngleX <= -2 * #PI
AngleX = 0 ; On initialise l'angle car un angle de 2*#Pi est équivalent à un angle de 0
If RotationX > RotationX_Objectif
RotationX - #VitesseEvolution
ElseIf RotationX < RotationX_Objectif
RotationX + #VitesseEvolution
Else
RotationX_Objectif = Objectif() ; On fixe une nouvelle vitesse de rotation en objectif
EndIf
EndIf
If AngleY >= 2 * #PI Or AngleY <= -2 * #PI
AngleY = 0
If RotationY > RotationY_Objectif
RotationY - #VitesseEvolution
ElseIf RotationY < RotationY_Objectif
RotationY + #VitesseEvolution
Else
RotationY_Objectif = Objectif()
EndIf
EndIf
If AngleZ >= 2 * #PI Or AngleZ <= -2 * #PI
AngleZ = 0
If RotationZ > RotationZ_Objectif
RotationZ - #VitesseEvolution
ElseIf RotationZ < RotationZ_Objectif
RotationZ + #VitesseEvolution
Else
RotationZ_Objectif = Objectif()
EndIf
EndIf
ClearScreen(0)
For n = #Echo To 0 Step -1
Cube3D(AngleX - n * #PI / RotationX / #Echo_Espacement, AngleY - n * #PI / RotationY / #Echo_Espacement, AngleZ - n * #PI / RotationZ / #Echo_Espacement, ColorLuminosity(#Couleur, 1 - n / (#Echo + 1)))
Next
; On affiche les différentes vitesses et les objectifs
; StartDrawing(ScreenOutput())
; Box(2, 2, 2 * RotationX / #VitesseEvolution, 3, $505050)
; Line(2, 5, 2 * RotationX_Objectif / #VitesseEvolution, 0, $7800)
;
; Box(2, 9, 2 * RotationY / #VitesseEvolution, 3, $505050)
; Line(2, 12, 2 * RotationY_Objectif / #VitesseEvolution, 0, $7800)
;
; Box(2, 16, 2 * RotationZ / #VitesseEvolution, 3, $505050)
; Line(2, 19, 2 * RotationZ_Objectif / #VitesseEvolution, 0, $7800)
; StopDrawing()
FlipBuffers()
ExamineKeyboard()
ExamineMouse()
Until KeyboardPushed(#PB_Key_All) Or Abs(MouseDeltaX()) > 2 Or Abs(MouseDeltaY()) > 2