using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using OpenTK; using OpenTK.Graphics.OpenGL; using Switch_Toolbox.Library; using System.Windows.Forms; namespace Switch_Toolbox.Library.Rendering { public enum STPolygonType : uint { Point = 0, Line = 1, LineStrip = 2, Triangle = 3 } public enum STIndexFormat : uint { UnsignedByte = 0, UInt16 = 1, UInt32 = 2, } public class STGenericObject : GenericObject { public override void OnClick(TreeView treeView) { } } public abstract class STGenericModel : GenericModel { public override void OnClick(TreeView treeView) { } } public abstract class GenericModel : TreeNode { public abstract void OnClick(TreeView treeview); } public class STGenericMaterial : GenericMaterial { public override void OnClick(TreeView treeView) { } } public class STGenericMatTexture { public int mapMode = 0; public int wrapModeS = 1; public int wrapModeT = 1; public int wrapModeW = 1; //Used for 3D textures public int minFilter = 3; public int magFilter = 2; public int mipDetail = 6; public string Name; public TextureType Type; //An enum for the assumed texture type by sampler //Many games have a consistant type of samplers and type. _a0 for diffuse, _n0 for normal, ect public enum TextureType { Unknown = 0, Diffuse = 1, Normal = 2, Specular = 3, Emission = 4, DiffuseLayer2 = 5, TeamColor = 6, Transparency = 7, Shadow = 8, AO = 9, Light = 10, Roughness = 11, Metalness = 12, MRA = 13, //Combined pbr texture HAL uses for KSA SphereMap = 14, SubSurfaceScattering = 15, } public static readonly Dictionary minfilter = new Dictionary() { { 0x00, TextureMinFilter.LinearMipmapLinear}, { 0x01, TextureMinFilter.Nearest}, { 0x02, TextureMinFilter.Linear}, { 0x03, TextureMinFilter.NearestMipmapLinear}, }; public static readonly Dictionary magfilter = new Dictionary() { { 0x00, TextureMagFilter.Linear}, { 0x01, TextureMagFilter.Nearest}, { 0x02, TextureMagFilter.Linear} }; public static Dictionary wrapmode = new Dictionary(){ { 0x00, TextureWrapMode.Repeat}, { 0x01, TextureWrapMode.MirroredRepeat}, { 0x02, TextureWrapMode.ClampToEdge}, { 0x03, TextureWrapMode.MirroredRepeat}, }; } public abstract class GenericMaterial : TreeNode { public abstract void OnClick(TreeView treeview); public List TextureMaps = new List(); } public abstract class GenericObject : TreeNode { public abstract void OnClick(TreeView treeview); public bool HasPos; public bool HasNrm; public bool HasUv0; public bool HasUv1; public bool HasUv2; public bool HasWeights; public bool HasIndices; public bool HasBitans; public bool HasTans; public bool HasVertColors; public int MaxSkinInfluenceCount; public string ObjectName; public int BoneIndex; public int MaterialIndex; public int VertexBufferIndex; public int DisplayLODIndex; public int Offset; public int GetMaxSkinInfluenceCount() { return vertices.Max(t => t.boneIds.Count); } public Vector3 GetOrigin() { Vector3 pos = Vector3.Zero; foreach (Vertex vert in vertices) { } return pos; } public List bones = new List(); public List weightsT = new List(); public List boneList = new List(); public List vertices = new List(); public List lodMeshes = new List(); public class LOD_Mesh { public STPolygonType PrimitiveType = STPolygonType.Triangle; public STIndexFormat IndexFormat = STIndexFormat.UInt16; public uint FirstVertex; public List subMeshes = new List(); public class SubMesh { public uint size; public uint offset; } public void GenerateSubMesh() { subMeshes.Clear(); SubMesh subMesh = new SubMesh(); subMesh.offset = 0; subMesh.size = (uint)faces.Count; subMeshes.Add(subMesh); } public int index = 0; public int strip = 0x40; public int displayFaceSize = 0; public List faces = new List(); public override string ToString() { return "LOD Mesh " + index; } public List getDisplayFace() { if ((strip >> 4) == 4) { displayFaceSize = faces.Count; return faces; } else { List f = new List(); int startDirection = 1; int p = 0; int f1 = faces[p++]; int f2 = faces[p++]; int faceDirection = startDirection; int f3; do { f3 = faces[p++]; if (f3 == 0xFFFF) { f1 = faces[p++]; f2 = faces[p++]; faceDirection = startDirection; } else { faceDirection *= -1; if ((f1 != f2) && (f2 != f3) && (f3 != f1)) { if (faceDirection > 0) { f.Add(f3); f.Add(f2); f.Add(f1); } else { f.Add(f2); f.Add(f3); f.Add(f1); } } f1 = f2; f2 = f3; } } while (p < faces.Count); displayFaceSize = f.Count; return f; } } } public List faces = new List(); #region Methods public void FlipUvsVertical() { foreach (Vertex v in vertices) { v.uv0 = new Vector2(v.uv0.X, 1 - v.uv0.Y); } } public void FlipUvsHorizontal() { foreach (Vertex v in vertices) { v.uv0 = new Vector2(1 - v.uv0.X, v.uv0.Y); } } public void CalculateTangentBitangent() { List f = lodMeshes[DisplayLODIndex].getDisplayFace(); Vector3[] tanArray = new Vector3[vertices.Count]; Vector3[] bitanArray = new Vector3[vertices.Count]; CalculateTanBitanArrays(f, tanArray, bitanArray); ApplyTanBitanArray(tanArray, bitanArray); } private void ApplyTanBitanArray(Vector3[] tanArray, Vector3[] bitanArray) { for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; Vector3 newTan = tanArray[i]; Vector3 newBitan = bitanArray[i]; // The tangent and bitangent should be orthogonal to the normal. // Bitangents are not calculated with a cross product to prevent flipped shading with mirrored normal maps. v.tan = new Vector4(Vector3.Normalize(newTan - v.nrm * Vector3.Dot(v.nrm, newTan)), 1); v.bitan = new Vector4(Vector3.Normalize(newBitan - v.nrm * Vector3.Dot(v.nrm, newBitan)), 1); v.bitan *= -1; } } private void CalculateTanBitanArrays(List faces, Vector3[] tanArray, Vector3[] bitanArray) { for (int i = 0; i < lodMeshes[DisplayLODIndex].displayFaceSize; i += 3) { Vertex v1 = vertices[faces[i]]; Vertex v2 = vertices[faces[i + 1]]; Vertex v3 = vertices[faces[i + 2]]; bool UseUVLayer2 = false; float x1 = v2.pos.X - v1.pos.X; float x2 = v3.pos.X - v1.pos.X; float y1 = v2.pos.Y - v1.pos.Y; float y2 = v3.pos.Y - v1.pos.Y; float z1 = v2.pos.Z - v1.pos.Z; float z2 = v3.pos.Z - v1.pos.Z; float s1, s2, t1, t2; if (UseUVLayer2) { s1 = v2.uv1.X - v1.uv1.X; s2 = v3.uv1.X - v1.uv1.X; t1 = v2.uv1.Y - v1.uv1.Y; t2 = v3.uv1.Y - v1.uv1.Y; } else { s1 = v2.uv0.X - v1.uv0.X; s2 = v3.uv0.X - v1.uv0.X; t1 = v2.uv0.Y - v1.uv0.Y; t2 = v3.uv0.Y - v1.uv0.Y; } float div = (s1 * t2 - s2 * t1); float r = 1.0f / div; // Fix +/- infinity from division by 0. if (r == float.PositiveInfinity || r == float.NegativeInfinity) r = 1.0f; float sX = t2 * x1 - t1 * x2; float sY = t2 * y1 - t1 * y2; float sZ = t2 * z1 - t1 * z2; Vector3 s = new Vector3(sX, sY, sZ) * r; float tX = s1 * x2 - s2 * x1; float tY = s1 * y2 - s2 * y1; float tZ = s1 * z2 - s2 * z1; Vector3 t = new Vector3(tX, tY, tZ) * r; // Prevents black tangents or bitangents due to having vertices with the same UV coordinates. float delta = 0.00075f; bool sameU, sameV; if (UseUVLayer2) { sameU = (Math.Abs(v1.uv1.X - v2.uv1.X) < delta) && (Math.Abs(v2.uv1.X - v3.uv1.X) < delta); sameV = (Math.Abs(v1.uv1.Y - v2.uv1.Y) < delta) && (Math.Abs(v2.uv1.Y - v3.uv1.Y) < delta); } else { sameU = (Math.Abs(v1.uv0.X - v2.uv0.X) < delta) && (Math.Abs(v2.uv0.X - v3.uv0.X) < delta); sameV = (Math.Abs(v1.uv0.Y - v2.uv0.Y) < delta) && (Math.Abs(v2.uv0.Y - v3.uv0.Y) < delta); } if (sameU || sameV) { // Let's pick some arbitrary tangent vectors. s = new Vector3(1, 0, 0); t = new Vector3(0, 1, 0); } // Average tangents and bitangents. tanArray[faces[i]] += s; tanArray[faces[i + 1]] += s; tanArray[faces[i + 2]] += s; bitanArray[faces[i]] += t; bitanArray[faces[i + 1]] += t; bitanArray[faces[i + 2]] += t; } } public void SmoothNormals() { Vector3[] normals = new Vector3[vertices.Count]; List f = lodMeshes[DisplayLODIndex].getDisplayFace(); for (int i = 0; i < lodMeshes[DisplayLODIndex].displayFaceSize; i += 3) { Vertex v1 = vertices[f[i]]; Vertex v2 = vertices[f[i + 1]]; Vertex v3 = vertices[f[i + 2]]; Vector3 nrm = CalculateNormal(v1, v2, v3); normals[f[i + 0]] += nrm; normals[f[i + 1]] += nrm; normals[f[i + 2]] += nrm; } for (int i = 0; i < normals.Length; i++) vertices[i].nrm = normals[i].Normalized(); // Compare each vertex with all the remaining vertices. This might skip some. for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; for (int j = i + 1; j < vertices.Count; j++) { Vertex v2 = vertices[j]; if (v == v2) continue; float dis = (float)Math.Sqrt(Math.Pow(v.pos.X - v2.pos.X, 2) + Math.Pow(v.pos.Y - v2.pos.Y, 2) + Math.Pow(v.pos.Z - v2.pos.Z, 2)); if (dis <= 0f) // Extra smooth { Vector3 nn = ((v2.nrm + v.nrm) / 2).Normalized(); v.nrm = nn; v2.nrm = nn; } } } } public void CalculateNormals() { Vector3[] normals = new Vector3[vertices.Count]; for (int i = 0; i < normals.Length; i++) normals[i] = new Vector3(0, 0, 0); List f = lodMeshes[DisplayLODIndex].getDisplayFace(); for (int i = 0; i < lodMeshes[DisplayLODIndex].displayFaceSize; i += 3) { Vertex v1 = vertices[f[i]]; Vertex v2 = vertices[f[i + 1]]; Vertex v3 = vertices[f[i + 2]]; Vector3 nrm = CalculateNormal(v1, v2, v3); normals[f[i + 0]] += nrm * (nrm.Length / 2); normals[f[i + 1]] += nrm * (nrm.Length / 2); normals[f[i + 2]] += nrm * (nrm.Length / 2); } for (int i = 0; i < normals.Length; i++) vertices[i].nrm = normals[i].Normalized(); } private Vector3 CalculateNormal(Vertex v1, Vertex v2, Vertex v3) { Vector3 U = v2.pos - v1.pos; Vector3 V = v3.pos - v1.pos; // Don't normalize here, so surface area can be calculated. return Vector3.Cross(U, V); } public void SetVertexColor(Vector4 intColor) { // (127, 127, 127, 255) is white. foreach (Vertex v in vertices) { v.col = intColor; } } #endregion } public class Vertex { public Vector3 pos = new Vector3(0); public Vector3 nrm = new Vector3(0); public Vector4 col = new Vector4(1); public Vector2 uv0 = new Vector2(0); public Vector2 uv1 = new Vector2(0); public Vector2 uv2 = new Vector2(0); public Vector4 tan = new Vector4(0); public Vector4 bitan = new Vector4(0); public List boneIds = new List(); public List boneWeights = new List(); public List boneNames = new List(); public List boneList = new List(); public class Bone { public string Name; public int Index; public bool HasWeights; public List weights = new List(); } public class BoneWeight { public float weight; } //For vertex morphing public Vector3 pos1 = new Vector3(); public Vector3 pos2 = new Vector3(); } }