La cocotte : étape 1

Répondre
Partager Rechercher
Comme suite à la discussion qui a eu lieu ici voici la première étape de la réalisation de la cocotte.

Pour mémoire j'ai déjà développé la réalisation de mouvements dans SL avec deux guides (Rotations pour les nuls et Guide des machines) qu'on peut trouver ici.

La première étape est de posséder la maîtrise de la version papier. Donc apprendre déjà, si ce n'est déjà fait, à en réaliser une, s'amuser avec, la plier et déplier dans tous les sens, en un mot apprivoiser la cocotte (enfin en 3 mots).

L'étape suivante est de modéliser les faces nécessaires. Voilà ce que ça donne pour la cocotte :

img01.png

Vérifiez sur votre exemplaire papier !

On se retrouve donc avec 14 facettes. En transposant en SL ça nous fera donc 14 primitives. D'autre part pour des questions de système de référence on va ajouter au milieu une primitive qui restera bien orientée par rapport au repère de base. Donc en tout on aura 15 primitives.

On pourrait gagner 2 primitives si on admet que les pattes ne seront jamais pliées mais on est pas à deux primitives près…

Pour s'y retrouver il faut aussi nommer les primitives. J'ai fait un truc simple :

img02.png

Comme les primitives sont parfaitement symétriques j'ai numéroté celles qui se font face avec juste une indexation pour les différencier.
J'ai ajouté une petit boule au centre pour figurer la primitive mère et aussi la représentation du système de coordonnées en haut à droite.

La réalisation du build dans SL ne présente pas de difficulté majeure. On part de prismes aplatis. Il faut juste être précis dans le positionnement des primitives (se rappeler que la diagonale d'un carré de côté 1 est égale à racine de 2). D'autre part il vaut mieux colorer les primitives pour les distinguer !

img03.PNG

Pour les dimensions je suis parti sur une base de prismes de 2 mètres de côté pour les deux centrales. Du coup la surface totale est de 4m x 4m. Mais on va s'arranger pour que le code fonctionne quelles que soient les dimensions. L'orienttaion de chaque primitive n'a pas d'importance puisqu'on tiendra compte de la rotation de départ pour chacune.

Pour ne pas se perdre il vaut mieux commencer modestement avec juste deux primitives à bouger, les plus simples : a1 et a2, ça permettra de s'échauffer…

On va donc se débarrasser de tout le reste et conserver que la primitive racine et les deux ailes :

img04.png

img05.PNG

Dans votre build assurez-vous d'avoir bien comme racine la primitive centrale.

En limitant les éléments à bouger on se simplifie la vie et on avance prudemment.

En plus comme les primitives sont des moitiés de carrés leur centre se retrouve fusionné avec le centre de l'objet, ce qui va aussi simplifier les calculs.

Les numéros de liaison

La première chose va être de connaître le numéro de liaison de ces deux primitives. Comme on les a nommées ça ne va pas être bien difficile et on va pouvoir commencer à coder.

On va mettre les noms des primitives dans une liste :

list PRIM_NAMES = ["a1","a2"];

Pour le moment la liste est courte mais elle se remplira peu à peu.

On va aussi prévoir une liste pour recueillir les numéros de liaison correspondants :

list l_link = [0,0];


Pour le moment c'est à zéro, on va actualiser les valeurs avec cette petite fonction :


linkedPrimsDetection(){
integer i;
integer n = llGetNumberOfPrims();
for(i = 2; i <= n; ++i){
string name = llList2String(llGetLinkPrimitiveParams(i, [PRIM_NAME]), 0);
integer id = llListFindList(PRIM_NAMES, [name]);
if(~id) l_link = llListReplaceList(l_link, [i], id, id);
}
}


On passe enrevue toutes les primitives de l'objet et lorsqu'on rencontre un nom de la liste PRIM_NAMES on met son numéro de liaison dans la case correspondante de la liste l_link.

Pour simplifier l'accès à ces valeur on va créer deux variables :

id_a1 = llList2Integer(l_link, llListFindList(PRIM_NAMES, ["a1"]));
id_a2 = llList2Integer(l_link, llListFindList(PRIM_NAMES, ["a2"]));

Dans la première on aura le numéro de liaison de a1 et dans la deuxième celui de a2.

Rotation de départ

On a aussi besoin de connaître la rotation de chacune des deux primitives (pour la position on a vu que c'est le centre de l'objet). En effet on a été obligés de les faire tourner pour les positionner du coup leurs axes locaux ne sont plus alignés avec les axes de l'objet.

On va créer une petite fonction pour ça :

list getPrimParams(integer id){
return llGetLinkPrimitiveParams(id, [PRIM_ROT_LOCAL, PRIM_POS_LOCAL]);
}

Cette fonction renvoie une liste contenant la rotation et la position locales. Pour le moment on n'a besoin que de la rotation mais la position sera nécessaire pour la suite.

On peut alors initialiser les variables pour les deux primitives :

r_a1 = llList2Rot(getPrimParams(id_a1), 0);
r_a2 = llList2Rot(getPrimParams(id_a2), 0);

On a la rotation de départ de a1 dans la première variable et celle de a2 dans la seconde.

Mouvement et Interpolation

Comme on va effectuer des mouvements on va avoir besoin d'une routine de calcul de valeurs intermédiaires, on parle dans ce cas d'interpolation :


float fCos(float x, float y, float t){
float F = (1 - llCos(t * PI)) / 2;
return x * (1 - F) + y * F;
}

Cette fonction qui est issue de la librairie de SL renvoie une valeur numérique entre x et y en fonction de la valeur de t qui doit être entre 0 et 1. Par exemple :

  • x = 10, y = 20, t = 0,1 => on a 11 en retour
  • x = 10, y = 20, t = 0,5 => on a 15 en retour
Pratique non ?

D'autre part j'ai choisi une interpolation sinusoïdale pour avoir un mouvement plus naturel (ça démarre lentement, ça accélère puis ça ralentit à la fin).

On va avoir besoin aussi de régler la vitesse du mouvement :

float TIME=5;
float TEMPS_BASE=.04;
float f_step;

La première constante va régler la durée totale en seconde.
Le seconde variable va régler la durée de chaque étape du mouvement en seconde aussi.
La variable step nous indiquera la fraction de durée sur la base de l'unité pour la durée totale (utile pour l »interpolation) :

f_step=1.0/(TIME/TEMPS_BASE);

Le mouvement

Venons-en maintenant au mouvement avec cette fonction :

movement(){
float step;
while((step+=f_step)<=1.0){
// Calculus a1 and a2
rotation rot_a1=llEuler2Rot(<fCos(0,-80,step)*DEG_TO_RAD,0,0>);
rotation rot_a2=llEuler2Rot(<fCos(0,80,step)*DEG_TO_RAD,0,0>);

// Movement
llSetLinkPrimitiveParamsFast(id_a1,[PRIM_ROT_LOCAL,r_a1*rot_a1]);
llSetLinkPrimitiveParamsFast(id_a2, [PRIM_ROT_LOCAL, r_a2 * rot_a2]);
llSleep(TEMPS_BASE);
}
}

On a une boucle avec while dans laquelle on fait évoluer la valeur de la variable step entre 0 et 1,0.

On va appliquer à chaque primitive une rotation de 80 degrés pour laisser un peu d'air. Le calcul pour a1 est ici :

rotation rot_a1=llEuler2Rot(<fCos(0,-80,step)*DEG_TO_RAD,0,0>);

On applique la rotation uniquement sur l'axe X en allant progressivement de 0 à -80 degrés. La fonction llEuler2Rot est destinée à transformer la représentation de la rotation sous forme de vecteur (compréhensible) en sa version sous forme de quaternion (moins compréhensible pour nous mais indispensable pour la machine).

Pour mémoire on a des vecteurs avec les trois coordonnées ainsi : <x, y, z>. On peut ainsi préciser une position ou une rotation dans l'espace en 3 dimensions.

Pour a2 on a le même code mais avec l'angle inversé.

On applique la rotation avec la fonction llSetLinkPrimitiveParamsFast qui présente l'avantage d'être sans délai après exécution :

llSetLinkPrimitiveParamsFast(id_a1,[PRIM_ROT_LOCAL,r_a1*rot_a1]);

Regardez au niveau de la rotation. On ajoute à la rotation de base de la primitive (r_a1) la rotation calculée (rot_a1). Pour ajouter deux rotations on utilise le signe de la multiplication (c'est comme ça faut s'y faire). En gros on dit :

  1. au départ ma primitive a une rotation r_a1
  2. je lui applique ensuite une rotation rot_a1
Cette logique deviendra importante lorsqu'on aura plusieurs rotations à prendre en compte.
Pour finir on attend le temps nécessaire avant de passer au micro-mouvement suivant :

llSleep(TEMPS_BASE);

La suite juste après...

Dernière modification par bestmomo ; 07/06/2016 à 13h27.
Cool
/me fond complètement ...........................


T'es un dieu techniquement parlant bien sûr !!

Je vais me l'imprimer ce tuto sur de sur !!

Mais euh nen m'appelle pas cocotte pour autant hein 'tention nonmé
La suite ici :

Si tout se passe bien on devrait avoir ce mouvement :

img06.png

Ce n'est pas encore une cocotte mais déjà un papillon !

Le code

Voilà le code complet de cette première étape :

/////////////////////////////////////////////////////////////////
// //
// Etape 1 //
// //
/////////////////////////////////////////////////////////////////

// Version
string VERSION = "1.0";

// Time
float TIME = 5;
float TEMPS_BASE = .04;

// Primitives
list PRIM_NAMES = ["a1","a2"];

// Dimensions
float SIDE = 2.0;


// -----------------------------------------------
// Variables
// -----------------------------------------------

// Dimensions
float diagonal;

// Link numbers
list l_link = [0,0];

// Indexes
integer id_a1;
integer id_a2;

// Rotations
rotation r_a1;
rotation r_a2;

// Utilities
float f_step;

// -----------------------------------------------
// Initialisations
// -----------------------------------------------
initialisations() {
// Links detection
linkedPrimsDetection();

// Time
f_step = 1.0 / (TIME / TEMPS_BASE);

// Indexes
id_a1 = llList2Integer(l_link, llListFindList(PRIM_NAMES, ["a1"]));
id_a2 = llList2Integer(l_link, llListFindList(PRIM_NAMES, ["a2"]));

// Dimensions
diagonal = SIDE * llCos(PI_BY_TWO / 2.0);

// Initialisations a1
r_a1 = llList2Rot(getPrimParams(id_a1), 0);

// Initialisations a2
r_a2 = llList2Rot(getPrimParams(id_a2), 0);
}

// -----------------------------------------------
// Get prim params
// -----------------------------------------------
list getPrimParams(integer id) {
return llGetLinkPrimitiveParams(id, [PRIM_ROT_LOCAL, PRIM_POS_LOCAL]);
}

// -----------------------------------------------
// Linked primitives detection
// -----------------------------------------------
linkedPrimsDetection(){
integer i;
integer n = llGetNumberOfPrims();
for(i = 2; i <= n; ++i) {
string name = llList2String(llGetLinkPrimitiveParams(i, [PRIM_NAME]), 0);
integer id = llListFindList(PRIM_NAMES, [name]);
if(~id) l_link = llListReplaceList(l_link, [i], id, id);
}
}

// -----------------------------------------------
// Interpolation
// -----------------------------------------------
float fCos(float x, float y, float t) {
float F = (1 - llCos(t * PI)) / 2;
return x * (1 - F) + y * F;
}

// -----------------------------------------------
// Movement
// -----------------------------------------------
movement() {
float step;
while((step += f_step) <= 1.0) {
// Calculus a1 and a2
rotation rot_a1 = llEuler2Rot(<fCos(0, -80, step) * DEG_TO_RAD, 0, 0>);
rotation rot_a2 = llEuler2Rot(<fCos(0, 80, step) * DEG_TO_RAD, 0, 0>);

// Movement
llSetLinkPrimitiveParamsFast(id_a1, [
PRIM_ROT_LOCAL, r_a1 * rot_a1,
PRIM_LINK_TARGET, id_a2, PRIM_ROT_LOCAL, r_a2 * rot_a2
]);
llSleep(TEMPS_BASE);
}
}

// -----------------------------------------------
// Base state
// -----------------------------------------------
default
{
state_entry() {
llWhisper(0, "Cocotte version " + VERSION);
initialisations();
}

touch_start(integer total_number)
{
movement();
state reset;
}
}

state reset
{
touch_start(integer total_number)
{
llSetLinkPrimitiveParamsFast(id_a1, [
PRIM_ROT_LOCAL, r_a1,
PRIM_LINK_TARGET, id_a2, PRIM_ROT_LOCAL, r_a2
]);
state default;
}
}

On y retrouve l'ensemble de ce que j'ai évoqué ci-dessus avec en plus un état (reset) destiné à tout remettre d'aplomb après le mouvement. Quand on fait des essais de rotation il est toujours judicieux de prévoir ce genre d'action.

J'ai pour habitude d'angliciser les nom et les commentaires.

J'attends vos questions si quelque chose n'est pas clair pour cette étape, on pourra ainsi passer à la suite un peu plus délicate pour les rotations...

Dernière modification par bestmomo ; 07/06/2016 à 13h42.
Citation :
Publié par bestmomo
D'accord ma cocotte .
Dis-moi ta prim du milieu elle est reliée à tes deux triangles ?


Vous ne connaissez pas un sandbox qui ne vous renvoie vos prims au bout de 5 minutes alors que vous êtes en pleine construction ?
Citation :
Publié par Grace HILL
Dis-moi ta prim du milieu elle est reliée à tes deux triangles ?


Vous ne connaissez pas un sandbox qui ne vous renvoie vos prims au bout de 5 minutes alors que vous êtes en pleine construction ?
La prim du milieu est la racine du build, donc effectivement reliée aux deux autres.

Personnellement je vais dans les sandbox beta, c'est assez tranquille, faut juste appartenir au groupe.
Bon, alors c'est ça le fameux "link".
Il faut "Link" (lier) toutes les pièces pour n'avoir qu'un seul objet composé de plusieurs "prim".
Lorsque tu fais cette opération tu sélectionnes d'abord toutes les prims que tu veux "link" (lier) en les cliquant, l'une après l'autre, en tenant la touche "shift" enfoncée puis tu les "link" avec la commande CTRL+L (CTRL+SHIFT+L pour les "unlink" (délier)).
Celle que tu sélectionnes en dernier sera la "root prim" donc la prim mère, les autres étant les prim enfant.

Dans le tuto de Bestmomo, les prims doivent toujours être liées ("link") le cube de base étant toujours le "root" (donc sélectionné en dernier avant le "link").

Grillé par le chef...

Effectivement la solution Grille Beta est une bonne option puisque le renvoi se fait toutes les 24h.
Citation :
Publié par bestmomo
D'accord ma cocotte .

Encore merci pour tout M'sieur best qui porte bien son nom c'est bien la première fois que quelqu'un s'est donné autant de mal pour arriver à mettre en oeuvre quelque chose qui vient de mon tit cerveau rikiki.

donc merci de tout coeur et ne t'arrête pas en si bon chemin tu finiras par créer un SL en origami un jour .


La biz à vous deux
Citation :
Publié par bestmomo
Peut-être qu'un jour je créerai un outil simple pour créer des origamis, au moins tout le monde pourrait débrider son imagination .
ET si un jour tu le mets en vente je te le prends bon euhh pas trop cher non pu hein suis pas crésus ^^

Mais j'adore cette idée .
J'ai commencé à regarder le truc mais pour faire un outil pratique à mettre entre toutes les mains c'est vraiment très complexe à réaliser, la cocote à côté c'est un amusement. Pour donner une idée le seul fait de déterminer le nombre et l'orientation des côtés créés par un découpage de box est déjà une aventure éprouvante.
Citation :
Publié par Grace HILL
Mon pauvre . Mais tu sais que tu n'es pas obligé d'en faire autant ? Tu en as déjà fait beaucoup .
Ne t'en fais pas j'ai pour principe de ne faire que ce qui me plait .
Citation :
Publié par bestmomo
Ne t'en fais pas j'ai pour principe de ne faire que ce qui me plait .
Oui parce que si ce n'était pas pour le plaisir mais pour me faire plaisir là je m'en voudrais ...

Et ce programme tu vas le finaliser ou matérialiser comment ?
Citation :
Publié par Grace HILL
Et ce programme tu vas le finaliser ou matérialiser comment ?
Ce n'est vraiment pas d'actualité parce que je ne sais même pas si je mènerai ce projet à son terme, tout dépendra de mes disponibilités.

Citation :
Publié par anpton
Comme tous les chats...
@ Best : ok c'est parce que tu avais relancé la discussion que du coup j'ai cru que tu y étais réellement remis dans le bain. Mais vu ce que tu écris effectivement, j'ai l'impression que cela va prendre du temps .


@ anpton : tu ne perds jamais le nord toa .
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés