\\ Home : Articoli : Stampa
Texture Lock
Di RobyDx (del 10/04/2007 @ 10:00:51, in DirectX9, linkato 1313 volte)

Come abbiamo visto DirectX usa degli array di vertici per rappresentare gli oggetti tridimensionali. Si suppone quindi che all'interno delle mesh ci siano array di vertici che vengano passati in qualche modo al device. Questo è corretto ma abbastanza approssimativo. La mesh tra i tanti campi che contiene (a noi nascosti) comprende 2 parti in cui sono contenuti i vertici e gli indici. I vertici sapete già cosa sono mentre forse non sapete cosa sono gli indici.
Una figura tridimensionale deve essere divisa in triangoli e come abbiamo visto dobbiamo riempire l'array di vertici in modo consecutivo per formare tutti i triangoli. Può capitare quindi che alcuni vertici debbano essere ripetuti. Ad esempio un cubo che ha solo otto vertici deve essere rappresentato con 12 triangoli ossia 36 vertici. Dato che i vertici occupano molta memoria (se contengono anche normali, coordinate texture e così via) si è preferito inserire un array di vertici senza ripetizioni (quindi solo gli 8 ipotetici) ed un array di short che determinano l'ordine con cui prenderli. Questi sono gli indici.
Gli indici sono in numero uguali al numero di triangoli moltiplicati per 3 (che vanno da 0 a n-1).
Inserendo i valori nell'array di vertici sarà quindi possibile ricostruire i triangoli e quindi l'intera figura. In realtà non si usano quasi mai gli array degli indici se non in qualche raro caso in cui occorra ricostruire la figura. Inoltre i vertici del nostro cubo potrebbero essere un pò più di 8 dato che esistono anche le normali e le coordinate texture ed è quindi necessario ripetere vertici uguali ma con altri componenti diversi. Rimane comunque il principio che non esistono 2 vertici identici nell'array dei vertici.
I vertici e gli indici sono memorizzati all'interno di due zone di memoria chiamate vertexbuffer e indexbuffer gestiti dagli oggetti omonimi che possono venir creati dalla mesh.
Tramite opportuni metodi potete accedere e modificare i valori in essi contenuti. Questo si rivela importante per milioni di cose come modificare la posizione o le coordinate texture o modificare la struttura dei vertici.
Ad esempio potete caricare un oggetto da file, modificare il tipo di vertice e dare un colore ad ognuno in base alle proprie esigenze (è quasi impossibile trovare un editor che invece dei materiali salvi i colori). Altri utilizzi possono anche essere l'estrazione del modello per ricevere informazione da utilizzare ad esempio per calcoli di collisione o per calcoli più elaborati. Negli effetti avanzati si usano spesso vertici molto particolari da gestire con l'accesso alla memoria.

Codice

Estrarre i dati non è particolarmente difficile ma è necessario che il formato della mesh e quello dell'array in cui andrete a caricare i valori corrispondano

Dim vertices() As Vertex = Nothing 'è un array dinamico

Dove il tipo vertex era un tipo che avevo definito così

Public Structure Vertex
Public p As Vector3
Public n As Vector3
Public tu, tv As Single
Public Const Format As VertexFormats = VertexFormats.Position Or VertexFormats.Normal Or VertexFormats.Texture1
End Structure

Ora con una funzione così estraete i vertici

Function getVertexArray(ByVal m As Mesh) As System.Array
Dim vertexBuffer As VertexBuffer = m.VertexBuffer
     getVertexArray = vertexBuffer.Lock(0, GetType(Vertex), 0, m.NumberVertices)
     vertexBuffer.Unlock()
End Function

Per usarlo basta porre l'array dinamico

vertices=getVertexArray(mesh)

Dichiarate un oggetto VertexBuffer e ponetelo uguale al VertexBuffer della mesh. Dopo di che blocchi la mesh in memoria con la funzione Lock della mesh. Questo significa fermare momentaneamente i vertici contenuti nella memoria della scheda video in modo da accellerare al massimo l'acceso.
La funzione richiede tra le opzioni il punto di partenza (vi consiglio uno 0 che indica l'inizio, è preferibile non fare errori qui), il tipo di vertice tramite un getType, un opzione (lasciate 0) ed il numero di vertici che andiamo a prendere (massimo il numero specificato dalla mesh). Il risultato sarà un array generico. Ponete l'array di vertici uguale al risultato e sbloccate la memoria con UnLock.
RICORDATEVI SEMPRE DI SBLOCCARE IL VERTEX BUFFER O VERRA' GENERATO UN ERRORE!!! SUI WINDOWS PIU' VECCHI (tipo il 98 o il me) POSSONO BLOCCARE IL PC COSTRINGENDOVI A RESETTARE!!!!!!!!!!!!!!!!!!
Quindi massima attenzione. La funzione in questo caso restituisce un array di vertici. Specificando il tipo verranno generati array differenti sempre a patto che il formato concordi con quello della mesh.
Per modificare il vertex buffer viene usata una funzione tipo questa

Sub setVertexArray(ByVal m As Mesh, ByVal v As Vertex())
Dim vertici As Vertex()
Dim vertexBuffer As VertexBuffer = m.VertexBuffer
     vertici = vertexBuffer.Lock(0, GetType(Vertex), 0, m.NumberVertices)
Dim i As Integer
For i = 0 To vertici.Length - 1
         vertici(i) = v(i)
Next
     vertexBuffer.Unlock()
End Sub

Per usarlo basta passare alla funzione la mesh e l'array.
Come notate viene usata sempre la stessa istruzione e non faccio altro che modificare i valori dell'array restituito dal vertexbuffer. Questo perchè l'array che mi restituisce ha un puntatore (praticamente è legato all'array contenuto nella mesh) facendo in modo che ogni modifica influenzi il valore originario. Quindi mentre il vertexbuffer è bloccato potete manipolare l'array. Potete farlo anche direttamente, questa è solo una funzione che copia i valori 1 per 1 nell'array. Non funziona se ponete l'array uguale all'altro.
Queste funzioni sono state realizzate per il tipo scelto da me ma potete usare i tipi che volete e anzi: siate sicuri che i tipi concordino. Potete chiedere alla mesh il formato

mesh1.VertexFormat

O modificarlo con una clonazione di mesh

mesh1=mesh1.Clone(MeshFlags.Managed, formato, device)

Questa istruzione crea una nuova mesh modificando il tipo di vertice di quella vecchia e può assegnarsi a se stessa.
Per gli indici è più tranquillo

Function getIndexArray(ByVal m As Mesh) As System.Array
Dim indexBuffer As IndexBuffer = m.IndexBuffer
     getIndexArray = indexBuffer.Lock(0, GetType(Short), 0, m.NumberFaces * 3)
     indexBuffer.Unlock()
End Function

Come per i vertici ma con array di interi

Dim indici() As Short
indici = getIndexArray(mesh)

Non vi scrivo la funzione per modificare gli indici (mai l'adopererete) ma comunque è sufficiente modificare i valori dell'array quando è lockato.

Animazioni in tweening

Una delle possibilità che subito si presenta è la possibilità di effettuare il cosiddetto tweening. Il tweening è una tecnica per animare gli oggetto effettuando un processo di morfing ossia un mutazione tra un oggetto ad un altro. Tutti i vertici dell'oggetto vengono letteralmente spostati tra una posizione iniziale ed una finale in linea retta. In questo modo avendo un certo numero di posizioni medie potete far variare la posizione dell'oggetto tra i vari intermedi. Per questo si utilizza la formula di interpolazione lineare che genera un vertice compreso fra 2 vertici rappresentanti le due posizioni iniziali e finali. La funzione riceve un valore chiamato coefficiente di interpolazione compreso tra 0 e 1. Per il valore uguale a 0 il vertici corrisponde con quello iniziale mentre aumentando questo si avvicina via via a quello finale (coincide per valore uguale ad 1). Effettuando le interpolazioni fra i vari frame l'oggetto verrà deformato per assumere le varie posizioni.
La formula è
punto=(puntoFinale * coefficiente) + puntoIniziale * (1# - coefficiente)
Occorre effettuarla per ogni componente del vertice.
DirectX permette di effettuare un interpolazione direttamente con i vettori

Vector3.Lerp(v1(i).p, v2(i).p, interpolazione)

Utile per il formato usato da me.
Il tweening forse non è adattissimo per animazioni di modelli molto complessi (per il quale è più adatta l'animazione con animationSet o lo skinning mesh). Tuttavia rimane un sistema molto comodo per alcune mutazioni che non si possono effettuare con i primi 2 metodi (ad esempio mutazione di volto).
Vi propongo un esempio che effettua tramite tweening una animazione

Esempio C#