[PUREBASIC] Courbes de béziers & sprite 3D

Cpl.Bator Message lu Posté le 27 Déc 2008 à 13:02 Bulle
Avatar de Cpl.Bator
Nouveau Membre

Messages : 17
GCPoints : 8751
Voici un petit post montrant comment déformé une serie de sprites donnant l'illusion d'une courbe :

Image



Cette technique n'est ni plus ni moins que l'application de la courbe de bezier, mais qu'est ce que la courbe de bezier ?

Extrait de l'article :
http://communaute.mangue.org/e107_plugins/cours/cours.php?id=prog_jeux&cid=cours3.html

Citation :

On la doit à l'ingénieur français, Pierre Bézier, qui travaillait chez Renault dans les années 60. Il se pencha sur les problèmes liés à la conception des surfaces en 3D pour les premiers programmes de CAO (conception assistée par ordinateur). Le système UNISURF issu de ses recherches permettra à Renault de dessiner et découper la carrosserie de la plupart des automobiles de la firme.

L'enjeu était de trouver un moyen pour définir des courbes de manière précise afin que les machines puissent procéder aux découpes. Il fallait donc trouver un système de paramètres défini de manière rigoureuse et le plus simple possible. Les dessins complexes (carrosseries, coques, fuselages...) étaient jusque alors fait à main levée par les designers et aucune machine ne pouvait récupérer facilement l'information depuis un dessin. Les machines les plus avancées utilisaient des ellipses pour calculer les zones à découper. Les courbes d'interpolation connues en ce temps n'étaient pas adaptées pour ce travail car elles n'offraient pas un contrôle suffisant sur la forme des arcs et la géométrie finale des objets, il fallait aussi attendre que la technologie des machines-robots évolue en parallèle avec l'électronique. Paul de Casteljau, qui travaillait lui chez Citroen, avait défini dès 1958 des méthodes pour créer des surfaces en 3D mais son approche était moins pratique que celle de Bézier. Les travaux de Casteljau permirent à Bézier d'améliorer le concept grâce à l'ajout des "poignées de contrôle" et cette nouvelle famille de courbes remporta un vif succès dans l'industrie. Par la suite, un groupe de recherche d'Apple (ces personnes créeront plus tard Adobe) récupéra cette invention pour définir les premières polices vectorielles, les imprimantes fonctionnant un peu comme les machines de Renault.

De manière plus précise, la courbe de Bézier (cubique, c'est la plus courante) est une courbe d'interpolation qui nécessite 4 points pour définir un arc. Le premier point et le dernier sont des noeuds, la courbe passe par ces points. Les deux autres points sont des points de contrôle, ils permettent de définir la forme de la courbe, celle-ci ne passe pas par ces points de contrôle (sauf pour les cas spéciaux avec des noeuds alignés). Les points de contrôle correspondent aux "poignées" des programmes de dessin vectoriel.



En gros il nous faut 4 points :

- 2 pour l'extrémité du segment
- 2 pour les points de controle

se qui nous donne :

Image



Les point de controles vont servir a donner la forme à la courbe finale, on obtient cette courbe en subdivisant les segments , plus on divise, plus la coube sera "lisse" :

Image

Image

Image


On obtient se resultat avec une fonction récursive ( qui s'appelle elle même n fois)

jusqu'a obtenir un tel resultat :

Image



je ne vais pas expliqué en détails la fonction, au moins vous aurez compris le concept d'une courbe de bezier.

Maintenant pour la déformation de sprites, c'est très simple,
Comme je connais chaque subdivision ( qui sont aussi des simples segment ) je peut facilement obtenir les point qui formeront des perpendiculaire à ces segment :

Image


Je n'ai plus qu'a stocké les coordonées des perpendiculaires puis à transformé mon sprite en fonction de ces perpendiculaires.

Voici un bout de code pour terminé mon petit article :

C'est une adaptation d'un code en C :
http://fr.wikipedia.org/wiki/Courbe_de_B%C3%A9zier_en_C

Comtois avait déjà fait la conversion en PureBasic :
http://www.purebasic.fr/french/viewtopic.php?t=6242&start=0



Code :
InitSprite() : InitKeyboard() : InitMouse() : InitSprite3D()
OpenWindow(0,0,0,800,600,"SPRITE TORSION")
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0)


Structure POINTF
x.f
y.f
EndStructure

Structure BEZIER
P.POINTF[4]
EndStructure

Structure Perpendiculaires
P1.POINTF
P2.POINTF
EndStructure

Global NewList Droites.Perpendiculaires()


Global SubDivide
Procedure CountSubDivide(*b.BEZIER,level)
SubDivide+1
If level<=0
Else
  CountSubDivide(*b,level-1)
  CountSubDivide(*b,level-1)
EndIf
EndProcedure


Declare DrawBezier(Global_Bezier,level)
MyBezier.BEZIER


MyBezier\\P[0]\\x = 100
MyBezier\\P[0]\\y = 500

MyBezier\\P[1]\\x = 100
MyBezier\\P[1]\\y = 100

MyBezier\\P[3]\\x = 700
MyBezier\\P[3]\\y = 500
; 
MyBezier\\P[2]\\x = 700
MyBezier\\P[2]\\y = 100



;Sprite Procedural
CreateSprite(0,256,256,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(0))
For y = -128 To 128
Line(0,y+128,256,1,RGB(64,128+y,255))
Next
StopDrawing()

CreateSprite3D(0,0)






LOD=5




Repeat
ExamineKeyboard() : ClearScreen(RGB(0,0,0))
ExamineMouse()

event = WindowEvent()


         Start3D()
         Sprite3DQuality(1)

        ForEach Droites()

            If NextElement(Droites())<>0
            Nx_a.f=Droites()\\P1\\x
            Ny_a.f=Droites()\\P1\\y
            Nx_b.f=Droites()\\P2\\x
            Ny_b.f=Droites()\\P2\\y
            PreviousElement(Droites())
            EndIf     
          
            If PreviousElement(Droites())<>0
            Px_a.f=Droites()\\P1\\x
            Py_a.f=Droites()\\P1\\y
            Px_b.f=Droites()\\P2\\x
            Py_b.f=Droites()\\P2\\y
            NextElement(Droites())
            EndIf     
       
       
       
       
       Sprite3DBlendingMode(7,2)
       TransformSprite3D(0,Nx_a,Ny_a,Droites()\\P1\\x,Droites()\\P1\\y,Droites()\\P2\\x,Droites()\\P2\\y,Nx_b,Ny_b)
       DisplaySprite3D(0,0,0)
        ;LineXY(Nx_a,Ny_a,Droites()\\P1\\x,Droites()\\P1\\y,RGB(0,255,0))
        ;LineXY(Nx_b,Ny_b,Droites()\\P2\\x,Droites()\\P2\\y,RGB(0,255,0))
        ;LineXY(Droites()\\P1\\x,Droites()\\P1\\y,Droites()\\P2\\x,Droites()\\P2\\y,RGB(0,255,0))


          Next 

       ClearList(Droites())
      Stop3D()




StartDrawing(ScreenOutput())

    Mx = MouseX()
    My = MouseY()  
    
    Circle(Mx,My,4,RGB(0,0,255))
    
    If t<ElapsedMilliseconds()
    t=ElapsedMilliseconds()+500
    ;LOD=(LOD+1)%8
EndIf
    
    
   DrawBezier(MyBezier,LOD)
  
    
  
    SubDivide=0
      For i = 0 To 3
      If Mx<MyBezier\\P[i]\\x+4 And Mx>MyBezier\\P[i]\\x-4 And My<MyBezier\\P[i]\\y+4 And My>MyBezier\\P[i]\\y-4
      Circle(MyBezier\\P[i]\\x,MyBezier\\P[i]\\y,8,RGB(0,255,0))
      CTRL = i
      Else
      Circle(MyBezier\\P[i]\\x,MyBezier\\P[i]\\y,2,RGB(0,255,0))
      EndIf 
    Next 
    
    
    If CTRL <> -1
      If MouseButton(1)
        MyBezier\\P[CTRL]\\x = Mx 
        MyBezier\\P[CTRL]\\y = My
        Else
        CTRL = -1
      EndIf
      
    EndIf
StopDrawing()



FlipBuffers()
 
Until KeyboardPushed(#PB_Key_Escape) Or event = #PB_Event_CloseWindow

Procedure DrawBezierBase(*b.BEZIER,color)
  LineXY(*b\\P[0]\\x,*b\\P[0]\\y,*b\\P[1]\\x,*b\\P[1]\\y,color)
  LineXY(*b\\P[1]\\x,*b\\P[1]\\y,*b\\P[2]\\x,*b\\P[2]\\y,color/4)
  LineXY(*b\\P[2]\\x,*b\\P[2]\\y,*b\\P[3]\\x,*b\\P[3]\\y,color)
EndProcedure

Procedure Distance(x1,y1,x2,y2)
Protected Result.f
Result = Sqr(  (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)  )
ProcedureReturn Result
EndProcedure

Procedure.f GetAngle(xb,yb,Xa,Ya) 

      
   ;calcul de l'angle en radian 
    
   ar.f=ATan((Ya-yb)/(Xa-xb)) 
   
   ;conversion en degres (je suis pas un crac des radians, je préfère bosser avec les degrés) 
   ad=ar*360/2/3.1415 
 
    
   ;ajout de la partie de l'angle suivant la position des points car Atang ne renvoie qu'un angle de -90 à 90 
   If Xa<xb And Ya<yb : ad=180+ad :EndIf ; cas haut gauche 
   If Xa>xb And Ya<yb : ad=360+ad :EndIf ;cas haut droite 
   If Xa<xb And Ya>yb : ad=180+ad :EndIf ; cas bas gauche 
   If Xa>xb And Ya>yb :           :EndIf ; cas bas droite 

   ; reconversion en radian si tu en as besoins 
   ;ar=ad*2*3.1415/360 
    
  ProcedureReturn ad+ar
EndProcedure 



Procedure DrawBezierRecursive (*b.BEZIER ,level.l) 

Static OldX.f,OldY.f,i.l,Pca.l,Pcb.l,h.f

  ; Change la hauteur des perpendiculaires
  h=20+10*Cos(ElapsedMilliseconds()/2000)
         
                  
                  
         If (level <= 0) 
          LineXY((*b\\P[0]\\x + 0.5),(*b\\P[0]\\y + 0.5),(*b\\P[3]\\x + 0.5),(*b\\P[3]\\y + 0.5),RGB(Random(255),Random(255),Random(255)))
          Ang = GetAngle((*b\\P[0]\\x + 0.5),(*b\\P[0]\\y + 0.5),(*b\\P[3]\\x + 0.5),(*b\\P[3]\\y + 0.5))
          Dist.f = Distance((*b\\P[0]\\x + 0.5),(*b\\P[0]\\y + 0.5),(*b\\P[3]\\x + 0.5),(*b\\P[3]\\y + 0.5))/2
          
          M.POINTF
          UP.POINTF
          DW.POINTF
          ;Calcule des perpendiculaires
          M\\x = *b\\P[0]\\x + Dist * Cos(Ang*#PI/180)
          M\\y = *b\\P[0]\\y + Dist * Sin(Ang*#PI/180)
 
          UP\\x = M\\x + h * Cos((Ang+90)*#PI/180)
          UP\\y = M\\y + h * Sin((Ang+90)*#PI/180)
       
          DW\\x = M\\x - h * Cos((Ang+90)*#PI/180)
          DW\\y = M\\y - h * Sin((Ang+90)*#PI/180)
      
          AddElement(Droites())
          Droites()\\P1\\x=UP\\x
          Droites()\\P1\\y=UP\\y
          Droites()\\P2\\x=DW\\x
          Droites()\\P2\\y=DW\\y
 
         ;Dessin des Perpendiculaire
         LineXY(UP\\x ,UP\\y,DW\\x,DW\\y,255)
          
          Else 
                  left.BEZIER 
                  right.BEZIER
                 
                 left\\P[0]\\x = *b\\P[0]\\x
                 left\\P[0]\\y = *b\\P[0]\\y
                 left\\P[1]\\x = (*b\\P[0]\\x + *b\\P[1]\\x) / 2
                 left\\P[1]\\y = (*b\\P[0]\\y + *b\\P[1]\\y) / 2
                 left\\P[2]\\x = (*b\\P[0]\\x + 2**b\\P[1]\\x + *b\\P[2]\\x) / 4
                 left\\P[2]\\y = (*b\\P[0]\\y + 2**b\\P[1]\\y + *b\\P[2]\\y) / 4
                 left\\P[3]\\x = (*b\\P[0]\\x + 3**b\\P[1]\\x + 3**b\\P[2]\\x + *b\\P[3]\\x) / 8
                 left\\P[3]\\y = (*b\\P[0]\\y + 3**b\\P[1]\\y + 3**b\\P[2]\\y + *b\\P[3]\\y) / 8
                  
  ;               DrawBezierBase(left,RGB(0,255,0))
                 right\\P[0]\\x = left\\P[3]\\x;
                 right\\P[0]\\y = left\\P[3]\\y;
                 right\\P[1]\\x = (*b\\P[1]\\x + 2**b\\P[2]\\x + *b\\P[3]\\x) / 4
                 right\\P[1]\\y = (*b\\P[1]\\y + 2**b\\P[2]\\y + *b\\P[3]\\y) / 4
                 right\\P[2]\\x = (*b\\P[2]\\x + *b\\P[3]\\x) / 2
                 right\\P[2]\\y = (*b\\P[2]\\y + *b\\P[3]\\y) / 2
                 right\\P[3]\\x = *b\\P[3]\\x
                 right\\P[3]\\y = *b\\P[3]\\y
                 
                
;                 DrawBezierBase(right,RGB(0,0,255));
                 DrawBezierRecursive (left, level -1);
                 DrawBezierRecursive (right, level -1);
         EndIf
         
 
 EndProcedure
 
Procedure DrawBezier(Global_Bezier,level)
       DrawBezierBase(Global_Bezier,RGB(255,255,255));
       DrawBezierRecursive(Global_Bezier,level);
 EndProcedure
 


Voila , vous pouvez utilisé se sytème dans les jeux, pour faire par exemple des routes, des tirs lasers de malade, ou même dans une application, dessin vectoriel, etc...


@++ :proud:
Répondre
GameCorp - Site d'apprentissage et d'entraide à la création de jeux vidéo.
XHTML Valid 1.1 - Page générée en 0.0269 secondes