Cube 3D + Perspective

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Le Soldat Inconnu
Messages : 4312
Inscription : mer. 28/janv./2004 20:58
Localisation : Clermont ferrand OU Olsztyn
Contact :

Cube 3D + Perspective

Message par Le Soldat Inconnu »

Voici un code qui réalise un cube en 3D (dessiner en filaire) qui tourne sur lui-même.

il est basé sur la procedure suivante qui permet de faire la convertion entre 2 repères (un repère 3D et l'écran 2D)
cette procedure est à utiliser tel quel sans aucune modification. Je ne l'explique pas ici, je l'ai déjà fait ailleurs sur le forum.

Pour son utilisation, on lui donne les coordonnées X Y Z du point, les angles entre le repère 3D et l'écran (angle de rotation sur l'axe X, Y et Z) et Une variable de type Point3D qui va recevoir le résultat.
Le résultat étant les coodonnées du point sur l'écran 2D avec prise en compte de la perpective

Code : Tout sélectionner

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
Mon code utilise cette procedure pour calculer tous les points nécessaire pour dessiner le cube.
j'ai donné un effet d'écho à l'image pour avoir une impression de vitesse également. c'est réalisé simplement en dessinant plusieurs fois le cube avec des angles différents

pour lancer le code, il faut la lib Coloreffect que vous trouverez sur mon site ainsi que le resident contenant la valeur de #Pi.

le code est tourné comme un écran de veille donc si vous l'enregistrez au format *.scr, il n'ya plus qu'a le mettre dans le dossier de windows

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, #PB_Window_BorderLess | #WS_MAXIMIZE, "Cube 3D") = 0
  End
EndIf
SetWindowPos_(WindowID(), -1, 0, 0, 0, 0, #SWP_NOSIZE | #SWP_NOMOVE) ; Pour mettre la fenêtre toujours au premier plan
UpdateWindow_(WindowID())

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, 0, 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
Pour les fainéants, l'écran de veille tout compilé est dispo sur mon site.
voir ici :
http://perso.wanadoo.fr/lesoldatinconnu/amusement.htm
(sur la page suivante de celle qui va s'afficher)
Je ne suis pas à moitié Polonais mais ma moitié est polonaise ... Vous avez suivi ?

[Intel quad core Q9400 2.66mhz, ATI 4870, 4Go Ram, XP (x86) / 7 (x64)]