#Region "File Description"
'----------------------------------------------------------------------------- 
' ModelViewerControl.cs 
' 
' Microsoft XNA Community Game Platform 
' Copyright (C) Microsoft Corporation. All rights reserved. 
'----------------------------------------------------------------------------- 
#End Region

#Region "Using Statements"
Imports System
Imports System.Diagnostics
Imports System.Windows.Forms
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Graphics
#End Region

''' <summary> 
''' Example control inherits from GraphicsDeviceControl, and displays 
''' a spinning 3D model. The main form class is responsible for loading 
''' the model: this control just displays it. 
''' </summary> 
Class ModelViewerControl
    Inherits GraphicsDeviceControl
    ''' <summary> 
    ''' Gets or sets the current model. 
    ''' </summary> 
    Public Property Model() As Model
        Get
            Return m_model
        End Get

        Set(ByVal value As Model)
            m_model = value

            If m_model IsNot Nothing Then
                MeasureModel()
            End If
        End Set
    End Property

    Private m_model As Model

    ' Cache information about the model size and position. 
    Private boneTransforms As Matrix()
    Private modelCenter As Vector3
    Private modelRadius As Single

    ' Timer controls the rotation speed. 
    Private timer As Stopwatch


    ''' <summary> 
    ''' Initializes the control. 
    ''' </summary> 
    Protected Overloads Overrides Sub Initialize()
        ' Start the animation timer. 
        timer = Stopwatch.StartNew()
        AddHandler Application.Idle, AddressOf Idle

        ' Hook the idle event to constantly redraw our animation. 
    End Sub


    ''' <summary> 
    ''' Draws the control. 
    ''' </summary> 
    Protected Overloads Overrides Sub Draw()
        ' Clear to the default control background color. 
        Dim backColor As New Color(Me.BackColor.R, Me.BackColor.G, Me.BackColor.B)

        GraphicsDevice.Clear(backColor)

        If m_model IsNot Nothing Then
            ' Compute camera matrices. 
            Dim rotation As Single = CSng(timer.Elapsed.TotalSeconds)

            Dim eyePosition As Vector3 = modelCenter

            eyePosition.Z += modelRadius * 2
            eyePosition.Y += modelRadius

            Dim aspectRatio As Single = GraphicsDevice.Viewport.AspectRatio

            Dim nearClip As Single = modelRadius / 100
            Dim farClip As Single = modelRadius * 100

            Dim world As Matrix = Matrix.CreateRotationY(rotation)
            Dim view As Matrix = Matrix.CreateLookAt(eyePosition, modelCenter, Vector3.Up)
            Dim projection As Matrix = Matrix.CreatePerspectiveFieldOfView(1, aspectRatio, nearClip, farClip)

            ' Draw the model. 
            For Each mesh As ModelMesh In m_model.Meshes
                For Each effect As BasicEffect In mesh.Effects
                    effect.World = boneTransforms(mesh.ParentBone.Index) * world
                    effect.View = view
                    effect.Projection = projection

                    effect.EnableDefaultLighting()
                    effect.PreferPerPixelLighting = True
                    effect.SpecularPower = 16
                Next

                mesh.Draw()
            Next
        End If
    End Sub


    ''' <summary> 
    ''' Whenever a new model is selected, we examine it to see how big 
    ''' it is and where it is centered. This lets us automatically zoom 
    ''' the display, so we can correctly handle models of any scale. 
    ''' </summary> 
    Private Sub MeasureModel()
        ' Look up the absolute bone transforms for this model. 
        boneTransforms = New Matrix(m_model.Bones.Count - 1) {}

        m_model.CopyAbsoluteBoneTransformsTo(boneTransforms)

        ' Compute an (approximate) model center position by 
        ' averaging the center of each mesh bounding sphere. 
        modelCenter = Vector3.Zero

        For Each mesh As ModelMesh In m_model.Meshes
            Dim meshBounds As BoundingSphere = mesh.BoundingSphere
            Dim transform As Matrix = boneTransforms(mesh.ParentBone.Index)
            Dim meshCenter As Vector3 = Vector3.Transform(meshBounds.Center, transform)

            modelCenter += meshCenter
        Next

        modelCenter /= m_model.Meshes.Count

        ' Now we know the center point, we can compute the model radius 
        ' by examining the radius of each mesh bounding sphere. 
        modelRadius = 0

        For Each mesh As ModelMesh In m_model.Meshes
            Dim meshBounds As BoundingSphere = mesh.BoundingSphere
            Dim transform As Matrix = boneTransforms(mesh.ParentBone.Index)
            Dim meshCenter As Vector3 = Vector3.Transform(meshBounds.Center, transform)

            Dim transformScale As Single = transform.Forward.Length()

            Dim meshRadius As Single = (meshCenter - modelCenter).Length() + (meshBounds.Radius * transformScale)

            modelRadius = Math.Max(modelRadius, meshRadius)
        Next
    End Sub

    Private Sub Idle(ByVal sender As Object, ByVal e As System.EventArgs)

        Invalidate()

    End Sub

End Class
