Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.IO;
- using UnityEngine;
- public class OptFile
- {
- /*
- * Notes:
- * - There are a lot of places where an offset should point to the very next byte.
- * I'm following the offset anyway in case they are doing something sneaky.
- * Easter Eggs, maybe.
- * - There are a lot of values that we don't know what they do but are normally zero.
- * Probably best to check them in order to find cases where they're not zero.
- * - Coordinates are swapped from x->east y->north z->up to unity's style.
- *
- * Bugs:
- * - Some models seem to not have their LOD meshes in decreasing order. Need to reorder for unity.
- * - XWing low LOD wing meshes are all black if using the standard unity shader.
- * - Minor type enum actually depends on major type.
- * - Pallets can be shared between meshes, but we're making extra copies each time.
- * - No serialize.
- */
- public enum MajorType
- {
- normal = 0,
- submodelList = 2,
- textrue = 20,
- }
- public enum MinorType
- {
- generic = 0,
- faceList = 1,
- mainJump = 2,
- meshVertex = 3,
- info = 4,
- textureReferenceByName = 7,
- vertexNormal = 11,
- textureVertex = 13,
- textureHeader = 20,
- meshLOD = 21,
- hardpoint = 22,
- transform = 23,
- skinSelector = 24,
- meshDescriptor = 25,
- unknown = 0xff,
- }
- public enum PartType
- {
- DefaultType = 0,
- MainHull = 1,
- Wing = 2,
- Fuselage = 3,
- GunTurret = 4,
- SmallGun = 5,
- Engine = 6,
- Bridge = 7,
- ShieldGen = 8,
- EnergyGen = 9,
- Launcher = 10,
- CommSys = 11,
- BeamSys = 12,
- CommandBeam = 13,
- DockingPlat = 14,
- LandingPlat = 15,
- Hangar = 16,
- CargoPod = 17,
- MiscHull = 18,
- Antenna = 19,
- RotWing = 20,
- RotGunTurret = 21,
- RotLauncher = 22,
- RotCommSys = 23,
- RotBeamSys = 24,
- RotCommandBeam = 25,
- Custom1 = 26,
- Custom2 = 27,
- Custom3 = 28,
- Custom4 = 29,
- Custom5 = 30,
- Custom6 = 31,
- }
- public enum HardpointType
- {
- None = 0,
- RebelLaser = 1,
- TurboRebelLaser = 2,
- EmpireLaser = 3,
- TurboEmpireLaser = 4,
- IonCannon = 5,
- TurboIonCannon = 6,
- Torpedo = 7,
- Missile = 8,
- SuperRebelLaser = 9,
- SuperEmpireLaser = 10,
- SuperIonCannon = 11,
- SuperTorpedo = 12,
- SuperMissile = 13,
- DumbBomb = 14,
- FiredBomb = 15,
- Magpulse = 16,
- TurboMagpulse = 17,
- SuperMagpulse = 18,
- NewWeapon1 = 19,
- NewWeapon2 = 20,
- NewWeapon3 = 21,
- NewWeapon4 = 22,
- NewWeapon5 = 23,
- NewWeapon6 = 24,
- InsideHangar = 25,
- OutsideHangar = 26,
- DockFromBig = 27,
- DockFromSmall = 28,
- DockToBig = 29,
- DockToSmall = 30,
- Cockpit = 31,
- }
- // XvT engine -> Unity engine
- // unity: forward is +z, right is +x, up is +y
- // XvT: forward is -y, right is +x(?), up is +z
- static readonly Matrix4x4 CoordinateConverter = new Matrix4x4(
- new Vector4(1, 0, 0, 0),
- new Vector4(0, 0, -1, 0),
- new Vector4(0, 1, 0, 0),
- new Vector4(0, 0, 0, 1)
- );
- private long globalOffset;
- private RootNode rootNode;
- // Generic node with sub-nodes
- public class Node
- {
- public List<Node> children;
- public MajorType majorType;
- public MinorType minorType;
- public long offsetInFile;
- public Node(BinaryReader reader)
- {
- offsetInFile = reader.BaseStream.Position;
- if (this.GetType() != typeof(RootNode))
- {
- reader.BaseStream.Seek(-8, SeekOrigin.Current);
- majorType = (MajorType)reader.ReadUInt32();
- minorType = (MinorType)reader.ReadUInt32();
- }
- }
- Node ReadNodeAt(BinaryReader reader, long globalOffset, long offset)
- {
- Node node;
- long preHeaderOffset = 0;
- reader.BaseStream.Seek(offset - globalOffset, SeekOrigin.Begin);
- long majorId = reader.ReadUInt32();
- long minorId = reader.ReadUInt32();
- // Edge case: one block type doesn't start with major/minor type id and actually start with another offset.
- // So peek ahead one more long and shuffle numbers where they go.
- // This may not work if globalOffset is 0.
- // Should be a pointer to an offset containing string "Tex00000" or similar.
- long peek = reader.ReadUInt32();
- if (majorId > globalOffset && minorId == (long)MajorType.textrue)
- {
- preHeaderOffset = majorId;
- majorId = minorId;
- minorId = peek;
- }
- else
- {
- reader.BaseStream.Seek(-4, SeekOrigin.Current);
- }
- // Figure out the type of node and build appropriate object.
- switch (majorId)
- {
- case (long)MajorType.normal:
- switch (minorId)
- {
- case (long)MinorType.generic:
- node = new Node(reader)
- {
- children = ReadSubNodes(reader, globalOffset, reader.ReadUInt32(), reader.ReadUInt32())
- };
- break;
- case (long)MinorType.meshVertex:
- node = new MeshVerticies(reader, globalOffset);
- break;
- case (long)MinorType.textureVertex:
- node = new TextureVerticies(reader, globalOffset);
- break;
- case (long)MinorType.textureReferenceByName:
- node = new TextureReferenceByName(reader, globalOffset);
- break;
- case (long)MinorType.vertexNormal:
- node = new VertexNormals(reader, globalOffset);
- break;
- case (long)MinorType.hardpoint:
- node = new Hardpoint(reader, globalOffset);
- break;
- case (long)MinorType.transform:
- node = new Transform(reader, globalOffset);
- break;
- case (long)MinorType.meshLOD:
- node = new MeshLOD(reader, globalOffset);
- break;
- case (long)MinorType.faceList:
- node = new FaceList(reader, globalOffset);
- break;
- case (long)MinorType.skinSelector:
- node = new SkinSelector(reader, globalOffset);
- break;
- case (long)MinorType.meshDescriptor:
- node = new PartDescriptor(reader, globalOffset);
- break;
- default:
- node = new Node(reader);
- Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
- break;
- }
- break;
- case (long)MajorType.textrue:
- switch (minorId)
- {
- case 0:
- node = new Texture(reader, globalOffset, preHeaderOffset);
- break;
- case 1:
- // seems to be a texture with alpha mask
- node = new Node(reader);
- Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
- break;
- default:
- node = new Texture(reader, globalOffset, preHeaderOffset);
- Debug.LogWarning("Found unknown node type " + node.majorType + " " + node.minorType + " at " + reader.BaseStream.Position);
- break;
- }
- break;
- default:
- node = new Node(reader);
- break;
- }
- return node;
- }
- // generates sub-nodes starting at a given offset
- // returns the stream to the same position it was before calling.
- protected List<Node> ReadSubNodes(BinaryReader reader, long globalOffset, long count, long jumpListOffset)
- {
- List<Node> nodes = new List<Node>();
- long pos = reader.BaseStream.Position;
- for (int i = 0; i < count; i++)
- {
- reader.BaseStream.Seek(jumpListOffset - globalOffset + 4 * i, SeekOrigin.Begin);
- long nextNode = reader.ReadUInt32();
- if (nextNode != 0)
- {
- nodes.Add(ReadNodeAt(reader, globalOffset, nextNode));
- }
- }
- reader.BaseStream.Seek(pos, SeekOrigin.Begin);
- return nodes;
- }
- // For figuring out if a value that we think is unused is actually used.
- protected void CheckUnknown(BinaryReader reader, long expected, long actual)
- {
- if (expected != actual)
- {
- Debug.Log(String.Format("Value we expected to be {0:X} is actually {1:X} at {2:X}", expected, actual, reader.BaseStream.Position));
- }
- }
- public List<Node> FindAll(MajorType majorType, MinorType minorType)
- {
- var found = new List<Node>();
- if (null == children)
- {
- return found;
- }
- foreach (Node child in children)
- {
- if (child.majorType == majorType && child.minorType == minorType)
- {
- found.Add(child);
- }
- found.AddRange(child.FindAll(majorType, minorType));
- }
- return found;
- }
- public Node Find(MajorType majorType, MinorType minorType)
- {
- if (null == children)
- {
- return null;
- }
- foreach (Node child in children)
- {
- if (child.majorType == majorType && child.minorType == minorType)
- {
- return child;
- }
- else
- {
- var found = child.Find(majorType, minorType);
- if (null != found)
- {
- return found;
- }
- }
- }
- return null;
- }
- }
- public class MeshVerticies : Node
- {
- public List<Vector3> verticies = new List<Vector3>();
- long count;
- public MeshVerticies(BinaryReader reader, long globalOffset) : base(reader)
- {
- // unknown zeros
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- count = reader.ReadUInt32();
- reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
- for (int i = 0; i < count; i++)
- {
- verticies.Add(new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()));
- }
- }
- }
- public class TextureVerticies : Node
- {
- public Vector2[] verticies;
- public TextureVerticies(BinaryReader reader, long globalOffset) : base(reader)
- {
- // unknown zeros
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- long count = reader.ReadUInt32();
- reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
- verticies = new Vector2[count];
- for (int i = 0; i < count; i++)
- {
- verticies[i].x = reader.ReadSingle();
- verticies[i].y = reader.ReadSingle();
- }
- }
- }
- public class VertexNormals : Node
- {
- public List<Vector3> normals = new List<Vector3>();
- public VertexNormals(BinaryReader reader, long globalOffset) : base(reader)
- {
- // unknown zeros
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- long count = reader.ReadUInt32();
- reader.BaseStream.Seek(reader.ReadUInt32() - globalOffset, SeekOrigin.Begin);
- for (int i = 0; i < count; i++)
- {
- normals.Add(new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()));
- }
- }
- }
- public class Hardpoint : Node
- {
- public HardpointType type;
- public Vector3 coords;
- public Hardpoint(BinaryReader reader, long globalOffset) : base(reader)
- {
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 1, reader.ReadUInt32());
- long jumpToCoords = reader.ReadUInt32();
- type = (HardpointType)reader.ReadUInt32();
- reader.BaseStream.Seek(jumpToCoords - globalOffset, SeekOrigin.Begin);
- // Unkown int
- reader.ReadUInt32();
- coords = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- }
- }
- // For a lot of ship parts, this seems to contain garbage.
- public class Transform : Node
- {
- public Vector3 offset; // Seems the same as MeshDescriptor.centerPoint
- public Vector3 yawAxis;
- public Vector3 rollAxis;
- public Vector3 pitchAxis;
- public Transform(BinaryReader reader, long globalOffset) : base(reader)
- {
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 1, reader.ReadUInt32());
- long jumpToTransformData = reader.ReadUInt32();
- reader.BaseStream.Seek(jumpToTransformData - globalOffset, SeekOrigin.Begin);
- offset = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- yawAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- rollAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- pitchAxis = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- }
- }
- // Contains a mesh for each level of detail.
- public class MeshLOD : Node
- {
- public float[] LODThresholds;
- // Models can have simplified lower LOD meshes.
- public MeshLOD(BinaryReader reader, long globalOffset) : base(reader)
- {
- long lodMeshCount = reader.ReadUInt32();
- long meshBlockOffset = reader.ReadUInt32();
- long lodMeshThresholdCount = reader.ReadUInt32();
- long lodMeshThresholdOffset = reader.ReadUInt32();
- // No idea why this would happen, but my understanding of this block is wrong if it does.
- if (lodMeshCount != lodMeshThresholdCount)
- {
- throw new IOException("Not the same number of LOD meshes as LOD offsets");
- }
- LODThresholds = new float[lodMeshCount];
- reader.BaseStream.Seek(lodMeshThresholdOffset - globalOffset, SeekOrigin.Begin);
- for (int i = 0; i < lodMeshCount; i++)
- {
- LODThresholds[i] = reader.ReadSingle();
- }
- children = ReadSubNodes(reader, globalOffset, lodMeshCount, meshBlockOffset);
- }
- }
- public class FaceList : Node
- {
- public int[,] vertexRef, edgeRef, UVRef, vertexNormalRef;
- public Vector3[] faceNormals;
- public Vector3[] accrossTop;
- public Vector3[] downSide;
- public long count, edgeCount;
- public FaceList(BinaryReader reader, long globalOffset) : base(reader)
- {
- // unknown zeros
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- count = reader.ReadUInt32();
- long faceDataOffset = reader.ReadUInt32();
- reader.BaseStream.Seek(faceDataOffset - globalOffset, SeekOrigin.Begin);
- edgeCount = reader.ReadUInt32();
- vertexRef = new int[count, 4];
- edgeRef = new int[count, 4];
- UVRef = new int[count, 4];
- vertexNormalRef = new int[count, 4];
- for (int i = 0; i < count; i++)
- {
- for (int j = 0; j < 4; j++)
- {
- vertexRef[i, j] = reader.ReadInt32();
- }
- for (int j = 0; j < 4; j++)
- {
- edgeRef[i, j] = reader.ReadInt32();
- }
- for (int j = 0; j < 4; j++)
- {
- UVRef[i, j] = reader.ReadInt32();
- }
- for (int j = 0; j < 4; j++)
- {
- vertexNormalRef[i, j] = reader.ReadInt32();
- }
- }
- faceNormals = new Vector3[count];
- for (int i = 0; i < count; i++)
- {
- faceNormals[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- }
- // Not sure these are actually useful in unity.
- accrossTop = new Vector3[count];
- downSide = new Vector3[count];
- for (int i = 0; i < count; i++)
- {
- accrossTop[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- downSide[i] = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- }
- }
- }
- public class Texture : Node
- {
- public long uid;
- public string name;
- public int width, height, mipLevels = 0;
- public byte[] texturePalletRefs;
- public List<byte[]> mipPalletRefs = new List<byte[]>();
- // 16 pallets at decreasing light levels.
- // colors packed 5-6-5 blue, green, red.
- public ushort[,] pallet;
- public Texture(BinaryReader reader, long globalOffset, long textureNameOffset) : base(reader)
- {
- // Alpha mask jump address? Only if minor_id == 1. Otherwise 0.
- CheckUnknown(reader, 0, reader.ReadUInt32());
- uid = reader.ReadUInt32();
- // supposedly models can share these.
- long palletAddressOffset = reader.ReadUInt32();
- reader.BaseStream.Seek(textureNameOffset - globalOffset, SeekOrigin.Begin);
- name = Missions.MissionLoader.ReadString(reader.BaseStream, 9);
- // This should be the next byte after the string, but seek anyway in case there are some "easter eggs" to skip.
- reader.BaseStream.Seek(palletAddressOffset - globalOffset, SeekOrigin.Begin);
- long palletOffset = reader.ReadUInt32();
- CheckUnknown(reader, 0, reader.ReadUInt32());
- int size = reader.ReadInt32();
- int sizeWithMips = reader.ReadInt32();
- width = reader.ReadInt32();
- height = reader.ReadInt32();
- texturePalletRefs = new byte[size];
- reader.Read(texturePalletRefs, 0, size);
- // Read Mip data
- // Not sure we need these.
- int nextMipWidth = width / 2, nextMipHeight = height / 2;
- int mipSize = nextMipWidth * nextMipHeight;
- int mipDataToRead = sizeWithMips - size;
- while (mipDataToRead >= mipSize && mipSize > 0)
- {
- byte[] nextMipRefs = new byte[mipSize];
- reader.Read(nextMipRefs, 0, mipSize);
- mipPalletRefs.Add(nextMipRefs);
- mipLevels++;
- nextMipWidth = nextMipWidth / 2;
- nextMipHeight = nextMipHeight / 2;
- mipDataToRead -= mipSize;
- mipSize = nextMipWidth * nextMipHeight;
- }
- // Ok, now go back and find the texture pallet.
- reader.BaseStream.Seek(palletOffset - globalOffset, SeekOrigin.Begin);
- // Always 16 pallets, 256 colors each
- // For some reason pallets 0-7 seem to be padding, 8-15 appear to be increasing brightness
- pallet = new ushort[16, 256];
- for (int i = 0; i < 16; i++)
- {
- for (int j = 0; j < 256; j++)
- {
- pallet[i, j] = reader.ReadUInt16();
- }
- }
- }
- // Returns RGB565 texture data suitable for consumption in unity.
- public Texture2D GenUnityTexture(int palletNumber = 8)
- {
- Texture2D texture = new Texture2D(width, height, TextureFormat.RGB565, false);
- // De-palletize from single byte palet reference to 16 bit RGB565
- // Useing brightest pallet
- byte[] colors = new Byte[texturePalletRefs.Length * 2];
- for (int i = 0; i < texturePalletRefs.Length; i++)
- {
- Array.Copy(BitConverter.GetBytes(pallet[palletNumber, texturePalletRefs[i]]), 0, colors, i * 2, 2);
- }
- texture.LoadRawTextureData(colors);
- texture.Apply();
- return texture;
- }
- }
- public class TextureReferenceByName : Node
- {
- public string name;
- public int id;
- public TextureReferenceByName(BinaryReader reader, long globalOffset) : base(reader)
- {
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- id = reader.ReadInt32();
- long textureNameOffset = reader.ReadUInt32();
- reader.BaseStream.Seek(textureNameOffset - globalOffset, SeekOrigin.Begin);
- name = Missions.MissionLoader.ReadString(reader.BaseStream, 9);
- }
- }
- // Used when a mesh can have more than one texture. EG a craft used by more than one faction.
- public class SkinSelector : Node
- {
- public SkinSelector(BinaryReader reader, long globalOffset) : base(reader)
- {
- long count = reader.ReadUInt32();
- long textureReferenceListOffset = reader.ReadUInt32();
- // Skipping optional 1 or 0 and optional pointer
- // Creating new nodes here.. I suspect may be recycled addresses.
- children = ReadSubNodes(reader, globalOffset, count, textureReferenceListOffset);
- }
- }
- public class PartDescriptor : Node
- {
- public PartType partType;
- // 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
- // Looks like bitmask but not sure
- public long ExplosionType;
- public Vector3 span; // width of the hit box
- public Vector3 centerPoint; // Center point of hit box. For targeting the specific part of the craft with "," key.
- public Vector3 minXYZ; // Lower X,Y,Z bound of the hit box
- public Vector3 maxXYZ; // Upper X,Y,Z bound of the hit box
- public long targetingGroupID;
- public PartDescriptor(BinaryReader reader, long globalOffset) : base(reader)
- {
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 0, reader.ReadUInt32());
- CheckUnknown(reader, 1, reader.ReadUInt32());
- long jumpToMeshType = reader.ReadUInt32();
- reader.BaseStream.Seek(jumpToMeshType - globalOffset, SeekOrigin.Begin);
- partType = (PartType)reader.ReadUInt32();
- ExplosionType = reader.ReadUInt32();
- span = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- centerPoint = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- minXYZ = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- maxXYZ = new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
- targetingGroupID = reader.ReadUInt32();
- }
- }
- public class RootNode : Node
- {
- public RootNode(BinaryReader reader, long globalOffset) : base(reader)
- {
- // validate block header
- // This is type of node is special in that the type is a single word
- CheckUnknown(reader, 2, reader.ReadUInt16());
- // Root node uses a single word for ID
- majorType = MajorType.submodelList;
- minorType = MinorType.generic;
- children = ReadSubNodes(reader, globalOffset, reader.ReadUInt32(), reader.ReadUInt32());
- }
- }
- // Read data from X-Wing / Tie Fighter OPT files
- // Reference: http://www.oocities.org/v_d_d/Alternative_OPT_File_Reading.html
- // http://www.oocities.org/v_d_d/OptSpecs.pdf
- public OptFile(string fileName)
- {
- FileStream missionFile = File.OpenRead(fileName);
- using (var reader = new BinaryReader(missionFile))
- {
- //long signature =
- reader.ReadInt32();
- //long fileLength =
- reader.ReadUInt32(); // Actual file length is this + 8
- globalOffset = reader.ReadUInt32() - 8; // jump offsets don't count the previous two longs
- rootNode = new RootNode(reader, globalOffset);
- }
- }
- //
- public void ShipPartsIntoObject(GameObject parent, int skin = 0)
- {
- //Shader partShader = Resources.Load("Shaders/ShipPart") as Shader;
- Shader partShader = Shader.Find("Standard");
- // Some models seem to share textures between parts by placing them at the top level.
- // So we need to gather all of the textures in the model
- // Making the assumption here that texture names are unique.
- Dictionary<string, Material> materialCache = new Dictionary<string, Material>();
- foreach (Node texture in rootNode.FindAll(MajorType.textrue, MinorType.generic))
- {
- Texture2D tex = ((Texture)texture).GenUnityTexture();
- Material mat = new Material(partShader)
- {
- name = ((Texture)texture).name,
- mainTexture = tex
- };
- materialCache[((Texture)texture).name] = mat;
- }
- foreach (Node shipPart in rootNode.children.FindAll(x => x.GetType() == typeof(Node)))
- {
- GameObject partObj = UnityEngine.Object.Instantiate(Resources.Load("Prefab/ShipPart")) as GameObject;
- // Fetch ship part top level data
- PartDescriptor descriptor = (PartDescriptor)shipPart.children.Find(x => x.GetType() == typeof(PartDescriptor));
- MeshVerticies verts = (MeshVerticies)shipPart.children.Find(x => x.GetType() == typeof(MeshVerticies));
- TextureVerticies vertUV = (TextureVerticies)shipPart.children.Find(x => x.GetType() == typeof(TextureVerticies));
- VertexNormals vertNormals = (VertexNormals)shipPart.children.Find(x => x.GetType() == typeof(VertexNormals));
- // Attach this part to parent.
- UnityEngine.Transform objTransform = partObj.GetComponent<UnityEngine.Transform>();
- objTransform.parent = parent.transform;
- objTransform.localPosition = new Vector3(0, 0, 0);
- objTransform.localRotation = new Quaternion(0, 0, 0, 0);
- if (null != descriptor)
- {
- // Set name from metadata
- partObj.name = descriptor.partType.ToString();
- // Create an object used for targeting.
- GameObject tp = new GameObject("TargetPoint");
- tp.transform.parent = partObj.transform;
- tp.transform.localPosition = CoordinateConverter.MultiplyPoint3x4(descriptor.centerPoint);
- tp.transform.localRotation = new Quaternion(0, 0, 0, 0);
- // Set up box collider
- BoxCollider box = partObj.GetComponent<BoxCollider>();
- box.center = CoordinateConverter.MultiplyPoint3x4(descriptor.centerPoint);
- var span = CoordinateConverter.MultiplyPoint3x4(descriptor.span);
- // Rotation into unity coordinate space results in negative vector components, but Unity gripes about negative BoxCollider sizes.
- span.x = Mathf.Abs(span.x);
- span.y = Mathf.Abs(span.y);
- span.z = Mathf.Abs(span.z);
- box.size = span;
- }
- var lodLevels = (MeshLOD)shipPart.Find(MajorType.normal, MinorType.meshLOD);
- var lods = new LOD[lodLevels.children.Count];
- int lodIndex = 0;
- foreach (Node meshesAndTextures in lodLevels.children)
- {
- GameObject lodObj = new GameObject(partObj.name + "_LOD" + lodIndex);
- lodObj.AddComponent<MeshFilter>();
- lodObj.AddComponent<MeshRenderer>();
- lodObj.transform.parent = partObj.transform;
- lodObj.transform.localPosition = new Vector3(0, 0, 0);
- lodObj.transform.localRotation = new Quaternion(0, 0, 0, 0);
- List<string> matsUsed = new List<string>();
- List<FaceList> faceLists = new List<FaceList>();
- // It seems there is no direct connection between meshes and the textures that go on
- // them besides that the texture preceeds the mesh in this list.
- // So keep track of the last mesh or mesh reference we've seen and apply it to the mesh.
- foreach (Node thing in meshesAndTextures.children)
- {
- if (thing.GetType() == typeof(Texture))
- {
- matsUsed.Add(((Texture)thing).name);
- }
- else if (thing.GetType() == typeof(TextureReferenceByName))
- {
- matsUsed.Add(((TextureReferenceByName)thing).name);
- }
- else if (thing.GetType() == typeof(SkinSelector))
- {
- Node selectedTexture = thing.children[skin];
- if (selectedTexture.GetType() == typeof(Texture))
- {
- matsUsed.Add(((Texture)selectedTexture).name);
- }
- else
- {
- matsUsed.Add(((TextureReferenceByName)selectedTexture).name);
- }
- }
- else if (thing.GetType() == typeof(FaceList))
- {
- faceLists.Add((FaceList)thing);
- }
- else
- {
- Debug.LogWarning("Skipping unexpected node type in model/texture data " + thing.GetType());
- }
- }
- // Look up materials used by name to get references to the actual materials.
- Material[] mats = new Material[matsUsed.Count];
- for (int i = 0; i < matsUsed.Count; i++)
- {
- mats[i] = materialCache[matsUsed[i]];
- }
- lodObj.GetComponent<MeshRenderer>().materials = mats;
- lodObj.GetComponent<MeshFilter>().mesh = GenMesh(faceLists, verts, vertNormals, vertUV);
- var lodThreshold = lodLevels.LODThresholds[lodIndex];
- if (lodThreshold > 0)
- {
- // XvT threshold seems to be based on distance. Unity is based on screen height.
- // 0.1F is a fudge factor based on increased resolution.
- // IE scale down the model at 1/10th of where it would be in XvT.
- lodThreshold = (float)(0.1F / Math.Atan(1 / lodThreshold));
- }
- lods[lodIndex] = new LOD(lodThreshold, new Renderer[] { lodObj.GetComponent<MeshRenderer>() });
- lodIndex++;
- }
- partObj.GetComponent<LODGroup>().SetLODs(lods);
- // Some low LOD meshes comletely replace meshes from other parts.
- // So ensure that all parts cutover at the same distance to avoid flickering.
- partObj.GetComponent<LODGroup>().localReferencePoint = new Vector3(0, 0, 0);
- partObj.GetComponent<LODGroup>().RecalculateBounds();
- // Generate hardpoints
- foreach (Node hardpoint in shipPart.FindAll(MajorType.normal, MinorType.hardpoint))
- {
- var hardpointObj = UnityEngine.Object.Instantiate(Resources.Load("Prefab/Hardpoint")) as GameObject;
- hardpointObj.name = ((Hardpoint)hardpoint).type.ToString();
- hardpointObj.transform.parent = partObj.transform;
- hardpointObj.transform.localPosition = CoordinateConverter.MultiplyPoint3x4(((Hardpoint)hardpoint).coords);
- hardpointObj.transform.localRotation = new Quaternion(0, 0, 0, 0);
- hardpointObj.GetComponent<Game.Hardpoint>().type = ((Hardpoint)hardpoint).type;
- }
- }
- }
- struct VertexSplitTuple
- {
- public int vId, uvId, normId;
- public static bool operator ==(VertexSplitTuple a, VertexSplitTuple b)
- {
- return a.vId == b.vId && a.uvId == b.uvId && a.normId == b.normId;
- }
- public static bool operator !=(VertexSplitTuple a, VertexSplitTuple b)
- {
- return a.vId != b.vId || a.uvId != b.uvId || a.normId != b.normId;
- }
- public override bool Equals(object obj)
- {
- return Equals((VertexSplitTuple)obj);
- }
- public bool Equals(VertexSplitTuple obj)
- {
- return vId == obj.vId && uvId == obj.uvId && normId == obj.normId;
- }
- public override int GetHashCode()
- {
- // overflow ok
- return vId + uvId + normId;
- }
- }
- Mesh GenMesh(List<FaceList> faceLists, MeshVerticies verts, VertexNormals vertNormals, TextureVerticies vertUV)
- {
- var mesh = new Mesh();
- var submeshList = new List<List<int>>();
- // Must remain the same length.
- var meshVerts = new List<Vector3>();
- var meshUV = new List<Vector2>();
- var meshNorms = new List<Vector3>();
- // Unity can only have one normal and UV per vertex.
- // All sub-meshes (triangle lists) in the same mesh have to share the same vertex list.
- // This data uses normal and UV data per face, per vertex.
- // So we have to make a new vertex any time a polygon references a different normal
- // or UV than another polygon.
- var usedVertLookup = new Dictionary<VertexSplitTuple, int>();
- // Build the vert/normal/UV lists
- foreach (FaceList faceList in faceLists)
- {
- var triangles = new List<int>();
- for (int i = 0; i < faceList.count; i++)
- {
- var newVertRefs = new int[4];
- // check each point for need to generate new vertex
- for (int j = 0; j < 4; j++)
- {
- VertexSplitTuple vt;
- vt.vId = faceList.vertexRef[i, j];
- vt.uvId = faceList.UVRef[i, j];
- vt.normId = faceList.vertexNormalRef[i, j];
- // Some faces are triangles instead of quads.
- if (vt.vId == -1 || vt.uvId == -1 || vt.normId == -1)
- {
- newVertRefs[j] = -1;
- continue;
- }
- if (usedVertLookup.ContainsKey(vt))
- {
- // reuse the vertex
- newVertRefs[j] = usedVertLookup[vt];
- }
- else
- {
- // make a new one
- if (vt.vId > verts.verticies.Count - 1)
- {
- Debug.LogError(string.Format("Vert {0}/{4} out of bound {1:X} {2} {3} ", vt.vId, faceList.offsetInFile, i, j, verts.verticies.Count));
- }
- if (vt.normId > vertNormals.normals.Count - 1)
- {
- Debug.LogError(string.Format("Normal {0}/{4} out of bound {1:X} {2} {3} ", vt.normId, faceList.offsetInFile, i, j, vertNormals.normals.Count));
- }
- if (vt.uvId > vertUV.verticies.Length - 1)
- {
- Debug.LogError(string.Format("UV {0}/{4} out of bound {1:X} {2} {3} ", vt.uvId, faceList.offsetInFile, i, j, vertUV.verticies.Length));
- }
- meshVerts.Add(CoordinateConverter.MultiplyPoint3x4(verts.verticies[vt.vId]));
- meshUV.Add(vertUV.verticies[vt.uvId]);
- meshNorms.Add(CoordinateConverter.MultiplyPoint3x4(vertNormals.normals[vt.normId]));
- // Index it so we can find it later.
- usedVertLookup[vt] = meshVerts.Count - 1;
- newVertRefs[j] = usedVertLookup[vt];
- }
- }
- // TODO: Less nieve quad split.
- // First triangle
- triangles.Add(newVertRefs[1]);
- triangles.Add(newVertRefs[0]);
- triangles.Add(newVertRefs[2]);
- // second triangle if a quad
- if (newVertRefs[3] != -1)
- {
- triangles.Add(newVertRefs[3]);
- triangles.Add(newVertRefs[2]);
- triangles.Add(newVertRefs[0]);
- }
- }
- submeshList.Add(triangles);
- }
- mesh.SetVertices(meshVerts);
- mesh.SetUVs(0, meshUV);
- mesh.SetNormals(meshNorms);
- mesh.subMeshCount = submeshList.Count;
- for (int i = 0; i < submeshList.Count; i++)
- {
- mesh.SetTriangles(submeshList[i], i);
- }
- return mesh;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement