Lo skinning mesh è attualmente il migliore sistema di animazione esistente. Lo skinning unisce la possibilità di deformare gli oggetti come il tweening ma migliorando la qualità tramite curve più morbide e soprattutto lasciando il lavoro alla velocissima GPU della scheda video. DirectX offre pieno supporto allo skinning anche nel formato X in cui è possibile creare delle mesh in modo che siano animate tramite questo sistema e diversi software offrono la possibilità di creare modelli animati in skinning (esempio il 3D Studio Max). Utilizzare lo skinning non è semplicissimo per due motivi:
1. Non esistono classi che fanno interamente il lavoro di caricamento e gestione;
2. I modi con cui vengono salvati le animazioni in skinning sono spesso diversi a secondo del programma e del file;
Entrambi devono venire risolti da noi. Il sistema di caricamento di file x avviene quasi come se stessimo caricando un file binario in visual basic e dovremo quindi cercare di rendere il nostro algoritmo il più flessibile possibile adattandolo a tutti i problemi e le imprecisioni dei fileX con cui potremo venire a trovarci (oppure affezionarci ad un programma o ad un plugins ed imparare il suo sistema di salvataggio). Passiamo alla teoria, molto importante in questo tutorial. Un modello per lo skinning è un modello 3D in cui vengono posizionate delle bones (ossa) che come suggerisce il nome altro non sono che lo scheletro del modello. Ogni bone influenza i vertici che si trovano nelle sue vicinanze (parametri regolati dai programmi grafici) e permettono di deformare il modello semplicemente muovendole. Si capisce che con la giusta abilità si può ricostruire uno scheletro per il modello che gestisca ogni movimento (esempio tutte le principali articolazioni del corpo umano). Guardate l’immagine qui sotto:
Nel cilindro si vede l’ossatura (poligoni rossi). Curvare l’ossatura significa curvare il modello. In DirectX lo skinning mesh significa creare una geometry blending mesh in cui sono già settati i pesi (weight). Le matrici utilizzate dal geometry blending saranno appunto le matrici delle ossa che saranno caricate nell’animazione. Le ossa infatti saranno animate in modo del tutto simile alle mesh nelle animazioni con animation set. Le fasi di caricamento saranno queste nell’ordine:
1. Apertura del modello
2. Trasformazione del modello in una mesh con vertici in blending
3. Caricare l’animazione
Il processo di caricamento richiede molto intervento da parte del programmatore. Alla base di tutto c’è la funzione LoadHierarchyFromFile che si occupa di avviare il processo di caricamento. La funzione LoadHierarchyFromFile usa come parametro una classe allocate hierarchy di tipo astratto. Il programmatore deve sovrascrivere le funzioni definite all’interno per gestire il caricamento di oggetti Frame ed oggetti MeshContainer. Un frame è una struttura che contiene tutti i dati di una mesh (non le animazioni). Può contenere anche altri frame che ne contengono a loro volta altri. I frame figli avranno come matrice di trasformazione la concatenazione delle matrici genitori più l'eventuale matrice animazione. Utilizzeremo come classe frame una classe derivata da Frame con una matrice che conterrà la concatenazione fra le matrici della animazione. I frame contengono dati chiamati meshContainer che noi useremo derivati per avere dati extra per l'animazione in skinning mesh.
Breve descrizione della classe:
la classe contiene all'interno 3 classi:
Ah che deriva da allocatedHierarchy. In pratica l'istruzione
Dim animazione As AnimationRootFrame 'contenitore della animazione
Dim alloc As New AH 'classe per l'allocazione
'caricamento animazione
animazione = Mesh.LoadHierarchyFromFile(filesrc, MeshFlags.Managed, device, alloc, Nothing)
la classe passata all'istruzione LoadHierarchy sovrascrive i metodi. DirectX chiama i metodi come se fossere eventi e mentre carica la mesh passa i dati alle 2 funzioni. Le funzioni richiedono la restituizione di un frame ma noi restituiremo una classe derivata al frame, idem per le meshContainer.
'classe derivata da frame che contiene la matrice combinata
'ossia la generazione della matrice in animazione
Public Class frameD
Inherits Frame
Public matrixC As Matrix 'matrice combinata
End Class
La classe frame aggiunge la matrice che sarà la matrice del frame di destinazione.
Public Class MeshContainerDerived
Inherits MeshContainer
Public meshTextures() As Texture 'texture
Public numAttr As Integer = 0 'numero attributi
Public numInfl As Integer = 0 'numero influenze delle bones
Public bones() As BoneCombination 'bones
Public frameMatrices() As frameD 'frame figli
Public offsetMatrices() As Matrix 'matrici delle bones
End Class
le funzioni di allocateHierarchy restituiranno i frame con matrice ad identità e la mesh container con tutti i dati inseriti. Tutti restituiranno classi derivate. Stavolta per avanzare i frame non bisogna fare altro che usare
animazione.AnimationController.AdvanceTime(t, Nothing)
t non indica il tempo nella animazione, ma la quantità di tempo da mandare avanti. Inserire 1 significa che ad ogni utilizzo l'animazione va avanti di 1. Per avere il tempo di durata di una animazione usate
Return animazione.AnimationController.GetTrackAnimationSet(0).Period
per settare la velocità usate invece
animazione.AnimationController.SetTrackSpeed(0, t)
Questo setta la velocità. In base alla velocità e allo scorrere del tempo le matrici degli oggetti animati si aggiorneranno.
Altre utili informazioni sono:
per avere l'istante del tempo
animazione.AnimationController.Time
per resettare il tempo.
animazione.AnimationController.ResetTime()
Dato che il tempo prosegue sempre aumentando potete azzerare il contatore quando volete. Il tempo può solo avanzare, attenzione. Per usare animazioni in senso inverso dovete regolare la velocità con un numero negativo.
La classe è abbastanza lunga ma ben commentata. La trovate all'interno dell'esempio: ricordate, serve directX9.0b e .Net 2003 per girare dato che .Net 2002 non può usare DirectX9.0b. Ecco qui l'API della Classe
Costruttori
Dim a as animationClass a = new animationClass(filesrc,texP)
|
Crea una nuova classe da file. Occorre passargli il path del file e della directory contenente le texture
|
Metodi Publici
setSpeed(velo)
|
imposta la velocità. Inserendo velocità negative l'animazione procederà al contrario
|
a.getTime()
|
restituisce il tempo trascorso
|
a.getAnimationLenght()
|
restituisce la lunghezza dell'animazione
|
a.ProcessNextFrame(t, matrice)
|
avanza l'animazione impostando il tempo e la matrice di trasformazione (world matrix)
|
a.resetTime()
|
azzera il contatore del tempo: questo non influenza l'animazione ma solo il contatore
|
a.drawMesh()
|
disegna la mesh
|
Proprietà publiche e condivise
a.animazione()
|
è l'animationRootFrame della mesh animata. Contiene tutte le informazioni sulla mesh
|
a.texPath()
|
proprietà condivisa da tutte le classi. contiene il path della texture dell'ultima animazione caricata
|
Potete derivare la classe sovrascrivendo il metodo drawMeshContainer (ad esempio per i vertex shader).
La classe permette di caricare sia le animazioni in skinning che quelle in animation set. Non funziona per le mesh immobili. Il modo migliore di vedere il funzionamento dell’intero processo è quello di studiare il codice ed in particolare le classi di gestione ed animazione.
Esempio VB.Net
Esempio C#
Animazioni multiple scritto da _Fa
Questo aggiornamento scritto da _Fa mostra come modificare la mia classe animationClass per renderla compatibile con il sistema multi-animation di DirectX. Ringrazio _Fa per l'ottimo lavoro fatto.
Modifiche da apportare alla classe “animazionClass”
Innanzi tutto è necessario dichiarare alcune variabili che saranno utili per il blending delle animazioni:
Public currentTime As Single
Public currentTrack As Byte
Public animationSet() As animationSet
Private blendTime As Single
Poi vanno memorizzati i puntatori a tutti gli animationSets contenuti nel file x.
Poi vanno memorizzati i puntatori a tutti gli animationSets contenuti nel file x.
ReDim animationSet(animazione.AnimationController.NumberAnimationSets)
Dim i As Integer
For i = 0 To animazione.AnimationController.NumberAnimationSets - 1
Me.animationSet(i) = animazione.AnimationController.GetAnimationSet(i)
Next
Per passare da un’animation set all’altro è sufficiente appoggiarsi all’animation controller
Protected Sub setAnimation(ByVal animationSetIndex As Byte)
Dim anim As AnimationSet
anim = animationSet(animationSetIndex)
animazione.AnimationController.SetTrackAnimationSet(0, anim)
animazione.AnimationController.ResetTime()
currentTrack = 0
End Sub
A questo punto è possibile animare il modello secondo i suoi animation set, però il passaggio fra un’animazione e l’altra sarà scattoso.
Per poter passare da un’animazione all’altra in maniera fluida invece, e necessario procedere come segue:
Nella funzione update frame va inserito un contatore che tenga traccia del processing temporale dell’animazione
currentTime += t
ovviamente anche nella funzione reset timer va aggiornato
currentTime = 0
Fatto questo è possibile creare l’algoritmo di blending delle animazioni.
Il principio è quello di comunicare a directx quale animazione deve essere terminata e quale deve cominciare e per quanto deve durare questa fase di transizione.
Public Sub BlendAnimation(ByVal goalAnimation As Integer)
Dim animazioneCorrente As AnimationSet
Dim prossimaAnimazione As AnimationSet
Dim newtrack As Byte
Questo controllo serve a far si che le animation Track (le animazioni attive) si alternino.
If currentTrack = 0 Then newtrack = 1 Else newtrack = 0
A questo punto viene memorizzata la nuova animazione attraverso l’indice delle animazioni
prossimaAnimazione = animationSet(goalAnimation)
viene settata l’animazione principale
With animazione.AnimationController
.SetTrackAnimationSet(newtrack, prossimaAnimazione)
prossimaAnimazione.Dispose()
.UnkeyAllTrackEvents(currentTrack)
.UnkeyAllTrackEvents(newtrack)
Viene comunicato a directX di disabilitare l’animazione corrente fra “blendTime” secondi
.KeyTrackEnable(currentTrack, False, currentTime + blendTime)
si diminuisce la velocità e l’influenza dell’animazione corrente
.KeyTrackSpeed(currentTrack, 0.0F, currentTime, blendTime, TransitionType.Linear)
.KeyTrackWeight(currentTrack, 0.0F, currentTime, blendTime, TransitionType.Linear)
A questo punto si procede al contrario per la nuova animazione
.SetTrackEnable(newtrack, True)
.KeyTrackSpeed(newtrack, 1.0F, currentTime, blendTime, TransitionType.Linear)
.KeyTrackWeight(newtrack, 1.0F, currentTime, blendTime, TransitionType.Linear)
End With
currentTrack = newtrack
Alcune animazioni è bene siano resettate dall’inizio, specie passando in fasi di idle:
If resetTimer Then
animazione.AnimationController.ResetTime()
currentTime = 0
End If
End Sub
Il barbatrucco è comunque sempre quello di usare l’animation controller, con lo stesso principio è anche possibile mescolare le animazioni per crearne delle nuove.
Usando le istruzioni set senza il key, (ad esempio SetTrackWeight o SetTrackEnable) e pesando opportunamente le due animazioni
Creare una classe per gli oggetti animati
Alla fine è comodo per ogni oggetto animato costruirsi una struttura per la sua gestione, ad esempio per tiny_4anim, vengono usate le seguenti animazioni:
Public Class Tiniy_x : Inherits animazionClass
Dim counter As Integer
Public Enum ANIMATIONSET_INDEX
WAVE_INDEX = 0
JOG_INDEX = 1
WALK_INDEX = 2
LOITER_INDEX = 3
End Enum
Quindi è possibile usare I metodi generali per muovere il modello particolare, il parametro bledingAnimationtime, serve a specificare quanto tempo deve durare il passaggio fra le due animazioni
Sub New(ByVal filesrc As String, ByVal texP As String, Optional ByVal blendingAnimationTime As Single = 0.25)
MyBase.New(filesrc, texP)
blendtime = blendingAnimationTime
End Sub
Public Shadows Sub setAnimation(ByVal animationSetIndex As ANIMATIONSET_INDEX)
MyBase.setAnimation(animationSetIndex)
End Sub
Public Sub changeAnimation(ByVal animationSetIndex As ANIMATIONSET_INDEX, ByVal resetTimer As Boolean)
Me.BlendAnimation(animationSetIndex, resetTimer)
End Sub
End Class
Esempio VB.net - MultiSkin
I commenti sono disabilitati.