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_plugi ... ours3.html
En gros il nous faut 4 points :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.
- 2 pour l'extrémité du segment
- 2 pour les points de controle
se qui nous donne :
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" :
On obtient se resultat avec une fonction récursive ( qui s'appelle elle même n fois)
jusqu'a obtenir un tel resultat :
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 :
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/viewtopi ... 42&start=0
Code : Tout sélectionner
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
@++