Advertisement
foonix

Untitled

Oct 7th, 2017
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 39.21 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using UnityEngine;
  5.  
  6. public class OptFile
  7. {
  8.     /*
  9.      * Notes:
  10.      *  - There are a lot of places where an offset should point to the very next byte.
  11.      *    I'm following the offset anyway in case they are doing something sneaky.
  12.      *    Easter Eggs, maybe.
  13.      *  - There are a lot of values that we don't know what they do but are normally zero.
  14.      *    Probably best to check them in order to find cases where they're not zero.
  15.      *  - Coordinates are swapped from x->east y->north z->up to unity's style.
  16.      *
  17.      * Bugs:
  18.      *  - Some models seem to not have their LOD meshes in decreasing order. Need to reorder for unity.
  19.      *  - XWing low LOD wing meshes are all black if using the standard unity shader.
  20.      *  - Minor type enum actually depends on major type.
  21.      *  - Pallets can be shared between meshes, but we're making extra copies each time.
  22.      *  - No serialize.
  23.      */
  24.  
  25.     public enum MajorType
  26.     {
  27.         normal = 0,
  28.         submodelList = 2,
  29.         textrue = 20,
  30.     }
  31.     public enum MinorType
  32.     {
  33.         generic = 0,
  34.         faceList = 1,
  35.         mainJump = 2,
  36.         meshVertex = 3,
  37.         info = 4,
  38.         textureReferenceByName = 7,
  39.         vertexNormal = 11,
  40.         textureVertex = 13,
  41.         textureHeader = 20,
  42.         meshLOD = 21,
  43.         hardpoint = 22,
  44.         transform = 23,
  45.         skinSelector = 24,
  46.         meshDescriptor = 25,
  47.         unknown = 0xff,
  48.     }
  49.     public enum PartType
  50.     {
  51.         DefaultType = 0,
  52.         MainHull = 1,
  53.         Wing = 2,
  54.         Fuselage = 3,
  55.         GunTurret = 4,
  56.         SmallGun = 5,
  57.         Engine = 6,
  58.         Bridge = 7,
  59.         ShieldGen = 8,
  60.         EnergyGen = 9,
  61.         Launcher = 10,
  62.         CommSys = 11,
  63.         BeamSys = 12,
  64.         CommandBeam = 13,
  65.         DockingPlat = 14,
  66.         LandingPlat = 15,
  67.         Hangar = 16,
  68.         CargoPod = 17,
  69.         MiscHull = 18,
  70.         Antenna = 19,
  71.         RotWing = 20,
  72.         RotGunTurret = 21,
  73.         RotLauncher = 22,
  74.         RotCommSys = 23,
  75.         RotBeamSys = 24,
  76.         RotCommandBeam = 25,
  77.         Custom1 = 26,
  78.         Custom2 = 27,
  79.         Custom3 = 28,
  80.         Custom4 = 29,
  81.         Custom5 = 30,
  82.         Custom6 = 31,
  83.     }
  84.     public enum HardpointType
  85.     {
  86.         None = 0,
  87.         RebelLaser = 1,
  88.         TurboRebelLaser = 2,
  89.         EmpireLaser = 3,
  90.         TurboEmpireLaser = 4,
  91.         IonCannon = 5,
  92.         TurboIonCannon = 6,
  93.         Torpedo = 7,
  94.         Missile = 8,
  95.         SuperRebelLaser = 9,
  96.         SuperEmpireLaser = 10,
  97.         SuperIonCannon = 11,
  98.         SuperTorpedo = 12,
  99.         SuperMissile = 13,
  100.         DumbBomb = 14,
  101.         FiredBomb = 15,
  102.         Magpulse = 16,
  103.         TurboMagpulse = 17,
  104.         SuperMagpulse = 18,
  105.         NewWeapon1 = 19,
  106.         NewWeapon2 = 20,
  107.         NewWeapon3 = 21,
  108.         NewWeapon4 = 22,
  109.         NewWeapon5 = 23,
  110.         NewWeapon6 = 24,
  111.         InsideHangar = 25,
  112.         OutsideHangar = 26,
  113.         DockFromBig = 27,
  114.         DockFromSmall = 28,
  115.         DockToBig = 29,
  116.         DockToSmall = 30,
  117.         Cockpit = 31,
  118.     }
  119.  
  120.     // XvT engine -> Unity engine
  121.     // unity: forward is +z, right is +x,    up is +y
  122.     // XvT:   forward is -y, right is +x(?), up is +z
  123.     static readonly Matrix4x4 CoordinateConverter = new Matrix4x4(
  124.         new Vector4(1, 0, 0, 0),
  125.         new Vector4(0, 0, -1, 0),
  126.         new Vector4(0, 1, 0, 0),
  127.         new Vector4(0, 0, 0, 1)
  128.     );
  129.  
  130.     private long globalOffset;
  131.  
  132.     private RootNode rootNode;
  133.  
  134.     // Generic node with sub-nodes
  135.     public class Node
  136.     {
  137.         public List<Node> children;
  138.         public MajorType majorType;
  139.         public MinorType minorType;
  140.  
  141.         public long offsetInFile;
  142.  
  143.         public Node(BinaryReader reader)
  144.         {
  145.             offsetInFile = reader.BaseStream.Position;
  146.             if (this.GetType() != typeof(RootNode))
  147.             {
  148.                 reader.BaseStream.Seek(-8, SeekOrigin.Current);
  149.                 majorType = (MajorType)reader.ReadUInt32();
  150.                 minorType = (MinorType)reader.ReadUInt32();
  151.             }
  152.         }
  153.  
  154.         Node ReadNodeAt(BinaryReader reader, long globalOffset, long offset)
  155.         {
  156.             Node node;
  157.             long preHeaderOffset = 0;
  158.  
  159.             reader.BaseStream.Seek(offset - globalOffset, SeekOrigin.Begin);
  160.  
  161.             long majorId = reader.ReadUInt32();
  162.             long minorId = reader.ReadUInt32();
  163.  
  164.             // Edge case: one block type doesn't start with major/minor type id and actually start with another offset.
  165.             // So peek ahead one more long and shuffle numbers where they go.
  166.             // This may not work if globalOffset is 0.
  167.             // Should be a pointer to an offset containing string "Tex00000" or similar.
  168.             long peek = reader.ReadUInt32();
  169.             if (majorId > globalOffset && minorId == (long)MajorType.textrue)
  170.             {
  171.                 preHeaderOffset = majorId;
  172.                 majorId = minorId;
  173.                 minorId = peek;
  174.             }
  175.             else
  176.             {
  177.                 reader.BaseStream.Seek(-4, SeekOrigin.Current);
  178.             }
  179.  
  180.             // Figure out the type of node and build appropriate object.
  181.             switch (majorId)
  182.             {
  183.                 case (long)MajorType.normal:
  184.                     switch (minorId)
  185.                     {
  186.                         case (long)MinorType.generic:
  187.                             node = new Node(reader)
  188.                             {
  189.                                 children = ReadSubNodes(reader, globalOffset, reader.ReadUInt32(), reader.ReadUInt32())
  190.                             };
  191.                             break;
  192.                         case (long)MinorType.meshVertex:
  193.                             node = new MeshVerticies(reader, globalOffset);
  194.                             break;
  195.                         case (long)MinorType.textureVertex:
  196.                             node = new TextureVerticies(reader, globalOffset);
  197.                             break;
  198.                         case (long)MinorType.textureReferenceByName:
  199.                             node = new TextureReferenceByName(reader, globalOffset);
  200.                             break;
  201.                         case (long)MinorType.vertexNormal:
  202.                             node = new VertexNormals(reader, globalOffset);
  203.                             break;
  204.                         case (long)MinorType.hardpoint:
  205.                             node = new Hardpoint(reader, globalOffset);
  206.                             break;
  207.                         case (long)MinorType.transform:
  208.                             node = new Transform(reader, globalOffset);
  209.                             break;
  210.                         case (long)MinorType.meshLOD:
  211.                             node = new MeshLOD(reader, globalOffset);
  212.                             break;
  213.                         case (long)MinorType.faceList:
  214.                             node = new FaceList(reader, globalOffset);
  215.                             break;
  216.                         case (long)MinorType.skinSelector:
  217.                             node = new SkinSelector(reader, globalOffset);
  218.                             break;
  219.                         case (long)MinorType.meshDescriptor:
  220.                             node = new PartDescriptor(reader, globalOffset);
  221.                             break;
  222.                         default:
  223.                             node = new Node(reader);
  224.                             Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
  225.                             break;
  226.                     }
  227.                     break;
  228.  
  229.                 case (long)MajorType.textrue:
  230.                     switch (minorId)
  231.                     {
  232.                         case 0:
  233.                             node = new Texture(reader, globalOffset, preHeaderOffset);
  234.                             break;
  235.                         case 1:
  236.                             // seems to be a texture with alpha mask
  237.                             node = new Node(reader);
  238.                             Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
  239.                             break;
  240.                         default:
  241.                             node = new Texture(reader, globalOffset, preHeaderOffset);
  242.                             Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
  243.                             break;
  244.                     }
  245.                     break;
  246.                 default:
  247.                     node = new Node(reader);
  248.                     break;
  249.             }
  250.  
  251.             return node;
  252.         }
  253.  
  254.         // generates sub-nodes starting at a given offset
  255.         // returns the stream to the same position it was before calling.
  256.         protected List<Node> ReadSubNodes(BinaryReader reader, long globalOffset, long count, long jumpListOffset)
  257.         {
  258.             List<Node> nodes = new List<Node>();
  259.             long pos = reader.BaseStream.Position;
  260.  
  261.             for (int i = 0; i < count; i++)
  262.             {
  263.                 reader.BaseStream.Seek(jumpListOffset - globalOffset + 4 * i, SeekOrigin.Begin);
  264.                 long nextNode = reader.ReadUInt32();
  265.                 if (nextNode != 0)
  266.                 {
  267.                     nodes.Add(ReadNodeAt(reader, globalOffset, nextNode));
  268.                 }
  269.             }
  270.  
  271.             reader.BaseStream.Seek(pos, SeekOrigin.Begin);
  272.             return nodes;
  273.         }
  274.  
  275.         // For figuring out if a value that we think is unused is actually used.
  276.         protected void CheckUnknown(BinaryReader reader, long expected, long actual)
  277.         {
  278.             if (expected != actual)
  279.             {
  280.                 Debug.Log(String.Format("Value we expected to be {0:X} is actually {1:X} at {2:X}", expected, actual, reader.BaseStream.Position));
  281.             }
  282.         }
  283.  
  284.         public List<Node> FindAll(MajorType majorType, MinorType minorType)
  285.         {
  286.             var found = new List<Node>();
  287.  
  288.             if (null == children)
  289.             {
  290.                 return found;
  291.             }
  292.  
  293.             foreach (Node child in children)
  294.             {
  295.                 if (child.majorType == majorType && child.minorType == minorType)
  296.                 {
  297.                     found.Add(child);
  298.                 }
  299.  
  300.                 found.AddRange(child.FindAll(majorType, minorType));
  301.             }
  302.  
  303.             return found;
  304.         }
  305.  
  306.         public Node Find(MajorType majorType, MinorType minorType)
  307.         {
  308.             if (null == children)
  309.             {
  310.                 return null;
  311.             }
  312.  
  313.             foreach (Node child in children)
  314.             {
  315.                 if (child.majorType == majorType && child.minorType == minorType)
  316.                 {
  317.                     return child;
  318.                 }
  319.                 else
  320.                 {
  321.                     var found = child.Find(majorType, minorType);
  322.                     if (null != found)
  323.                     {
  324.                         return found;
  325.                     }
  326.                 }
  327.             }
  328.  
  329.             return null;
  330.         }
  331.     }
  332.  
  333.     public class MeshVerticies : Node
  334.     {
  335.         public List<Vector3> verticies = new List<Vector3>();
  336.  
  337.         long count;
  338.  
  339.         public MeshVerticies(BinaryReader reader, long globalOffset) : base(reader)
  340.         {
  341.             // unknown zeros
  342.             CheckUnknown(reader, 0, reader.ReadUInt32());
  343.             CheckUnknown(reader, 0, reader.ReadUInt32());
  344.  
  345.             count = reader.ReadUInt32();
  346.  
  347.             reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
  348.  
  349.             for (int i = 0; i < count; i++)
  350.             {
  351.                 verticies.Add(new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()));
  352.             }
  353.         }
  354.     }
  355.  
  356.     public class TextureVerticies : Node
  357.     {
  358.         public Vector2[] verticies;
  359.  
  360.         public TextureVerticies(BinaryReader reader, long globalOffset) : base(reader)
  361.         {
  362.             // unknown zeros
  363.             CheckUnknown(reader, 0, reader.ReadUInt32());
  364.             CheckUnknown(reader, 0, reader.ReadUInt32());
  365.  
  366.             long count = reader.ReadUInt32();
  367.  
  368.             reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
  369.  
  370.             verticies = new Vector2[count];
  371.             for (int i = 0; i < count; i++)
  372.             {
  373.                 verticies[i].x = reader.ReadSingle();
  374.                 verticies[i].y = reader.ReadSingle();
  375.             }
  376.         }
  377.     }
  378.  
  379.     public class VertexNormals : Node
  380.     {
  381.         public List<Vector3> normals = new List<Vector3>();
  382.  
  383.         public VertexNormals(BinaryReader reader, long globalOffset) : base(reader)
  384.         {
  385.             // unknown zeros
  386.             CheckUnknown(reader, 0, reader.ReadUInt32());
  387.             CheckUnknown(reader, 0, reader.ReadUInt32());
  388.  
  389.             long count = reader.ReadUInt32();
  390.  
  391.             reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
  392.  
  393.             for (int i = 0; i < count; i++)
  394.             {
  395.                 normals.Add(new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()));
  396.             }
  397.         }
  398.     }
  399.  
  400.     public class Hardpoint : Node
  401.     {
  402.         public HardpointType type;
  403.         public Vector3 coords;
  404.  
  405.         public Hardpoint(BinaryReader reader, long globalOffset) : base(reader)
  406.         {
  407.             CheckUnknown(reader, 0, reader.ReadUInt32());
  408.             CheckUnknown(reader, 0, reader.ReadUInt32());
  409.             CheckUnknown(reader, 1, reader.ReadUInt32());
  410.  
  411.             long jumpToCoords = reader.ReadUInt32();
  412.  
  413.             type = (HardpointType)reader.ReadUInt32();
  414.  
  415.             reader.BaseStream.Seek(jumpToCoords - globalOffset, SeekOrigin.Begin);
  416.  
  417.             // Unkown int
  418.             reader.ReadUInt32();
  419.             coords = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  420.         }
  421.     }
  422.  
  423.     // For a lot of ship parts, this seems to contain garbage.
  424.     public class Transform : Node
  425.     {
  426.         public Vector3 offset; // Seems the same as MeshDescriptor.centerPoint
  427.         public Vector3 yawAxis;
  428.         public Vector3 rollAxis;
  429.         public Vector3 pitchAxis;
  430.  
  431.         public Transform(BinaryReader reader, long globalOffset) : base(reader)
  432.         {
  433.             CheckUnknown(reader, 0, reader.ReadUInt32());
  434.             CheckUnknown(reader, 0, reader.ReadUInt32());
  435.             CheckUnknown(reader, 1, reader.ReadUInt32());
  436.  
  437.             long jumpToTransformData = reader.ReadUInt32();
  438.             reader.BaseStream.Seek(jumpToTransformData - globalOffset, SeekOrigin.Begin);
  439.  
  440.             offset = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  441.             yawAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  442.             rollAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  443.             pitchAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  444.         }
  445.     }
  446.  
  447.     // Contains a mesh for each level of detail.
  448.     public class MeshLOD : Node
  449.     {
  450.         public float[] LODThresholds;
  451.  
  452.         // Models can have simplified lower LOD meshes.
  453.         public MeshLOD(BinaryReader reader, long globalOffset) : base(reader)
  454.         {
  455.             long lodMeshCount = reader.ReadUInt32();
  456.             long meshBlockOffset = reader.ReadUInt32();
  457.             long lodMeshThresholdCount = reader.ReadUInt32();
  458.             long lodMeshThresholdOffset = reader.ReadUInt32();
  459.  
  460.             // No idea why this would happen, but my understanding of this block is wrong if it does.
  461.             if (lodMeshCount != lodMeshThresholdCount)
  462.             {
  463.                 throw new IOException("Not the same number of LOD meshes as LOD offsets");
  464.             }
  465.  
  466.             LODThresholds = new float[lodMeshCount];
  467.             reader.BaseStream.Seek(lodMeshThresholdOffset - globalOffset, SeekOrigin.Begin);
  468.             for (int i = 0; i < lodMeshCount; i++)
  469.             {
  470.                 LODThresholds[i] = reader.ReadSingle();
  471.             }
  472.  
  473.             children = ReadSubNodes(reader, globalOffset, lodMeshCount, meshBlockOffset);
  474.         }
  475.     }
  476.  
  477.     public class FaceList : Node
  478.     {
  479.         public int[,] vertexRef, edgeRef, UVRef, vertexNormalRef;
  480.  
  481.         public Vector3[] faceNormals;
  482.         public Vector3[] accrossTop;
  483.         public Vector3[] downSide;
  484.  
  485.         public long count, edgeCount;
  486.  
  487.         public FaceList(BinaryReader reader, long globalOffset) : base(reader)
  488.         {
  489.             // unknown zeros
  490.             CheckUnknown(reader, 0, reader.ReadUInt32());
  491.             CheckUnknown(reader, 0, reader.ReadUInt32());
  492.  
  493.             count = reader.ReadUInt32();
  494.             long faceDataOffset = reader.ReadUInt32();
  495.             reader.BaseStream.Seek(faceDataOffset - globalOffset, SeekOrigin.Begin);
  496.             edgeCount = reader.ReadUInt32();
  497.  
  498.             vertexRef = new int[count, 4];
  499.             edgeRef = new int[count, 4];
  500.             UVRef = new int[count, 4];
  501.             vertexNormalRef = new int[count, 4];
  502.  
  503.             for (int i = 0; i < count; i++)
  504.             {
  505.                 for (int j = 0; j < 4; j++)
  506.                 {
  507.                     vertexRef[i, j] = reader.ReadInt32();
  508.                 }
  509.  
  510.                 for (int j = 0; j < 4; j++)
  511.                 {
  512.                     edgeRef[i, j] = reader.ReadInt32();
  513.                 }
  514.  
  515.                 for (int j = 0; j < 4; j++)
  516.                 {
  517.                     UVRef[i, j] = reader.ReadInt32();
  518.                 }
  519.  
  520.                 for (int j = 0; j < 4; j++)
  521.                 {
  522.                     vertexNormalRef[i, j] = reader.ReadInt32();
  523.                 }
  524.             }
  525.  
  526.             faceNormals = new Vector3[count];
  527.             for (int i = 0; i < count; i++)
  528.             {
  529.                 faceNormals[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  530.             }
  531.  
  532.             // Not sure these are actually useful in unity.
  533.             accrossTop = new Vector3[count];
  534.             downSide = new Vector3[count];
  535.             for (int i = 0; i < count; i++)
  536.             {
  537.                 accrossTop[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  538.                 downSide[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  539.             }
  540.         }
  541.     }
  542.  
  543.     public class Texture : Node
  544.     {
  545.         public long uid;
  546.         public string name;
  547.         public int width, height, mipLevels = 0;
  548.  
  549.         public byte[] texturePalletRefs;
  550.         public List<byte[]> mipPalletRefs = new List<byte[]>();
  551.  
  552.         // 16 pallets at decreasing light levels.
  553.         // colors packed 5-6-5 blue, green, red.
  554.         public ushort[,] pallet;
  555.  
  556.         public Texture(BinaryReader reader, long globalOffset, long textureNameOffset) : base(reader)
  557.         {
  558.             // Alpha mask jump address?  Only if minor_id == 1.  Otherwise 0.
  559.             CheckUnknown(reader, 0, reader.ReadUInt32());
  560.  
  561.             uid = reader.ReadUInt32();
  562.  
  563.             // supposedly models can share these.
  564.             long palletAddressOffset = reader.ReadUInt32();
  565.  
  566.             reader.BaseStream.Seek(textureNameOffset - globalOffset, SeekOrigin.Begin);
  567.             name = Missions.MissionLoader.ReadString(reader.BaseStream, 9);
  568.  
  569.             // This should be the next byte after the string, but seek anyway in case there are some "easter eggs" to skip.
  570.             reader.BaseStream.Seek(palletAddressOffset - globalOffset, SeekOrigin.Begin);
  571.  
  572.             long palletOffset = reader.ReadUInt32();
  573.  
  574.             CheckUnknown(reader, 0, reader.ReadUInt32());
  575.  
  576.             int size = reader.ReadInt32();
  577.             int sizeWithMips = reader.ReadInt32();
  578.  
  579.             width = reader.ReadInt32();
  580.             height = reader.ReadInt32();
  581.  
  582.             texturePalletRefs = new byte[size];
  583.             reader.Read(texturePalletRefs, 0, size);
  584.  
  585.             // Read Mip data
  586.             // Not sure we need these.
  587.             int nextMipWidth = width / 2, nextMipHeight = height / 2;
  588.             int mipSize = nextMipWidth * nextMipHeight;
  589.             int mipDataToRead = sizeWithMips - size;
  590.             while (mipDataToRead >= mipSize && mipSize > 0)
  591.             {
  592.                 byte[] nextMipRefs = new byte[mipSize];
  593.                 reader.Read(nextMipRefs, 0, mipSize);
  594.                 mipPalletRefs.Add(nextMipRefs);
  595.                 mipLevels++;
  596.  
  597.                 nextMipWidth = nextMipWidth / 2;
  598.                 nextMipHeight = nextMipHeight / 2;
  599.                 mipDataToRead -= mipSize;
  600.                 mipSize = nextMipWidth * nextMipHeight;
  601.             }
  602.  
  603.             // Ok, now go back and find the texture pallet.
  604.             reader.BaseStream.Seek(palletOffset - globalOffset, SeekOrigin.Begin);
  605.             // Always 16 pallets, 256 colors each
  606.             // For some reason pallets 0-7 seem to be padding, 8-15 appear to be increasing brightness
  607.             pallet = new ushort[16, 256];
  608.             for (int i = 0; i < 16; i++)
  609.             {
  610.                 for (int j = 0; j < 256; j++)
  611.                 {
  612.                     pallet[i, j] = reader.ReadUInt16();
  613.                 }
  614.             }
  615.         }
  616.  
  617.         // Returns RGB565 texture data suitable for consumption in unity.
  618.         public Texture2D GenUnityTexture(int palletNumber = 8)
  619.         {
  620.             Texture2D texture = new Texture2D(width, height, TextureFormat.RGB565, false);
  621.  
  622.             // De-palletize from single byte palet reference to 16 bit RGB565
  623.             // Useing brightest pallet
  624.             byte[] colors = new Byte[texturePalletRefs.Length * 2];
  625.             for (int i = 0; i < texturePalletRefs.Length; i++)
  626.             {
  627.                 Array.Copy(BitConverter.GetBytes(pallet[palletNumber, texturePalletRefs[i]]), 0, colors, i * 2, 2);
  628.             }
  629.  
  630.             texture.LoadRawTextureData(colors);
  631.             texture.Apply();
  632.  
  633.             return texture;
  634.         }
  635.     }
  636.  
  637.     public class TextureReferenceByName : Node
  638.     {
  639.         public string name;
  640.  
  641.         public int id;
  642.  
  643.         public TextureReferenceByName(BinaryReader reader, long globalOffset) : base(reader)
  644.         {
  645.             CheckUnknown(reader, 0, reader.ReadUInt32());
  646.             CheckUnknown(reader, 0, reader.ReadUInt32());
  647.             id = reader.ReadInt32();
  648.  
  649.             long textureNameOffset = reader.ReadUInt32();
  650.             reader.BaseStream.Seek(textureNameOffset - globalOffset, SeekOrigin.Begin);
  651.             name = Missions.MissionLoader.ReadString(reader.BaseStream, 9);
  652.         }
  653.     }
  654.  
  655.     // Used when a mesh can have more than one texture.  EG a craft used by more than one faction.
  656.     public class SkinSelector : Node
  657.     {
  658.         public SkinSelector(BinaryReader reader, long globalOffset) : base(reader)
  659.         {
  660.             long count = reader.ReadUInt32();
  661.  
  662.             long textureReferenceListOffset = reader.ReadUInt32();
  663.             // Skipping optional 1 or 0 and optional pointer
  664.  
  665.             // Creating new nodes here.. I suspect may be recycled addresses.
  666.             children = ReadSubNodes(reader, globalOffset, count, textureReferenceListOffset);
  667.         }
  668.     }
  669.  
  670.     public class PartDescriptor : Node
  671.     {
  672.         public PartType partType;
  673.  
  674.         // 0,1,4,5,8,9 = Mesh continues in straight line when destroyed 2,3,6,10 = Mesh breaks off and explodes 7 = destructible parts
  675.         // Looks like bitmask but not sure
  676.         public long ExplosionType;
  677.  
  678.         public Vector3 span; // width of the hit box
  679.         public Vector3 centerPoint; // Center point of hit box. For targeting the specific part of the craft with "," key.
  680.         public Vector3 minXYZ; // Lower X,Y,Z bound of the hit box
  681.         public Vector3 maxXYZ; // Upper X,Y,Z bound of the hit box
  682.  
  683.         public long targetingGroupID;
  684.  
  685.         public PartDescriptor(BinaryReader reader, long globalOffset) : base(reader)
  686.         {
  687.             CheckUnknown(reader, 0, reader.ReadUInt32());
  688.             CheckUnknown(reader, 0, reader.ReadUInt32());
  689.             CheckUnknown(reader, 1, reader.ReadUInt32());
  690.  
  691.             long jumpToMeshType = reader.ReadUInt32();
  692.             reader.BaseStream.Seek(jumpToMeshType - globalOffset, SeekOrigin.Begin);
  693.  
  694.             partType = (PartType)reader.ReadUInt32();
  695.             ExplosionType = reader.ReadUInt32();
  696.  
  697.             span = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  698.             centerPoint = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  699.             minXYZ = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  700.             maxXYZ = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
  701.  
  702.             targetingGroupID = reader.ReadUInt32();
  703.         }
  704.     }
  705.  
  706.     public class RootNode : Node
  707.     {
  708.         public RootNode(BinaryReader reader, long globalOffset) : base(reader)
  709.         {
  710.             // validate block header
  711.             // This is type of node is special in that the type is a single word
  712.             CheckUnknown(reader, 2, reader.ReadUInt16());
  713.  
  714.             // Root node uses a single word for ID
  715.             majorType = MajorType.submodelList;
  716.             minorType = MinorType.generic;
  717.             children = ReadSubNodes(reader, globalOffset, reader.ReadUInt32(), reader.ReadUInt32());
  718.         }
  719.     }
  720.  
  721.     // Read data from X-Wing / Tie Fighter OPT files
  722.     // Reference: http://www.oocities.org/v_d_d/Alternative_OPT_File_Reading.html
  723.     //            http://www.oocities.org/v_d_d/OptSpecs.pdf
  724.     public OptFile(string fileName)
  725.     {
  726.         FileStream missionFile = File.OpenRead(fileName);
  727.         using (var reader = new BinaryReader(missionFile))
  728.         {
  729.  
  730.             //long signature =
  731.             reader.ReadInt32();
  732.             //long fileLength =
  733.             reader.ReadUInt32(); // Actual file length is this + 8
  734.             globalOffset = reader.ReadUInt32() - 8; // jump offsets don't count the previous two longs
  735.  
  736.             rootNode = new RootNode(reader, globalOffset);
  737.         }
  738.     }
  739.  
  740.     //
  741.     public void ShipPartsIntoObject(GameObject parent, int skin = 0)
  742.     {
  743.         //Shader partShader = Resources.Load("Shaders/ShipPart") as Shader;
  744.         Shader partShader = Shader.Find("Standard");
  745.  
  746.         // Some models seem to share textures between parts by placing them at the top level.
  747.         // So we need to gather all of the textures in the model
  748.         // Making the assumption here that texture names are unique.
  749.         Dictionary<string, Material> materialCache = new Dictionary<string, Material>();
  750.         foreach (Node texture in rootNode.FindAll(MajorType.textrue, MinorType.generic))
  751.         {
  752.             Texture2D tex = ((Texture)texture).GenUnityTexture();
  753.             Material mat = new Material(partShader)
  754.             {
  755.                 name = ((Texture)texture).name,
  756.                 mainTexture = tex
  757.             };
  758.             materialCache[((Texture)texture).name] = mat;
  759.         }
  760.  
  761.         foreach (Node shipPart in rootNode.children.FindAll(x => x.GetType() == typeof(Node)))
  762.         {
  763.             GameObject partObj = UnityEngine.Object.Instantiate(Resources.Load("Prefab/ShipPart")) as GameObject;
  764.  
  765.             // Fetch ship part top level data
  766.             PartDescriptor descriptor = (PartDescriptor)shipPart.children.Find(x => x.GetType() == typeof(PartDescriptor));
  767.             MeshVerticies verts = (MeshVerticies)shipPart.children.Find(x => x.GetType() == typeof(MeshVerticies));
  768.             TextureVerticies vertUV = (TextureVerticies)shipPart.children.Find(x => x.GetType() == typeof(TextureVerticies));
  769.             VertexNormals vertNormals = (VertexNormals)shipPart.children.Find(x => x.GetType() == typeof(VertexNormals));
  770.  
  771.             // Attach this part to parent.
  772.             UnityEngine.Transform objTransform = partObj.GetComponent<UnityEngine.Transform>();
  773.             objTransform.parent = parent.transform;
  774.             objTransform.localPosition = new Vector3(0, 0, 0);
  775.             objTransform.localRotation = new Quaternion(0, 0, 0, 0);
  776.  
  777.             if (null != descriptor)
  778.             {
  779.                 // Set name from metadata
  780.                 partObj.name = descriptor.partType.ToString();
  781.  
  782.                 // Create an object used for targeting.
  783.                 GameObject tp = new GameObject("TargetPoint");
  784.                 tp.transform.parent = partObj.transform;
  785.                 tp.transform.localPosition = CoordinateConverter.MultiplyPoint3x4(descriptor.centerPoint);
  786.                 tp.transform.localRotation = new Quaternion(0, 0, 0, 0);
  787.  
  788.                 // Set up box collider
  789.                 BoxCollider box = partObj.GetComponent<BoxCollider>();
  790.                 box.center = CoordinateConverter.MultiplyPoint3x4(descriptor.centerPoint);
  791.                 var span = CoordinateConverter.MultiplyPoint3x4(descriptor.span);
  792.                 // Rotation into unity coordinate space results in negative vector components, but Unity gripes about negative BoxCollider sizes.
  793.                 span.x = Mathf.Abs(span.x);
  794.                 span.y = Mathf.Abs(span.y);
  795.                 span.z = Mathf.Abs(span.z);
  796.                 box.size = span;
  797.             }
  798.  
  799.             var lodLevels = (MeshLOD)shipPart.Find(MajorType.normal, MinorType.meshLOD);
  800.             var lods = new LOD[lodLevels.children.Count];
  801.             int lodIndex = 0;
  802.             foreach (Node meshesAndTextures in lodLevels.children)
  803.             {
  804.                 GameObject lodObj = new GameObject(partObj.name + "_LOD" + lodIndex);
  805.                 lodObj.AddComponent<MeshFilter>();
  806.                 lodObj.AddComponent<MeshRenderer>();
  807.  
  808.                 lodObj.transform.parent = partObj.transform;
  809.                 lodObj.transform.localPosition = new Vector3(0, 0, 0);
  810.                 lodObj.transform.localRotation = new Quaternion(0, 0, 0, 0);
  811.  
  812.                 List<string> matsUsed = new List<string>();
  813.                 List<FaceList> faceLists = new List<FaceList>();
  814.  
  815.                 // It seems there is no direct connection between meshes and the textures that go on
  816.                 // them besides that the texture preceeds the mesh in this list.
  817.                 // So keep track of the last mesh or mesh reference we've seen and apply it to the mesh.
  818.                 foreach (Node thing in meshesAndTextures.children)
  819.                 {
  820.                     if (thing.GetType() == typeof(Texture))
  821.                     {
  822.                         matsUsed.Add(((Texture)thing).name);
  823.                     }
  824.                     else if (thing.GetType() == typeof(TextureReferenceByName))
  825.                     {
  826.                         matsUsed.Add(((TextureReferenceByName)thing).name);
  827.                     }
  828.                     else if (thing.GetType() == typeof(SkinSelector))
  829.                     {
  830.                         Node selectedTexture = thing.children[skin];
  831.                         if (selectedTexture.GetType() == typeof(Texture))
  832.                         {
  833.                             matsUsed.Add(((Texture)selectedTexture).name);
  834.                         }
  835.                         else
  836.                         {
  837.                             matsUsed.Add(((TextureReferenceByName)selectedTexture).name);
  838.                         }
  839.                     }
  840.                     else if (thing.GetType() == typeof(FaceList))
  841.                     {
  842.                         faceLists.Add((FaceList)thing);
  843.                     }
  844.                     else
  845.                     {
  846.                         Debug.LogWarning("Skipping unexpected node type in model/texture data " + thing.GetType());
  847.                     }
  848.                 }
  849.  
  850.                 // Look up materials used by name to get references to the actual materials.
  851.                 Material[] mats = new Material[matsUsed.Count];
  852.                 for (int i = 0; i < matsUsed.Count; i++)
  853.                 {
  854.                     mats[i] = materialCache[matsUsed[i]];
  855.                 }
  856.  
  857.                 lodObj.GetComponent<MeshRenderer>().materials = mats;
  858.  
  859.                 lodObj.GetComponent<MeshFilter>().mesh = GenMesh(faceLists, verts, vertNormals, vertUV);
  860.  
  861.                 var lodThreshold = lodLevels.LODThresholds[lodIndex];
  862.                 if (lodThreshold > 0)
  863.                 {
  864.                     // XvT threshold seems to be based on distance. Unity is based on screen height.
  865.                     // 0.1F is a fudge factor based on increased resolution.
  866.                     // IE scale down the model at 1/10th of where it would be in XvT.
  867.                     lodThreshold = (float)(0.1F / Math.Atan(1 / lodThreshold));
  868.                 }
  869.                 lods[lodIndex] = new LOD(lodThreshold, new Renderer[] { lodObj.GetComponent<MeshRenderer>() });
  870.  
  871.                 lodIndex++;
  872.             }
  873.  
  874.             partObj.GetComponent<LODGroup>().SetLODs(lods);
  875.             // Some low LOD meshes comletely replace meshes from other parts.
  876.             // So ensure that all parts cutover at the same distance to avoid flickering.
  877.             partObj.GetComponent<LODGroup>().localReferencePoint = new Vector3(0, 0, 0);
  878.             partObj.GetComponent<LODGroup>().RecalculateBounds();
  879.  
  880.             // Generate hardpoints
  881.             foreach (Node hardpoint in shipPart.FindAll(MajorType.normal, MinorType.hardpoint))
  882.             {
  883.                 var hardpointObj = UnityEngine.Object.Instantiate(Resources.Load("Prefab/Hardpoint")) as GameObject;
  884.                 hardpointObj.name = ((Hardpoint)hardpoint).type.ToString();
  885.                 hardpointObj.transform.parent = partObj.transform;
  886.                 hardpointObj.transform.localPosition = CoordinateConverter.MultiplyPoint3x4(((Hardpoint)hardpoint).coords);
  887.                 hardpointObj.transform.localRotation = new Quaternion(0, 0, 0, 0);
  888.                 hardpointObj.GetComponent<Game.Hardpoint>().type = ((Hardpoint)hardpoint).type;
  889.             }
  890.         }
  891.     }
  892.  
  893.     struct VertexSplitTuple
  894.     {
  895.         public int vId, uvId, normId;
  896.         public static bool operator ==(VertexSplitTuple a, VertexSplitTuple b)
  897.         {
  898.             return a.vId == b.vId && a.uvId == b.uvId && a.normId == b.normId;
  899.         }
  900.         public static bool operator !=(VertexSplitTuple a, VertexSplitTuple b)
  901.         {
  902.             return a.vId != b.vId || a.uvId != b.uvId || a.normId != b.normId;
  903.         }
  904.         public override bool Equals(object obj)
  905.         {
  906.             return Equals((VertexSplitTuple)obj);
  907.         }
  908.         public bool Equals(VertexSplitTuple obj)
  909.         {
  910.             return vId == obj.vId && uvId == obj.uvId && normId == obj.normId;
  911.         }
  912.         public override int GetHashCode()
  913.         {
  914.             // overflow ok
  915.             return vId + uvId + normId;
  916.         }
  917.     }
  918.  
  919.     Mesh GenMesh(List<FaceList> faceLists, MeshVerticies verts, VertexNormals vertNormals, TextureVerticies vertUV)
  920.     {
  921.         var mesh = new Mesh();
  922.  
  923.         var submeshList = new List<List<int>>();
  924.  
  925.         // Must remain the same length.
  926.         var meshVerts = new List<Vector3>();
  927.         var meshUV = new List<Vector2>();
  928.         var meshNorms = new List<Vector3>();
  929.  
  930.         // Unity can only have one normal and UV per vertex.
  931.         // All sub-meshes (triangle lists) in the same mesh have to share the same vertex list.
  932.         // This data uses normal and UV data per face, per vertex.
  933.         // So we have to make a new vertex any time a polygon references a different normal
  934.         // or UV than another polygon.
  935.         var usedVertLookup = new Dictionary<VertexSplitTuple, int>();
  936.  
  937.         // Build the vert/normal/UV lists
  938.         foreach (FaceList faceList in faceLists)
  939.         {
  940.             var triangles = new List<int>();
  941.             for (int i = 0; i < faceList.count; i++)
  942.             {
  943.                 var newVertRefs = new int[4];
  944.  
  945.                 // check each point for need to generate new vertex
  946.                 for (int j = 0; j < 4; j++)
  947.                 {
  948.                     VertexSplitTuple vt;
  949.                     vt.vId = faceList.vertexRef[i, j];
  950.                     vt.uvId = faceList.UVRef[i, j];
  951.                     vt.normId = faceList.vertexNormalRef[i, j];
  952.  
  953.  
  954.                     // Some faces are triangles instead of quads.
  955.                     if (vt.vId == -1 || vt.uvId == -1 || vt.normId == -1)
  956.                     {
  957.                         newVertRefs[j] = -1;
  958.                         continue;
  959.                     }
  960.  
  961.                     if (usedVertLookup.ContainsKey(vt))
  962.                     {
  963.                         // reuse the vertex
  964.                         newVertRefs[j] = usedVertLookup[vt];
  965.                     }
  966.                     else
  967.                     {
  968.                         // make a new one
  969.                         if (vt.vId > verts.verticies.Count - 1)
  970.                         {
  971.                             Debug.LogError(string.Format("Vert {0}/{4} out of bound {1:X} {2} {3} ", vt.vId, faceList.offsetInFile, i, j, verts.verticies.Count));
  972.                         }
  973.                         if (vt.normId > vertNormals.normals.Count - 1)
  974.                         {
  975.                             Debug.LogError(string.Format("Normal {0}/{4} out of bound {1:X} {2} {3} ", vt.normId, faceList.offsetInFile, i, j, vertNormals.normals.Count));
  976.                         }
  977.                         if (vt.uvId > vertUV.verticies.Length - 1)
  978.                         {
  979.                             Debug.LogError(string.Format("UV {0}/{4} out of bound {1:X} {2} {3} ", vt.uvId, faceList.offsetInFile, i, j, vertUV.verticies.Length));
  980.                         }
  981.                         meshVerts.Add(CoordinateConverter.MultiplyPoint3x4(verts.verticies[vt.vId]));
  982.                         meshUV.Add(vertUV.verticies[vt.uvId]);
  983.                         meshNorms.Add(CoordinateConverter.MultiplyPoint3x4(vertNormals.normals[vt.normId]));
  984.  
  985.                         // Index it so we can find it later.
  986.                         usedVertLookup[vt] = meshVerts.Count - 1;
  987.                         newVertRefs[j] = usedVertLookup[vt];
  988.                     }
  989.                 }
  990.  
  991.                 // TODO: Less nieve quad split.
  992.                 // First triangle
  993.                 triangles.Add(newVertRefs[1]);
  994.                 triangles.Add(newVertRefs[0]);
  995.                 triangles.Add(newVertRefs[2]);
  996.  
  997.                 // second triangle if a quad
  998.                 if (newVertRefs[3] != -1)
  999.                 {
  1000.                     triangles.Add(newVertRefs[3]);
  1001.                     triangles.Add(newVertRefs[2]);
  1002.                     triangles.Add(newVertRefs[0]);
  1003.                 }
  1004.  
  1005.             }
  1006.             submeshList.Add(triangles);
  1007.         }
  1008.  
  1009.         mesh.SetVertices(meshVerts);
  1010.         mesh.SetUVs(0, meshUV);
  1011.         mesh.SetNormals(meshNorms);
  1012.  
  1013.         mesh.subMeshCount = submeshList.Count;
  1014.         for (int i = 0; i < submeshList.Count; i++)
  1015.         {
  1016.             mesh.SetTriangles(submeshList[i], i);
  1017.         }
  1018.  
  1019.         return mesh;
  1020.     }
  1021. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement