/*************************************************************************************************************************************/ /* */ /* D E F I N I T I O N S D E L A V I S U A L I S A T I O N D ' U N E S P H E R E : */ /* ( V R A I E S P H E R E P L E I N E T R I D I M E N S I O N N E L L E ) */ /* */ /* */ /* Author of '$xrq/nucleon.LS.2$I' : */ /* */ /* Jean-Francois Colonna (LACTAMME, 1992??????????). */ /* */ /*************************************************************************************************************************************/ /*===================================================================================================================================*/ /*************************************************************************************************************************************/ /* */ /* S I M U L A T I O N D E L A V I S U A L I S A T I O N D ' U N E L L I P S O I D E E C L A I R E : */ /* */ /*************************************************************************************************************************************/ DEFV(Local,DEFV(Logical,INIT(mode_lent_mais_exact,LUNDEF))); /* Pour des raisons de compatibilite avec '$xrv/champs_5.1C$I'... */ #define RAYON_INTERIEUR_RELATIF_D_UNE_PARTICULE \ FU DEFV(Local,DEFV(Float,INIT(rayon_interieur_relatif_d_une_particule,RAYON_INTERIEUR_RELATIF_D_UNE_PARTICULE))); /* Constante destinee a definir la couche interne d'une particule a partir de laquelle on */ /* commence a "randomiser" son apparence : */ /* */ /* =0.0 : la "randomisation" a lieu partout (a partir du centre jusqu'a la peripherie), */ /* =1.0 : la "randomisation" est inhibee. */ /* */ /* On notera que le rayon "interieur" doit etre inferieur ou egal au rayon "exterieur"... */ #define RAYON_EXTERIEUR_RELATIF_D_UNE_PARTICULE \ FU DEFV(Local,DEFV(Float,INIT(rayon_exterieur_relatif_d_une_particule,RAYON_EXTERIEUR_RELATIF_D_UNE_PARTICULE))); /* Constante destinee a definir la couche externe d'une particule en ce qui concerne le */ /* processus de "randomisation". En fait, 'rayon_exterieur_relatif_d_une_particule' permet */ /* de modifier la densite : par exemple une valeur superieur a 1.0 (par exemple 3.0) donnera */ /* une forte densite de points representatifs... */ #define SEUIL_DE_RANDOMISATION_DES_PARTICULES \ GRO9(FRA10(FU)) DEFV(Local,DEFV(Float,INIT(seuil_de_randomisation_des_particules,SEUIL_DE_RANDOMISATION_DES_PARTICULES))); /* Constante destinee a choisir les points situes entre la couche interieure et la couche */ /* exterieure et qui ont deja ete pre-selectionnes par tirage au sort et comparaison a la */ /* distance au sort. En effet, si ce tirage au sort n'est pas fait, il y a trop de points */ /* a marquer, or etant en tridimensionnel, apres projection, la sphere de materialisation */ /* parait pleine... */ #define INTENSITE_SPECULAIRE \ QUATRE DEFV(Local,DEFV(Float,INIT(intensite_speculaire,FLOT(INTENSITE_SPECULAIRE)))); /* Constante destinee a accentuer le caractere speculaire de l'eclairage... */ #define Xc1 \ SOUS(FLOT(X) \ ,ASD1(centre_du_disque,x) \ ) \ /* Abscisse centree dans le referentiel (OX1,OY1,OZ1), */ #define Yc1 \ SOUS(FLOT(Y) \ ,ASD1(centre_du_disque,y) \ ) \ /* Ordonnee centree dans le referentiel (OX1,OY1,OZ1), */ #define Zc1 \ SOUS(FLOT(Z) \ ,ASD1(centre_du_disque,z) \ ) \ /* Profondeur centree dans le referentiel (OX1,OY1,OZ1). */ #define Xc2(direction_axe,grand_axe_a,petit_axe_b) \ DIVI(LIZ2(NEUT(ASD1(direction_axe,dx)),Xc1 \ ,NEUT(ASD1(direction_axe,dy)),Yc1 \ ) \ ,grand_axe_a \ ) \ /* Abscisse ramenee au grand axe 'a', suppose non nul, dans le referentiel (OX2,OY2), */ #define Yc2(direction_axe,grand_axe_a,petit_axe_b) \ DIVI(LIZ2(NEGA(ASD1(direction_axe,dy)),Xc1 \ ,NEUT(ASD1(direction_axe,dx)),Yc1 \ ) \ ,petit_axe_b \ ) \ /* Ordonnee ramenee au petit axe 'b', suppose non nul, dans le referentiel (OX2,OY2), */ #define Zc2(direction_axe,grand_axe_a,petit_axe_b) \ DIVI(Zc1 \ ,grand_axe_a \ ) \ /* Profondeur ramenee au grand axe 'a', suppose non nul, dans le referentiel (OX2,OY2). */ #define DEFINITION_DU_CONTOUR_APPARENT_DE_LA_PARTICULE \ FU \ /* Constante destinee a definir ce qui est a l'interieur d'un contour apparent : l'interieur */ \ /* correspond aux valeurs de 'equation_representative_du_contour' inferieures a cette */ \ /* constante. */ #define EPAISSEUR_DE_L_INTERSECTION \ MOIT(epaisseur_de_la_couronne_d_anti_aliasing) \ /* Definition de la distance-seuil en deca de laquelle on decrete que deux spheres forment */ \ /* a cet endoit leur intersection... */ #define niveau_RVB(niveau_RVB_du_point,nbRVB,ncRVB,iRVB) \ Bblock \ DEFV(genere_p,INIT(niveau_anterieur_du_point,load_point(iRVB,X,Y))); \ /* Definition du niveau anterieur du point(X,Y), et ce afin d'aider le compilateur... */ \ DEFV(genere_p,INIT(niveau_posterieur_du_point \ ,GENP(TRON(NIVA(HOMO(modulation_d_eclairement \ ,COORDONNEE_BARYCENTRIQUE_MINIMALE \ ,COORDONNEE_BARYCENTRIQUE_MAXIMALE \ ,MUL2(modulation_courante_de_la_luminance \ ,FLOT(NIVR(nbRVB)) \ ) \ ,MUL2(modulation_courante_de_la_luminance \ ,FLOT(NIVR(ncRVB)) \ ) \ ) \ ) \ ,FLOT(NOIR_PLANCHER_SUBSTITUTION) \ ,FLOT__BLANC \ ) \ ) \ ) \ ); \ /* Enfin, calcul du niveau du point courant. On notera la modulation de la luminance qui */ \ /* permet d'une part de simuler des fondus a l'ouverture et a la fermeture, et d'autre part */ \ /* de compenser la faible luminosite des premieres images due a la longueur limitee (voire */ \ /* nulle) des trainees lorsqu'il y en a... */ \ Test(IFNE(niveau_anterieur_du_point,niveau_posterieur_du_point)) \ Bblock \ EGAL(niveau_RVB_du_point \ ,NIVA(BARY(NIVR(niveau_anterieur_du_point) \ ,NIVR(niveau_posterieur_du_point) \ ,correction_d_anti_aliasing_au_bord \ ) \ ) \ ); \ /* Enfin, prise en compte de la correction d'anti-aliasing ; le niveau a utiliser est donc */ \ /* interpole entre ce qui a ete calcule ('niveau_RVB_du_point') et ce qui se trouvait */ \ /* la avant ('load_point(...)'), de facon a ce que tres pres du bord, le contenu anterieur */ \ /* soit preponderant : */ \ /* */ \ /* niveau = h.niveau + (1-h).anterieur */ \ /* */ \ /* ou 'h' designe 'correction_d_anti_aliasing_au_bord'. */ \ Eblock \ ATes \ Bblock \ EGAL(niveau_RVB_du_point \ ,niveau_posterieur_du_point \ ); \ /* Cas ou une correction n'est pas necessaire (ce test est surtout la pour eviter des */ \ /* problemes de "rebond" des splines...). */ \ Eblock \ ETes \ \ Test(NINCff(niveau_RVB_du_point,niveau_anterieur_du_point,niveau_posterieur_du_point)) \ Bblock \ PRINT_ERREUR("un niveau interpole est mal encadre"); \ CAL1(Prer1(" niveau anterieur = %d\n",niveau_anterieur_du_point)); \ CAL1(Prer1(" niveau calcule = %d\n",niveau_posterieur_du_point)); \ CAL1(Prer1(" niveau corrige = %d\n",niveau_RVB_du_point)); \ CAL1(Prer1(" parametre d'interpolation = %g\n",correction_d_anti_aliasing_au_bord)); \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ Eblock \ /* Calcul d'une des composantes du niveau du point courant. */ #define store_ellipsoide(nbR,nbV,nbB,ncR,ncV,ncB,iR,iV,iB,X_centre,Y_centre,Z_centre,petit_axe_b,grand_axe_a,direction_axe) \ /* ATTENTION : {X,Y,Z} E [Xmin,Xmax]x[Ymin,Ymax]x[Zmin,Zmax]. */ \ /* On notera que 'direction_axe' definit les cosinus et sinus directeurs du grand axe de */ \ /* l'ellipsoide representatif... */ \ /* */ \ /* Nota important : cette possibilite de tracer des ellipsoides projetes fut introduites */ \ /* afin de visualiser les gluons sous forme allongee (et traduisant ainsi leur deplacement), */ \ /* mais malheureusement, la methode implementee ci-dessous fait que la troisieme coordonnee */ \ /* ('Z') est perdue dans le cas general, et ne peut etre reconstituee. En consequence de */ \ /* quoi, 'store_ellipsoide(...)' sera appelee dorenavant avec 'petit_axe_b=grand_axe_a', et */ \ /* la forme allongee sera representee a l'aide d'une chaine de spheres disposees le long de */ \ /* la vitesse de chaque gluon... */ \ /* */ \ /* Nota important : cette procedure est tres approximative puisque la coordonnee 'Z' de */ \ /* chaque point de la sphere est lui meme approxime de facon tres grossiere. Une solution */ \ /* a ce probleme est apporte dans '$xrv/champs_5.1C$I'. */ \ Bblock \ DEFV(pointF_3D,centre_du_disque); \ /* Redefinition locale du centre du disque ; cela est rendu necessaire par le fait que */ \ /* la macro 'store_ellipsoide()' peut etre appelee avec comme argument 'X' et 'Y', qui se */ \ /* retrouveraient dans 'begin_colonneQ()' et 'begin_ligneQ()', et qui seraient donc */ \ /* confondus avec les 'X' et 'Y' locaux a ces fonctions de parcours. D'autre part, il */ \ /* est defini en 'Float' afin de conserver l'eventuelle precision des arguments d'appel... */ \ DEFV(Float,INIT(equation_representative_du_contour,FLOT__UNDEF)); \ /* Valeur courante au point {X,Y} de la fonction representative du contour de la forme */ \ /* materialisant la particule. */ \ DEFV(Float,INIT(distance_normalisee_au_centre,FLOT__UNDEF)); \ /* Distance normalisee dans [0,1] du point courant {X,Y} au centre du disque. */ \ DEFV(deltaF_3D,vecteur_normal1); \ /* --> */ \ /* Vecteur normal N au point courant de l'ellipsoide dans le referentiel (OX1,OY1), soit */ \ /* donc apres la rotation de l'angle '-t'. */ \ DEFV(deltaF_3D,vecteur_normal2); \ /* --> */ \ /* Vecteur normal N au point courant de l'ellipsoide dans le referentiel (OX2,OY2), soit */ \ /* donc avant la rotation de l'angle '-t'. */ \ DEFV(deltaF_3D,rayon_lumineux_inverse); \ /* --> */ \ /* Rayon lumineux inverse S (allant du point courant de l'ellipsoide a la source */ \ /* lumineuse). */ \ DEFV(Float,INIT(produit_des_modules_de_N_et_S,FLOT__UNDEF)); \ /* --> */ \ /* Produit du module du vecteur normal N et du module du vecteur rayon lumineux */ \ /* --> */ \ /* inverse S . */ \ DEFV(Float,INIT(modulation_d_eclairement,FLOT__UNDEF)); \ /* Modulation de l'eclairement du point courant calcule a partir de l'angle entre la */ \ /* --> --> */ \ /* vecteur normal N et le rayon lumineux inverse S . */ \ \ INITIALISATION_DE_LA_GENERATION_D_UNE_VALEUR_SANS_DEPLACEMENT(ZERO); \ /* Cette reinitialisation systematique a l'entree de 'store_ellipsoide(...)' est due au fait */ \ /* que la generation des images a lieu composante par composante ; 'store_ellipsoide(...)' */ \ /* est donc appele trois fois de suite pour chaque particule : il est donc absolument */ \ /* necessaire que la generation de l'aspect "random" des particules soit la meme lors des */ \ /* des trois appels... */ \ \ Test(IFNE(petit_axe_b,grand_axe_a)) \ Bblock \ PRINT_ATTENTION("dorenavant le petit axe et le grand axe doivent etre egaux"); \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ \ CALCUL_DE_LA_DISTANCE_CRITIQUE_NORMALISEE_AU_BORD(MAX2(petit_axe_b,grand_axe_a),FLOT__UNDEF); \ /* Preparation du traitement anti-aliasing pour cet ellipsoide particulier... */ \ /* */ \ /* L'argument 'FLOT__UNDEF' a ete introduit le 20060224151504 ; cette valeur indifferente */ \ /* est sans consequenece car, en effet, on est ici necessairement dans le cas ou */ \ /* 'IL_NE_FAUT_PAS(moduler_l_epaisseur_de_la_couronne_d_anti_aliasing)' car d'une part */ \ /* 'IL_NE_FAUT_PAS(moduler_l_epaisseur_de_la_couronne_d_anti_aliasing)' car c'est l'etat */ \ /* defaut ('v $xrq/nucleon.LP$I MODULER_L_EPAISSEUR_DE_LA_COURONNE_D_ANTI_ALIASING') et */ \ /* cette option n'est pas activable ('v $xrq/nucleon.Lb.1$I couronne=' par exemple...). */ \ \ INITIALISATION_POINT_3D(centre_du_disque \ ,F__cDENORMALISE_OX(Projection_OX(_____cNORMALISE_OX(X_centre) \ ,_____cNORMALISE_OY(Y_centre) \ ,_____cNORMALISE_OZ(Z_centre) \ ) \ ) \ ,F__cDENORMALISE_OY(Projection_OY(_____cNORMALISE_OX(X_centre) \ ,_____cNORMALISE_OY(Y_centre) \ ,_____cNORMALISE_OZ(Z_centre) \ ) \ ) \ ,F__cDENORMALISE_OZ(_____cNORMALISE_OZ(Z_centre)) \ ); \ /* Initialisation du centre effectif du disque, en se souvenant du fait que si le point */ \ /* (X_centre,Y_centre,Z_centre) etait en 'Float', la precision est conservee... */ \ \ begin_colonneQ(DoIn \ ,VINTE(SOUS(ASD1(centre_du_disque,y),MAX2(petit_axe_b,grand_axe_a))) \ ,VINTE(ADD2(ASD1(centre_du_disque,y),MAX2(petit_axe_b,grand_axe_a))) \ ,pasY \ ) \ /* Nota : on utilise 'MAX2(petit_axe_b,grand_axe_a)' et non pas 'petit_axe_b' car en effet */ \ /* les coordonnees {X,Y} sont dans le referentiel (OX1,OY1), alors que les axes de l'ellipse */ \ /* (grand_axe_a,petit_axe_b) sont mesures dans le referentiel (OX2,OY2). Ainsi, en prenant */ \ /* 'MAX2(...)' on est sur de ne pas manquer des points, meme si des calculs superflus sont */ \ /* malheureusement effectues... */ \ /* */ \ /* ATTENTION, il y avait autrefois : */ \ /* */ \ /* begin_colonneQ(DoIn */ \ /* ,TRON(INTE(SOUS(...,...)),Ymin,Ymax) */ \ /* ,TRON(INTE(ADD2(...,...)),Ymin,Ymax) */ \ /* ,pasY */ \ /* ) */ \ /* */ \ /* ce qui est tout a fait anormal, puisque cela ramenait sur la bordure de l'image des */ \ /* ellipsoides qui en fait en etaient en dehors... */ \ Bblock \ begin_ligneQ(DoIn \ ,VINTE(SOUS(ASD1(centre_du_disque,x),MAX2(petit_axe_b,grand_axe_a))) \ ,VINTE(ADD2(ASD1(centre_du_disque,x),MAX2(petit_axe_b,grand_axe_a))) \ ,pasX \ ) \ /* Nota : on utilise 'MAX2(petit_axe_b,grand_axe_a)' et non pas 'grand_axe_a' car en effet */ \ /* les coordonnees {X,Y} sont dans le referentiel (OX1,OY1), alors que les axes de l'ellipse */ \ /* (grand_axe_a,petit_axe_b) sont mesures dans le referentiel (OX2,OY2). Ainsi, en prenant */ \ /* 'MAX2(...)' on est sur de ne pas manquer des points, meme si des calculs superflus sont */ \ /* malheureusement effectues... */ \ /* */ \ /* ATTENTION, il y avait autrefois : */ \ /* */ \ /* begin_ligneQ(DoIn */ \ /* ,TRON(INTE(SOUS(...,...)),Xmin,Xmax) */ \ /* ,TRON(INTE(ADD2(...,...)),Xmin,Xmax) */ \ /* ,pasX */ \ /* ) */ \ /* */ \ /* ce qui est tout a fait anormal, puisque cela ramenait sur la bordure de l'image des */ \ /* ellipsoides qui en fait en etaient en dehors... */ \ Bblock \ begin_fuiteQ(DoIn \ ,VINTE(SOUS(ASD1(centre_du_disque,z),MAX2(petit_axe_b,grand_axe_a))) \ ,VINTE(ADD2(ASD1(centre_du_disque,z),MAX2(petit_axe_b,grand_axe_a))) \ ,pasZ \ ) \ /* Nota : on utilise 'MAX2(petit_axe_b,grand_axe_a)' et non pas 'grand_axe_a' car en effet */ \ /* les coordonnees {X,Y} sont dans le referentiel (OX1,OY1), alors que les axes de l'ellipse */ \ /* (grand_axe_a,petit_axe_b) sont mesures dans le referentiel (OX2,OY2). Ainsi, en prenant */ \ /* 'MAX2(...)' on est sur de ne pas manquer des points, meme si des calculs superflus sont */ \ /* malheureusement effectues... */ \ Bblock \ Test(IFOU(IZGT(petit_axe_b),IZGT(grand_axe_a))) \ Bblock \ EGAL(equation_representative_du_contour \ ,ADD2(fCOND(IZGT(grand_axe_a) \ ,EXP2(Xc2(direction_axe,grand_axe_a,petit_axe_b)) \ ,FZERO \ ) \ ,fCOND(IZGT(petit_axe_b) \ ,EXP2(Yc2(direction_axe,grand_axe_a,petit_axe_b)) \ ,FZERO \ ) \ ) \ ); \ /* Valeur courante au point {X,Y} de la fonction representative du contour de la forme */ \ /* materialisant la particule. Les deux operateurs 'LIN2(...)' correspondent a une rotation */ \ /* du grand axe de l'ellipse plane (2D) de facon a ce qu'il s'aligne dans la direction */ \ /* argument 'direction_axe'. On notera que le rapport entre le grand axe et le petit */ \ /* axe est d'une part une fonction de la nature de la particule visualisee, mais aussi de */ \ /* la direction de sa vitesse par rapport a l'observateur : plus elle se situera dans le */ \ /* plan de projection, et plus l'ellipsoide sera allonge, alors qu'inversement, plus elle */ \ /* sera alignee avec l'observateur, et plus il sera spherique... */ \ /* */ \ /* --> */ \ /* V * */ \ /* * */ \ /* * */ \ /* * */ \ /* Y| * */ \ /* | * */ \ /* | projection des deux */ \ /* |______ --> */ \ /* /O X extremites de V : */ \ /* / */ \ /* Z/ Y1| */ \ /* | */ \ /* | . */ \ /* . | ---> . X2 */ \ /* Y2 . | V . */ \ /* . | p * */ \ /* . | * */ \ /* . | * */ \ /* . | * \t */ \ /* O-------------------- */ \ /* X1 */ \ /* */ \ /* ---> --> */ \ /* Soit 't' l'angle que fait le vecteur V (projection du vecteur vitesse V de la */ \ /* p */ \ /* particule a representer) avec l'axe OX1. On a les formules de changement de coordonnees */ \ /* suivantes : */ \ /* */ \ /* ( X2 ) ( +cos(t) +sin(t) ) ( X1 ) */ \ /* ( ) = ( )*( ) */ \ /* ( Y2 ) ( -sin(t) +cos(t) ) ( Y1 ) */ \ /* */ \ /* et */ \ /* */ \ /* ( X1 ) ( +cos(t) -sin(t) ) ( X2 ) */ \ /* ( ) = ( )*( ) */ \ /* ( Y1 ) ( +sin(t) +cos(t) ) ( Y2 ) */ \ /* */ \ /* Le grand axe de l'ellipse est defini par 'direction_axe' qui correspond au vecteur */ \ /* ---> */ \ /* V et a l'axe OX2 ; on a : */ \ /* p */ \ /* */ \ /* direction_axe = [cos(t),sin(t)]. */ \ /* */ \ /* L'ellipse est d'abord construite dans le referentiel (OX2,OY2), ou son equation est */ \ /* simple : */ \ /* */ \ /* 2 2 */ \ /* X2 Y2 */ \ /* ----- + ----- <= 1 */ \ /* 2 2 */ \ /* a b */ \ /* */ \ /* ou 'a' designe le grand axe, et 'b' le petit axe (a >= b), */ \ /* */ \ /* */ \ /* puis par rotation de l'angle '-t', on trouve son equation dans le referentiel correct */ \ /* (OX1,OY1). */ \ /* */ \ /* --> */ \ /* Enfin, la projection du vecteur vitesse V traduit, par son module, la direction de la */ \ /* particule par rapport a l'observateur : plus ce module sera petit (ce qui signifie que */ \ /* la particule va vers (ou "viens de") l'observateur ; on aura alors un grand axe voisin */ \ /* du petit axe (a ~ b), et l'apparence sera voisine d'une sphere. A l'oppose, si la */ \ /* particule se deplace dans le plan de projection, le grand axe sera maximal, et l'ellipse */ \ /* la plus allongee possible... */ \ /* */ \ /* Nota sur la degenerescence des ellipses : rappelons l'equation */ \ /* */ \ /* 2 2 */ \ /* X2 Y2 */ \ /* ----- + ----- <= 1 */ \ /* 2 2 */ \ /* a b */ \ /* */ \ /* lorsque l'un des axes est nul, l'ellipse degenere en l'autre axe, soit : */ \ /* */ \ /* si b=0 : */ \ /* */ \ /* 2 */ \ /* X2 */ \ /* ----- + 0 <= 1, soit : -a <= X2 <= +a. */ \ /* 2 */ \ /* a */ \ /* */ \ /* si a=0 : */ \ /* */ \ /* 2 */ \ /* Y2 */ \ /* 0 + ----- <= 1, soit : -b <= Y2 <= +b. */ \ /* 2 */ \ /* b */ \ /* */ \ Eblock \ ATes \ Bblock \ EGAL(equation_representative_du_contour \ ,DEFINITION_DU_CONTOUR_APPARENT_DE_LA_PARTICULE \ ); \ /* Lorsque les deux axes sont nuls, l'appartenance est systematique... */ \ Eblock \ ETes \ EGAL(distance_normalisee_au_centre \ ,RACX(equation_representative_du_contour) \ ); \ /* Calcul de la distance euclidienne normalisee du point courant {X,Y} au centre du disque. */ \ Test(IFLE(distance_normalisee_au_centre,DEFINITION_DU_CONTOUR_APPARENT_DE_LA_PARTICULE)) \ Bblock \ DEFV(Float,INIT(distance_aleatoire_au_centre,FLOT__UNDEF)); \ /* Valeur aleatoire permettant de determiner si le point courant {X,Y} va etre marque... */ \ DEFV(genere_p,INIT(niveau_ROUGE_du_point,ncR)); \ DEFV(genere_p,INIT(niveau_VERTE_du_point,ncV)); \ DEFV(genere_p,INIT(niveau_BLEUE_du_point,ncB)); \ /* Niveau du point courant initialise sur le maximum, et ce au cas ou le grand axe et le */ \ /* petit axe de l'ellipsoide seraient nuls... */ \ Test(IFET(IZGT(petit_axe_b),IZGT(grand_axe_a))) \ /* Ce test etait autrefois : */ \ /* */ \ /* Test(IZGT(MAX2(petit_axe_b,grand_axe_a))) */ \ /* */ \ /* mais maintenant, on prend en compte l'equation correcte de l'ellipse, alors... */ \ Bblock \ DEFV(Float,INIT(correction_d_anti_aliasing_au_bord \ ,CORRECTION_D_ANTI_ALIASING(distance_normalisee_au_centre) \ ) \ ); \ /* En fait, 'modulation_d_eclairement' pourra etre corrige par ce facteur, car en effet, */ \ /* dans certaines circonstances, le point le plus lumineux de l'ellipsoide peut se trouver */ \ /* tres pres de son contour apparent, et donc creer un probleme d'aliasing. On calcule donc */ \ /* ici la correction en fonction du point courant par rapport au contour apparent de */ \ /* l'ellipsoide. Il convient de remarquer que la distance utilisee ici est une distance */ \ /* bidimensionnelle dans le plan, et non pas une distance dans l'espace tridimensionnel. */ \ /* Elle definie bien (ou du moins son complement a 'COORDONNEE_BARYCENTRIQUE_MAXIMALE') une */ \ /* distance du point courant bidimensionnel {X,Y} au contour apparent... */ \ INITIALISATION_ACCROISSEMENT_3D(vecteur_normal2 \ ,Xc2(direction_axe,grand_axe_a,petit_axe_b) \ ,Yc2(direction_axe,grand_axe_a,petit_axe_b) \ ,Zc2(direction_axe,grand_axe_a,petit_axe_b) \ ); \ /* Le vecteur normal est approxime : */ \ /* */ \ /* --> */ \ /* Le vecteur normal N au point {x,y,z} est defini dans le cas d'une sphere par : */ \ /* */ \ /* --> x y z */ \ /* N = [--- , --- , ---] */ \ /* R R R */ \ /* */ \ /* ce que l'on generalise courageusement en : */ \ /* */ \ /* --> X2 Y2 Z2 */ \ /* N = [---- , ---- , ----] */ \ /* a b a */ \ /* */ \ /* qui n'est pas normalisee et se situe dans le referentiel (OX2,OY2)... */ \ INITIALISATION_ACCROISSEMENT_3D(vecteur_normal1 \ ,LIZ2(NEUT(ASD1(direction_axe,dx)) \ ,ASD1(vecteur_normal2,dx) \ ,NEGA(ASD1(direction_axe,dy)) \ ,ASD1(vecteur_normal2,dy) \ ) \ ,LIZ2(NEUT(ASD1(direction_axe,dy)) \ ,ASD1(vecteur_normal2,dx) \ ,NEUT(ASD1(direction_axe,dx)) \ ,ASD1(vecteur_normal2,dy) \ ) \ ,ASD1(vecteur_normal2,dz) \ ); \ /* Puis passage du referentiel (OX2,OY2) a (OX1,OY1)... */ \ INITIALISATION_ACCROISSEMENT_3D(rayon_lumineux_inverse \ ,SOUS(X_DE_VISUALISATION(X_PHYSIQUE_DANS_01(ASD1(LsourceT \ ,x \ ) \ ) \ ) \ ,FLOT(X) \ ) \ ,SOUS(Y_DE_VISUALISATION(Y_PHYSIQUE_DANS_01(ASD1(LsourceT \ ,y \ ) \ ) \ ) \ ,FLOT(Y) \ ) \ ,SOUS(Z_DE_VISUALISATION(Z_PHYSIQUE_DANS_01(ASD1(LsourceT \ ,z \ ) \ ) \ ) \ ,FLOT(Z) \ ) \ ); \ /* Calcul du rayon lumineux inverse... */ \ EGAL(produit_des_modules_de_N_et_S \ ,RACX(MUL2(pytF3D(vecteur_normal1) \ ,pytF3D(rayon_lumineux_inverse) \ ) \ ) \ ); \ /* Calcul de : */ \ /* */ \ /* --> --> */ \ /* |N|.|S| */ \ /* */ \ Test(IZGT(produit_des_modules_de_N_et_S)) \ Bblock \ EGAL(modulation_d_eclairement \ ,PUIX(COS1(DIVI(prdF3D(vecteur_normal1,rayon_lumineux_inverse) \ ,produit_des_modules_de_N_et_S \ ) \ ) \ ,intensite_speculaire \ ) \ ); \ /* --> --> */ \ /* designant par N la normale et par S le rayon lumineux inverse, la modulation de */ \ /* --> --> */ \ /* l'eclairement s'obtient en calculant le cosinus de l'angle entre N et S : */ \ /* */ \ /* --> --> */ \ /* --> --> N . S */ \ /* cos( N , S ) = --------- */ \ /* --> --> */ \ /* |N|.|S| */ \ /* */ \ /* puis en le ramenant dans [0,1] et en accentuant le phenomene (reflets speculaires)... */ \ /* L'utilisation de la fonction 'COS1(...)' a ete introduite le 19971229165528. */ \ Eblock \ ATes \ Bblock \ EGAL(modulation_d_eclairement \ ,COORDONNEE_BARYCENTRIQUE_MAXIMALE \ ); \ /* Nous sommes ici dans le cas ou la particule est "tangente" a la source lumineuse... */ \ Eblock \ ETes \ /* Nota : il y avait ici autrefois : */ \ /* */ \ /* EGAL(modulation_d_eclairement */ \ /* ,ADD2(COORDONNEE_BARYCENTRIQUE_MINIMALE */ \ /* ,MUL2(correction_d_anti_aliasing_au_bord */ \ /* ,SOUS(modulation_d_eclairement,COORDONNEE_BARYCENTRIQUE_MINIMALE) */ \ /* ) */ \ /* ) */ \ /* ); */ \ /* */ \ /* ce qui avait l'inconvenient de faire l'hypothese que la particule que l'on marque etait */ \ /* entouree de noir (ici, on interpolait implicitement avec du noir). Ceci a ete corrige a */ \ /* l'aide d'un 'BARY(...)' entre le niveau calcule et le niveau anterieur... */ \ Test(IFEXff(modulation_d_eclairement \ ,COORDONNEE_BARYCENTRIQUE_MINIMALE \ ,COORDONNEE_BARYCENTRIQUE_MAXIMALE \ ) \ ) \ Bblock \ PRINT_ERREUR("la modulation d'eclairement est hors de [0,1]"); \ CAL1(Prer1(" elle vaut : %g\n",modulation_d_eclairement)); \ CAL1(Prer1(" petit axe = %g\n",petit_axe_b)); \ CAL1(Prer1(" grand axe = %g\n",grand_axe_a)); \ CAL1(Prer1(" equation = %g\n",equation_representative_du_contour)); \ CAL1(Prer1(" distance = %g\n",distance_normalisee_au_centre)); \ CAL1(Prer1(" aliasing = %g\n",correction_d_anti_aliasing_au_bord)); \ CAL1(Prer3(" normale N = (%g,%g,%g)\n" \ ,ASD1(vecteur_normal1,dx) \ ,ASD1(vecteur_normal1,dy) \ ,ASD1(vecteur_normal1,dz) \ ) \ ); \ CAL1(Prer3(" lumiere S = (%g,%g,%g)\n" \ ,ASD1(rayon_lumineux_inverse,dx) \ ,ASD1(rayon_lumineux_inverse,dy) \ ,ASD1(rayon_lumineux_inverse,dz) \ ) \ ); \ CAL1(Prer1(" N.S = %g\n",prdF3D(vecteur_normal1,rayon_lumineux_inverse))); \ CAL1(Prer1(" |N|.|S| = %g\n",produit_des_modules_de_N_et_S)); \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ \ Test(IFLE(SOUA(_____cNORMALISE_OZ(Z) \ ,loadF_point_valide(Z_Buffer,X,Y) \ ) \ ,_____lNORMALISE_OZ(EPAISSEUR_DE_L_INTERSECTION) \ ) \ ) \ /* Nota : on ecrit ainsi, et non pas : */ \ /* */ \ /* Test(IFLE(SOUA(Z */ \ /* ,_cDENORMALISE_OZ(loadF_point_valide(Z_Buffer,X,Y)) */ \ /* ) */ \ /* ,EPAISSEUR_DE_L_INTERSECTION */ \ /* ) */ \ /* ) */ \ /* */ \ /* afin d'alleger le travail du compilateur... */ \ Bblock \ DEFV(Float \ ,INIT(correction_d_anti_aliasing_aux_intersections \ ,INTERPOLATION_CUBIQUE(COORDONNEE_BARYCENTRIQUE_MINIMALE \ ,FZERO \ ,COORDONNEE_BARYCENTRIQUE_MAXIMALE \ ,FZERO \ ,DIVI(SOUA(_____cNORMALISE_OZ(Z) \ ,loadF_point_valide(Z_Buffer,X,Y) \ ) \ ,_____lNORMALISE_OZ(EPAISSEUR_DE_L_INTERSECTION) \ ) \ ) \ ) \ ); \ /* Lorsque le point a marquer est pratiquement a la meme profondeur que le point precedent, */ \ /* c'est qu'il y a intersection de deux spheres : on va donc faire la moyenne du niveau a */ \ /* a marquer et du niveau anterieur... */ \ Test(IFEQ(correction_d_anti_aliasing_au_bord,COORDONNEE_BARYCENTRIQUE_MAXIMALE)) \ Bblock \ EGAL(correction_d_anti_aliasing_au_bord \ ,correction_d_anti_aliasing_aux_intersections \ ); \ /* Cas ou l'on est juste en presence d'une intersection... */ \ Eblock \ ATes \ Bblock \ EGAL(correction_d_anti_aliasing_au_bord \ ,MIN2(correction_d_anti_aliasing_au_bord \ ,correction_d_anti_aliasing_aux_intersections \ ) \ ); \ /* Cas ou l'on est en presence simultanement d'un bord et d'une intersection. On notera que */ \ /* l'on utilise la fonction 'MIN2(...)', car en effet l'utilisation de 'MOYE(...)' faisait */ \ /* apparaitre des defauts visuels (points sur-lumineux) la ou il y avait une forte disparite */ \ /* entre les deux correctiond ("de bord" et "d'intersection") ; l'utilisation de 'MAX2(...)' */ \ /* ne faisait qu'amplifier ces defauts... */ \ Eblock \ ETes \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ Test(NINCff(correction_d_anti_aliasing_au_bord \ ,COORDONNEE_BARYCENTRIQUE_MINIMALE \ ,COORDONNEE_BARYCENTRIQUE_MAXIMALE \ ) \ ) \ Bblock \ PRINT_ERREUR("le parametre d'interpolation n'est pas dans [0,1]"); \ CAL1(Prer1(" parametre d'interpolation = %g\n",correction_d_anti_aliasing_au_bord)); \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ \ \ niveau_RVB(niveau_ROUGE_du_point,nbR,ncR,iR); \ niveau_RVB(niveau_VERTE_du_point,nbV,ncV,iV); \ niveau_RVB(niveau_BLEUE_du_point,nbB,ncB,iB); \ /* Enfin, calcul du niveau du point courant. */ \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ GENERATION_D_UNE_VALEUR_SANS_DEPLACEMENT(distance_aleatoire_au_centre \ ,MUL2(rayon_interieur_relatif_d_une_particule \ ,MAX2(petit_axe_b,grand_axe_a) \ ) \ ,MUL2(rayon_exterieur_relatif_d_une_particule \ ,MAX2(petit_axe_b,grand_axe_a) \ ) \ ); \ /* Calcul d'une valeur aleatoire permettant de determiner si le point courant {X,Y} doit */ \ /* etre marque. On utilise la fonction 'GENERATION_D_UNE_VALEUR_SANS_DEPLACEMENT(...)' et */ \ /* non pas 'GENERATION_D_UNE_VALEUR(...)' car en effet les calculs sont effectues ici dans */ \ /* les coordonnees de visualisation, et en particulier apres projection perspective. Dans */ \ /* ces conditions, lorsqu'un calcul en stereoscopie est effectuee, il y a peu de chance */ \ /* pour qu'une meme particule soit materialisee avec le meme nombre de points sur les vues */ \ /* 'DROITE' et 'GAUCHE'. Dans ces conditions, 'GENERATION_D_UNE_VALEUR(...)' n'est pas */ \ /* appele le meme nombre de fois pour les images 'DROITE' et 'GAUCHE', et donc les suites */ \ /* d'evenements pour les deux images seront donc completement differentes, ce qui est */ \ /* impensable... */ \ Test(IFGE(distance_aleatoire_au_centre \ ,RdisF3D(FLOT(X),FLOT(Y),FLOT(Z) \ ,ASD1(centre_du_disque,x),ASD1(centre_du_disque,y),ASD1(centre_du_disque,z) \ ) \ ) \ ) \ /* Nota : on pourrait dire a juste raison que ce test n'est pas tres optimise ; en effet, */ \ /* il serait plus asticieux de le placer tout en haut de cette sequence, et par exemple */ \ /* juste apres le test : */ \ /* */ \ /* Test(IFOU(IZGT(petit_axe_b),IZGT(grand_axe_a))) */ \ /* */ \ /* Mais en fait j'ai procede ainsi afin de simplifier la mise en place de cette possibilite. */ \ /* */ \ /* D'autre part, le fait qu'il soit fait systematiquement, sans optimiser (par exemple dans */ \ /* le cas ou les rayons "interieur" et "exterieur" sont tous deux egaux a 1.0) fait que la */ \ /* sequence des nombres aleatoires generes (avec tous les autres parametres inchanges) est */ \ /* est toujours la meme, ce qui fait que la sequence des evenements est elle aussi la meme. */ \ Bblock \ DEFV(Float,INIT(selecteur_de_marquage_du_point_courant,FLOT__UNDEF)); \ /* Valeur aleatoire permettant de determiner si le point courant {X,Y} va etre marque... */ \ GENERATION_D_UNE_VALEUR_SANS_DEPLACEMENT(selecteur_de_marquage_du_point_courant \ ,EVENEMENT_IMPOSSIBLE \ ,EVENEMENT_CERTAIN \ ); \ /* Calcul d'une valeur aleatoire permettant de determiner si le point courant {X,Y} doit */ \ /* etre marque. En effet, si ce tirage au sort n'est pas fait, il y a trop de points a */ \ /* marquer, or etant en tridimensionnel, apres projection, la sphere de materialisation */ \ /* parait pleine. On utilise la fonction 'GENERATION_D_UNE_VALEUR_SANS_DEPLACEMENT(...)' et */ \ /* non pas 'GENERATION_D_UNE_VALEUR(...)' car en effet les calculs sont effectues ici dans */ \ /* les coordonnees de visualisation, et en particulier apres projection perspective. Dans */ \ /* ces conditions, lorsqu'un calcul en stereoscopie est effectuee, il y a peu de chance */ \ /* pour qu'une meme particule soit materialisee avec le meme nombre de points sur les vues */ \ /* 'DROITE' et 'GAUCHE'. Dans ces conditions, 'GENERATION_D_UNE_VALEUR(...)' n'est pas */ \ /* appele le meme nombre de fois pour les images 'DROITE' et 'GAUCHE', et donc les suites */ \ /* d'evenements pour les deux images seront donc completement differentes, ce qui est */ \ /* impensable... */ \ Test(IFGT(selecteur_de_marquage_du_point_courant,seuil_de_randomisation_des_particules)) \ Bblock \ store_point_ND_RVB(niveau_ROUGE_du_point,niveau_VERTE_du_point,niveau_BLEUE_du_point \ ,iR,iV,iB \ ,X,Y \ ,_____cNORMALISE_OZ(Z) \ ,TRI_DIMENSIONNEL \ ); \ /* Generation des points de l'ellipsoide, et ce uniquement si la distance aleatoire se situe */ \ /* dans [rayon_interieur_relatif_d_une_particule,rayon_exterieur_relatif_d_une_particule] */ \ /* (en renormalisant dans [0,1]), puis si ce point est selectionne a l'aide d'un deuxieme */ \ /* tirage au sort afin de ne pas trop remplir la sphere... */ \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ Eblock \ ATes \ Bblock \ Eblock \ ETes \ Eblock \ end_fuiteQ(EDoI) \ Eblock \ end_ligneQ(EDoI) \ Eblock \ end_colonneQ(EDoI) \ Eblock \ /* ATTENTION : {X,Y,Z} E [Xmin,Xmax]x[Ymin,Ymax]x[Zmin,Zmax]. */