jueves, 22 de agosto de 2013

8 .-. Modelos 3D. Mallas. Código.

Bien, una vez vista la teoría de cómo se implementa una animación y cuales son sus partes, que existe una jerarquía en los elementos que forman el esqueleto, que también existen unas uniones entre las distintas partes del esqueleto, y todo ello mezclado con las animaciones de cada parte del esqueleto; vamos a crear en nuestro motor una animación para lo cual emplearé un tanque que amablemente ha sido realizado por Alberto Durán Vaz.



Vamos a agregar una nueva clase a nuestro proyecto. Ya sabéis, botón derecho en el Explorador de Soluciones - Agregar - Clase y como nombre le ponéis MallaAnimada. Esta clase va a ser la base para todas las mallas animadas que necesitemos en el programa.

Ahora pegamos este código en la clase creada y vamos viendo qué hace cada parte.

Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D

Public Class MallaAnimada(Of TMaterial As {ContenedorDeMaterial, New})
    Inherits BaseDeLaAnimacion(Of TMaterial, MallaAnimadaPersonalizada(Of TMaterial))
    Public Sub New(ByVal dispositivo As Device, ByVal xRuta As String, ByVal texRuta As String)
        MyBase.New(dispositivo, xRuta, texRuta)
    End Sub
End Class

Public MustInherit Class BaseDeLaAnimacion(Of MaterialPersonalizado As {ContenedorDeMaterial, New}, MallaPersonalizada As {BaseDeLaMallaAnimada(Of MaterialPersonalizado), New})
    Private fotogramaRaiz As AnimationRootFrame
    Private materialExtra As New List(Of ExtendedMaterial)()
    Private listaDeMaterial As New List(Of MaterialPersonalizado)()

    Public Sub New(ByVal dispositivo As Device, ByVal fichero As String, ByVal rutaDeLaTextura As String)
        Try
            ' Creamos un almacen que alojara del objeto X cargado, mallas y materiales
            Dim almacen As New AlojadorJerarquicoPersonalizado(Of MaterialPersonalizado, MallaPersonalizada)(dispositivo, Me, rutaDeLaTextura)
            ' Le indicamos la estructua jerarquica de la animacion de nuestro objeto X
            fotogramaRaiz = Mesh.LoadHierarchyFromFile(fichero, MeshFlags.Managed, dispositivo, almacen, Nothing)

            ' Personalizamos los fotogramas
            ConfiguraMatrizDeHuesos(DirectCast(fotogramaRaiz.FrameHierarchy, Fotograma))
        Catch ex As Exception

        End Try
    End Sub

    Friend Function MeteMaterial(ByVal dispositivo As Device, ByVal material As ExtendedMaterial, ByVal rutaDeLaTextura As String) As MaterialPersonalizado
        Try
            Dim i As Integer = 0
            For Each mate As ExtendedMaterial In materialExtra
                If mate.Equals(material) Then
                    Return listaDeMaterial(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1))
                End If
            Next
            Dim nuevoMaterial As New MaterialPersonalizado()
            nuevoMaterial.inicializa(dispositivo, material, rutaDeLaTextura)
            listaDeMaterial.Add(nuevoMaterial)
            materialExtra.Add(material)
            Return nuevoMaterial
        Catch ex As Exception

        End Try
    End Function

    Private Sub ConfiguraMatrizDeHuesos(ByVal fotograma As Fotograma)
        Try
            ' Recursamos todas las veces que sean necesarias hasta tener convertidos todos los fotogramas
            If fotograma.MeshContainer IsNot Nothing Then ConfiguraMatrizDeHuesos(DirectCast(fotograma.MeshContainer, ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada)))
            If fotograma.FrameSibling IsNot Nothing Then ConfiguraMatrizDeHuesos(DirectCast(fotograma.FrameSibling, Fotograma))
            If fotograma.FrameFirstChild IsNot Nothing Then ConfiguraMatrizDeHuesos(DirectCast(fotograma.FrameFirstChild, Fotograma))
        Catch ex As Exception

        End Try
    End Sub

    Private Sub ConfiguraMatrizDeHuesos(ByVal contenedorPersonalizado As ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada))
        Try
            ' si el contenedor de mallas personalizado tiene informacion de la animacion
            If contenedorPersonalizado.SkinInformation IsNot Nothing Then
                Dim numhuesos_ As Integer = contenedorPersonalizado.SkinInformation.NumberBones
                ' tomamos todos los fotogramas del contenedor
                Dim MatrizDeFotogramas As Fotograma() = New Fotograma(numhuesos_ - 1) {}
                For i As Integer = 0 To numhuesos_ - 1
                    ' Convertimos los fotogramas (Frame) al tipo de nuestra clase Fotograma
                    Dim fotograma As Fotograma = DirectCast(Frame.Find(fotogramaRaiz.FrameHierarchy, contenedorPersonalizado.SkinInformation.GetBoneName(i)), Fotograma)
                    If fotograma Is Nothing Then Throw New ArgumentException()
                    MatrizDeFotogramas(i) = fotograma
                Next
                contenedorPersonalizado.PonFotogramas(MatrizDeFotogramas)
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub ActualizaMatrizDelFotograma(ByVal fotograma As Fotograma, ByVal matrixPadre As Matrix)
        Try
            ' Combinamos la matriz padre del fotograma actual y su propia matriz
            fotograma.MatrizTransformadaYCombinada = fotograma.TransformationMatrix * matrixPadre
            ' Si el fotograma actual, tiene hijos o no tiene hermanos, actualizamos de nuevo
            If fotograma.FrameSibling IsNot Nothing Then ActualizaMatrizDelFotograma(DirectCast(fotograma.FrameSibling, Fotograma), matrixPadre)
            If fotograma.FrameFirstChild IsNot Nothing Then ActualizaMatrizDelFotograma(DirectCast(fotograma.FrameFirstChild, Fotograma), fotograma.MatrizTransformadaYCombinada)
        Catch ex As Exception

        End Try
    End Sub

    Private Sub DibujaFotograma(ByVal dispositivo As Device, ByVal fotograma As Fotograma)
        Try
            Dim contenedorPersonalizado As ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada) = DirectCast(fotograma.MeshContainer, ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada))
            While contenedorPersonalizado IsNot Nothing
                DibujaContenedorDeMallas(dispositivo, contenedorPersonalizado)
                contenedorPersonalizado = DirectCast(contenedorPersonalizado.NextContainer, ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada))
            End While
            ' Si el fotograma actual, tiene hijos o tiene hermanos, volvemos a dibujar
            If fotograma.FrameSibling IsNot Nothing Then DibujaFotograma(dispositivo, DirectCast(fotograma.FrameSibling, Fotograma))
            If fotograma.FrameFirstChild IsNot Nothing Then DibujaFotograma(dispositivo, DirectCast(fotograma.FrameFirstChild, Fotograma))
        Catch ex As Exception

        End Try
    End Sub

    Private Sub DibujaContenedorDeMallas(ByVal dispositivo As Device, ByVal contenedorPersonalizado As ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada))
        If contenedorPersonalizado.MallaAnimada.tieneInformacionAnimada_ Then
            contenedorPersonalizado.MallaAnimada.Dibuja(dispositivo, contenedorPersonalizado.CogeFotogramas(), contenedorPersonalizado.materiales)
        End If
    End Sub

    Public Sub Anima(ByVal tiempo As Single, ByVal MatrixGlobal As Matrix)
        Try
            If fotogramaRaiz.AnimationController IsNot Nothing Then
                fotogramaRaiz.AnimationController.AdvanceTime(tiempo, Nothing)
            End If

            ActualizaMatrizDelFotograma(DirectCast(fotogramaRaiz.FrameHierarchy, Fotograma), MatrixGlobal)
        Catch ex As Exception

        End Try
    End Sub

    Public Sub Dibuja(ByVal dispositivo As Device)
        Try
            DibujaFotograma(dispositivo, DirectCast(Me.fotogramaRaiz.FrameHierarchy, Fotograma))
        Catch ex As Exception

        End Try
    End Sub

    Public Sub Dispose()
    End Sub
End Class

Public Class MallaAnimadaPersonalizada(Of MaterialPersonalizado As {ContenedorDeMaterial, New})
    Inherits BaseDeLaMallaAnimada(Of MaterialPersonalizado)
    Public Overrides Sub GeneraMallaAnimada(ByVal contenedorDeLaMalla As MeshContainer)
        Try
            ' Si hay informacion de animacion en el contenedor de la malla
            If contenedorDeLaMalla.SkinInformation IsNot Nothing Then
                tieneInformacionAnimada = True
                Dim numhuesos_ As Integer = contenedorDeLaMalla.SkinInformation.NumberBones
                matrices = New Matrix(numhuesos_ - 1) {}
                For i As Integer = 0 To numhuesos_ - 1
                    matrices(i) = contenedorDeLaMalla.SkinInformation.GetBoneOffsetMatrix(i)
                Next

                ' Genera Adyacencia, trianguliza la malla
                Dim datosDeLaMalla As MeshData = contenedorDeLaMalla.MeshData
                adia = New Integer(miMalla.NumberFaces * 3 - 1) {}
                miMalla.GenerateAdjacency(0.001F, adia)
                miMalla = contenedorDeLaMalla.SkinInformation.ConvertToBlendedMesh(datosDeLaMalla.Mesh, MeshFlags.Managed Or MeshFlags.OptimizeVertexCache, adia, numeroDeInfluencias, huesos)
                numeroDeAtributos = huesos.Length
                contenedorDeLaMalla.MeshData = datosDeLaMalla
            End If
        Catch ex As Exception

        End Try
    End Sub

    Public Overrides Sub Dibuja(ByVal dispositivo As Device, ByVal frameMatrices As Fotograma(), ByVal materiales As MaterialPersonalizado())
        Try
            Dim atributoIdAnterior As Integer = -1

            ' Dibuja o renderiza el fotograma actual de nuestro objeto X
            For iatributo As Integer = 0 To numeroDeAtributos_ - 1
                Dim numBlend As Integer = 0

                For i As Integer = 0 To numeroDeInfluencias_ - 1
                    If huesos(iatributo).BoneId(i) <> -1 Then numBlend = i
                Next

                If dispositivo.DeviceCaps.MaxVertexBlendMatrices >= numBlend + 1 Then
                    Dim matrices As Matrix() = matrices_
                    For i As Integer = 0 To numeroDeInfluencias_ - 1
                        Dim iMatrix As Integer = huesos(iatributo).BoneId(i)
                        If iMatrix <> -1 Then
                            Dim tempMatrix As Matrix = matrices(iMatrix) * frameMatrices(iMatrix).MatrizTransformadaYCombinada
                            dispositivo.Transform.SetWorldMatrixByIndex(i, tempMatrix)
                        End If
                    Next

                    dispositivo.RenderState.VertexBlend = DirectCast(numBlend, VertexBlend)

                    If (atributoIdAnterior <> huesos(iatributo).AttributeId) OrElse (atributoIdAnterior = -1) Then
                        atributoIdAnterior = huesos(iatributo).AttributeId
                    End If
                    materiales(huesos(iatributo).AttributeId + 1).aplica(dispositivo)

                    ' Dibujamos la Malla
                    miMalla.DrawSubset(iatributo)
                End If
            Next
        Catch ex As Exception

        End Try
    End Sub

    Public Overrides Sub Dispose()
        Try
            miMalla.Dispose()
        Catch ex As Exception

        End Try
    End Sub
End Class

Public MustInherit Class BaseDeLaMallaAnimada(Of MaterialPersonalizado As {ContenedorDeMaterial, New})
    Inherits Malla(Of MaterialPersonalizado)
    Protected huesos As BoneCombination()
    Protected matrices As Matrix()
    Protected adia As Integer() ' Buffer que contiene información sobre los bordes, caras y caras adyacentes

    Protected numeroDeInfluencias As Integer
    Public ReadOnly Property numeroDeInfluencias_() As Integer
        Get
            Return numeroDeInfluencias
        End Get
    End Property
    Protected numeroDeAtributos As Integer
    Public ReadOnly Property numeroDeAtributos_() As Integer
        Get
            Return numeroDeAtributos
        End Get
    End Property
    Public ReadOnly Property matrices_() As Matrix()
        Get
            Return matrices
        End Get
    End Property
    Protected tieneInformacionAnimada As Boolean
    Public ReadOnly Property tieneInformacionAnimada_() As Boolean
        Get
            Return tieneInformacionAnimada
        End Get
    End Property

    Public Sub Inicializa(ByVal contenedor As MeshContainer)
        miMalla = contenedor.MeshData.Mesh
        GeneraMallaAnimada(contenedor)
        listaDeMaterial = New MaterialPersonalizado(0) {}
        listaDeMaterial(0) = New MaterialPersonalizado()
    End Sub

    Public MustOverride Sub GeneraMallaAnimada(ByVal malla As MeshContainer)
    Public MustOverride Sub Dibuja(ByVal dispositivo As Device, ByVal frameMatrices As Fotograma(), ByVal materiales As MaterialPersonalizado())
    Public MustOverride Sub Dispose()
End Class

' Personalizamos la clase AllocateHierarchy
Public Class AlojadorJerarquicoPersonalizado(Of MaterialPersonalizado As {ContenedorDeMaterial, New}, MallaPersonalizada As {BaseDeLaMallaAnimada(Of MaterialPersonalizado), New})
    Inherits AllocateHierarchy
    Private clasePadre As BaseDeLaAnimacion(Of MaterialPersonalizado, MallaPersonalizada)
    Private texRuta As String = ""
    Private miDispositivo As Device

    Public Sub New(ByVal dispositivo As Device, ByVal padre As BaseDeLaAnimacion(Of MaterialPersonalizado, MallaPersonalizada), ByVal rutaDeLaTextura As String)
        clasePadre = padre
        texRuta = rutaDeLaTextura
        miDispositivo = dispositivo
    End Sub

    Public Overrides Function CreateFrame(ByVal nombre As String) As Frame
        Dim fotograma As New Fotograma(nombre)
        Return fotograma
    End Function

    Public Overrides Function CreateMeshContainer(ByVal nombre As String, ByVal datosDeLaMalla As MeshData, ByVal materiales As ExtendedMaterial(), ByVal efectos As EffectInstance(), ByVal adyacencia As GraphicsStream, ByVal skinInfo As SkinInformation) As MeshContainer
        Try
            'Si no hay mallas o si no estan formateados los vertices no podemos crear un contenedor
            If datosDeLaMalla.Mesh Is Nothing Then Throw New ArgumentException()
            If datosDeLaMalla.Mesh.VertexFormat = VertexFormats.None Then Throw New ArgumentException()
            ' de lo contrario creamos un contenedor de mallas, de momento vacio
            Dim contenedorDeMallas As New ContenedorDeMallaPersonalizado(Of MaterialPersonalizado, MallaPersonalizada)()
            contenedorDeMallas.Name = nombre ' le damos nombre al contenedor

            Dim dispositivo As Device = datosDeLaMalla.Mesh.Device

            contenedorDeMallas.SetMaterials(materiales)
            contenedorDeMallas.SetAdjacency(adyacencia)
            ' crea una coleccion de materiales y se los manda al nuevo contenedor
            Dim mateList As MaterialPersonalizado() = New MaterialPersonalizado(materiales.Length) {}
            Dim i As Integer = 0
            For Each material As ExtendedMaterial In materiales
                mateList(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)) = clasePadre.MeteMaterial(miDispositivo, material, texRuta)
            Next
            contenedorDeMallas.materiales = mateList
            contenedorDeMallas.MeshData = datosDeLaMalla
            ' Si existe informacion de la animacion, se la pasamos al nuevo contenedor
            If skinInfo IsNot Nothing Then contenedorDeMallas.SkinInformation = skinInfo
            ' Para mas referencias ver la clase MallaAnimadaPersonalizada
            contenedorDeMallas.MallaAnimada = New MallaPersonalizada()
            contenedorDeMallas.MallaAnimada.Inicializa(contenedorDeMallas)
            Return contenedorDeMallas ' devuelve el nuevo contenedor de mallas
        Catch ex As Exception

        End Try
    End Function
End Class
' Personalizamos la clase MeshContainer
Public Class ContenedorDeMallaPersonalizado(Of MaterialPersonalizado As {ContenedorDeMaterial, New}, MallaPersonalizada As BaseDeLaMallaAnimada(Of MaterialPersonalizado))
    Inherits MeshContainer
    Private materiales_ As MaterialPersonalizado()
    Private miMalla As MallaPersonalizada
    Private fotogramas As Fotograma()

    Public Property MallaAnimada() As MallaPersonalizada
        Get
            Return miMalla
        End Get
        Set(ByVal value As MallaPersonalizada)
            miMalla = value
        End Set
    End Property

    Public Property materiales() As MaterialPersonalizado()
        Get
            Return materiales_
        End Get
        Set(ByVal valor As MaterialPersonalizado())
            materiales_ = valor
        End Set
    End Property
    Public Function CogeFotogramas() As Fotograma()
        Return fotogramas
    End Function
    Public Sub PonFotogramas(ByVal fotogramas_ As Fotograma())
        fotogramas = fotogramas_
    End Sub
End Class
' Personalizamos la clase Frame
Public Class Fotograma
    Inherits Frame
    Private combinado As Matrix = Matrix.Identity

    Public Sub New(ByVal nombre As String)
        ' Personalizamos nuestro fotograma con una matriz combinada
        Me.Name = nombre
        Me.TransformationMatrix = Matrix.Identity
        Me.combinado = Matrix.Identity
    End Sub

    Public Property MatrizTransformadaYCombinada() As Matrix
        Get
            Return combinado
        End Get
        Set(ByVal valor As Matrix)
            combinado = valor
        End Set
    End Property
End Class

No hay comentarios:

Publicar un comentario