using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace OpenVIII.Battle.Dat
{
///
/// Section 2e: Triangle
///
///
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = ByteSize)]
public class Triangle
{
#region Fields
[field: FieldOffset(10)]
public readonly ushort TexUnk;
[field: FieldOffset(14)]
public readonly ushort U;
[field: FieldOffset(6)]
public readonly UV Vta;
[field: FieldOffset(8)]
public readonly UV Vtb;
[field: FieldOffset(12)]
public readonly UV Vtc;
private const int ByteSize = 16;
[field: FieldOffset(0)]
private readonly ushort _a;
[field: FieldOffset(2)]
private readonly ushort _b;
[field: FieldOffset(4)]
private readonly ushort _c;
#endregion Fields
#region Constructors
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private Triangle()
{ }
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private Triangle(BinaryReader br)
{
_a = br.ReadUInt16();//0
_b = br.ReadUInt16();//2
_c = br.ReadUInt16();//4
Vta = UV.CreateInstance(br);//6
Vtb = UV.CreateInstance(br);//8
TexUnk = br.ReadUInt16();//10
Vtc = UV.CreateInstance(br);//12
U = br.ReadUInt16();//14
}
#endregion Constructors
#region Properties
public static byte Count => 3;
public ushort A => (ushort)(_a & 0xFFF);
public ushort B => (ushort)(_b & 0xFFF);
public ushort C => (ushort)(_c & 0xFFF);
public byte TextureIndex => (byte)((TexUnk >> 6) & 0b111);
#endregion Properties
#region Indexers
public byte this[int i]
{
get
{
switch (i)
{
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
}
throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
}
}
#endregion Indexers
#region Methods
public static Triangle CreateInstance(BinaryReader br) => Extended.ByteArrayToClass(br.ReadBytes(ByteSize));
public static IReadOnlyList CreateInstances(BinaryReader br, ushort count) => Enumerable.Range(0, count).Select(_ => CreateInstance(br)).ToList().AsReadOnly();
public VertexPositionTexture[] GenerateVPT(List vertices, Quaternion rotation,
Vector3 translationPosition, TextureHandler preVarTex)
{
var tempVPT = new VertexPositionTexture[Count];
VertexPositionTexture GetVPT(Triangle triangle, byte i)
{
Vector3 GetVertex(ref Triangle refTriangle, byte j)
{
return DatFile.TransformVertex(vertices[refTriangle.GetIndex(j)], translationPosition, rotation);
}
// begin stupid hacks to fix Rinoa and Ward in remaster. might break when going above 3X upscale
// if you choose to upscale more crop off dead bottom 128 pixels from SE's render.
// also hack adjusts aspect ratio to handle the eye closing animation frames.
// in remaster you just need to shift all the uv's to the right when eyes close.
var (x, y) = preVarTex.ScaleFactor;
float height;
if (x > y)
height = (float)(preVarTex.Height / Math.Floor(y));
else
height = (float)(preVarTex.Height / Math.Floor(x));
var (newAR, oldAR) = ((float)preVarTex.Width / preVarTex.Height,
preVarTex.ClassicWidth / height);
var width = Math.Abs(newAR - oldAR) < float.Epsilon ? preVarTex.ClassicWidth : height * newAR;
// end hack.
return new VertexPositionTexture(GetVertex(ref triangle, i),
triangle.GetUV(i).ToVector2(width, height));
}
tempVPT[0] = GetVPT(this, this[0]);
tempVPT[1] = GetVPT(this, this[1]);
tempVPT[2] = GetVPT(this, this[2]);
return tempVPT;
}
public ushort GetIndex(int i)
{
switch (i)
{
case 0:
return C; //for some reason C is first in triangle
case 1:
return A;
case 2:
return B;
}
throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
}
public UV GetUV(int i)
{
switch (i)
{
case 0:
return Vta;
case 1:
return Vtb;
case 2:
return Vtc;
}
throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
}
#endregion Methods
}
}