/*************************************************************************************************************************************/
/* */
/* 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]. */