LorisWave sotto l'albero
Sfrutta al meglio i software per semplificarti la vita

Rig in LightWave - Part5

Realizzare un rig completo per la character animation - Speciale LScript

Pubblicato in Lightwave
GooglePlus icon
LinkedIn icon
MySpace icon
Pinterest icon
whatsapp icon
Come promesso eccoci al nostro appuntamento con gli LScript. Oggi vedremo come realizzare uno script che simula il setup che avevamo realizzato la volta scorsa ma senza dover riscrivere o assegnare tutte le espressioni che avevamo fatto. Premetto che non sono ne un programmatore ne un esperto di Lscript perciò alcune cose potrebbero essere eseguite meglio, questa è solo una base da dove partire.. Questa vota niente video.
Apriamo Lightwave e Lscript Editor, in quest ultimo da Tools\Templates scegliamo Item Animation, dovrebbe .......
apparire il seguente codice senza i miei commenti che ho aggiunto per chiarire alcune cose.

//-----------------------------------------
// LScript Item Animation template

// il doppio slash indica un commento, il codice che lo segue non viene interpretato durante
//l'esecuzione

/*possiamo usare lo slash e l'asterisco per racchiudere un commento che è più lungo di una
riga, in questo modo tutto il testo contenuto non verrà considerato
durante l'esecuzione del codice, attenzione a non dimenticare in giro un "*/" altrimenti lo script non viene caricato da Lightwave*/

@version 2.2
@warnings
@script motion
@name Rot Heel // qui ho indicato il nome al mio script fate anche voi questa aggiunta

// global values go here
// qui vengono dichiarate le variabili globali, utilizzabili da tutto lo script

// create: obj // with the Layout Object Agent argument
create // without the Layout Object Agent argument
{
// one-time initialization takes place here
}

destroy
{
// take care of final clean-up activities here
}

process: ma, frame, time
{
// called each time the object's transformational values change
/* come indicato viene eseguita ogni volta che cambiano i valori, questa è il cuore del nostro script, sarà qui che andremo ad inserire il codice che modifica posizione e rotazione per l'item a cui è assegnato il nostro script */
}

load: what,io
{
if(what == SCENEMODE) // processing an ASCII scene file
{
//carica i dati salvati nel file della scena, impostazioni dello script ecc....
}
}

save: what,io
{
if(what == SCENEMODE)
{
//salva i dati salvati nel file della scena, impostazioni dello script ecc..
}
}

options
{
// options è il menù che si apre quando si vogliono editare i parametri dello script,
// ci sono script che non hanno un menù e questo spazio rimane vuoto.
reqbegin("");

c0 = ctlinteger("Integer Control",1); // questi sono esempi di menù che possono essere realizzati.
c1 = ctlnumber("Number Control",1.0);
c2 = ctlstring("String Control","my string");
c3 = ctlcheckbox("Checkbox Control",true);
c4 = ctlpopup("Popup control",1,@"Item 1","Item 2","Item 3"@);
c5 = ctlchoice("Choice Control",1,@"X","Y","Z"@);

return if !reqpost();

// get requester control values here

reqend();
}

La struttura degli Lscript è composta da una serie di funzioni standard tipo questa:

nome_funzione: variabile1, variabile2
{
codice;
}

Queste funzioni sono eseguite e comunicano tra loro in maniera particolare essendo già previste dal codice di Lightwave, con la stessa modalità però possiamo creare delle nostre funzioni da richiamare quando vogliamo con la seguente struttura

valore = nome_funzione( primo_valore_da_considerare, secondo_valore_da_considerare);

Consideriamo il nostro script, lo applicheremo ad un null (Heel_B, inizialmente in posizione neutra, perciò con x,y e z con valore 0 ) e questo dovrà muoversi in base alla posizione di due null che per il momento chiameremo SX e DX tenendo conto del raggio di due ipotetici cerchi che verranno determinati da SX_dist e DX_dist e dalla rotazione sul bank di un quinto null la struttura della scena sarà la seguente:



Dovremo in pratica far muovere Heel_B lungo le due linee, Line_A e Line_B, della figura qui sotto in base alla rotazione di Heel_ctrl.



Ci serve perciò la possibilità di indicare allo script 5 null. andiamo a modificare il codice dentro a "options" in questa maniera:

options
{
reqbegin("Rotation Heel"); // nome della finestra di inserimento dati
reqsize(245, 216); // la grandezza della finestra di inserimento dati

//creiamo i 5 menù a tendina dove andremo a definire gli oggetti che influenzano la posizione di
//Heel_B

c1 = ctlmeshitems("Rot SX",var1); //menù a tendina per oggetti con valore var1, se non
//ancora impostato diventa nill e perciò nessun oggetto
//selezionato.
ctlposition(c1,5,37); //posizione del menù a tendina nel pannellino di
//controllo

c2 = ctlmeshitems("Rot DX",var2);
ctlposition(c2,4,62);

c3 = ctlmeshitems("Dist SX",var3);
ctlposition(c3,4,87);

c4 = ctlmeshitems("Dist DX",var4);
ctlposition(c4,3,112);

c5 = ctlmeshitems("Rot B",var5);
ctlposition(c5,12,137);

//quando si da ok nel panello dello script si salvano i dati che si
//sono impostati, tramite il seguente codice
return if !reqpost(); // se dai OK fai quello che segue

var1 = getvalue(c1); // prendi il valore di c1 e rendi var1 uguale a questo.
var2 = getvalue(c2);
var3 = getvalue(c3);
var4 = getvalue(c4);
var5 = getvalue(c5);

reqend(); //infine chiudi il pannello
}

In questo modo abbiamo creato un pannello che ci chiede di indicare 5 nul e quando lo si chiude assegna alle variabili var1, var2, var3, var4 e var5 il valore assegnato. Fatto questo salviamo lo script appena realizzato dove ci torna più comodo ( Lightwave di default carica all'apertura i plugin e gli script che si trovano nella sua cartella plugin) con il nome provvisorio di Heel_B e passiamo a Lightwave, costruiamo una scena di test come indicato in figura e carichiamo da "Utilities/Add Plugins" il nostro script, selezioniamo il null Heel_B e da "Motion Modifiers/IK and Modifiers/Add Modifier" scegliamo "Rotation Heel", doppio click sul modificatore appena aggiunto e controlliamo che si possano impostare i vari nul, chiudiamolo e riapriamolo. Salviamo la scena di Lightwave e ricarichiamola, se riapriamo lo script ci accorgeremo che abbiamo perso i valori, questo perché dobbiamo occuparci noi di scrivere il codice che salva le impostazioni, ma prima di questo dobbiamo farei in modo che i valori var1, var2 ecc siano disponibili a tutto lo script. In questo momento infatti queste variabili esistono all'interno di option e non in tutto lo script. Per fare in modo che le altre parti dello script possano leggere questi valori bisogna dichiarare le variabili all'esterno della funzione option, e precisamente all'inizio dello script, perciò andiamo nella prima parte del codice e modifichiamolo come segue:
......
// global values go here
var1; var2; var3; var4; var5;

flags
..........

In questa maniera potremo richiamare le nostre variabili in qualsiasi punto del nostro codice.
La prima cosa di cui mi occupo quando realizzo uno script è il salvataggio e ricaricamento dei dati e delle impostazioni, questo mi permette di ricaricare la scena ed eseguire test senza dover ogni volta reimpostare i parametri.
Le variabili appena definite non sono un semplice numero, rappresentano una serie molto complessa di dati ed elementi, non possiamo scrivere tutto ciò nel file .lws, andremo a scrivere solo il suo ID, che è il nome interno, sotto forma di codice numerico, che Lightwave usa per identificare un Item, quando ricaricheremo la scena e perciò lo script, il load si occuperà di trasformare questo nome/id in una variabile completa. Vediamo il save di una sola variabile:

save: what,io
{
if(what == SCENEMODE)
{
if( var1 != nil ) //se var1 non è nil, perciò se è stato definito nel panello
{
io.writeln( var1.id ); // scrivi sul .lws l'ID dell'item var1
}
else //se var1 non è stato definito
{
io.writeln( 0 ); //scrivi 0
}
}
}

Una volta salvato l'ID di var1 bisogna anche poter ricaricare il dato e ritrasformare l'id nella variabile completa, vediamo il codice:

load: what,io
{
if(what == SCENEMODE) // processing an ASCII scene file
{
var1 = io.read().asInt(); //leggi la prima riga scritta in .lws dopo l'indicazione dello script e
//per precauzione trasformalo in un intero.
if( var1 == 0 ) //se var1 appena letto è 0
{ var1 = nil;} //var1 diventa nil, e lightwave sa come
//comportarsi in questi casi
else
{ var1 = Mesh(var1); } //Se var1 aveva un ID riassegamo la mesh e tutti i
//dati anessi con l'ID corrispondente alla variabile var1
}
}

Ovviamente dobbiamo inserire il codice di tutte e 5 le variabili dove io ho inserito solo quello per var1. Fate attenzione all'ordine di scrittura e lettura dei dati, infatti non abbiamo inserito un controllo, le nostre variabili verranno scritte una dietro l'altra semplicemente andando a capo ogni volta.
Fatto questo salviamo lo script, ricarichiamo la scena, impostiamo i vari null, salviamo e ricarichiamo, riaprendo infine il pannello dello script dovremmo ritrovare i valori impostati precedentemente.
Adesso che abbiamo gli elementi necessari vediamo di calcolare il movimento del nostro item, in primo luogo inseriremo tutto il codice in un solo blocco, in un secondo momento vedremo come usare le funzioni per ridurre il codice e fare un lavoro più pulito, in questa fase perciò accertiamoci che i 5 menù a tendina abbiano un null di rifermento altrimenti avremmo dei messaggi di errore ogni volta che lo script tenta di leggere il valore di un null che non esiste.
Analizziamo il nostro problema, il null a cui è applicato lo script dovrà muoversi in base alla posizione e rotazione degli altri elementi, in particolare il suo spostamento sull'asse X sarà la somma di:



A) La parte di circonferenza percorsa attraverso la rotazione,
B) La distanza in X del punto di applicazione della rotazione
C) La proiezione sull'asse X della distanza A_X del punto di applicazione della rotazione corretta in base alla rotazione
D) La proiezione in X della distanza in A_Y del punto di applicazione della rotazione corretta in base alla rotazione.

perciò spostX = B + A - C + D ;

Ci serve conoscere una serie di dati, prima di tutto la rotazione che vogliamo prendere in considerazione, avevamo assegnato a var5 la variabile legata al null di cui consideriamo la rotazione. Inseriamo nella funzione process il seguente codice:

process: ma, frame, time
{
rot = var5.getRotation(time).b;
}

la funzione process è il cuore dello script, viene attivata ogni volta che si modifica l'item a cui è applicato lo script, passa alla funzione stessa l'item a cui è applicato lo script, ossia "ma" e il frame/time corrente.
getRotation è una specie di comando che estrae dalla variabile var5 la sua rotazione nel momento corrente (time), aggiungendo l'elemento ".b" si chiede solo la rotazione sul bank di quell'elemento.

Dopo di che ci servono la posizione X e Y di var1 e var2:

var1_x = var1.getPosition(time).x;
var1_y = var1.getPosition(time).y;
var2_x = var2.getPosition(time).x;
var2_y = var2.getPosition(time).y;

Come prima usiamo il getPosition per estrarre il valore di posizione in X e Y nel momento corrente ed assegnarlo ad una variabile.

Nello stesso modo estraiamo il valore dei due raggi ricordando che nel nostro setup avevamo usato lo spostamento sull'asse Y per definire queste lunghezze:

var3_y = var3.getPosition(time).y;
var4_y = var4.getPosition(time).y;

Adesso che abbiamo tutti gli elementi incominciamo ad analizzare le nostre esigenze:
B: La distanza in X del punto di applicazione della rotazione, perciò var1_x.

A: La parte di circonferenza percorsa attraverso la rotazione, avevamo già visto nello scorso appuntamento che era la rotazione per la distanza per 6.28 diviso 360, perciò:
rot * var3_y * 6.28/360

C: La proiezione sull'asse X della distanza in A_X del punto di applicazione della rotazione corretta in base alla rotazione. Sappiamo da reconditi studi di matematica che si trova moltiplicando il coseno dell'angolo per la lunghezza del lato, bisogna ricordarsi che lightwave in questo caso vuole angoli in radianti, perciò prendiamo rot e lo trasformiamo in radiante
rot_rad = rad(rot);
ne calcoliamo il coseno
rot_cos = cos( rot_rad);
e lo moltiplichiamo per var1_x.
C = var1_x*rot_cos;

possiamo però anche scrivere tutto in un unica formula
C = var1_x*cos(rad(rot));

D: La proiezione in X della distanza in A_Y del punto di applicazione della rotazione corretta in base alla rotazione, perciò simile a C
D = var1_y*sin(rad(rot));

Mettendo tutto insieme
spostX = var1_x + rot * var3_y * 6.28/360 - var1_x*cos(rad(rot)) + var1_y*sin(rad(rot));

Questa formula vale ovviamente per le rotazionii negative di rot, quando il nostro item dovrà spostarsi attorno al punto DX, la formula per il punto SX sarà la seguente:

spostX = var2_x + rot * var4_y * 6.28/360 - var2_x*cos(rad(rot)) + var2_y*sin(rad(rot));

La formula sarà uguale, andranno a cambiare gli elementi al suo interno ed avendo questi segno negativo il risultato è corretto. Starà a noi ottimizzare in questo caso il codice per evitare di scriverle tutte e due.
A questo punto affrontiamo il problema dello spostamento sull'asse delle Y, in questo caso non dovremmo tenere conto dello spazio percorso dal ipotetico cerchio che circoscrive il nostro tallone, avremo da calcolare lo spostamento sull'asse Y, la proiezione sull'asse Y della distanza in A_Y del punto di applicazione della rotazione corretta in base alla rotazione e la proiezione in Y della distanza in A_X del punto di applicazione della rotazione corretta in base alla rotazione.
Saltando qualche passaggio che abbiamo spiegato nel caso precedente ecco che la nostra formula si presenta così :

spostY = var1_y - var1_y*cos(rad(rot)) - var1_x*sin(rad(rot)) ;
per il lato A e
spostY = var2_y - var2_y*cos(rad(rot)) - var2_x*sin(rad(rot)) ;
per il lato B.

Quello che ci manca a questo punto è inserire quste formule per verificare il codice, non prima di aver trovato un modo per utilizzare a seconda della rotazione di Rot B ossia var5 le formule per il perno DX e per il perno SX. Ci viene incontro in questo caso il costrutto if/else, lo stesso che avevamo usato con le espressioni. In LScript si usa così:

if( condizione da verificare)
{
//se è vera esegue il codice che c'è qui
}
else
{
//se falsa esegue questo codice
}

Perciò il nostro process dovrebbe essere così:
process: ma, frame, time
{
rot = var5.getRotation(time).b;
var1_x = var1.getPosition(time).x;
var1_y = var1.getPosition(time).y;
var2_x = var2.getPosition(time).x;
var2_y = var2.getPosition(time).y;
var3_y = var3.getPosition(time).y;
var4_y = var4.getPosition(time).y;


if( rot <= 0 )
{
spostX = var1_x + rot * var3_y * 6.28/360 - var1_x*cos(rad(rot)) + var1_y*sin(rad(rot));
spostY = var1_y - var1_y*cos(rad(rot)) - var1_x*sin(rad(rot)) ;
}
else
{
spostX = var2_x + rot * var4_y * 6.28/360 - var2_x*cos(rad(rot)) + var2_y*sin(rad(rot));
spostY = var2_y - var2_y*cos(rad(rot)) - var2_x*sin(rad(rot)) ;
}

}

Se salviamo lo script e ricarichiamo la scena di Lightwave ci accorgeremmo che nulla è cambiato, anche se il nostro script dovesse eseguire un codice noi non ce ne accorgeremmo, infatti non viene modificato nessun parametro della scena. Quello che noi dobbiamo modificare sono posizione in x e in y del null a cui è applicato lo script, perciò dobbiamo comandare tramite questo una nuova posizione, il comando è:

ma.set(POSITION, );

In questa maniera comunichiamo le nuove cordinate di "ma", l'oggetto a cui è applicato lo script, ci manca però un elemento, la sua posizione in Z, che non dovrà essere modificata e perciò dovrà passare in manera trasparente. Andiamo perciò a leggere la posizione di ma con il seguente comando:

maZ = ma.get(POSITION,time).z;

dopo di che indichiamo la nuova posizione

ma.set(POSITION, );

Queste due righe di codice vanno inserite tra le due parentesi graffe che chiudevano la parte di codice riguardante il process. Salviamo lo script, ricarichiamo la scena e dovremmo vedere che la posizione di Heel_B non viene modificata immediatamente quando si ruota Heel_ctrl, eppure se si avanza di un frame ci si accorge che l'influenza della rotazione esiste, questo perché "process" entra in funzione quando vengono eseguite modifiche su "ma" e avanzare di un frame è aggiornare tutta la scena Heel_B compreso; Ruotando Hell_ctrl non avviene questo, bisogna perciò innescare una dipendenza nei confronti di Heel_ctrl. Andiamo ad aggiungere all'inizio di "process" il seguente codice: .

…........a, frame, time
{

effectedby( var5.name);

rot = var5.getRotation(time).b;
var1_x = var1.getPos..........

Questo comando fa in modo che anche quando modifichiamo var5, ossia il nostro Heel_ctrl, venga processato lo script e si aggiorni perciò la posizione di Heel_B. Come al solito salviamo lo script, ricarichiamo la scena e controlliamo il nostro lavoro. Se testiamo il nostro script troveremmo che qualcosa si muove, ma ancora non funziona. Non abbiamo ancora fatto una cosa fondamentale, agganciare la rotazione di Heel_B sul bank con quella di Heel_ctrl, questo potremmo farlo facilmente via script, ma per il momento selezioniamo Heel_B, e da "motion Options/Controllers and Limits/Rotation" mettiamo come "Rotation Item" Heel_ctrl e "Bank controllers" su "Saim at Item". Arrivati a questo punto dovremmo vedere che lo script funziona, ossia muove Heel_B quando si ruota Heel_ctrl, non è detto che però si muova nel modo giusto. Può succedere che l'inserimento dei null nei menù a tendina sia stato sbagliato, ossia si sia invertito l'ordine e si sia messo quello che doveva essere a destra a sinistra e viceversa, dobbiamo prevedere la possibilità di invertire il calcolo tramite script e non dover reimpostare le impostazioni dello script. Dobbiamo aggiungere all'interno di "option" un flag che inverta il calcolo in casi come quello appena presentato. Dovremmo aggiungere il seguente codice:

c6 = ctlcheckbox("Invert",var6);
ctlposition(c6,125,162,70);

Questo crea un check che se attivo ritorna valore 1 e se lasciato inattivo restituisce valore 0, bisogna però inizializzare questo controllo aggiungendo prima un sistema per indicare la condizione iniziale. Aggiungiamo var6 = 0 nei global value:

…...
// global values go here
var1; var2; var3; var4; var5; var6 = 0;

flags.......

Dobbiamo a questo punto salvare e ricaricare il valore che abbiamo impostato, inseriamo perciò in "save" il seguente codice in coda a quello che avevamo già inserito:

io.writeln( var6 );

e nell load sempre in coda al codice già inserito il seguente:

var6 = io.read().asInt();

Così da poter salvare e ricaricare il valore di Invert. Potremmo aggiungere a monte del if che teneva conto della rotazione di Heel_B un ulteriore if che tenga conto del valore var6 e potrebbe essere più o meno così:

if(var6 > 0 )
{

if( rot <= 0 )
{
spostX = var1_x + rot * var3_y * 6.28/360 - var1_x*cos(rad(rot)) + var1_y*sin(rad(rot));
spostY = var1_y - var1_y*cos(rad(rot)) - var1_x*sin(rad(rot)) ;
}
else
{
spostX = var2_x + rot * var4_y * 6.28/360 - var2_x*cos(rad(rot)) + var2_y*sin(rad(rot));
spostY = var2_y - var2_y*cos(rad(rot)) - var2_x*sin(rad(rot)) ;
}

}
else
{

if( rot <= 0 )
{
spostX = var2_x + rot * var4_y * 6.28/360 - var2_x*cos(rad(rot)) + var2_y*sin(rad(rot));
spostY = var2_y - var2_y*cos(rad(rot)) - var2_x*sin(rad(rot)) ;
}
else
{
spostX = var1_x + rot * var3_y * 6.28/360 - var1_x*cos(rad(rot)) + var1_y*sin(rad(rot));
spostY = var1_y - var1_y*cos(rad(rot)) - var1_x*sin(rad(rot)) ;
}

}

Nella prima parte calcoliamo spostX e spostY come prima, poi invertiamo semplicemente il tutto per adeguarci all'invert (var6). Questo metodo per quanto produca un risultato corretto e sicuramente lungo e poco versatile nel caso debba creare delle modifiche, vediamo come fare qualcosa di più comodo. Se osserviamo le espressioni che calcolano spostX possiamo notare che sono tutte uguali esattamente come avevamo detto all'inizio di questo articolo:

spostX = B + A - C + D;

Cambiano i valori che vengono usati per calcolare questa espressione, perciò possiamo creare una funzione che valga per tutte e poi dare in pasto a questa di volta in volta i valori corretti. Prima di tutto prendiamo una delle espressioni che calcola spostX,

spostX = var1_x + rot * var3_y * 6.28/360 - var1_x*cos(rad(rot)) + var1_y*sin(rad(rot));

Vediamone i singoli elementi:
var1_x, rot, var3_y, var1_y
Possiamo definirli come x, rot, y e raggio, perciò la nuova espressione diventa:

spostX = x + rot * raggio * 6.28/360 - x*cos(rad(rot)) + y*sin(rad(rot));

Detto questo dobbiamo creare una funzione che andrà scritta in fondo al nostro codice, dopo "options", che verrà utilizzata ogni volta che si deve calcolare spostX andando a indicare i valori corretti, la funzione la scriveremo così:

calcX: x, y, rot, raggio
{
X = x + rot * raggio * 6.28/360 - x*cos(rad(rot)) + y*sin(rad(rot));
return (X);
}

e la richiameremo, facendo finta di dover calcolare intorno al punto SX, in questo modo:

spostX = calcX(var1_x, var1_y, rot, var3_y);

per gli spostamenti sull'asse y useremo lo stesso procedimento ottenendo la seguente funzione:

calcY: x, y, rot
{
Y = y - y*cos(rad(B)) - x*sin(rad(B)) ;
return (Y);
}

questo ci permette di riscrivere il codice per il calcolo degli spostamenti così:

if( var6 == 1)
{
if ( rot >= 0 )
{
spostX = calcX(var2_x, var2_y, rot, var4_y);
spostY = calcY( var2_x, var2_y, rot);
}
else
{
spostX = calcX(var1_x, var1_y, rot, var3_y);
spostY = calcY( var1_x, var1_y, rot);
}
}
else
{
if ( rot >= 0 )
{
spostX = calcX(var1_x, var1_y, rot, var2_y);
spostY = calcY( var1_x, var1_y, rot);
}
else
{
spostX = calcX(var2_x, var2_y, rot, var4_y);
spostY = calcY( var2_x, var2_y, rot);
}
}

Un ulteriore possibilità potrebbe essere usare l'invert non durante il calcolo ma durante l'assegnamento, nella parte iniziale di "option", più o meno in questa maniera:

process: ma, frame, time
{
rot = var5.getRotation(time).b;

if(var6>0)
{
var1_x = var1.getPosition(time).x;
var1_y = var1.getPosition(time).y;
var2_x = var2.getPosition(time).x;
var2_y = var2.getPosition(time).y;
var3_y = var3.getPosition(time).y;
var4_y = var4.getPosition(time).y;
}
else
{
var1_x = var2.getPosition(time).x;
var1_y = var2.getPosition(time).y;
var2_x = var1.getPosition(time).x;
var2_y = var1.getPosition(time).y;
var3_y = var4.getPosition(time).y;
var4_y = var3.getPosition(time).y;
}

in questo modo il resto del codice sarebbe semplicemente:

if ( rot >= 0 )
{
spostX = calcX(var1_x, var1_y, rot, var3_y);
spostY = calcY( var1_x, var1_y, rot);
}
else
{
spostX = calcX(var2_x, var2_y, rot, var4_y);
spostY = calcY( var2_x, var2_y, rot);
}

Dovremmo avere ora un sistema che più o meno funziona, bisogna però prevedere che qualcuno decida di utilizzare il tutto non per dei talloni tondi, ma per esempio per il piede di un robot, con spigoli netti, allora potrebbe decidere di non inserire il raggio di curvatura e il nostro script come è attualmente non funzionerebbe dando una serie di errori. Per sopperire a questo inseriremo un if/else quando andiamo a definire i vari x e y in modo che se il null non è stato assegnato si prenda come valore 0.


if(var6>0)
{
if(var1 != nil) var1_x = var1.getPosition(time).x; else var1_x = 0;
if(var1 != nil) var1_y = var1.getPosition(time).y; else var1_y = 0;
if(var2 != nil) var2_x = var2.getPosition(time).x; else var2_x = 0;
if(var2 != nil) var2_y = var2.getPosition(time).y; else var2_y = 0;
if(var3 != nil) var3_y = var3.getPosition(time).y; else var3_y = 0;
if(var4 != nil) var4_y = var4.getPosition(time).y; else var4_y = 0;
}
…....

Perciò se un var è diverso da nil trova il suo valore altrimenti si impone 0, questo codice va ripetuto anche per la seconda parte di if/else. A questo punto il nostro script dovrebbe funzionare senza errori anche quando qualche null non viene indicato, ma se mancasse var5 e perciò le rotazioni, sarebbe ancora meglio non incominciare neppure il calcolo, perciò aggiungiamo un ulteriore if/else in testa al "process":

process: ma, frame, time
{
if( var5 != nil )
{
effectedby(var5.name);
…..
…...
ma.set(POSITION, );
}

}

Dopo if{} no inseriamo else{} infatti non ci sarebbe nulla de eseguire e possiamo perciò fermarci lì.
Questo dovrebbe essere il codice definitivo:

//-----------------------------------------
// LScript Item Animation template
//

@version 2.2
@warnings
@script motion
@name ALP Rot Heel
// global values go here
var1; var2; var3; var4; var5; var6 = 0;

flags
{
}
//create: obj // with the Layout Object Agent argument
create // without the Layout Object Agent argument
{
// one-time initialization takes place here
}

destroy
{
// take care of final clean-up activities here
}

//-----------------------process----------------------------------------------------------------
process: ma, frame, time
{
if( var5 != nil )
{
effectedby(var5.name);

rot = var5.getRotation(time).b;

if(var6>0)
{
if(var1 != nil) var1_x = var1.getPosition(time).x; else var1_x = 0 ;
if(var1 != nil) var1_y = var1.getPosition(time).y; else var1_y = 0;
if(var2 != nil) var2_x = var2.getPosition(time).x; else var2_x = 0;
if(var2 != nil) var2_y = var2.getPosition(time).y; else var2_y = 0;
if(var3 != nil) var3_y = var3.getPosition(time).y; else var3_y = 0;
if(var4 != nil) var4_y = var4.getPosition(time).y; else var4_y = 0;
}
else
{
if(var1 != nil) var2_x = var1.getPosition(time).x; else var2_x = 0 ;
if(var1 != nil) var2_y = var1.getPosition(time).y; else var2_y = 0;
if(var2 != nil) var1_x = var2.getPosition(time).x; else var1_x = 0;
if(var2 != nil) var1_y = var2.getPosition(time).y; else var1_y = 0;
if(var3 != nil) var4_y = var3.getPosition(time).y; else var4_y = 0;
if(var4 != nil) var3_y = var4.getPosition(time).y; else var3_y = 0;
}


if ( rot >= 0 )
{
spostX = calcX(var1_x, var1_y, rot, var3_y);
spostY = calcY( var1_x, var1_y, rot);
}
else
{
spostX = calcX(var2_x, var2_y, rot, var4_y);
spostY = calcY( var2_x, var2_y, rot);
}

maZ = ma.get(POSITION,time).z;
ma.set(POSITION, );
}

}
//---------------------------Load-e-Save----------------------------------------------------------
load: what,io
{
if(what == SCENEMODE) // processing an ASCII scene file
{

var1 = io.read().asInt();
var2 = io.read().asInt();
var3 = io.read().asInt();
var4 = io.read().asInt();
var5 = io.read().asInt();
var6 = io.read().asInt();

if( var1 == 0 ) { var1 = nil;} else { var1 = Mesh(var1); }
if( var2 == 0 ) { var2 = nil;} else { var2 = Mesh(var2); }
if( var3 == 0 ) { var3 = nil;} else { var3 = Mesh(var3); }
if( var4 == 0 ) { var4 = nil;} else { var4 = Mesh(var4); }
if( var5 == 0 ) { var5 = nil;} else { var5 = Mesh(var5); }

setdesc("Rot Heel");
}
}

save: what,io
{
if(what == SCENEMODE)
{
if( var1 != nil ) io.writeln( var1.id ); else io.writeln( 0 );
if( var2 != nil ) io.writeln( var2.id ); else io.writeln( 0 );
if( var3 != nil ) io.writeln( var3.id ); else io.writeln( 0 );
if( var4 != nil ) io.writeln( var4.id ); else io.writeln( 0 );
if( var5 != nil ) io.writeln( var5.id ); else io.writeln( 0 );

io.writeln( var6 );

}
}
//----------------------------Menu-----------------------------------------------------------
options
{
//var6 = integer(var6);

reqbegin("Rot Heel");
reqsize(245, 216);

c1 = ctlmeshitems("Rot SX",var1);
ctlposition(c1,5,37);

c2 = ctlmeshitems("Rot DX",var2);
ctlposition(c2,4,62);

c3 = ctlmeshitems("Dist SX",var3);
ctlposition(c3,4,87);

c4 = ctlmeshitems("Dist DX",var4);
ctlposition(c4,3,112);

c5 = ctlmeshitems("Rot B",var5);
ctlposition(c5,12,137);

c6 = ctlcheckbox("Invert",var6);
ctlposition(c6,125,162,70);

return if !reqpost();

var1 = getvalue(c1);
var2 = getvalue(c2);
var3 = getvalue(c3);
var4 = getvalue(c4);
var5 = getvalue(c5);
var6 = getvalue(c6);

reqend();
}

//-----------------------------Function----------------------------------------------------------

calcX: x, y, rot, raggio
{
X = x + rot * raggio * 6.28/360 - x*cos(rad(rot)) + y*sin(rad(rot));
return (X);
}

calcY: x, y, rot
{
Y = y - y*cos(rad(rot)) - x*sin(rad(rot)) ;
return (Y);
}//-----------------------------------------
// LScript Item Animation template
//

@version 2.2
@warnings
@script motion
@name ALP Rot Heel
// global values go here
var1; var2; var3; var4; var5; var6 = 0;

flags
{
}
//create: obj // with the Layout Object Agent argument
create // without the Layout Object Agent argument
{
// one-time initialization takes place here
}

destroy
{
// take care of final clean-up activities here
}

//-----------------------process----------------------------------------------------------------
process: ma, frame, time
{
if( var5 != nil )
{
effectedby(var5.name);

rot = var5.getRotation(time).b;

if(var6>0)
{
if(var1 != nil) var1_x = var1.getPosition(time).x; else var1_x = 0 ;
if(var1 != nil) var1_y = var1.getPosition(time).y; else var1_y = 0;
if(var2 != nil) var2_x = var2.getPosition(time).x; else var2_x = 0;
if(var2 != nil) var2_y = var2.getPosition(time).y; else var2_y = 0;
if(var3 != nil) var3_y = var3.getPosition(time).y; else var3_y = 0;
if(var4 != nil) var4_y = var4.getPosition(time).y; else var4_y = 0;
}
else
{
if(var1 != nil) var2_x = var1.getPosition(time).x; else var2_x = 0 ;
if(var1 != nil) var2_y = var1.getPosition(time).y; else var2_y = 0;
if(var2 != nil) var1_x = var2.getPosition(time).x; else var1_x = 0;
if(var2 != nil) var1_y = var2.getPosition(time).y; else var1_y = 0;
if(var3 != nil) var4_y = var3.getPosition(time).y; else var4_y = 0;
if(var4 != nil) var3_y = var4.getPosition(time).y; else var3_y = 0;
}


if ( rot >= 0 )
{
spostX = calcX(var1_x, var1_y, rot, var3_y);
spostY = calcY( var1_x, var1_y, rot);
}
else
{
spostX = calcX(var2_x, var2_y, rot, var4_y);
spostY = calcY( var2_x, var2_y, rot);
}

maZ = ma.get(POSITION,time).z;
ma.set(POSITION, );
}

}
//---------------------------Load-e-Save----------------------------------------------------------
load: what,io
{
if(what == SCENEMODE) // processing an ASCII scene file
{

var1 = io.read().asInt();
var2 = io.read().asInt();
var3 = io.read().asInt();
var4 = io.read().asInt();
var5 = io.read().asInt();
var6 = io.read().asInt();

if( var1 == 0 ) { var1 = nil;} else { var1 = Mesh(var1); }
if( var2 == 0 ) { var2 = nil;} else { var2 = Mesh(var2); }
if( var3 == 0 ) { var3 = nil;} else { var3 = Mesh(var3); }
if( var4 == 0 ) { var4 = nil;} else { var4 = Mesh(var4); }
if( var5 == 0 ) { var5 = nil;} else { var5 = Mesh(var5); }

setdesc("Rot Heel");
}
}

save: what,io
{
if(what == SCENEMODE)
{
if( var1 != nil ) io.writeln( var1.id ); else io.writeln( 0 );
if( var2 != nil ) io.writeln( var2.id ); else io.writeln( 0 );
if( var3 != nil ) io.writeln( var3.id ); else io.writeln( 0 );
if( var4 != nil ) io.writeln( var4.id ); else io.writeln( 0 );
if( var5 != nil ) io.writeln( var5.id ); else io.writeln( 0 );

io.writeln( var6 );

}
}
//----------------------------Menu-----------------------------------------------------------
options
{
//var6 = integer(var6);

reqbegin("Rot Heel");
reqsize(245, 216);

c1 = ctlmeshitems("Rot SX",var1);
ctlposition(c1,5,37);

c2 = ctlmeshitems("Rot DX",var2);
ctlposition(c2,4,62);

c3 = ctlmeshitems("Dist SX",var3);
ctlposition(c3,4,87);

c4 = ctlmeshitems("Dist DX",var4);
ctlposition(c4,3,112);

c5 = ctlmeshitems("Rot B",var5);
ctlposition(c5,12,137);

c6 = ctlcheckbox("Invert",var6);
ctlposition(c6,125,162,70);

return if !reqpost();

var1 = getvalue(c1);
var2 = getvalue(c2);
var3 = getvalue(c3);
var4 = getvalue(c4);
var5 = getvalue(c5);
var6 = getvalue(c6);

reqend();
}

//-----------------------------Function----------------------------------------------------------

calcX: x, y, rot, raggio
{
X = x + rot * raggio * 6.28/360 - x*cos(rad(rot)) + y*sin(rad(rot));
return (X);
}

calcY: x, y, rot
{
Y = y - y*cos(rad(rot)) - x*sin(rad(rot)) ;
return (Y);
}

Proviamo ad applicarlo e giocarci un po', inserendo o meno dei null, salvando e ricaricando le scene, vedendo il risultato di invert, tutto dovrebbe funzionare abbastanza bene, andiamo perciò ad utilizzarlo con la scena della gamba in ik e vedremo che invece le cose non vanno, la rotazione produce uno spostamento ma questo non viene seguito alla perfezione dalla gamba. Il problema credo risieda nell'ordine che utilizza Lightwave per eseguire i vari calcoli, infatti ci sono vari step, nel caso della nostra gamba probabilmente viene calcolato prima il target/pole, poi gli script e i plugin, poi l'ik insieme a i "tool saim at item" e infine i plugin e gli script con opzione afterIK. Per il momento andiamo ad abilitare nell'osso upper leg l'ik anche per l'angolo handing e dovremmo eliminare il problema. Per questa volta è tutto, vedremo ovviamente in seguito di approfondire questa problematica.

Ti piace questo post? Pagalo!

Un condivisione è la moneta migliore con cui ripagare chi scrive. Se ti è piaciuto quello che hai letto o se lo hai trovato utile condividilo con i tuoi amici.

GooglePlus icon
LinkedIn icon
MySpace icon
Pinterest icon
whatsapp icon

L'articolo precedente: "Rig in LightWave - Part4"

Aggiungi un commento

Notifica