ink-blog | Le blog d'un développeur ActionScript 3

Dessin en 3D native.

Préambule :

Un des buts de cet article est de détailler et d’expliquer les mécanismes mathématiques simples qui permettent de tracer un trait entre deux éléments situés dans l’espace en trois dimensions de flash. Il constitue la première étape d’un approche de la 3D dans flash sans utiliser de librairie externe. Nous allons avoir besoin, en gros, de Pythagore (niveau 4ème) et d’un peu de trigonométrie (pas bien loin derrière).

Salut à tous,

Dans mon délire de réinventer la roue, je me suis posé quelques questions élémentaires sur le dessin en 3D dans flash. Je ne parle pas tant du dessin artistique que de simplement tracer des traits. Et une des choses qui nous saute aux yeux dès que cette idée nous traverse l’esprit, est que flash ou l’ActionScript ne nous permettent pas de tracer des éléments en 3D. La seule chose que nous pouvons faire est tracer un trait, ou une courbe (ou même une forme), dans un espace en deux dimensions. Là j’entends déjà pas mal de monde dire (non je déconne vous êtes trois ;)) : « en même temps si on veut faire de la 3D on utilise une librairie comme Papervision ou Sandy et ça va marcher tout seul!! ». Ce qui est totalement juste. Cependant, comprendre les bases des choses que l’on utilise n’est pas forcément un mal en soi. Et, de temps en temps, il est plus simple et efficace de faire les choses avec juste le code qu’il faut, qu’utiliser des librairies lourdes, complexes et coûteuse en temps de compilation …Oui je ne suis pas patient et attendre trois plombes que mon swf compile m’exaspère. Surtout que c’est le genre d’actions que l’on ne fait pas qu’une fois :).

Partant de là, je me suis demandé comment faire pour tracer un trait d’un point A à un point B mais dans un espace à trois dimensions. Flash ne gérant pas les objets en trois dimensions, j’en suis assez vite arrivé à la conclusion suivante :  de tracer un trait dans un espace en deux dimensions puis de le déplacer et de l’orienter pour qu’il corresponde au trait voulu dans l’espace en trois dimensions (au cas où, je rappelle que l’on ne peut utiliser la 3D native de flash qu’à partir de flash 9 et en AS3). Pour cela, il faut d’une part, avoir les bons paramètres pour la longueur  et les rotations. D’autre part, faire ce trait dans un DisplayObject qui possède au moins les propriétés de dessin. Et comme on est un minimum économe, on choisit l’objet qui a seulement les propriétés et méthodes qui nous sont utiles. Donc une Shape …Déjà que nous allons créer plusieurs objets, nous ne sommes pas en plus obligés de prendre les plus lourds. ;)

Tout d’abord, pour des raisons de simplicité et parce que nous allons modifier l’orientation et la position du trait. Il ne sert à rien de tracer un trait suivant un axe exotique, à partir d’un point de départ qui l’est tout autant. Un des deux axes directeurs du plan suffit largement et l’axe des abscisses (x) est donc tout indiqué pour cela. De plus, comme ce trait va être déplacé, autant le faire partir d’un point on ne peut plus simple, j’ai nommé : « l’origine », (0,0) pour les intimes.

Maintenant que nous savons d’où nous partons et vers où nous allons (vers la droite en gros), la question suivante est : « Où  nous arrêtons-nous? ». C’est grosso modo le moment que choisit Pythagore pour montrer le bout de son nez.

Nous voulons donc, au départ, tracer un trait de A(x1,y1,z1) vers B(x2,y2,z2) (où (x,y,z) représente la position d’un point sur, respectivement, l’axe des x, des y et des z. Si ces notations vous gênent, je pourrais faire avec un exemple, à la demande). Pour commencer il faut donc trouver la longueur du segment [A,B]. Les coordonnées d’un point étant relatives aux axes des abscisses (x), des ordonnées (y) et des cotes (z). Les points A et B forment un triangle rectangle avec le projeté de B (C) sur l’axe passant par A, parallèle avec l’un des axes directeurs. Dans cette représentation [AB] est l’hypoténuse du triangle rectangle. Et là, notre pote Pythagore nous dit un truc dans le style « Le carré de l’hypoténuse est égal à la somme des carrés des deux autres côtés ». Sympa, merci, c’est trop clair!! Donc là pour décoder, l’hypoténuse c’est le côté d’un triangle rectangle qui n’est pas à côté d’un angle droit (cf. image ci-dessous, ou le côté qui est en face de l’angle droit, ça marche aussi). Donc Pythagore nous dit que la longueur du segment [AB] au carré (c’est-à-dire longueur de [AB] fois longueur de [AB]) est égale à la longueur de [AC] également au carré plus la longueur de [BC] encore au carré. Or, lorsque notre point A est en (0,0), il se trouve que la longueur de [AC] c’est exactement l’abscisse du point B et que la longueur de [BC] est exactement l’ordonnée (position sur l’axe des y) de B. Donc ça, c’est pour le cas ultra simple.

Pour le cas tout juste un peu moins simple où A n’est pas en (0,0) (mais en (12,6) par exemple) il faut se ramener à un cas où A est en (0,0) ce qui équivaut à effectuer les mêmes opérations sur tous les points que celles que l’on effectue sur le point A pour le faire passer de A(xa,ya) à A’(0,0). Dans mon exemple (12,6) pour que A se retrouve à l’origine, il faut soustraire 12 à son abscisse et 6 à son ordonnée. Il faut donc soustraire xa à l’abscisse de tous les points et ya à l’ordonnée de tous les points (là, mine de rien, on vient de faire une translation et même un changement de repère). Ce qui nous donne l[AB]^2 (longueur du segment AB au carré) =  (xb-xa)^2 + (yb-ya)^2. Et là on est super content, on sait trouver la longueur d’un segment dans un espace à deux dimensions… Sauf que nous, notre segment il est dans un espace à deux dimensions. Ça tombe bien, là, on a un peu de bol parce que c’est exactement la même chose mais avec une dimension en plus. En gros pour chaque dimension de l’espace, on soustrait la position de A dans cette dimension à la position de B dans cette même dimension (exemple xb-xa). On fait la même chose en trois dimensions. Ce qui donne : l[AB]^2 = (xb-xa)^2 + (yb-ya)^2 + (zb-za)^2. Maintenant nous avons la longueur au carré, il ne reste plus qu’à faire la racine carrée de tout ça (√, ce qui nous donne le nombre seul, en gros  √(x^2) = x, la fonction racine est la fonction réciproque de la fonction carré cf. plus bas). D’où √(l[AB]^2) = √((xb-xa)^2 + (yb-ya)^2 + (zb-za)^2). Par conséquent, notre code pour tracer le trait sera :

  1. monShape.graphics.lineTo(Math.sqrt((xb-xa)^2 + (yb-ya)^2 + (zb-za)^2), 0);

Une petite démonstration pour le fun : si A est en (0,0,0) et B en (xb,yb,zb), la longueur de [AB] d’après Pythagore est égale à la racine carrée de [AM]^2 + yb^2. Où M est le projeté de B sur le plan formé par l’axe des abscisses (x) et l’axe des cotes (z). Du coup, la longueur de [AM] est égale, toujours d’après Pythagore, à la racine carrée de xm^2 + zm^2. Comme M est le projeté de B sur le plan (oxz) xm = xb et zm = zb, on a donc [AM]^2 = xb^2 + zb^2. D’où [AB]^2 = xb^2 + yb^2 + zb^2. CQFD!

Triangle rectangle

Maintenant, tout fiers que nous sommes d’avoir un trait à la bonne longueur, il serait peut être sympathique qu’il aille dans le bon sens. Nous allons donc passer à la partie orientation. Ça va être un petit peu plus rigolo!!

L’idée principale est de passer de ce que l’on appelle les coordonnées cartésiennes aux coordonnées sphériques. C’est simple, les coordonnées cartésiennes sont les coordonnées que l’on utilise depuis le début, c’est-à-dire qu’un point est défini par trois composantes : sa coordonnée x, position sur l’axe des abscisses, la coordonnée y, position sur l’axe des ordonnées et la coordonnée z, position sur l’axe des cotes. Ainsi, un point est représenté par un « cube » (en vrai, un parallélépipède). Les coordonnées sphériques se servent quant à elles, de la distance du point à l’origine (d’où notre utilisation de cette longueur dans notre transformation) et de deux angles de rotation. En effet, ce point se trouvant à une distance « d » du centre, si il est placé sur l’axe des abscisses et qu’on lui fait subir une rotation par rapport à l’axe des cotes (z), il va décrire un cercle. Maintenant, imaginons que c’est possible, si depuis chaque point de ce cercle nous lui faisons faire une rotation par rapport à l’axe des ordonnées (y). En gros, si on fait tourner le cercle obtenu au départ sur lui-même, pas comme une roue bien évidemment. Ce cercle se transforme en sphère, d’où le nom de coordonnées sphériques. Donc pour une distance donnée « d », grâce à deux rotations nous pouvons atteindre n’importe quel point de la sphère de rayon « d ». En faisant varier « d », on fait varier la taille de la sphère et l’on peut atteindre n’importe quel point de l’espace.

Donc, dans notre cas, nous avons déjà une composante des coordonnées sphériques. La distance « d » (la longueur de [AB] pour les poissons rouges ;), √((xb-xa)^2 + (yb-ya)^2 + (zb-za)^2)). Reste à trouver les 2 autres, à savoir les angles de rotation suivant l’axe des y et suivant l’axe des z.

Il est important de noter que les rotations dans l’espace ne sont pas commutatives. La commutativité est la capacité d’une opération à avoir le même résultat qu’on la fasse dans un sens ou dans l’autre. Par exemple, l’addition usuelle est commutative ; 3+6 = 6+3 = 9. Les rotations dans l’espace, elles, ne le sont pas. C’est-à-dire que si l’on fait subir une rotation suivant l’axe des abscisses (x) à un point, puis une rotation suivant l’axe des ordonnées (y) à ce même point, il ne sera pas à la même position que si nous lui faisons d’abord faire la rotation suivant l’axe des ordonnées (y) puis suivant l’axe des abscisses (x).

Un exemple simple. Un point sur l’axe des abscisses. Si nous lui faisons faire une rotation de 90° suivant l’axe des ordonnées (y) il se retrouve sur l’axe des cotes (z). Puis si nous lui faisons faire la même rotation suivant l’axe des abscisses (x), il passe de l’axe des cotes (z) à l’axe des ordonnées (y). Maintenant si nous commençons par la rotation suivant l’axe des abscisses (x) : le point ne bouge pas (il est déjà sur l’axe des abscisses (x) ça ne lui fait faire qu’une rotation sur lui-même). Si on fait après la rotation suivant l’axe des ordonnées : il se retrouve sur l’axe des cotes (z). À la fin du premier essai le point se trouve sur l’axe des ordonnées (y) ; à la fin du second il se trouve sur l’axe des cotes (z). Seul l’ordre des rotations a changé, mais le résultat est différent.

Dans flash, les rotations se font dans l’ordre x, puis y, puis z, même si le le rendu n’est fait qu’après que les 3 rotations ne soient appliquées.

Nous devons donc faire nos calculs pour que la rotation suivant l’axe des ordonnées (y) soit la première (sachant que l’on ne fera pas de rotation suivant l’axe des abscisses (x), le point étant initialement sur l’axe des abscisses (x), il ne ferait qu’une rotation sur lui-même).

Pour mieux comprendre quelles rotations nous devons effectuer, nous allons faire les transformations inverses. Nous allons partir du point que nous voulons atteindre pour arriver au point de départ.

Le point B (que nous voulons atteindre) appartient à la sphère violette de rayon √(x^2+y^2+z^2) (cf. image repère 3D 1).
C’est-à-dire que par des rotations appropriées du point suivant les axes des abscisses  (x en bleu), des ordonnées (y en rouge) ou des cotes (z en jaune), nous pouvons atteindre n’importe quel point de cette sphère.

image repère 3D 1

1 : image repère 3D 1

Seulement, notre transformation est spécifique. Elle ne fait apparaitre que deux rotations, l’une suivant l’axe des ordonnées (y en rouge) et l’autre suivant l’axe des cotes (z en jaune) (dans cet ordre pour la construction du point, dans l’ordre inverse pour la dé-construction). Donc comme nous « déconstruisons » le point, nous allons faire une rotation suivant l’axe des cotes (z).

Ainsi, les points accessibles depuis le point final par une rotation suivant l’axe des cotes (z) se situent à l’intersection de la sphère (tous les points que nous pouvons atteindre par n’importe quelle rotation) avec le plan passant par b et parallèle au plan (oxy) (plan 1) (cf. image repère 3D 2 et 3).

intersection sphère/plan Z face

2 : intersection sphère/plan Z face

intersection sphère/plan Z biais

3 : intersection sphère/plan Z biais

Nous savons également autre chose. Pour qu’une rotation suivant l’axe des ordonnées (y) nous ramène sur l’axe des abscisses (x), il faut que le point soit sur le plan formé par l’axe des abscisses lui-même et l’axe des cotes (plan oxz  cf. image repère 3D 4). En effet faire une rotation suivant l’axe des ordonnées revient à décrire des cercles sur la sphère comme les cercles bleus. Nous voyons bien que seul celui qui est sur le plan (oxz) passe par l’axe des abscisses.

4 : plan (ozx)

4 : plan (ozx)

Cette information nous permet de dire que non seulement nous voulons une rotation qui nous mène à l’intersection de la sphère et du plan 1 mais qui soit aussi à l’intersection du plan (oxz). Il ne nous reste plus que deux points possibles : l’un avec une abscisse négative, l’autre avec une abscisse positive. Pour faire simple, nous allons utiliser celui avec l’abscisse positive.

La rotation suivant l’axe des cotes (z) est donc celle qui nous permet de passer de l’état où le point B est à sa position d’origine à l’état où le point B appartient au plan (oxz) (cf. image repère 3D 5).

5 :  point B après une rotation suivant l'axe des cotes(z).

5 : point B après une rotation suivant l'axe des cotes(z).

Pour effectuer cette rotation, nous avons dû trouver l’angle adéquat, qui s’avère souvent être un rapport de longueur assortie d’une petite formule de trigonométrie. C’est ici qu’elles arrivent !! :D

Dans notre dessin du triangle rectangle (première image), l’angle formé par les segments [AB] et [AC] (α) peut être obtenu de trois manières différentes :

  1. sin(α)  = (côté opposé)/(hypoténuse) = l[BC] / l[AB].
  2. cos(α) = (côté adjacent)/(hypoténuse) = l[AC] / l[AB].
  3. tan(α) = (côté opposé)/(côté adjacent) = l[BC] / l[AC].

Donc là c’est sympa mais on a le sinus, le cosinus, ou la tangente de l’angle mais pas l’angle lui-même. Pour l’obtenir nous allons devoir utiliser une fonction réciproque. J’en profite donc pour expliquer ce que c’est.

Une fonction réciproque à une autre fonction permet de trouver le paramètre de la première fonction. Elle fait le chemin inverse. C’est un peu ce que la soustraction est à l’addition ; 3+6 = 9 –> 9-3 = 6. C’est une analogie, l’addition n’étant pas une fonction mais une loi. La fonction réciproque est souvent notée f^-1. Ainsi f^-1(f(x)) = x et f(f^-1(x)) = x. Donc il existe pas mal de fonctions qui ont des fonctions réciproques ; la fonction racine est donc la fonction réciproque de la fonction carrée : √(x^2) = (√x)^2 = x (à quelque chose près mais on ne va pas rentrer dans les détails), comme nous l’avons vu tout-à-l’heure. La fonction sinus a également sa fonction réciproque arc-sinus ou sin-1 : arcsin(sin(α)) = α… Je ne vais pas vous montrer un exemple pour toutes, on ne va pas y passer la journée :). Cependant, il faut quand même remarquer que la fonction cosinus a son arc-cosinus et la fonction tangente son arc-tangente.

Ainsi, pour notre tan(α)= l[BC] / l[AC], il suffit de tout passer dans la fonction arc-tangente pour récupérer l’angle α :

tan(α)= l[BC] / l[AC] <=> (équivaut) arctan(tan(α)) = arctan(l[BC] / l[AC]) <=> α = arctan(l[BC] / l[AC]).

Revenons à notre rotation suivant l’axe des cotes… Nous pouvons trouver dans le dessin un triangle rectangle qui nous arrange pas mal pour faire nos calculs (cf. image repère 3D 6).

6 : rotation du point B suivant l'angle alpha.

6 : rotation du point B suivant l'angle alpha.

Nous voyons clairement que xb et yb sont présents dans ce schéma. Ce sont des valeurs que nous connaissons bien et qui vont nous permettre grâce à la tangente ou plutôt à l’arc-tangente de trouver notre fameux angle α.

Donc comme par miracle, nous avons α que  nous allons appeler αz (puisque c’est la rotation suivant l’axe des cotes) qui vaut arctan(yb / xb).

Ce qui se traduit en ActionScript par :

  1. rotationZ = Math.atan2(yb , xb);

Sauf que Math.atan2(v.y , v.x) nous retourne un angle en radians et que rotationZ prend un angle en degrés… Il n’auront donc pas fini de nous faire ch*** cela!! Heureusement, la conversion n’est pas compliquée. Un angle en radians est compris entre 0 et 2xÏ€ (pi, environ 3,14) et un angle en degrés est compris entre 0 et 360. pour faire la conversion, il suffit de ramener l’angle en radians dans un intervalle compris entre 0 et 1 et de multiplier le tout par 360. Ce qui nous donne en ActionScript :

  1. rotationZ = Math.atan2(yb , xb) / Math.PI * 180;

Nous avons quand même pas mal avancé, Notre point B étant maintenant sur le plan (oxz) (cf. image repère 3D 7). Il ne nous reste plus que la rotation suivant l’axe des ordonnées… Une formalité maintenant. ;)

7 : point B après une rotation suivant l'axe des cotes(z), vue de "dos".

7 : point B après une rotation suivant l'axe des cotes(z), vue de "dos".

Comme vous l’avez sûrement remarqué, une rotation suivant un axe ne fait pas varier la position du point sur cet axe. Par la transformation que nous venons de faire, le point B a vu ses coordonnées x et y changées, mais sa coordonnée z n’a pas bougé. De même, la distance entre le point B  et l’axe des cotes (z) avant la rotation par ce même axe,  et la distance entre le point B et l’axe des cotes (z) après cette rotation est la même (en même temps, c’est un peu la définition du cercle que d’avoir tous ses points à égale distance du centre… :)) (cf. image repère 3D 6). Nous avons donc un moyen simple de connaitre cette distance (B-axe des cotes). D’après Pythagore, cette distance est √(xb^2 +yb^2). Nous connaissons également la distance entre B et l’axe des abscisses (x) ; c’est zb. Et par le plus grand des miracles, il se trouve que ces grandeurs sont exactement celles qui vont nous servir à trouver la rotation suivant l’axe des ordonnées (y) (cf. image repère 3D 8).

8 : triangle rectangle et distance pour la rotation suivant l'axe des ordonnées.

8 : triangle rectangle et distance pour la rotation suivant l'axe des ordonnées.

En réutilisant nos petites formules de trigonométrie et en particulier celle de la arc tangente, nous trouvons que l’angle entre le point B (qui est un peu caché par toutes ces flèches et qui a changé de couleur pour être plus visible… Gagné ;)) et l’axe des abscisses (x en bleu toujours)  vaut :

arctan((côté opposé)/(côté adjacent)) = arctan(zb/(√(xb^2 + yb^2))).

Magique !! Nous avons nos trois paramètres, la distance entre les 2 points, la rotation suivant l’axe des ordonnées (y) : arctan(zb/(√(xb^2 + yb^2))), et la rotation  suivant l’axe des cotes (z) : arctan(yb/xb).

Là, nous avons presque fini. Il nous reste à mettre ces formules en ActionScript, pensant à passer des radians aux degrés :

  1. rotationY = Math.atan2(zb , Math.sqrt(xb*xb + yb*yb)) / Math.PI * 180;// Math.sqrt(x) retourne la racine carrée de x.
  2. rotationZ = Math.atan2(yb , xb) / Math.PI * 180;

Nous somme vraiment pas mal, mais comme ça ne peut pas être aussi simple, il faut rappeler que l’axe des ordonnées dans flash ne va pas vers le haut mais vers le bas ce qui inverse le sens de rotation. Du coup pour que notre rotation suivant l’axe des ordonnées (y) soit juste, il faut utiliser l’opposé de la valeur (l’opposé de x étant -x). Nous avons donc au final :

  1. rotationY = -Math.atan2(zb , Math.sqrt(xb*xb + yb*yb)) / Math.PI * 180;// Math.sqrt(x) retourne la racine carrée de x.
  2. rotationZ = Math.atan2(yb , xb) / Math.PI * 180;

La toute dernière touche finale consiste à déplacer le trait qui est bien orienté au bon endroit. Comme vous vous en souvenez peut-être, pour simplifier les calculs nous avons fait un changement de repère, en utilisant A comme centre. Il suffit de tout décaler dans l’autre sens en déplaçant notre trait des coordonnées de A.

Une fonction qui permet de tracer un point en 3D flash pourrait ressembler à ça :

  1. function create3DShape(v1:Vector3D, v2:Vector3D):Shape{
  2.         var r:Shape = new Shape();
  3.         var v:Vector3D = v2.subtract(v1);
  4.         r.graphics.lineStyle(2,Math.random()*0xffffff);
  5.         r.graphics.lineTo(Math.sqrt(v.x*v.x +v.y*v.y +v.z*v.z), 0);
  6.  
  7.         r.x = v1.x;
  8.         r.y = v1.y;
  9.         r.z = v1.z;
  10.         r.rotationX = 0;
  11.         r.rotationY = -Math.atan2(v.z , Math.sqrt(v.y*v.y + v.x*v.x) ) / Math.PI * 180
  12.         r.rotationZ = Math.atan2(v.y , v.x) / Math.PI * 180;
  13.  
  14.         return r;
  15. }

On lui passe deux Vector3D en paramètre et elle retourne une Shape bien positionnée et bien orientée… On peut ensuite faire des dessins merveilleux, plein de créativité et de sens artistique, comme celui là :

lien vers le swf.

avec un code qui ressemble à ça :

  1. import flash.geom.Vector3D;
  2. import fl.events.SliderEvent;
  3. import fl.controls.Slider;
  4. import flash.display.Sprite;
  5. import flash.display.Shape;
  6.  
  7. function create3DShape(v1:Vector3D, v2:Vector3D):Shape{
  8.         var r:Shape = new Shape();
  9.         var v:Vector3D = v2.subtract(v1);
  10.         r.graphics.lineStyle(2,Math.random()*0xffffff);
  11.         r.graphics.lineTo(Math.sqrt(v.x*v.x +v.y*v.y +v.z*v.z), 0);
  12.  
  13.         r.x = v1.x;
  14.         r.y = v1.y;
  15.         r.z = v1.z;
  16.         r.rotationX = 0;
  17.         r.rotationY = -Math.atan2(v.z , Math.sqrt(v.y*v.y + v.x*v.x) ) / Math.PI * 180
  18.         r.rotationZ = Math.atan2(v.y , v.x) / Math.PI * 180;
  19.  
  20.         return r;
  21. }
  22. function createRoue(pRayon:Number, pProf:Number, pDef:Number = 10):Sprite{
  23.         pRayon*= 2
  24.         var s:Sprite = new Sprite();
  25.         var v1:Vector3D;
  26.         var v2:Vector3D;
  27.         var vp1:Vector3D;
  28.         var vp2:Vector3D;
  29.         var sh:Shape;
  30.         for(var i:Number = 0 ; i < pDef; i ++){
  31.                 v1 = new Vector3D(Math.cos(i*Math.PI/pDef*2)*pRayon, Math.sin(i*Math.PI*2/pDef)*pRayon, 0);
  32.                 v2 = new Vector3D(Math.cos((i+1)*Math.PI*2/pDef)*pRayon, Math.sin((i+1)*Math.PI*2/pDef)*pRayon, 0);
  33.                 vp1 = new Vector3D(Math.cos(i*Math.PI/pDef*2)*pRayon, Math.sin(i*Math.PI/pDef*2)*pRayon, pProf);
  34.                 vp2 = new Vector3D(Math.cos((i+1)*Math.PI/pDef*2)*pRayon, Math.sin((i+1)*Math.PI*2/pDef)*pRayon, pProf);
  35.                 s.addChild(create3DShape(v1,v2));
  36.                 s.addChild(create3DShape(vp1,vp2));
  37.                 sh = create3DShape(v2,vp2)
  38.                 sh.rotationX = Math.cos((i+1)*Math.PI*2/pDef)*pRayon
  39.                 s.addChild(sh);
  40.         }
  41.         return s
  42. }
  43.  
  44. var _points:Vector. = new Vector.();
  45. // x
  46. var xavant:Number = -100;
  47. var xpar:Number = 50;
  48. var xtoit:Number = 100;
  49. var xparArr:Number = 200;
  50. var xcoffre:Number = 250;
  51. var xarr:Number = 350;
  52. var xroueAv:Number = 20
  53. var xroueAr:Number = 250;
  54. // y
  55. var ysol:Number = 0;
  56. var ybas:Number = -50;
  57. var yhautCap:Number = -100;
  58. var ybasPar:Number = -110;
  59. var yhaut:Number = -200;
  60. var ybasParArr:Number = -110;
  61. var yhautCoffre:Number = -110;
  62. var ybasCoffre:Number = -60;
  63. var yroue:Number = 30;
  64. //z
  65. var zdroit:Number = -100;
  66. var zgauche:Number = 100;
  67.  
  68. // calandre
  69. _points.push(new Vector3D(xavant,yhautCap,zdroit));// haut droit
  70. _points.push(new Vector3D(xavant,ybas,zdroit));// bas droit
  71. _points.push(new Vector3D(xavant,ybas,zgauche));// bas gauche
  72. _points.push(new Vector3D(xavant,yhautCap,zgauche));// haut gauche
  73. // parebrise
  74. _points.push(new Vector3D(xpar, ybasPar, zdroit));
  75. _points.push(new Vector3D(xpar, ybasPar, zgauche));
  76. _points.push(new Vector3D(xtoit, yhaut, zgauche));
  77. _points.push(new Vector3D(xtoit, yhaut, zdroit));
  78. // parebrise arrière
  79. _points.push(new Vector3D(xparArr, yhaut, zdroit));
  80. _points.push(new Vector3D(xparArr, yhaut, zgauche));
  81. _points.push(new Vector3D(xcoffre, ybasParArr, zgauche));
  82. _points.push(new Vector3D(xcoffre, ybasParArr, zdroit));
  83. //coffre
  84. _points.push(new Vector3D(xarr, yhautCoffre, zdroit));
  85. _points.push(new Vector3D(xarr, yhautCoffre, zgauche));
  86. _points.push(new Vector3D(xarr, ybasCoffre, zgauche));
  87. _points.push(new Vector3D(xarr, ybasCoffre, zdroit));
  88.  
  89. var c:Sprite = new Sprite();
  90. c.x = stage.stageWidth/2;
  91. c.y = stage.stageHeight/2;
  92. addChild(c);
  93.  
  94. c.addChild(create3DShape(_points[0],_points[1]))
  95. c.addChild(create3DShape(_points[1],_points[2]))
  96. c.addChild(create3DShape(_points[2],_points[3]))
  97. c.addChild(create3DShape(_points[3],_points[0]))
  98. c.addChild(create3DShape(_points[0],_points[4]))
  99. c.addChild(create3DShape(_points[3],_points[5]))
  100. c.addChild(create3DShape(_points[5],_points[4]))
  101. c.addChild(create3DShape(_points[4],_points[7]))
  102. c.addChild(create3DShape(_points[5],_points[6]))
  103. c.addChild(create3DShape(_points[6],_points[7]))
  104. c.addChild(create3DShape(_points[7],_points[8]))
  105. c.addChild(create3DShape(_points[8],_points[9]))
  106. c.addChild(create3DShape(_points[6],_points[9]))
  107. c.addChild(create3DShape(_points[9],_points[10]))
  108. c.addChild(create3DShape(_points[8],_points[11]))
  109. c.addChild(create3DShape(_points[11],_points[10]))
  110. c.addChild(create3DShape(_points[11],_points[12]))
  111. c.addChild(create3DShape(_points[10],_points[13]))
  112. c.addChild(create3DShape(_points[12],_points[13]))
  113. c.addChild(create3DShape(_points[13],_points[14]))
  114. c.addChild(create3DShape(_points[12],_points[15]))
  115. c.addChild(create3DShape(_points[14],_points[15]))
  116. c.addChild(create3DShape(_points[1],_points[15]))
  117. c.addChild(create3DShape(_points[2],_points[14]))
  118. c.addChild(create3DShape(_points[1],_points[14]))
  119. c.addChild(create3DShape(_points[2],_points[15]))
  120.  
  121. // roues
  122. var r:Sprite = createRoue(yroue, 10)
  123. r.x = xroueAv;
  124. r.y = yroue;
  125. r.z = zgauche;
  126. c.addChild(r);
  127. r = createRoue(yroue, 10)
  128. r.x = xroueAr;
  129. r.y = yroue;
  130. r.z = zgauche;
  131. c.addChild(r);
  132. r = createRoue(yroue, 10)
  133. r.x = xroueAv;
  134. r.y = yroue;
  135. r.z = zdroit-10;
  136. c.addChild(r);
  137. r = createRoue(yroue, 10)
  138. r.x = xroueAr;
  139. r.y = yroue;
  140. r.z = zdroit – 10;
  141. c.addChild(r);
  142.  
  143. var rx:Slider = new Slider();
  144. rx.maximum = 360;
  145. rx.liveDragging = true;
  146. rx.x = 10;
  147. rx.y = 10;
  148. addChild(rx);
  149. var ry:Slider = new Slider();
  150. ry.maximum = 360;
  151. ry.liveDragging = true;
  152. ry.x = 10;
  153. ry.y = 30
  154. addChild(ry);
  155. var rz:Slider = new Slider();
  156. rz.maximum = 360;
  157. rz.liveDragging = true;
  158. rz.x = 10;
  159. rz.y = 50;
  160. addChild(rz);
  161.  
  162. rx.addEventListener(SliderEvent.CHANGE, _rxChange);
  163. ry.addEventListener(SliderEvent.CHANGE, _ryChange);
  164. rz.addEventListener(SliderEvent.CHANGE, _rzChange);
  165.  
  166. function _rxChange(e:SliderEvent):void{
  167.         c.rotationX = rx.value;
  168. }
  169. function _ryChange(e:SliderEvent):void{
  170.         c.rotationY = ry.value;
  171. }
  172. function _rzChange(e:SliderEvent):void{
  173.         c.rotationZ = rz.value;
  174. }

Dans un fla vide avec un Slider dans la bibliothèque.

There are 4 Comments to "Dessin en 3D native."

  • tlecoz dit :

    Salut LaPieuvre !

    Je suis a fond dans la 3D moi aussi en ce moment ^^
    Je ne veux pas te décourager (loin de moi cette idée) mais je pense que tu ne t’y prends pas comme il faut dans le sens ou Flash dispose d’outil permettant de gerer plusieurs éléments (tout les éléments 3d d’une anim en fait) dans le même Graphics de la même Shape (comme dans paperVision/ Away3D).
    C’est suffisament puissant pour qu’un non matheux comme moi aie pu poser les bases d’un moteur3D en 1 weekend ^^

    Fais des recherches autour de Utils3D.projectVectors ou matrix3D.transformVectors , ou plus simplement, fais comme moi et étudie en détail ce code la :
    http://actionsnippet.com/?p=2158

    Bon courage !

    • la pieuvre dit :

      Salut Tlecoz,

      Tu as tout à fait raison. Mais je crois que je me suis mal exprimé dans l’article.

      Là, le but pour moi n’est pas de faire un moteur 3D efficace (ou non d’ailleurs). C’est plus un genre de cas d’école, où on utilise justement le moins d’outils (de programmation et mathématiques) possible pour comprendre tous les mécanismes qui sous-tendent un aspect de la géométrie en 3D.

      L’intérêt étant au final de proposer plusieurs articles, qui partent de zéro (comme là) et qui utilisent à chaque fois de nouveaux outils pour comprendre, d’une part leurs limitations et les avantages qu’ils apportent.

      Donc clairement, l’idée ne m’a pas traverser l’esprit d’utiliser ce que j’ai fait pour vraiment faire de la 3D. J’ai juste fait un exemple pour monter que ça marche. :D

      Je t’avouerais même, que pour moi, la méthode que tu proposes était prévu comme la 3ème étape. avec avant, l’explication et l’utilisation des matrices pour comprendre l’intérêt de cet outils (au sens mathématique).

      (Mais c’est vrai qu’à la relecture ce n’est pas clair… C’est un peu le moment où j’essuie les plâtres de ma ligne éditoriale ;))

  • Cédric dit :

    Salut la pieuvre !
    Pour le coup c’est clair que tu réinvente la roue. Non seulement tu refais du code pour de la 3D, mais en plus tu nous explique from scratch ce qu’on appelle couramment à partir de la 2nde (?) la … projection orthogonale.
    Bon article. J’ai hate d’en voir d’autre de ce style :)

    • la pieuvre dit :

      Salut, merci pour ton message… C’est effectivement le but, ré-expliquer depuis le départ les bases de ce qui va nous servir plus tard pour tous ceux qui n’ont aucune connaissance préalable en maths et qui pensent souvent à tort que ce n’est pas pour eux!! ;)

Write a Comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

 

Essentials