Prima lezione di scripting a craft…. 24 aprile 2012

Le lezioni di scripting su opensim sono ormai possibili a livelli qualitativi equivalenti a SecondLife e per certi versi anche meglio :)

Anche ieri c’erano almeno 7-8 avatar molto interessati che pur nella giornata prefestiva si sono ingegnati a seguire il mio corso. Grazie a Ivan Ragu per questi appunti presi in forma di script che potete usare per recuperare il contenuto del corso se per caso l’avete perduto..

 

Script Running


// Questa è una linea di commento, serve appunto per commentare quello che succede dentro lo script
// la linea di commento inizia con le due barrette e viene ignorata durante l'esecuzione dello script

// Setta lo stato dell'oggetto. e' lo stato di inizio. Tutti gli script cominciano dichiarando questo stato.
// se paragoniamo uno script a un libro possiamo dire che se lo script corrisponde a un libro
// gli stati corrispondono ai capitoli.L'esempio più classico dove uno script usa più stati
// è lo script di una porta dove ci sono di solito due Stati, uno aperto quando la porta è aperta e quindi
// in quello stato ci sarà il prezzo di script e controlla la porta quando e' aperta
// è uno stato chiusa quando la porta è chiusa e lo script controlla quando la porta è chiusa.
default

 

// qui apro una parentesi graffa per indicare che da qui inizia parte di script relativa allo stato di default
{
 // all'interno degli vari stati ci sono gli eventi dove a sua volta contengono la parte di programma che viene eseguito
 // quando l'evento si verifica.
 // Sempre rifacendosi all'esempio della porta all'interno dell'evento tocco
 // ci sarà il pezzo di programma che si occuperà di fare aprire la porta.
 // Nel nostro caso troviamo l'evento "state_entry()" che è l'evento che si verifica tutte le volte
 // che si entra in un nuovo stato. Ogni stato avrà il suo "state_entry()"
 // Ricapitolando: Quando eseguo lo scrpt si entra nello stato di default e quindi si verificherà l'evento state_entry.
 state_entry()

 // Apro ora un'altra parentesi graffa per indicare quale parte dello script deve essere eseguito quanto si verifica
 // l'evento
 {

 // llSay è il primo vero comando e significa parla. Il contenuto all'interno delle parentesi tonde sono i parametri
 // il comando llSay si aspetta due parametri, i parametri vanno separati dalla virgola.
 // Il primo è su quale canale deve parlare il secondo è cosa deve dire.
 // il canale zero è il canale della chat pubblica è la frase che deve dire deve essere racchiusa tra virgolette.
 // tutte le le linee di comando finiscono con un punto e virgola ;
 // quindi questa riga significa:
 // parla sul canale zero e di "Script running"

 llSay(0, "Script running");

 // chiudo ora la parentesi graffa che indica che l'evento state_entry è terminato
 }

// chiudo ancora la parentesi graffa per indicare che è lo stato di default è terminato.
}

Metto del testo sopra il cubo


// entra nello stato di default
default
{
 // evento che si verifica quando si esegue il programma
 state_entry()
 {
 // questa comando fa scrivere un testo sopra a un prim. E si apetta tre parametri. Il primo è la frase che deve scrivere,
 // il secondo è un vettore ed è composto a sua volta dalle componenti R G B del colore. Il valore va da zero a uno
 // il vettore è contenuto all'interno dei due simboli minore maggiore < > E i vaori sono separati da una virgola
 // il terzo parametro indica l'opacità, più la scritta e' opaca è piu visibile. il valore va da zero a uno
 // ricapitolando: Scrivi un testo con scritto CIAO e sotto ancora ciao
 // (il carattere \n forza di scrivere la parola sucessiva in una nuova linea
 // con colore massimo delle 3 componenti RGB verra' quindi visualizzata in colore bianco e con la massima opacita' visibilita' massima.
 llSetText ("CIAO! \n ciao",<1,1,1>,1);

 // questo comando serve per colorare il cubo e si aspetta due parametri il primo è indicato dal vettore RGB il secondo
 // indica quale faccia del primo deve essere colorata ALL_SIDES è un valore costante e dice che deve colorare tutte le facce
 // quindi colora il prim di rosso tutte le facce.
 llSetColor (< 1,0,0>, ALL_SIDES);

 // colora il prima con il colore generato dal valore 0,5 componente rossa,
 // con il valore 0,5 componente verde e massima componente blu la faccia zero del cubo
 // si ricorda che il cubo a sei facce ma sono numerate da zero a cinque.
 llSetColor (<0.5,0.5,1>,0);


 // chiudo la parentesi graffa per indicare che e' finito lo state_entry
 }

// chiudo la parentesi graffa per indicare che e' finito lo stato default
}

 

Colori facce a caso


// inizia stato di default
default
{
 // inizia state_entry
 state_entry()
 {

 // scrive "Cambio Colore" sopra il prim con il colore blu
 llSetText(" Cambio colore ",<0,0,1>,1 );

 //questo comando fa partire un cronometro con cadenza il tempo espresso in secondi contenuto nel suo parametro
 //quindi fa partire un cronometro che scandisce il tempo ogni mezzo secondo e serve per provocare l'evento timer
 llSetTimerEvent(0.5);


 //chiudo la graffa per indicare che e' finito lo stato_entry
 }


 //Questo evento si verifica ogni volta che scade il tempo del cronometro llSetTimerEvent
 // quindi ogni mezzo secondo verra' eseguita questa parte di programma.
 timer()


 // Apro la graffa per indicare che inizia l'evento timer
 {


 // in questa riga troviamo 2 comandi nuovi:
 // llFrand crea un numero casuale da zero al numero contenuto nel suo parametro.
 // Il numero non sara' un numero intero ma avra' all'interno dei decimali.
 // il comando llRound arrotonda il suo parametro contenuto al numero intero inferiore in sostanza elimina i valori dopo la virgola
 // esempio trasforma il valore 3,76 al valore 3
 // quindi ecco cosa fa questa linea:
 // setta il prim di colore avente un numero Casuale da 0 a 1 come valore sia R che valore G che valore B
 // e colora di quel colore una faccia nomero a caso da zero a 5.
 llSetColor ( <llFrand(1),llFrand(1),llFrand(1)>, llRound (llFrand (5.9)));

 // chiudo la graffa per indicare che e' finito l'evento timer
 }

// chiudo la graffa per dire che e' finito lo stato di default
}

Presentazione di Salahzar in video al 4o compleanno di Pyramid Cafè, introduzione a Mesh Town

http://urly.it/17i7

English version at the bottom :)
Buon anno a tutti e soprattutto buon compleanno a Pyramid Cafè che compie 4 anni.
Come sapete non amo mostrarmi: io sono e sarò sempre una persona riservata che non ama le folle.
Posso dirvi che di tutto quello che ho vissuto in SecondLife da quando sono entrato nel 2007, forse questo progetto di PyramidCafè è l’unico che sta continuando in modo vivace, condiviso e che ha perfino sorpassato le frontiere del luogo di nascita.
Pyramid Cafè è partito infatti dallo Spirito di Vulcano, nel tentativo di offrire un luogo di condivisione, di svago e di amicizia.
Ora le attività delle persone collegate si estende in molte altre zone tutte molto virtuali, a partire dall’estensione a OpenSim con la grid Craft, ma anche e soprattutto attraverso svariati sistemi di comunicazione più classici che vanno dalle telefonate sul cellulare, a skype, alle discussioni su facebook, blog, chat e senza dimenticare gli incontri personali che abbiamo avuto nel tempo in cui ci siamo visti di persona e abbiamo discusso di idee progetti sogni ma anche condiviso momenti di stare insieme, pranzi, giochi ed altro.
La premessa di tutto questo è di poter utilizzare le nuove tecnologie “virtuali”  per riuscire a costruire e sperimentare nuovi modelli sociali di condivisione, divertimento ed educazione, imparando tra noi a fare cose che normalmente non potremmo fare o che le norme convenzionali della nostra società ci impediscono di fare perchè certe cose è meglio lasciarle fare ai “professionisti”. Ora senza nulla togliere a chi su molte di queste cose ci lavora e guadagna dignitosamente il proprio pane, abbiamo dimostrato più volte che è possibile sviluppare degli obiettivi di forte qualità e di alto coinvolgimento sfruttando appieno oltre alle risorse tecnologiche anche le ricchezze personali ed umane che ci sono in ognuno di noi.
Quello che mi è sempre piaciuto di Pyramid, di Magic e di tutte le persone con cui ho lavorato, giocato, costruito, è che il modo di stare insieme era sempre stato leggero, entusiasmante, senza ricatti nè pesantezze o recriminazioni.
Se una cosa funziona e viene bene siamo tutti contenti, se qualcosa che ci siamo prefissati non riusciamo a farla per problemi in rl o limiti che non riusciamo a superare, pazienza ci abbiamo provato e comunque abbiamo cementato il nostro rapporto. L’importante è che le nostre esperienze siano sempre arricchenti e che non lascino quel sapore amaro in bocca che troppo spesso ci troviamo nelle brutte esperienze, delusioni e frustrazioni della nostra vita: cerchiamo di mettere in questa nuova virtualità il nostro lato più positivo, di curiosità, disponibilità, amicizia e voglio di giocare, di metterci in gioco su obiettivi anche se apparentemente più grandi di noi.
Detto questo, fra le varie attività che stiamo facendo in Pyramid, voglio sottolineare questo esperimento che stiamo chiamando “Mesh Town” in cui stiamo cercando di offrire alle persone uno spazio in cui possono essere creative, producendo modellazioni tridimensionali liberi dalla schiavitù dei prim, e soprattutto liberi dalla schiavitù economica imposta dalla Linden Lab: ora come ora, lavorare con le mesh in SecondLife ha un costo altissimo: occorre avere una sim che costa circa 300 euri al mese e pagare fior di quattrini per fare l’upload delle mesh.
Noi vogliamo offrire degli spazi gratuiti a chi vuole autosperimentarsi in cui possa scaricare le proprie mesh utilizzando il software opensim sulle grid Craft, CloneLife, o anche se lo ritengono sul proprio computer di casa, in modo da poter organizzare delle vetrine di esposizione e di condivisione delle proprie creazioni.
Poi io personalmente sono anni che sto lavorando per poter far superare insieme alle persone il gradino di difficoltà e complessità che si incontra usando questi strumenti di lavoro e modellazione 3d. Noi usiamo e privilegiamo il software libero opensource e in particolare parlando di modellazione tridimensionale il programma Blender.
Imparare ad utilizzare blender non è facile e ci va una guida psicologica anzi tutto e una community di riferimento in cui chiarire i propri dubbi.
Quindi il progetto Mesh town si articola essenzialmente nei seguenti punti:
1\ spazio opensim su una sim o su + sim da concordare su cui sperimentare le proprie creazioni
2\ un programma di corsi in SL, sui blog, su facebook, Youtube, su craft etc, dove la gente possa imparare ad utilizzare OpenSim e Blender
3\ L’organizzazione quando si riesca ad avere sufficiente materiale di vetrine ed eventi per mostrare agli altri in modo “tattile” sia pure virtuale cosa si possa realizzare collettivamente.
Augurandomi che nel 2012 riusciremo a fare una cosa del genere, per tutti coloro interessati, partecipate ai miei incontri blender (il primo è una serie di lezioni di blender di base a partire da domani stesso presso la Torno Kohime foundation tutti i giovedì alle 22), e comunque incontriamoci per una chiacchierata davanti ad un bicchiere di birra virtuale (o un te o un narghile a seconda delle vostre inclinazioni) per poter riempire questa idea di contenuti, idee e persone.
Senza impegno: quel che si riuscirà a fare si farà, se non si farà nulla, pazienza, è stato un tassello in più che ci permette di conoscerci e di confrontarci.
English version:
Happy New Year to everybody and especially Happy Birthday to Pyramid Cafe, which is now  4 year old..
As you know I’m a bit introvert, do not like to show me off and  am and always be a private person who does not like crowds..
I can tell you that among all  I have experienced since I joined Second Life in 2007, perhaps PyramidCafè  is the only  project that is continuing to be in a lively, shared and has even surpassed the borders of the place where it was born.
Pyramid Cafe in fact started by the Spirit of Vulcan, in an attempt to offer a place of sharing, fun and friendship.
Now the activities of associated people extends to many other areas  all very virtual, starting with the outreach  to OpenSim grids Craft, but also and above all through a variety of communication systems ranging from classic to mobile phone, skype, the Discussions on facebook, blogs, chats, and without forgetting the personal meetings that we had at the time when we get to know  in person and we discuss ideas, projects, dreams not excluding  shared moments of being together, dinners, games and much more.
The premise of this is to be able to use the new “virtual” technologies  to be able to build and test new models of social sharing, fun and learning, learning by ourselves  to do things normally we think we could not do or that the conventional rules of our society hinder us from doing  because they are best left to the “professionals”. But without taking anything away from experts who earn their lives honestly with their work with dignity, we have repeatedly demonstrated that it is possible to develop objectives of high quality and high involvement in addition to making full use of technological resources possessed by each and every  person and friend participating. There is much talent in everyone of us!.
What I always liked in Pyramid, Magic and all the people with whom I worked, played, built, is that the way of being together had always been light, funny without heaviness or blackmail or recriminations.
If something works well we are all happy, if anything goes wrong or we can’t do it because of  rl problems or limitations that we can not
overcome, it’s OK,  we can say we tried, and in any case we have cemented our relationships. The important thing is that our experiences are always enriching and not leave that bitter taste in mouth too often we find ourselves when living bad experiences, frustrations and disappointments of our lives.: Let’s pour in this new virtuality our most positive sides: curiosity, availability, friendship, will to play, to get involved on targets even if they appear larger than us.

That said, among other activities we are doing in Pyramid, I want to emphasize this experiment we’re calling “Mesh Town” in which we strive to provide a space where people can be creative, producing three-dimensional modeling free from the slavery of the “prims” and especially free from financial bondage imposed by Linden Lab. Currently, working with meshes in SecondLife has a very high cost: we must have a sim costing about 300 $/month and pay good money to upload meshes.
We want to offer free spaces to those who want self-experiment where to download your meshes using the software on OpenSim grids Craft CloneLife, or even (if you prefer) on your home computer, so you can organize and display exhibitions to  share your creations.
I’m personally being working for years in order to overcome the learning step with the people having difficulty and to cope with the complexity associated with the usage of these tools and 3D modeling. We use free software and privilege open source, and in particular – talking about  3D software –  Blender.
Learning how to use Blender is not easy and there is the need for a psychological guidance and especially a reference community where people can clarify their doubts.
So the Mesh Town project consists primarily  in the following points:
1 \ OpenSim  space on a grid where to experiment with your own creations
2 \ a calendar of classes in Second Life, Craftm blogs, facebook, YouTube, etc. where people can learn to use OpenSim and Blender
3 \ The organization of shows, events and exhibitions when we have enough material to show in a “fleshy” and vivid experience what can be achieved collectively.
Hope that in 2012 we will be able to do such a thing, for all those interested, please  attend my Blender meetings (the first is a series of blender classes starting  tomorrow: I’ll be back at Kohima foundation every Thursday at 22) and please find me and hang out  for a chat over a glass of virtual beer (or a tea or a hookah, depending on your inclinations) in order to fill this idea of ​​content, ideas and people.

Without commitment: what you will be able to do is OK, if nothing is done, patience, that was an additional bit of experience  enabling us to know and cope with ourselves….

“Unwrap” della prima lezione di Modellazione Blender di Optimo Maximo al MIC 9 Novembre 2011

Introduzione

Questo articolo è il frutto del lavoro svolto da Optimo Maximo al MiC in Secondlife (cfr promozione http://museiincomuneroma.wordpress.com/2011/11/09/second-life-corso-avanzato-di-blender-al-mic/) .
L’argomento è complesso, l’insegnante bravissimo ma è necessario che queste cose vengano anche diffuse in modo un po’ più rallentato in modo che chi segue possa prendersi i suoi tempi e seguire le parti più compless con calma ed attenzione.
Questo “post” è il frutto di una notecard inviatami da Optimo con la “scaletta” dei passaggi e della sua successiva
esecuzione in un ambiente Blender, dove ho cercato quanto è possibile di non saltare alcun passaggio e di evidenziare
tutti o quasi i tasti premuti. E’ ovviamente necessario che le persone conoscano le basi di blender come ad esempio:
  • Selezionare, muovere, ridimensionare oggetti
  • Selezionare, muovere, ruotare etc mesh in edit mode
  • Sappiano aprire la vista UVImage
  • Abbiano installato l’AddOn Primstar (non è essenziale, ma fornisce la prima sculpted map)

Scopo

Al termine di questa lezione sarete in grado di
  • modellare una parte di un oggetto usando una curva bezier
  •  fare una ambient occlusion (fate riferimento anche a questo video: http://www.youtube.com/watch?v=B_WSDKkG8aY
  •  costruire varie texture con differenti uvmap per texturizzare varie parti dell’oggetto (compresa una opzione di “decalcomania”
  • unire tutte le texture in una sola immagine risultante che, seguendo la “sculptiemap” può essere importata in SL

SETUP:

Dovete avere installato Blender 2.59 e l’addon Primstar2.

Vi Suggerisco di abilitare “rotate around selection” (nella sezione interface), che semplifica il modo in cui ruotate attorno agli oggetti in modo da essere più naturale per chi proviene da SL.
Cancellate il cubo di partenza:  DEL + Invio

Parte I: MODELLAZIONE:

M1) aggiungi un cilindro sculpt
SHIFT-A Mesh->UV Shape -> Cylinder
M1a) Modificate il default del cilindro 8×8 in modo che appaia invece 4×32 per averlo “oblungo” con una sculpted map 16 x 128 che consente di avere molti dettagli nella direzione verticale.

M2) nel modifiers stack, in subsurf modifier, accanto al nome del modificatore vi sono 4 pulsanti attivi, disattivare il quarto da sinistra (con un triangolino disegnato, deve rimanere con sfondo grigio chiaro) per la visualizzazione classica della gabbia (come si vede a dx)

M3) In edit mode selezionare 2 edgeloop contigui in modo da frazionare lo sculpt in 2 parti, lasciando almeno 4-6 edgeloops disponibili per il manico.
M4) allontanare il pezzo per il manico in modo da non essere influenzato dal proportional editing (da attivare nei prossimi passi)
M5) aggiungere un edgeloop in basso, fra l’ultimo ed il penultimo edgeloop, in modo da poter chiudere la base. Selezionare l’ultimo edgeloop e collassare i vertici al centro.
M6) eseguire la medesima operazione del punto 5 sulla sezione destinata a diventare il manico (aggiunta di un edgeloop fra l’ultimo ed il penultimo edgeloop in alto)
ecco l’oggetto in modalità object fin qui costruito:

7) attivare il proportional editing con il settaggio di default (smooth) ed iniziare la modellazione del corpo del vaso
8) modellare il manico per ottenere la forma finale della sua sezione, incluso lo spessore.
9) con i due pezzi ora modelati, deselezionare tutto e selezionare solo la sezione del manico
10) nel pannello delle proprietà, aprire il pannello Object properties (triangolino disegnato) e raggiungere il sottopannello vertex group. Creare un gruppo (rinominarlo se necessario, ad esempio quando i pezzi da modellare con i vertex groups sono più di uno)

11) cliccare il pulsante “assign” per assegnare la selezione corrente (la sezione del manico) al vertex group selezionato (quello appena creato)

12) tab in object mode e aggiungere una Bezier Curve

13) da top view, passare in edit mode della curva ed appiattirla scalandola a zero sull’asse Y (S+Y). La curva ora è una linea retta.

14) tab in object mode e spostare la curva al lato del vaso e ruotarla di 90 gradi in modo da avere la direzione della curva verso il basso (controllare in edit mode)
alternativa migliore:

15) selezionare il vaso, aggiungere il modificatore Curve. Impostare i parametri nome oggetto (BezierCurve) e Vertex Group, per influenzare solo una parte del vaso (la sezione del manico)

16) spostare l’oggetto in modo tale che la curva risieda all’interno dela sezione del manico (sempre in object mode)

17) selezionare la curva e passare in edit mode. Modellare la curva nella forma che vogliamo per il manico

Modelliamo anche il vaso sottostante, scegliendo alcuni edge loop e facendo una forma un po vaseggiante :)
Ricordatevi di usare alcuni dei loop in alto per farli rientrare nel vaso per dare la mesh per l’interno del vaso

18) applicare tutti i modificatori partendo dal primo in alto nello stack

19) eliminare la curva non ci serve più
20) posizionare il  manico nella posizione finale sull’oggetto

TEXTURIZZAZIONE:

1) aprire il material panel ed aggiungere un nuovo materiale
2) passare nel pannello textures e creare una nuova texture, cambiare da clouds ad image or movie

3) selezionare la texture per il materiale di base del vaso
4) cambiare il settaggio di mapping coordinates da generated a UV e selezionare immediatamente la UV sculptie

5) passare al panello object data (triangolino) e creare una nuova UV, rinominarla in modo da riconoscere a quale texture appartiene. Attivare il pulsante della fotocamera al lato del nome della UV
Se avete cliccato create UVTex al momento della creazione del cilindro ve ne trovate già una.
Per semplicità cancelliamo con il “-” la UVTex e ripartiamo con quella di base, aggiungendone una (dobbiamo essere in edit mode e avere aperto la vista UV Image editing !)

Selezioniamo la macchina fotografica

8) attivare textured view nella visuale 3d ed eventualmente spostare la lampada in modo da poter vedere il mapping che ci accingiamo ad eseguire

Vedete che nonostante che abbiamo messo tutta la uvmap fuori dalla immagine, vediamo egualmente texturizzato :( Non è importante al momento perchè quello che importa è quello che vede il rendering (lo vediamo meglio fra un poco). Limitiamoci solo a far coincidere la UVMAP di destra con l’immagine
9) scalare la parte di UV che ci interessa in modo da coprire tutta l’immagine che vogliamo mappare. Le textures fornite per l’esercizio sono seamless (affiancabili)

La trasciniamo sulla treccia e poi con
Pack islands (ctrl-P) la disponiamo in modo che si appoggi alla treccia
Poi usando un po’ di editing (GY per spostare al centro e poi SY per ampliare la uvmap), vedete che possiamo far coincidere
chiamiamo magari “treccia” questa uvmap in luogo di uvtex che è troppo anonima:
10) se la mappatura sembra andar bene, passare nel texture panel e creare una nuova texture, utilizzando questa immagine

… cfr immagine riassuntiva al punto 11

11) mapping coordinates da generated a UV selezionando la UV che abbiamo appena modificato e che sarà la nostra origine.

12) nel sottopannello image mapping, cambiare da repeat a clip. in questa maniera solo la parte di UV posizionata sopra l’immagine verrà mappata sulla texture finale

L’effetto di 10 11 e 12 è quello di avere la seguente:

13) tornare nel pannello object data, selezionare la UV chiamata sculptie e da li creare una nuova UV per aggiungere una nuova texture da mappare sull’oggetto. Rinominarla per distinguerla dalle altre ed asicurarsi che sia la UV attiva.

::::::::IMPORTANTE!!! :::::::::

L’aggiunta di nuove UV copia la UV correntemente selezionata per creare quella nuova. E’ sempre consigliabile crearne di nuove partendo da quella di base e modificandola sul posto per i propri scopi per evitare interferenze dovute ai settaggi della UV di origine.

14) ripetere i passaggi relativi alla selezione dell’area su cui applicare la nuova immagine (decalcomania)
Portiamo fuori tutta la uvmap verso il basso
Selezioniamo nella finestra 3D la parte in cui applicare la decalcomania
con un insieme di trucchetti SX SY GY GX e riusciamo a ricoprire perfettamente la zona con l’immagine:

15) creare una ulteriore texture per la decalcomania come già fatto in precedenza, associandola all’immagine e alla uv scendere in basso e sotto influence cambiare blending mode da mix a darken

e la parte di darken subito dopo

16) cambiare image coordinates da generated a UV selezionando la UV creata per questa texture
17) in image mapping cambiare da repeat a clip

ORA abbiamo fatto 3 texture una per il materiale generale del vaso, UNA per la base con una treccia, UNA per la decalcomania. Tanto per non farci mancare nulla aggiungiamo anche UNA texture per l’ambient occlusion, ma PRIMA dobbiamo generarla

18) in object properties, selezionare ora la UV chiamata sculptie ed attivarla per il render. In edit mode, creare una nuova immagine per fare il bake dell’ambient occlusion.

19) in world panel (tasto con una sfera celeste) attivare il pannello ambient occlusion e cambiare il blending mode da add a multiply. Poco più in basso, sottopannello Gather, selezionare il numero di samples desiderati (di default è 5). Più alto questo valore, più tempo per il bake e più alta la risoluzione della AO map.

20) in render panel (bottone con fotocamera), sottopannello bake, cambiare la selezione da full render a Ambient Occlusion

21) attivare la casella normalized per evitare influenze da parte del materiale. Aggiungere un Subsurf modifier sulla mesh con 2 suddivisioni per ammorbidire le ombreggiature che verranno generate dall’Ambient Occlusion.

22) “hit the BAKE button” e salvare l’immagine sull’hard disk per permettere a blender di utilizzarla. Salvare anche il file .blend appena dopo aver salvato l’immagine.
L’ambient occlusion potrebbe essere simile a questo.

23) creare ora una nuova immagine su cui la texture finale verrà generata, assicurandosi che sia sempre attiva la UV chiamata sculptie.

24) in texture panel, creare una nuova texture utilizzando l’immagine dell’AO appena creato. Selezionare UV chiamata sculptie e cambiare il blending mode da mix a multiply. Nel campo COLOR poco più in alto, abbassare il valore da 1.000 a 0.700 o 0.500 (a seconda del vostro gusto).
25) tornare nel pannello render (macchinetta fotografica) e nel sottopannello bake cambiare da Ambient Occlusion a Texture

26) “hit bake button” e la nostra texture finale è generata!!
Nel preview rendered si vede già il baking del vaso:

Ricapitoliamo

Ricapitolando, abbiamo imparato a fare le seguenti cose:

Modellazione:
1) costruire un oggetto da un cilindro
2) separarlo in due parti collegate da un capello
3) modellare le due parti indipendentemente
4) usare il modifier curva per applicare una curva ad una parte di un oggetto

Texturing
1) Applicare una texture su una uvmap (quella di default sculptie di Primstar)
2) Costruire una uvmap derivata per mappare una parte dell’oggetto (la parte di sotto con la corda)
3) Costruire una uvmap derivata per mappare una decalcomania sull’oggetto
4) Costruire una immagine di ambient occlusion
5) sommare le texture 1-2-3-4 per ottenere il baking finale che si può importare in SL (!!!)

BEL LAVORO RAGAZZI!!!

Alcuni video tutorial in italiano per usare rokuro (pro)

MI è stato chiesto di divulgare tecniche di modellazione basate su Rokuro che è un software che consente la costruzione di sculpted prim usando una metodologia molto più semplice rispetto a Blender.

Rokuro lo potete trovare in versione gratuita qui: http://kanae.net/secondlife/rokuro.html, mentre la versione professionale la potete scaricare qui: http://kanae.net/secondlife/rokuro.html Per comprare la licenza d’uso di questi prodotti dovete andare in SL nella terra del creatore http://maps.secondlife.com/secondlife/Phasic%20Foo/33/40/23 (Rokuro Pro costa 3500 L$),

La versione gratuita di Rokuro consente comunque di fare quasi tutto quello che fa RokuroPro incluso l’export dell’oggetto in formato mesh (COLLADA, .dae), con la differenza fondamentale che nella versione base non è possibile vedere il preview dell’oggetto. Ricordo che Rokuro non può fare la texturizzazione (c’è somato o altre varianti vendute in quel negozio per quello), e che in generale la qualità prodotta non è ottimale (in alcuni miei esperimenti ho avuto bisogno di fare uno smoothing dei vertici con blender per avere una qualità accettabile).

Per coloro interessati, ecco un video in italiano prodotto da Tonyno Vella http://tonynovella.blogspot.com/ che è un ottimo insegnante di building ed altro che trovate facilmente in SecondLife.

 

E qui i video fatti dal creatore originale del prodotto senza audio da seguire con molta attenzione.

Corso di Blender di base 101-102-103 / Basic Blender Class 101-102-103

Per coloro interessati all’uso di blender per fare mesh, ecco una sequenza di 3 video che culminano con un video che insegna a fare una spada importata in SL. I video sono titolati in inglese ed italiano. Per seguirli basta scaricarsi la versione di blender 2.59 dal sito www.blender.org lanciarla ed eseguire passo passo i vari pezzi. Se avete un laptop o un mac è conveniente impostare nelle preferenze http://wiki.blender.org/index.php/Doc:Manual/Interface/Keyboard_and_Mouse come spiegato all’inizio del video 103.

For those interested in Blender usage to make mesh, here a sequence of 3 videos which terminates for now with a tutorial on how to build a Sword and import it in SL. Videso are titled in English and Italian. To follow them you just need to download blender 2.59 from www.blender.org run it and follow step by step various pieces. If you have a laptop or a mac it’s convenient to set up preferences as  http://wiki.blender.org/index.php/Doc:Manual/Interface/Keyboard_and_Mouse also explained at beginning of video 103.

Blender 101 le basi per muoversi:

Blender 102: le basi per editare i vertici:

Blender 103: costruiamo una spada con subsurf/crease e importiamo come mesh in SL:

Corso di base di Blender bilingue a Penguin Tribu

Sto iniziando una collaborazione con Penguin Tribu dove tengo dei corsi bilingue ITA/ENG il venerdì ore 22 italiane su Blender.

I’m starting a cooperation with Penguin Tribu where I’m holding bilingual ITA/ENG classes.

Finora ho fatto due incontri di cui riporto qui i link per seguirli anche per chi non è potuto assistere:

Until now I held two meetings, I put here links for slides and material for who couldn’t assist.

Venerdì 2 Settembre / Friday September 2nd:

Venerdì 9 Settembre / Friday September 9th:

* ITA/English slides: http://alisl.org/penguin/slide.php?file=Blender101.txt&page=1

Ecco i video youtube correlati:

 

 

 

 

 

Snippet: funzione di debug

Una delle cose più complesse negli script di SL è quella di affrontare la fase di debug. Ho scritto una minuscola funzione che mi consente il debug in maniera rapida e semplice e credo sia utile condividere questa riga di codice in quanto mi ha fatto risparmiare un sacco di tempo:

integer DEBUG = TRUE;
debug(list _lst)
{
    if(DEBUG) llOwnerSay(llDumpList2String(_lst," "));
}

La comodità è che possiamo chiamarla senza alcun casting dei dati come da esempio:

debug( ["x=",x,"y=",y,"z=",z,"vettore=",vector] );

Comodo no?

Se vogliamo effettuare un debug in un ciclo:

list deb=[];
for( i=0; i<l; i++) {
    //.....
    deb += ["i vale",i, "pippo=",pippo];
}
debug(deb);

Unica accortezza deve essere usata per le liste:

list giorni=["LU","MA","ME","GI","VE","SA","DO"];
debug( ["giorni vale"] + giorni + ["i=",i] );

Questo perché una lista non può contenere liste e scrivendo:

debug(  ["giorni vale", giorni,"i=",i]);

otterremmo un errore di runtime

Ora un altro trucco per attivare e disattivare il debug senza modificare lo script. Personalmente utilizzo il touch verificando il tempo di pressione del tasto del mouse. Se è corto (diciamo < 2 secondi) faccio le cose che dovrei fare al touch, se supera quel tempo cambio stato a DEBUG:

 

integer DEBUG=FALSE;
default
{
    state_entry() { }

    // quando inizia il touch resetto il time
    touch_start(integer total_number) {  float x = llGetAndResetTime(); }

    // quando il tasto viene rilasciato gestisco l'evento
    touch_end(integer total_number) 
    {
        // se sono passati più di due secondi e chi tocca è l'owner
        if( llGetTime() > 2.0 && llGetOwner() == llDetectedKey(0) ) {
            if( DEBUG ) { DEBUG = FALSE; llOwnerSay("DEBUG OFF"); }
            else { DEBUG = TRUE; llOwnerSay("DEBUG ON"); }
        } else {
            // altro codice touch
        }
    }
}

Spero di essere stato minimalisticamente utile a qualcuno

Vittorio Bing

Navigatore di diapositive MxN

Questo è un “semplice” script uno dei primi che avevo fatto che consente di navigare delle diapositive “Multiple”, leggendole dall’inventory del “proiettore di diapositive”.
Le immagini devono terminare con xxxxx-3×3 per indicare che sono una matrice di 3 righe per 3 colonne. Lo script è molto sofisticato e consente anche di lasciare a vista le ultime diapositive viste. Inoltre consente anche di fare una ricerca e di visualizzare dei commenti sulle diapositive.


Lo script computa i parametri

integer iM=4;
integer iN=4;

float fSCALX=1;
float fSCALY=1;
float fSPANX=.5;
float fSPANY=.5;

e poi fa evolvere con la funzione next() verso l’elemento successivo. Si può semplificare per adempiere al suo scopo su una singola texture

//
// Pakkio Jan 2008, V3.0
// PowerPoint projector
//
// Textures must be made MXN and have it in their name as in <name>-MxN
// example: my texture-4x4 (multiple images can be mixed)
// for each must find <name>-MxN.txt to have one line for each slide with "|" representing newlines
// if we find a <name>-MxN-i.give this will be given to people if required
//
// script will be able to select next texture, prev texture, and will keep last 5 slides, comments, inventory for retrieving them
//
integer iDELAY=10;
integer iLISTENHANDLER=0;
integer iMENUCHANNEL;
integer iCHANNEL=100;
integer iDEBUG=0;
integer iHOVERTEXT=1;
vector  vHOVERCOLOR=<1,1,1>;
integer iMENULISTENER=0;
vector  vSTARTPOS;
string sTELEPORT="";
vector vTELEPORT;

string sINVENTORY;

string sSEARCH="";

// will be set up during reset looking at the names of each link
integer eCONTROLLER;
integer eBOARD=1;
integer iPLAY=0;



// to loop over all the textures in the inventory
integer iTEXTURE;
string sTEXTURE;
integer bENDTEXTURE;

// to loop over the comments in notecards (
string sCOMMENT;
key kCOMMENT;
integer iCOMMENT;

integer iM=4;
integer iN=4;

float fSCALX=1;
float fSCALY=1;
float fSPANX=.5;
float fSPANY=.5;

string sME="MAIN";

list lHISTORYLINK=[];
list lHISTORY=[ "","","","","" ];
list lTEXT=[ "","","","","" ];

push(string texture, vector scale, vector offset)
{
    lHISTORY=llDeleteSubList(lHISTORY,0,0)+llDumpList2String([ texture, scale, offset ],"|");
    integer i;
    for(i=0;i<5;i++)
    {
        list data=peek(i);

        llSetLinkPrimitiveParams(llList2Integer(data,0),
            [ PRIM_TEXTURE, ALL_SIDES,
            llList2String(data,1), // texture
            (vector)llList2String(data,2), // scale
            (vector)llList2String(data,3), // offset
            0.0 ]);
    }
}
push_text(string text)
{
    lTEXT=llDeleteSubList(lTEXT,0,0)+text;
}
// return [linknum, texture, scale, offset]
list peek(integer offset)
{
    return [ llList2Integer(lHISTORYLINK,2*offset+1)] + llParseStringKeepNulls(llList2String(lHISTORY,offset),["|"],[]);
}


string str_replace(string src, string from, string to)
{//replaces all occurrences of 'from' with 'to' in 'src'.
    integer len = (~-(llStringLength(from)));
    if(~len)
    {
        string  buffer = src;
        integer b_pos = -1;
        integer to_len = (~-(llStringLength(to)));
        @loop;//instead of a while loop, saves 5 bytes (and run faster).
        integer to_pos = ~llSubStringIndex(buffer, from);
        if(to_pos)
        {

            buffer = llGetSubString(src = llInsertString(llDeleteSubString(src, b_pos -= to_pos, b_pos + len), b_pos, to), (-~(b_pos += to_len)), 0x8000);
            jump loop;
        }
    }
    return src;
}

// use channel 10 to debug (no cluttering of 0 channel, easy to listen with mystitool)
debug(string str)
{
    if(iDEBUG==1) llSay(10,str);
}

string dumpList(list lst)
{
    return llDumpList2String(lst,"|");
}
list parseString(string str)
{
    return llParseStringKeepNulls(str,["|"],[]);
}
//
// Will display the xxx|yyyy|zzz text loading <> color and @ for giving inventory
//
info(string str)
{
    sINVENTORY=""; string vcolor="<1,1,1>";
    list lst=llParseStringKeepNulls(str,["|"],[]);
    integer goon=1;
    while(goon==1)
    {
        string piece=llList2String(lst,0); string first=llGetSubString(piece,0,0);
        if(first=="@" || first=="<")
        {
            string rest=llGetSubString(piece,1,-1);
            if(first=="@")
            {
                sINVENTORY=rest; debug("Inventory: "+sINVENTORY);
                lst+="==>"+sINVENTORY;
            }
            if(first=="<")
            {
                vcolor=piece;
                debug("Color changed to. "+vcolor);
            }
            lst=llDeleteSubList(lst,0,0);
        }
        else goon=0;
    }
    str=llDumpList2String(lst,"\n");

    llMessageLinked(LINK_SET,1,dumpList( [ sME, "TEXT", "SHOW", str, vcolor ]),"");
}

// utility variables

vector vPOS; // starting point is the last one so that next will go on the first ;)
integer iENABLED=1;     // autoslideshow enabled
integer iDIRECTION=1;   // direction on next
integer iCNT=0;         // counting from 1
integer iPOKE = FALSE;  // no dblclick




setBoard()
{
    list parms=[ PRIM_TEXTURE,0 , sTEXTURE, <fSCALX, fSCALY,0.0>, vPOS, 0.0 ];
    debug("SetPrimitiveParams: "+llList2CSV(parms));
    llSetPrimitiveParams(parms);
}
//
// go to next images in the grid
//
// order is from left to right and from top to bottom (like reading)
// Quite intuitive
//
next ()
{
    debug("doing next");
    iDIRECTION=1;
    vPOS.x=vPOS.x+fSCALX;
    if(vPOS.x>fSPANX){
        debug("vPOS.x over fSPANX");
        vPOS.x=-fSPANX;
        vPOS.y=vPOS.y-fSCALY;

        if(vPOS.y<-fSPANY)
        {
            debug("vPOS.y over fSPANY");
            vPOS.y=fSPANY;
        }
    }
    iCNT++;
    if(iCNT>(iM*iN))
    {
        debug("iCNT over "+(string)(iM*iN));
        iTEXTURE++;
        changeTexture(); // will go on next texture
        return;
    }




    if(sSEARCH=="")
    {
        setBoard();
        push( sTEXTURE, <fSCALX, fSCALY, 0>, vPOS);
    }
    //info((string)iCNT +"/"+(string)(iM*iN)+"/"+(string)(iTEXTURE+1));
    //llSetText((string)cnt    +"/"+(string)NUM+"\nx="+(string)pos.x+"-y="+(string)pos.y,<1,0,0>,1);

    if(bENDTEXTURE==0)
    {
       iCOMMENT++; sTELEPORT="";

       if(llGetInventoryType(sCOMMENT)!=INVENTORY_NOTECARD)
       {
        // check if this is a teleport URL xxxxxx,sim (x,y,z)
        // find "("
        integer i=llSubStringIndex(sTEXTURE,"(");
        if(i>=0)
        {
            integer j=llSubStringIndex(sTEXTURE,")");
            if(j>=0)
            {
                // look for first ","
                integer k=llSubStringIndex(sTEXTURE,",");

                sTELEPORT=llStringTrim(llGetSubString(sTEXTURE,k+1,i-1),STRING_TRIM);
                string position=llStringTrim(llGetSubString(sTEXTURE,i+1,j-1),STRING_TRIM);;
                vTELEPORT=(vector)("<"+position+">");
                llMessageLinked(LINK_SET,1,dumpList( [ sME, "TEXT", "SHOW", "Teleport==>"+sTELEPORT+(string)vTELEPORT, "<1,1,1>" ]),"");
                return;
            }

        }

        llMessageLinked(LINK_SET,1,dumpList( [ sME, "TEXT", "SHOW", " ", "<1,1,1>" ]),"");

       } else {

          kCOMMENT=llGetNotecardLine(sCOMMENT,iCOMMENT);
       }
    }


}


changeTexture()
{
    debug("changing texture to "+(string)iTEXTURE);
    sTEXTURE=llGetInventoryName(INVENTORY_TEXTURE,iTEXTURE);
    if(sTEXTURE=="")
    {
        if(sSEARCH!="")
        {
            sSEARCH="";
            return;
        }
        // if textures finished then go back to first
        iTEXTURE=0;
        changeTexture();
        return;
    }
    // use only textures beginning with _
    //if(llGetSubString(sTEXTURE,0,0)=="_")
    {
        list pieces=llParseStringKeepNulls(sTEXTURE,["-"],[]);
        string lastpiece=llList2String(pieces,-1);
        pieces=llParseStringKeepNulls(lastpiece,["x"],[]);

        iM=(integer)llList2String(pieces,0);
        iN=(integer)llList2String(pieces,1);
        if(iM==0)iM=1;
        if(iN==0)iN=1;
        debug("read iM="+(string)iM+" iN="+(string)iN);

        // look for a comment notecard
        sCOMMENT=sTEXTURE+".txt";
        iCOMMENT=-1;

        fSPANY=((float)iM-1)/(2*(float)iM);
        fSCALY=1/(float)iM;
        fSPANX=((float)iN-1)/(2*(float)iN);
        fSCALX=1/(float)iN;

        vSTARTPOS=<fSPANX,-fSPANY,0>;
        vPOS=vSTARTPOS;
        iCNT=0;

        bENDTEXTURE=0;

        // show first image/subimage while notecardline will setText their content
        next();
    }
    //else
    //{
    //    iTEXTURE++;
    //    changeTexture();
    //}

}



default
{


    state_entry()
    {


        debug("Starting...");
        // read notecard, at end will start viewer
        //iSETUP=0;
        //kSETUP=llGetNotecardLine(sSETUP,iSETUP);

        // find LINKNUM of History-0 until History-4
        integer i;
        for(i=2;i<llGetNumberOfPrims();i++)
        {
            string name=llGetLinkName(i);
            debug("Found name: "+name+" linknum: "+(string)i);
            list pieces=llParseStringKeepNulls(name,["-"],[]);
            if(llList2String(pieces,0)=="History")
            {
                // it is an history
                debug("marking history piece");
                lHISTORYLINK+=[ llList2String(pieces,1), i ];

            }

        }
        // have history links ordered by History-x parm
        lHISTORYLINK=llListSort(lHISTORYLINK,2,TRUE);

        debug("lHISTORYLINK: "+llList2CSV(lHISTORYLINK));

        // reset play button to play and not pause if needed
        llMessageLinked(LINK_SET,1,dumpList( [ sME, "PLAY", "OFFSET", "<.333,.333,0>"]),"");

        // look for first image in the inventory
        iTEXTURE=0;
        changeTexture();

        //llListen(0,"",NULL_KEY,""); // for searching




    }
    changed(integer change)
    {
        if(change==CHANGED_INVENTORY)
        {
            //llResetScript();
            return;
        }
    }



    // notecard reader
    dataserver(key id, string data)
    {

       if(id==kCOMMENT)
         if(data!=EOF)
         {
             debug("Reading comment line #"+(string)iCOMMENT);

             if(llGetSubString(data,0,0)!="#")
             {


                 if(sSEARCH!="")
                 {

                     // either find uppercase or with no casing (numbers)
                     if(llSubStringIndex(llToUpper(data),llToUpper(sSEARCH))<0)

                     {
                         llMessageLinked(LINK_SET,1,dumpList( [ sME, "TEXT", "SHOW", "searching for "+sSEARCH+"...", "<1,1,1>" ]),"");
                         // if not found go next
                         next();
                         return;
                     }
                     else
                     {
                         // found!!
                         sSEARCH="";
                         setBoard();
                         push( sTEXTURE, <fSCALX, fSCALY, 0>, vPOS);
                     }

                 }
                 //string replaced=str_replace(data,"|","\n");
                 push_text(data);

                 // only first line | replaced by \n
                 info(data);




                 return;
             }
             iCOMMENT++;
             kCOMMENT=llGetNotecardLine(sCOMMENT, iCOMMENT);



         }
         else
         {
            debug("End of comments");

            bENDTEXTURE=1;

         }


    }



    touch_start(integer total_number)
    {

        key avatar=llDetectedKey(0);

        if(iLISTENHANDLER!=0)llListenRemove(iLISTENHANDLER);

        integer touched=llDetectedLinkNumber(0);

        debug("touched: "+(string)touched);
        if(touched==1){
            if(sTELEPORT!="")
            {
                llMapDestination(sTELEPORT,vTELEPORT,ZERO_VECTOR);
                return;
            }
            if(sINVENTORY=="") next();
            else
            {
                llGiveInventory(avatar,sINVENTORY);
                return;
            }
        }

        string linkname=llGetLinkName(touched);
        if(linkname=="Search")
        {
            iLISTENHANDLER=llListen(0,"",avatar,"");
            llSetTimerEvent(60);
            llDialog(avatar,"Say on normal chat FIND xxx or SEARCH xxx. You have 60 seconds for doing it.",[],-1);
            return;

        }

        if(linkname=="Stop")
        {
            llResetScript();
            return;
        }
        if(linkname=="Next")
        {
            next();
            return;
        }
        if(linkname=="FastNext")
        {
            iTEXTURE++;
            changeTexture();
            return;
        }
        if(linkname=="FastPrev" && iTEXTURE>0)
        {
            iTEXTURE-=1;
            changeTexture();
            return;
        }
        if(linkname=="Play")
        {
            if(iLISTENHANDLER!=0)
            {
                llListenRemove(iLISTENHANDLER);
                iLISTENHANDLER=0;
            }
            if(iPLAY==0)
            {
                iPLAY=1;
                llSetTimerEvent(0.1);
                llMessageLinked(LINK_SET,1,dumpList( [ sME, "PLAY", "OFFSET", "<.333,-.333,0>"]),"");
                return;
            }
            else
            {
                iPLAY=0;
                llMessageLinked(LINK_SET,1,dumpList( [ sME, "PLAY", "OFFSET", "<.333,.333,0>"]),"");
                llSetTimerEvent(0);
                return;
            }
        }


        list pieces=llParseStringKeepNulls(linkname,["-"],[]);
        if(llList2String(pieces,0)=="History")
        {
            // touched history, will display its content
            integer index=(integer)llList2String(pieces,1);
            debug("Trying to reinstate image");
            list data=peek(index);

            llSetLinkPrimitiveParams(eBOARD,
                [ PRIM_TEXTURE, ALL_SIDES,
                llList2String(data,1), // texture
                (vector)llList2String(data,2), // scale
                (vector)llList2String(data,3), // offset
                0.0 ]);

            info(llList2String(lTEXT,index));

            return;

        }



    }

    timer()
        {
            if(iLISTENHANDLER!=0)
            {
                llListenRemove(iLISTENHANDLER);
                iLISTENHANDLER=0;
                llSetTimerEvent(0);
                return;
            }
            next();
            llSetTimerEvent(iDELAY);
        }

    listen(integer channel,string name,key id,string message)
    {
        // SEARCH?

        if(llToUpper(llGetSubString(message,0,5))=="SEARCH")
        {

            sSEARCH=llGetSubString(message,7,-1);
            llSay(0,"Starting search for "+sSEARCH);
            // go on until found it
            next();
            return;
        }
        if(llToUpper(llGetSubString(message,0,3))=="FIND")
        {
            debug("FIND command");
            string sfind=llGetSubString(message,5,-1);
            llSay(0,"Starting find for "+sfind);
            for(iTEXTURE=iTEXTURE;iTEXTURE<llGetInventoryNumber(INVENTORY_TEXTURE);iTEXTURE++)
            {

                sTEXTURE=llGetInventoryName(INVENTORY_TEXTURE,iTEXTURE);
                debug("Scanning texture "+sTEXTURE);
                if(llGetSubString(sTEXTURE,0,0)=="_")
                {
                    string src=llToUpper(sTEXTURE);
                    string find=llToUpper(sfind);
                    debug("Checking if '"+src+"' contains '"+find+"' "+(string)llSubStringIndex(src,find));
                    if(llSubStringIndex(src,find)>=0)
                    {
                        debug("Found");
                        changeTexture();
                        return;
                    }
                }
            }
            return;

        }
    }

}