Switch-Toolbox/Switch_FileFormatsMain/FileFormats/KCL.cs
2018-11-11 20:51:12 -05:00

533 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Switch_Toolbox;
using System.Windows.Forms;
using Switch_Toolbox.Library;
using KCLExt;
using SFGraphics.GLObjects.Shaders;
using Smash_Forge.Rendering;
using GL_Core.Interfaces;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using Switch_Toolbox.Library.Rendering;
using WeifenLuo.WinFormsUI.Docking;
using GL_Core;
namespace FirstPlugin
{
public class KCL : IFileFormat
{
public bool CanSave { get; set; } = false;
public bool FileIsEdited { get; set; } = false;
public bool FileIsCompressed { get; set; } = false;
public string[] Description { get; set; } = new string[] { "KCL" };
public string[] Extension { get; set; } = new string[] { "*.kcl" };
public string Magic { get; set; } = "";
public CompressionType CompressionType { get; set; } = CompressionType.None;
public byte[] Data { get; set; }
public string FileName { get; set; }
public TreeNode EditorRoot { get; set; }
public bool IsActive { get; set; } = false;
public bool UseEditMenu { get; set; } = false;
public int Alignment { get; set; } = 0;
public string FilePath { get; set; }
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
return types.ToArray();
}
}
public void Load()
{
IsActive = true;
EditorRoot = new KCLRoot(FileName);
}
public void Unload()
{
}
public byte[] Save()
{
return null;
}
public enum GameSet : ushort
{
MarioOdyssey = 0x0,
MarioKart8D = 0x1,
Splatoon2 = 0x2,
}
public enum CollisionType_MarioOdssey : ushort
{
}
public enum CollisionType_MK8D : ushort
{
Road_Default = 0,
Road_Bumpy = 2,
Road_Sand = 4,
Offroad_Sand = 6,
Road_HeavySand = 8,
Road_IcyRoad = 9,
OrangeBooster = 10,
AntiGravityPanel = 11,
Latiku = 16,
Wall5 = 17,
Wall4 = 19,
Wall = 23,
Latiku2 = 28,
Glider = 31,
SidewalkSlope = 32,
Road_Dirt = 33,
Unsolid = 56,
Water = 60,
Road_Stone = 64,
Wall1 = 81,
Wall2 = 84,
FinishLine = 93,
RedFlowerEffect = 95,
Wall3 = 113,
WhiteFlowerEffect = 127,
Road_Metal = 128,
Road_3DS_MP_Piano = 129,
Road_RoyalR_Grass = 134,
TopPillar = 135,
YoshiCuiruit_Grass = 144,
YellowFlowerEffect = 159,
Road_MetalGating = 160,
Road_3DS_MP_Xylophone = 161,
Road_3DS_MP_Vibraphone = 193,
SNES_RR_road = 227,
Offroad_Mud = 230,
Trick = 4096,
BoosterStunt = 4106,
TrickEndOfRamp = 4108,
Trick3 = 4130,
Trick6 = 4160,
Trick4 = 4224,
Trick5 = 8192,
BoostTrick = 8202,
}
public static Shader shader = null;
public class KCLRoot : TreeNodeCustom
{
public KCLRoot(string Name)
{
Text = Name;
}
KCLRendering Renderer;
public override void OnClick(TreeView treeView)
{
//If has models
if (Nodes[0].Nodes.Count > 0)
{
Renderer.LoadViewport();
Renderer.UpdateVertexData();
}
}
}
public class KCLRendering : AbstractGlDrawable
{
// gl buffer objects
int vbo_position;
int ibo_elements;
//Set the game's material list
public GameSet GameMaterialSet = GameSet.MarioKart8D;
public List<KCLModel> models = new List<KCLModel>();
public KCLRendering()
{
GL.GenBuffers(1, out vbo_position);
GL.GenBuffers(1, out ibo_elements);
}
public void UpdateVertexData()
{
DisplayVertex[] Vertices;
int[] Faces;
int poffset = 0;
int voffset = 0;
List<DisplayVertex> Vs = new List<DisplayVertex>();
List<int> Ds = new List<int>();
foreach (KCLModel m in models)
{
m.Offset = poffset * 4;
List<DisplayVertex> pv = m.CreateDisplayVertices();
Vs.AddRange(pv);
Console.WriteLine(m.displayFaceSize);
for (int i = 0; i < m.displayFaceSize; i++)
{
Ds.Add(m.display[i] + voffset);
}
poffset += m.displayFaceSize;
voffset += pv.Count;
}
// Binds
Vertices = Vs.ToArray();
Faces = Ds.ToArray();
// Bind only once!
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position);
GL.BufferData<DisplayVertex>(BufferTarget.ArrayBuffer, (IntPtr)(Vertices.Length * DisplayVertex.Size), Vertices, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements);
GL.BufferData<int>(BufferTarget.ElementArrayBuffer, (IntPtr)(Faces.Length * sizeof(int)), Faces, BufferUsageHint.StaticDraw);
}
string FileName;
public void LoadViewport()
{
if (!EditorIsViewportActive(FirstPlugin.DockedViewport))
{
Viewport.Instance.gL_ControlModern1.MainDrawable = this;
FirstPlugin.DockedViewport.Text = FileName;
FirstPlugin.DockedViewport = Viewport.Instance;
}
}
public bool EditorIsViewportActive(DockContent dock)
{
if (dock is Viewport)
{
dock.Text = FileName;
((Viewport)dock).gL_ControlModern1.MainDrawable = this;
return true;
}
return false;
}
public override void Prepare(GL_ControlModern control)
{
}
public override void Prepare(GL_ControlLegacy control)
{
}
public override void Draw(GL_ControlLegacy control)
{
}
public override void Draw(GL_ControlModern control)
{
shader = OpenTKSharedResources.shaders["KCL"];
shader.UseProgram();
shader.EnableVertexAttributes();
SetRenderSettings(shader);
Matrix4 previewScale = Utils.TransformValues(Vector3.Zero, Vector3.Zero, Runtime.previewScale);
Matrix4 camMat = previewScale * control.mtxCam * control.mtxProj;
shader.SetVector3("difLightDirection", Vector3.TransformNormal(new Vector3(0f, 0f, -1f), camMat.Inverted()).Normalized());
shader.EnableVertexAttributes();
SetRenderSettings(shader);
shader.SetMatrix4x4("modelview", ref camMat);
foreach (KCLModel mdl in models)
{
DrawModel(mdl, shader);
}
shader.DisableVertexAttributes();
}
private void SetRenderSettings(Shader shader)
{
shader.SetBoolToInt("renderVertColor", Runtime.renderVertColor);
shader.SetInt("renderType", (int)Runtime.viewportShading);
}
private void DrawModel(KCLModel m, Shader shader, bool drawSelection = false)
{
if (m.faces.Count <= 3)
return;
SetVertexAttributes(m, shader);
if (m.Checked)
{
if ((m.IsSelected))
{
DrawModelSelection(m, shader);
}
else
{
if (Runtime.RenderModelWireframe)
{
DrawModelWireframe(m, shader);
}
if (Runtime.RenderModels)
{
GL.DrawElements(PrimitiveType.Triangles, m.displayFaceSize, DrawElementsType.UnsignedInt, m.Offset);
}
}
}
}
private static void DrawModelSelection(KCLModel p, Shader shader)
{
//This part needs to be reworked for proper outline. Currently would make model disappear
GL.DrawElements(PrimitiveType.Triangles, p.displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
GL.Enable(EnableCap.StencilTest);
// use vertex color for wireframe color
shader.SetInt("colorOverride", 1);
GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
GL.Enable(EnableCap.LineSmooth);
GL.LineWidth(1.5f);
GL.DrawElements(PrimitiveType.Triangles, p.displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
shader.SetInt("colorOverride", 0);
GL.Enable(EnableCap.DepthTest);
}
private void SetVertexAttributes(KCLModel m, Shader shader)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position);
GL.VertexAttribPointer(shader.GetAttribLocation("vPosition"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 0);
GL.VertexAttribPointer(shader.GetAttribLocation("vNormal"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 12);
GL.VertexAttribPointer(shader.GetAttribLocation("vColor"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 24);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements);
}
private static void DrawModelWireframe(KCLModel p, Shader shader)
{
// use vertex color for wireframe color
shader.SetInt("colorOverride", 1);
GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
GL.Enable(EnableCap.LineSmooth);
GL.LineWidth(1.5f);
GL.DrawElements(PrimitiveType.Triangles, p.displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
shader.SetInt("colorOverride", 0);
}
public MarioKart.MK7.KCL kcl = null;
public void Read(byte[] file_data)
{
try
{
kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.LittleEndian);
}
catch
{
kcl = new MarioKart.MK7.KCL(file_data, Syroot.BinaryData.ByteOrder.BigEndian);
}
/*
int CurModelIndx = 0;
foreach (MarioKart.MK7.KCL.KCLModel mdl in kcl.Models)
{
KCLModel kclmodel = new KCLModel();
kclmodel.Text = "Model " + CurModelIndx;
KclFace[] indicesArray = mdl.Faces;
int ft = 0;
foreach (KclFace f in mdl.Faces)
{
Vertex vtx = new Vertex();
Vertex vtx2 = new Vertex();
Vertex vtx3 = new Vertex();
Vector3 CrossA = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex]));
Vector3 CrossB = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal2Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex]));
Vector3 CrossC = Vector3.Cross(Vec3F_To_Vec3(mdl.Normals[f.Normal3Index]), Vec3F_To_Vec3(mdl.Normals[f.DirectionIndex]));
Vector3 normal_a = Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]);
Vector3 normal_b = Vec3F_To_Vec3(mdl.Normals[f.Normal2Index]);
Vector3 normal_c = Vec3F_To_Vec3(mdl.Normals[f.Normal3Index]);
float result1 = Vector3.Dot(new Vector3(CrossB.X, CrossB.Y, CrossB.Z), (new Vector3(normal_c.X, normal_c.Y, normal_c.Z)));
float result2 = Vector3.Dot(new Vector3(CrossA.X, CrossA.Y, CrossA.Z), (new Vector3(normal_c.X, normal_c.Y, normal_c.Z)));
Vector3 pos = Vec3F_To_Vec3(mdl.Positions[f.PositionIndex]);
Vector3 nrm = Vec3F_To_Vec3(mdl.Normals[f.Normal1Index]);
Vector3 Vertex1 = pos;
Vector3 Vertex2 = pos + CrossB * (f.Length / result1);
Vector3 Vertex3 = pos + CrossA * (f.Length / result2);
vtx.pos = new Vector3(Vertex1.X, Vertex1.Y, Vertex1.Z);
vtx2.pos = new Vector3(Vertex2.X, Vertex2.Y, Vertex2.Z);
vtx3.pos = new Vector3(Vertex3.X, Vertex3.Y, Vertex3.Z);
var dir = Vector3.Cross(Vertex2 - Vertex1, Vertex3 - Vertex1);
var norm = Vector3.Normalize(dir);
vtx.nrm = norm;
vtx2.nrm = norm;
vtx3.nrm = norm;
KCLModel.Face face = new KCLModel.Face();
face.Text = f.CollisionFlags.ToString();
face.MaterialFlag = f.CollisionFlags;
Color color = SetMaterialColor(face);
AllFlags.Add(face.MaterialFlag);
Vector3 ColorSet = new Vector3(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f);
vtx.col = new Vector3(ColorSet);
vtx2.col = new Vector3(ColorSet);
vtx3.col = new Vector3(ColorSet);
kclmodel.faces.Add(ft);
kclmodel.faces.Add(ft + 1);
kclmodel.faces.Add(ft + 2);
ft += 3;
kclmodel.vertices.Add(vtx);
kclmodel.vertices.Add(vtx2);
kclmodel.vertices.Add(vtx3);
}
models.Add(kclmodel);
Nodes.Add(kclmodel);
CurModelIndx++;
}*/
}
//Convert KCL lib vec3 to opentk one so i can use the cross and dot methods
public static Vector3 Vec3F_To_Vec3(Syroot.Maths.Vector3F v)
{
return new Vector3(v.X, v.Y, v.Z);
}
}
public struct DisplayVertex
{
// Used for rendering.
public Vector3 pos;
public Vector3 nrm;
public Vector3 tan;
public Vector3 bit;
public Vector2 uv;
public Vector4 col;
public Vector4 node;
public Vector4 weight;
public Vector2 uv2;
public Vector2 uv3;
public Vector3 pos1;
public Vector3 pos2;
public static int Size = 4 * (3 + 3 + 3 + 3 + 2 + 4 + 4 + 4 + 2 + 2 + 3 + 3);
}
public class KCLModel : STGenericObject
{
public int[] display;
public int Offset; // For Rendering
public int strip = 0x40;
public int displayFaceSize = 0;
public List<DisplayVertex> CreateDisplayVertices()
{
// rearrange faces
display = getDisplayFace().ToArray();
List<DisplayVertex> displayVertList = new List<DisplayVertex>();
if (faces.Count <= 3)
return displayVertList;
foreach (Vertex v in vertices)
{
DisplayVertex displayVert = new DisplayVertex()
{
pos = v.pos,
nrm = v.nrm,
col = v.col,
};
displayVertList.Add(displayVert);
}
return displayVertList;
}
public List<int> getDisplayFace()
{
if ((strip >> 4) == 4)
{
displayFaceSize = faces.Count;
return faces;
}
else
{
List<int> f = new List<int>();
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;
}
}
}
}
}