#Region "System Includes"
'Required Namespaces
Imports System.IO
Imports WindowsApplication1.MatrixMath
Imports WindowsApplication1.VectorMath
Imports WindowsApplication1.Utility
#End Region
#Region "C/C++ types in VB.NET"
Imports qword = System.UInt64
Imports dword = System.UInt32
Imports float = System.Single
Imports word = System.UInt16
Imports bool = System.Boolean
#End Region

#Region "User Defined types and their aliases"
'User Defined Types and their aliases...
Imports XYZcoord = WindowsApplication1.NJCM.Types.XYZCoord
Imports UVCoord = WindowsApplication1.NJCM.Types.UVCoord
Imports vector = WindowsApplication1.NJCM.Types.XYZCoord
Imports normal = WindowsApplication1.NJCM.Types.XYZCoord
Imports material = WindowsApplication1.NJCM.Types.Material
Imports bone = WindowsApplication1.NJCM.Types.Bone
Imports triangle = WindowsApplication1.NJCM.Types.Triangle
Imports msvertex = WindowsApplication1.NJCM.Types.MVertex
Imports group = WindowsApplication1.NJCM.Types.FaceGroup
Imports face = WindowsApplication1.NJCM.Types.Face
#End Region
Public Class ModelExport
#Region "Export Module Internal Data"
    Private triangles As New List(Of triangle)
    Private vertices As New List(Of XYZcoord)
    Private normals As New List(Of normal)
    Private texcoords As New List(Of UVCoord)
    Private materials As New List(Of material)
    Private bones As New List(Of bone)
    Private msvertices As New List(Of msvertex)
    Private groups As New List(Of group)
#End Region

#Region "Initializers"
    Sub New()
        With Me
            .triangles.Clear()
            .vertices.Clear()
            .normals.Clear()
            .texcoords.Clear()
            .materials.Clear()
            .bones.Clear()
            .msvertices.Clear()
        End With
    End Sub
#End Region

#Region "Indexing Functions"
    'These only exist because the IndexOf method requires boxing
    'Each takes a single argument, and returns an integer index
    'Into the respective list.
    Private Function GetVertexIndex(ByVal A As XYZcoord) As Integer
        Dim wrkv As XYZcoord = A.Clone
        Dim retv As Integer = -1
        Dim idx As Integer = 0

        Do While idx < Me.vertices.Count
            If Me.vertices(idx) = wrkv Then retv = idx : Exit Do
            idx += 1
        Loop
        If retv = -1 Then
            retv = Me.vertices.Count
            Me.vertices.Add(wrkv)
        End If
        Return retv
    End Function
    Private Function GetNormalIndex(ByVal A As XYZcoord) As Integer
        Dim wrkv As XYZcoord = A.Clone
        Dim retv As Integer = -1
        Dim idx As Integer = 0
        Do While idx < Me.normals.Count
            If Me.normals(idx) = wrkv Then retv = idx : Exit Do
            idx += 1
        Loop
        If retv = -1 Then
            retv = Me.normals.Count
            Me.normals.Add(wrkv)
        End If
        Return retv
    End Function
    Private Function GetUVCoordIndex(ByVal A As UVCoord) As Integer
        Dim wrkv As UVCoord = A.Clone
        Dim retv As Integer = -1
        Dim idx As Integer = 0

        Do While idx < Me.texcoords.Count
            If Me.texcoords(idx) = wrkv Then retv = idx : Exit Do
            idx += 1
        Loop
        If retv = -1 Then
            retv = Me.texcoords.Count
            Me.texcoords.Add(wrkv)
        End If
        Return retv
    End Function
    Private Function GetMaterialIndex(ByVal A As material) As Integer
        Dim wrkv As material = A.Clone
        Dim retv As Integer = -1
        Dim idx As Integer = 0

        Do While idx < Me.materials.Count
            If Me.materials(idx) = wrkv Then retv = idx : Exit Do
            idx += 1
        Loop
        If retv = -1 Then
            retv = Me.materials.Count
            Me.materials.Add(wrkv)
        End If
        Return retv
    End Function
    Private Function GetMSVertexIndex(ByVal A As msvertex) As Integer
        Dim wrkv As msvertex = A.Clone
        Dim retv As Integer = -1
        Dim idx As Integer = 0
        Do While idx < Me.msvertices.Count
            If Me.msvertices(idx) = wrkv Then retv = idx : Exit Do
            idx += 1
        Loop
        If retv = -1 Then
            retv = Me.msvertices.Count
            Me.msvertices.Add(wrkv)
        End If
        Return retv
    End Function
#End Region

#Region "Local Utility Functions"
    Private Sub BuildFaceGroups(ByVal model As NJCM.Model)
        Dim cs As Integer = 0
        Dim cf As Integer = 0
        If model.fc.Count = 0 Then Exit Sub 'If no faces, no need to build face groups
        Dim lg As Integer = model.fc(0).Group
        Dim lv As WindowsApplication1.NJCM.Types.Face
        Dim I As Integer = 0
        For I = 0 To model.fc.Count - 1
            lv = model.fc(I)
            If lv.Group <> lg Then
                cf = I - 1
                Me.groups.Add(New group(cs, cf, model.fc(cf).MaterialIndex))
                cs = I
                lg = lv.Group
            End If
        Next
        Me.groups.Add(New group(cs, I - 1, model.fc(model.fc.Count - 1).Mi))
    End Sub
    Private Sub RenumberGroups(ByVal model As NJCM.Model)
        Dim lg As Integer = -1
        Dim cg As Integer = 0
        Dim cm As Integer = -1
        With model.fc
            For I As Integer = 0 To .Count - 1
                If .Item(I).Group <> lg Then
                    lg = .Item(I).GroupNumber
                    cm = .Item(I).MaterialIndex
                    cg += 1
                End If
                .Item(I).GroupNumber = cg
                .Item(I).MaterialIndex = cm
            Next
        End With

    End Sub
    Private Sub TransferMaterials(ByVal model As NJCM.Model)
        For I As Integer = 0 To model.MatCount - 1
            Me.materials.Add(model.mt(I).Clone)
        Next
    End Sub
    Private Sub CleanMaterials()
        'Remove Duplicate Material Definitions from the materials list
        'Each model should have only a few, not one for every group...

    End Sub
    Private Sub Clean(ByVal model As NJCM.Model)
        RenumberGroups(model)
        BuildFaceGroups(model)
        TransferMaterials(model)
    End Sub
    Private Function MakeMS3DName(ByVal B As String, ByVal A As Integer) As String
        Dim retv As String = ""
        If A = -1 Then
            retv = Chr(34) & Chr(34)
        Else
            retv = Chr(34) & B & Format(A) & Chr(34)
        End If
        Return retv
    End Function
#End Region
   
#Region "Export Modules"
    Public Function ToRaw(ByVal filename As String, ByVal model As NJCM.Model) As Integer
        'Raw Triangle Export 1.0
        Dim fo As New StreamWriter(New FileStream(filename, FileMode.Create))
        Dim tg As Integer = -1
        Dim ts As String = ""
        Dim tf As New NJCM.Types.Face
        For I As Integer = 0 To model.fc.Count - 1
            tf = model.fc(I)
            If tf.Group <> tg Then
                tg = tf.Group
                fo.WriteLine("Object" & Format(tg + 1))
            End If
            ts = ""
            ts &= tf.A.v.ToString2 & " "
            ts &= tf.B.v.ToString2 & " "
            ts &= tf.C.v.ToString2
            fo.WriteLine(ts)
        Next
        fo.Close()
        WriteLn(filename & " written.")
        Return 0
    End Function
    Public Function ToObj(ByVal filename As String, ByVal model As NJCM.Model) As Integer
        'Wavefront OBJ Export Ver 2.0
        Dim f1 As String = ""
        Dim f2 As String = ""
        Dim tmat As New material
        Dim cg As Integer = -1

        '1) Convert faces in model to triangles for writing...
        Clean(model)
        Dim a(10) As Single
        For I As Integer = 0 To model.FaceCount - 1
            With model.Face(I)
                a(0) = GetVertexIndex(.A.v.Clone) + 1
                a(1) = GetUVCoordIndex(.A.t.Clone) + 1
                a(2) = GetNormalIndex(.A.n.Clone) + 1
                a(3) = GetVertexIndex(.B.v.Clone) + 1
                a(4) = GetUVCoordIndex(.B.t.Clone) + 1
                a(5) = GetNormalIndex(.B.n.Clone) + 1
                a(6) = GetVertexIndex(.C.v.Clone) + 1
                a(7) = GetUVCoordIndex(.C.t.Clone) + 1
                a(8) = GetNormalIndex(.C.n.Clone) + 1
                a(9) = .GroupNumber
                a(10) = .MaterialIndex
                Me.triangles.Add(New triangle(a))
            End With
        Next
        '2) Write Out the Material File, if Needed

        f1 = filename
        Mid(f1, InStrRev(f1, ".") + 1) = "MTL"
        f2 = StripFileName(model.InputFile)
        Dim so As New StreamWriter(New FileStream(f1, FileMode.Create))

        For I As Integer = 0 To Me.materials.Count - 1
            tmat = Me.materials(I)
            so.WriteLine("newmtl " & f2 & "-" & Format(I))
            With tmat
                so.WriteLine("Kd " & Format(.Kd.R, "0.0") & " " & Format(.Kd.G, "0.0") & " " & Format(.Kd.B, "0.0"))
                so.WriteLine("Ka " & Format(.Ka.R, "0.0") & " " & Format(.Ka.G, "0.0") & " " & Format(.Ka.B, "0.0"))
                so.WriteLine("Ks " & Format(.Ks.R, "0.0") & " " & Format(.Ks.G, "0.0") & " " & Format(.Ks.B, "0.0"))
                so.WriteLine("d " & Format(.Kd.A, "0.0"))
                If .Km <> -1 Then
                    'so.WriteLine("map_Kd " & f2 & "-" & Format(model.GBIX(.Km + 1), "0") & ".PNG")
                    so.WriteLine("map_Kd " & model.itl(.Km) & ".png")
                End If
                so.WriteLine()
            End With
        Next
        so.Close()
        Report("Preparing to Write:", 2)
        Report("Faces = " & model.fc.Count.ToString, 2)
        Report("Vertices = " & Me.vertices.Count.ToString & " (" & (model.fc.Count * 3).ToString & ")", 2)
        Report("Tex Coords = " & Me.texcoords.Count.ToString, 2)
        Report("Normals = " & Me.normals.Count.ToString, 2)
        Report("Triangles = " & Me.triangles.Count.ToString, 2)
        Report("Writing " & filename & "....", 0)
        Dim fo As New StreamWriter(New FileStream(filename, FileMode.Create))
        fo.WriteLine("#ExMLDNet .OBJ Export 2.0")
        fo.WriteLine("#Program Version " & GetVersion())
        fo.WriteLine("#2008-02-28 Steven Pettit")
        fo.WriteLine()
        '3) Write out the vertices, texture coordinates, and normals
        If model.isTextured Or model.ExportMode = 0 Then
            fo.WriteLine("mtllib " & f2 & ".mtl")
            fo.WriteLine()
        End If

        fo.WriteLine("# " & Format(Me.vertices.Count) & " Total Vertices")
        For I As Integer = 0 To Me.vertices.Count - 1
            fo.WriteLine("v " & Me.vertices(I).ToString2)
        Next
        fo.WriteLine()
        'Write UV Coordinates...
        fo.WriteLine("# " & Format(Me.texcoords.Count) & " Total UV Coords.")
        For I As Integer = 0 To Me.texcoords.Count - 1
            fo.WriteLine("vt " & Me.texcoords(I).ToString2)
        Next
        fo.WriteLine()
        'Write Normal Data...
        fo.WriteLine("# " & Format(Me.normals.Count) & " Total Normals.")
        For I As Integer = 0 To Me.normals.Count - 1
            fo.WriteLine("vn " & Me.normals(I).ToString2)
        Next
        fo.WriteLine()
        'Write some information...
        fo.WriteLine("# " & Format(Me.triangles.Count) & " Total Faces")
        fo.WriteLine("# " & Format(model.gr.Count) & " Total Groups")
        '4) Write out the groups
        For I As Integer = 0 To Me.triangles.Count - 1
            With Me.triangles(I)
                If cg <> .T(9) Then
                    cg = .T(9)
                    fo.WriteLine()
                    fo.WriteLine("g " & f2 & Format(cg))
                    fo.WriteLine("usemtl " & f2 & "-" & Format(.T(10)))
                End If
                fo.WriteLine(Me.triangles(I).ToString)
            End With
        Next
        fo.Close()
        Report("....Done.", 0)
        Return 0
    End Function
    Public Function ToMS3DASCII(ByVal filename As String, ByVal model As NJCM.Model) As Integer
        'Milkshape 3D ASCII Export 1.0
        Clean(model)
        Dim t3 As String = ""
        Dim ti As Integer = 0
        Dim ta(10) As Single
        Dim tf As New face
        Dim t2 As String = StripFileName(filename)
        Dim t1 As String = StripFileName(model.InputFile)
        Dim LGBIX() As Integer = model.GBIX
        Dim LMat As material

        'Milkshape 3D's ASCII format
        Dim so As New StreamWriter(New FileStream(filename, FileMode.Create))
        so.WriteLine("// MilkShape 3D ASCII")
        so.WriteLine("// Program Version " & GetVersion())
        so.WriteLine()
        so.WriteLine("Frames: 1")
        so.WriteLine("Frame: 1")
        so.WriteLine()
        so.WriteLine("Meshes: " & Me.groups.Count.ToString)
        'Mesh Output
        For I As Integer = 0 To Me.groups.Count - 1
            so.WriteLine(MakeMS3DName(t2 & "-", I + 1) & " 0 " & Format(Me.groups(I).Mat))
            Me.msvertices.Clear()
            Me.normals.Clear()
            Me.triangles.Clear()
            For J As Integer = Me.groups(I).Start To Me.groups(I).Finish
                tf = model.fc(J).Clone
                With tf
                    ta(0) = 0
                    ta(1) = GetMSVertexIndex(New msvertex(0, .A.v.Clone, .A.t.Clone, .A.b))
                    ta(2) = GetMSVertexIndex(New msvertex(0, .B.v.Clone, .B.t.Clone, .B.b))
                    ta(3) = GetMSVertexIndex(New msvertex(0, .C.v.Clone, .C.t.Clone, .C.b))
                    ta(4) = GetNormalIndex(VectorMath.Normalize(.A.n.Clone))
                    ta(5) = GetNormalIndex(VectorMath.Normalize(.B.n.Clone))
                    ta(6) = GetNormalIndex(VectorMath.Normalize(.C.n.Clone))
                    ta(7) = 0
                    ta(8) = 0
                    ta(9) = 0
                    ta(10) = 0
                    Me.triangles.Add(New triangle(ta))
                End With
            Next

            so.WriteLine(Me.msvertices.Count.ToString)
            For J As Integer = 0 To Me.msvertices.Count - 1
                so.WriteLine(Me.msvertices(J).ToString)
            Next

            so.WriteLine(Me.normals.Count.ToString)
            For J As Integer = 0 To Me.normals.Count - 1
                so.WriteLine(Me.normals(J).ToString2)
            Next

            so.WriteLine(Me.triangles.Count.ToString)
            For J As Integer = 0 To Me.triangles.Count - 1
                so.WriteLine(Me.triangles(J).ToString2)
            Next
        Next
        so.WriteLine()
        'Material Output

        If model.OutputMode = 0 Then
            so.WriteLine("Materials: " & Me.materials.Count.ToString)
            For J As Integer = 0 To Me.materials.Count - 1
                so.WriteLine(MakeMS3DName("Material", J))
                LMat = Me.materials(J)
                With Me.materials(J)
                    so.WriteLine(Format(.Ka.R, "0.000000") & " " & Format(.Ka.G, "0.000000") & " " & Format(.Ka.B, "0.000000") & " 0.000000")
                    so.WriteLine(Format(.Kd.R, "0.000000") & " " & Format(.Kd.G, "0.000000") & " " & Format(.Kd.B, "0.000000") & " 0.000000")
                    so.WriteLine(Format(.Ks.R, "0.000000") & " " & Format(.Ks.G, "0.000000") & " " & Format(.Ks.B, "0.000000") & " 0.000000")
                    so.WriteLine("0.000000 0.000000 0.000000 0.000000")
                    so.WriteLine("0.000000")
                    so.WriteLine(Format(.Kd.A, "0.000000"))
                    If .Km <> -1 Then
                        t3 = Chr(34) & t1 & "-" & Format(model.GBIX(.Km + 1), "0") & ".PNG" & Chr(34)
                    Else
                        t3 = Chr(34) & Chr(34)
                    End If
                    so.WriteLine(t3)
                    so.WriteLine(Chr(34) & Chr(34))
                End With
            Next
        Else
            so.WriteLine("Materials: 0")
        End If
        'Bones Output
        so.WriteLine()
        so.WriteLine("// Bones")
        so.WriteLine("Bones: " & Format(Me.bones.Count))
        For I As Integer = 0 To Me.bones.Count - 1
            so.WriteLine(MakeMS3DName("Bone", I))
            With Me.bones(I)
                so.WriteLine(MakeMS3DName("Bone", .Parent))
                so.WriteLine("0 " & Format(.Position.X) & " " & Format(.Position.Y) & " " & Format(.Position.Z) & " " & _
                             Format(.Rotation.X) & " " & Format(.Rotation.Y) & " " & Format(.Rotation.Z))
                so.WriteLine("0")
                so.WriteLine("0")
            End With
        Next
        'Comment Blocks
        so.WriteLine("GroupComments: 0")
        so.WriteLine("MaterialComments: 0")
        so.WriteLine("BoneComments: 0")
        so.WriteLine("ModelComment: 0")
        'Close the File
        so.Close()
        Return 0
    End Function
#End Region
End Class
