Si on a des pépettes, on peut aussi utiliser Houdini qui fait ça à merveille et récupérer les particules dans Maya.

Ce que je vous propose est une méthode un peu "clef en main". Vous sélectionnez votre mesh, appliquez le script, réglez deux trois trucs appuyez sur play pour lancer la génération et c'est bon!

remplir_mesh_particule001.png

remplir_mesh_particule002.png

Mais avant ça, je vais vous expliquer un peu comment ça marche. :reflechi:

:longBar:
Theorie

:mayaProf:

Alors alors, comment ça se passe?

Pour faire simple, il faut lancer des particules dans un espace/volume, de manière aléatoire, puis déterminer si la particule créé est dans un mesh ou non...

Créer des particules de manière aléatoire dans un espace, ça on sait faire via un volume emitter

remplir_mesh_particule003.png

Avec cette méthode, chaque particule créé est placé quelque part dans le cube ou la sphere.

Bien, mais comment savoir si ses particules sont dans un mesh? Et bien on va se servir d'un node dont je vois ai déjà parlé (faire un lien): closestPointOnMesh.

oriente_particle_maya002.png

Ce node permet de faire pas mal de choses. On lui donne en entrée un mesh ainsi qu'une position dans l'espace, et il nous donne, en sortie, d'autres informations.

Les informations qui nous intéressent sont:

  • La position, sur le mesh, du point le plus proche du point donné en entré.
  • La normale de ce point.

Et il n'en faut pas plus pour déterminer, avec ses informations, si le point est à l'intérieur ou à l'exterieur du mesh (si si je vous assure! :dentcasse: ).

Les plus perspicaces d'entre vous l'auront remarqué, votre mesh se doit d'être un minimum propre pour faire fonctionner correctement cette méthode.

En effet, si les normales du mesh sont "bizarre", il ne s'en sortira pas! :nannan:

Le principe est le suivant:

Schema_001.png

  • Vous prenez un point, dans l'espace, notre particule.

Schema_002.png

  • Vous cherchez le point le plus proche de votre particule sur le mesh à remplir.

Schema_003.png

  • Vous récupérez la normale de ce point (sous forme de vecteur).

Schema_004.png

  • Vous soustrayez la position du point sur le mesh à la position de la particule pour obtenir un vecteur du point du mesh vers votre particule.

Schema_005.png

V1-V2=V3 :sourit:
  • Vous récupérez l'angle entre ses deux vecteurs (vecteurs normale et vecteurs "point du mesh vers particule").

Schema_006.png

  • Si cet angle est supérieur à 90 degrée, le point est à l'intérieur du mesh!

La magie des maths!

:longBar:
Pratique :mechantCrash:

Allez, on y va! Commencez par créer un emitter volumetric:

remplir_mesh_particule004.png

remplir_mesh_particule005.png

remplir_mesh_particule006.png

remplir_mesh_particule003.png

Créez un mesh:

remplir_mesh_particule007.png

Créer un closestPointOnMesh:

remplir_mesh_particule008.png

Connectez lui le mesh:

remplir_mesh_particule009.png

worldMesh pour le mesh et worldMatrix pour savoir ou est placé le mesh dans l'espace. :hehe:

Maintenant on passe à la seconde partie de la création, la plus dur. :grenadelauncher:

Créez un expression de création de particule:

remplir_mesh_particule010.png

remplir_mesh_particule011.png

Et voici l'expression:

vector $particlePos = myParticleSystem.position;
 
setAttr myClosestPointOnMeshNode.inPositionX ($particlePos.x);
setAttr myClosestPointOnMeshNode.inPositionY ($particlePos.y);
setAttr myClosestPointOnMeshNode.inPositionZ ($particlePos.z);
 
float $pointPos[] = `getAttr myClosestPointOnMeshNode.position`;
float $n[] = `getAttr myClosestPointOnMeshNode.normal`;
 
vector $difference = myParticleSystem.position - <<$pointPos[0],$pointPos[1],$pointPos[2]>>;
 
vector $normal = <<$n[0],$n[1],$n[2]>>;
 
float $test = rad_to_deg(angle($normal,unit($difference)));
 
if ($test > 90)
{
	myParticleSystem.lifespanPP = 1000000;
}
else
{
	myParticleSystem.lifespanPP = 0;
}

(Ouïe que je suis méchant! :gne2: )

Voici les explications:

vector $particlePos = myParticleSystem.position;

Ici, on récupère la position de la particule courante, celle qu'on va créer.

setAttr myClosestPointOnMeshNode.inPositionX ($particlePos.x);
setAttr myClosestPointOnMeshNode.inPositionY ($particlePos.y);
setAttr myClosestPointOnMeshNode.inPositionZ ($particlePos.z);

On entre les valeurs de position de la particule courante via "setAttr" (afin de s'assurer que le node est évalué).

float $pointPos[] = `getAttr myClosestPointOnMeshNode.position`;

Dans le même esprit que précédemment, ici on récupère la position du point le plus proche de notre particule.

float $n[] = `getAttr myClosestPointOnMeshNode.normal`;

On récupère, dans un array de float, les valeurs XYZ de la normal du point (sur le mesh) le plus proche de notre particule.

vector $difference = myParticleSystem.position - <<$pointPos[0],$pointPos[1],$pointPos[2]>>;

On récupère le vecteur du "point sur la surface" vers la position de la particule (rappelez vous! Le coups de la soustraction des vecteurs. C'est ici! :hehe: ).

vector $normal = <<$n[0],$n[1],$n[2]>>;

On "convertie" nos valeurs de normal (qui étaient dans un array), en vecteur.

float $test = rad_to_deg(angle($normal, unit($difference)));

On récupère l'angle (en radian) entre les deux vecteurs ($normal et $difference) que l'on convertie en dégrée. (unit() sert à normaliser un vecteur, angle() sert a récupérer l'angle en radian entre deux vecteur et rad_to_deg() convertie un angle radian en dégrée).

if ($test > 90)
{
	myParticleSystem.lifespanPP = 1000000;
}
else
{
	myParticleSystem.lifespanPP = 0;
}

Et pour finir, on test la valeur de l'angle. Si il est supérieur à 90°, on considère que la particule à une durée de vie infini (enfin très grande... :pasClasse: ).

Sinon, on la fait directement "mourrir" :gniarkgniark: .

:longBar:
Le script MEL

Ouf, bon, maintenant que vous avez tout bien compris (n'est ce pas? :seSentCon: ), je vous donne un script qui vous créé ce petit système tout seul!

La seul chose que vous ayez à faire, c'est sélectionner un mesh, appliquer le script et bien placer le cube emitter pour qu'il englobe intelligemment la géométrie. Lancer la dynamique (play) et c'est bon!

{
	string $mySelectedMeshArray[] = `ls -sl`;	// on récupère la sélection
	string $mySelectedMesh = $mySelectedMeshArray[0];	// on ne prend que le premier élément de la selection
	string $mySelectedMeshShapeArray[] = `listRelatives -shapes $mySelectedMesh`;	// on récupère le node de mesh depuis le node de transforme
	string $mySelectedMeshShape = $mySelectedMeshShapeArray[0];	// on ne prend que le premier élément de la liste renvoyé
 
	string $myClosestPointOnMeshNode = `createNode closestPointOnMesh`;	// on cree le node de closestPointOnMesh
	connectAttr -f ($mySelectedMeshShape+".worldMesh[0]") ($myClosestPointOnMeshNode+".inMesh");    // on lui connecte le mesh
	connectAttr -f ($mySelectedMeshShape+".worldMatrix[0]") ($myClosestPointOnMeshNode+".inputMatrix");	// et la matrice de transformation
 
	//create Emitter Volume
	string $myEmitterArray[] = `emitter -pos 0 0 0 -type volume -r 100 -sro 0 -nuv 0 -cye none -cyi 1 -spd 1 -srn 0 -nsp 1 -tsp 0 -mxd 0 -mnd 0 -dx 1 -dy 0 -dz 0 -sp 0 -vsh cube -vof 0 0 0 -vsw 360 -tsr 0.5 -afc 1 -afx 1 -arx 0 -alx 0 -rnd 0 -drs 0 -ssz 0`;
	string $myEmitter = $myEmitterArray[0];
	string $myParticleSystemArray[] = `particle`;	// créé un système de particule
	string $myParticleSystem = $myParticleSystemArray[0];
	connectDynamic -em $myEmitter $myParticleSystem;	// connecte l'emitter au système de particule
 
 
 
	setAttr ($myEmitter+".awayFromCenter") 0;	// désactive la vitesse des particules
	setAttr ($myParticleSystem+".lifespanMode") 3;	// on va mettre le lifespan nous même
 
	// créé l'expression
	string $expression = "vector $particlePos = "+$myParticleSystem+".position;\n";
	$expression += "\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionX ($particlePos.x);\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionY ($particlePos.y);\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionZ ($particlePos.z);\n";
	$expression += "\n";
	$expression += "float $n[] = `getAttr "+$myClosestPointOnMeshNode+".normal`;\n";
	$expression += "float $pointPos[] = `getAttr "+$myClosestPointOnMeshNode+".position`;\n";
	$expression += "\n";
	$expression += "vector $difference = "+$myParticleSystem+".position - <<$pointPos[0],$pointPos[1],$pointPos[2]>>;\n";
	$expression += "\n";
	$expression += "vector $normal = <<$n[0],$n[1],$n[2]>>;\n";
	$expression += "\n";
	$expression += "float $test = rad_to_deg(angle($normal,unit($difference)));\n";
	$expression += "\n";
	$expression += "if ($test > 90)\n";
	$expression += "{\n";
	$expression += "	"+$myParticleSystem+".lifespanPP = 1000000;\n";
	$expression += "}\n";
	$expression += "else\n";
	$expression += "{\n";
	$expression += "	"+$myParticleSystem+".lifespanPP = 0;\n";
	$expression += "}\n";
 
	dynExpression -s $expression -c $myParticleSystem;
}

Et voila!

remplir_mesh_particule001.png

Et comme toujours: ->la scène Maya< (bouton droit de souris, save as...).

:longBar:
Conclusion

Bien entendu, il reste un bon nombre de méthodes pour optimiser ce script (utiliser des produits scalaires plutôt que des angles, le lifespanPP = 1000000 est pas forcément des plus pratique, etc...).

Mais le principe est là! :)

N'hésitez pas à laisser un commentaire si vous voyez un moyen d'améliorer ce script. Je mettrai ce billet à jour.

:longBar:
Le lien qui m'a beaucoup aidé

http://blog.maxtorag.sk/?p=39

Un grand merci à son auteur! :sourit:

A bientôt!

Dorian

:longBar:
MAJ 8 Mars 2010

On peut facilement faire évoluer cette expression. :reflechi:

L'exemple ci dessous vous permet de faire un dégradé du nombre de particules générées sur les bords du mesh.

float $fallOffDist = 2;	// la distance le long de laquelle la génération des particules est diminué.
 
vector $particlePos = myParticleSystem.position;
 
setAttr myClosestPointOnMeshNode.inPositionX ($particlePos.x);
setAttr myClosestPointOnMeshNode.inPositionY ($particlePos.y);
setAttr myClosestPointOnMeshNode.inPositionZ ($particlePos.z);
 
float $n[] = `getAttr myClosestPointOnMeshNode.normal`;
float $pointPos[] = `getAttr myClosestPointOnMeshNode.position`;
vector $pointPosVec = <<$pointPos[0],$pointPos[1],$pointPos[2]>>;
 
vector $difference = particleShape1.position - $pointPosVec;
 
vector $normal = <<$n[0],$n[1],$n[2]>>;
 
float $test = rad_to_deg(angle($normal,unit($difference)));
 
if ($test > 90)
{
	float $dist = mag($particlePos - $pointPosVec);	// on calcule la distance entre la particule et le point sur le mesh
 
	float $luck = $dist/$fallOffDist;	// on fait un ratio.
 
	if($luck > rand(1))	// si le ratio est plus grand qu'une valeur alléatoire entre 0 et 1, on genere la particule.
	{
		myParticleSystem.lifespanPP = 1000000;
	}
	else
	{
		myParticleSystem.lifespanPP = 0;
	}
 
}
else
{
	myParticleSystem.lifespanPP = 0;
}

Il suffit de faire varier la variable $fallOffDist pour agrandir ou diminuer la taille du dégradé.

Bien entendu, cette méthode est beaucoup plus longue que la précédente (qui n'était déjà pas un exemple en terme de rapidité. :seSentCon: ).

Exemple:

remplir_mesh_particule012.png

Avant. Les bords de la géométrie sont très facilement discernables.

remplir_mesh_particule013.png

Après. On voit bien que les bords ont moins de particules que l'intérieur.

Ducoup, le script MEL de génération de l'expression sur le mesh selectionné est le suivant:

{
	string $mySelectedMeshArray[] = `ls -sl`;	// on récupère la sélection
	string $mySelectedMesh = $mySelectedMeshArray[0];	// on ne prend que le premier élément de la selection
	string $mySelectedMeshShapeArray[] = `listRelatives -shapes $mySelectedMesh`;	// on récupère le node de mesh depuis le node de transforme
	string $mySelectedMeshShape = $mySelectedMeshShapeArray[0];	// on ne prend que le premier élément de la liste renvoyé
 
	string $myClosestPointOnMeshNode = `createNode closestPointOnMesh`;	// on cree le node de closestPointOnMesh
	connectAttr -f ($mySelectedMeshShape+".worldMesh[0]") ($myClosestPointOnMeshNode+".inMesh");    // on lui connecte le mesh
	connectAttr -f ($mySelectedMeshShape+".worldMatrix[0]") ($myClosestPointOnMeshNode+".inputMatrix");	// et la matrice de transformation
 
	//create Emitter Volume
	string $myEmitterArray[] = `emitter -pos 0 0 0 -type volume -r 100 -sro 0 -nuv 0 -cye none -cyi 1 -spd 1 -srn 0 -nsp 1 -tsp 0 -mxd 0 -mnd 0 -dx 1 -dy 0 -dz 0 -sp 0 -vsh cube -vof 0 0 0 -vsw 360 -tsr 0.5 -afc 1 -afx 1 -arx 0 -alx 0 -rnd 0 -drs 0 -ssz 0`;
	string $myEmitter = $myEmitterArray[0];
	string $myParticleSystemArray[] = `particle`;	// créé un système de particule
	string $myParticleSystem = $myParticleSystemArray[0];
	connectDynamic -em $myEmitter $myParticleSystem;	// connecte l'emitter au système de particule
 
 
 
	setAttr ($myEmitter+".awayFromCenter") 0;	// désactive la vitesse des particules
	setAttr ($myParticleSystem+".lifespanMode") 3;	// on va mettre le lifespan nous même
 
	// créé l'expression
	string $expression = "float $fallOffDist = 2;\n";
	$expression += "\n";
	$expression += "vector $particlePos = "+$myParticleSystem+".position;\n";
	$expression += "\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionX ($particlePos.x);\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionY ($particlePos.y);\n";
	$expression += "setAttr "+$myClosestPointOnMeshNode+".inPositionZ ($particlePos.z);\n";
	$expression += "\n";
	$expression += "float $n[] = `getAttr "+$myClosestPointOnMeshNode+".normal`;\n";
	$expression += "float $pointPos[] = `getAttr "+$myClosestPointOnMeshNode+".position`;\n";
	$expression += "vector $pointPosVec = <<$pointPos[0],$pointPos[1],$pointPos[2]>>;\n";
	$expression += "\n";
	$expression += "vector $difference = "+$myParticleSystem+".position - $pointPosVec;\n";
	$expression += "\n";
	$expression += "vector $normal = <<$n[0],$n[1],$n[2]>>;\n";
	$expression += "\n";
	$expression += "float $test = rad_to_deg(angle($normal,unit($difference)));\n";
	$expression += "\n";
	$expression += "if ($test > 90)\n";
	$expression += "{\n";
	$expression += "	float $dist = mag($particlePos - $pointPosVec);\n";
	$expression += "	float $luck = $dist/$fallOffDist;\n";
	$expression += "\n";
	$expression += "	if($luck > rand(1))\n";
	$expression += "	{\n";
	$expression += "		"+$myParticleSystem+".lifespanPP = 1000000;\n";
	$expression += "	}\n";
	$expression += "	else\n";
	$expression += "	{\n";
	$expression += "		"+$myParticleSystem+".lifespanPP = 0;\n";
	$expression += "	}\n";
	$expression += "}\n";
	$expression += "else\n";
	$expression += "{\n";
	$expression += "	"+$myParticleSystem+".lifespanPP = 0;\n";
	$expression += "}\n";
 
	dynExpression -s $expression -c $myParticleSystem;
}

En espérant que ça vous aide! :sourit: