using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using Syroot.NintenTools.NSW.Bfres;
using Syroot.NintenTools.NSW.Bfres.Helpers;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System.Windows.Forms;
using GL_EditorFramework.GL_Core;
using GL_EditorFramework.Interfaces;
using Switch_Toolbox.Library;
using Switch_Toolbox.Library.Rendering;
using Switch_Toolbox.Library.IO;
using Switch_Toolbox.Library.Forms;
using ResU = Syroot.NintenTools.Bfres;
using Bfres.Structs;
using SF = SFGraphics.GLObjects.Shaders;

namespace FirstPlugin
{
    public class BFRESRender : AbstractGlDrawable
    {
        private bool Disposing = false;

        public Matrix4 ModelTransform = Matrix4.Identity;
        Vector3 position = new Vector3(0);

        public static Vector4 hoverColor = new Vector4(1);
        public static Vector4 selectColor = new Vector4(1);

        protected bool Selected = false;
        public bool Hovered = false;

        public bool IsSelected() => Selected;

        // gl buffer objects
        int vbo_position;
        int ibo_elements;

        private List<FMDL> _models = new List<FMDL>();
        public List<FMDL> models
        {
            get
            {
                return _models;
            }
        }

        public void UpdateModelList()
        {
            _models.Clear();
            foreach (var node in ResFileNode.Nodes)
            {
                if (node is BFRESGroupNode &&
                    ((BFRESGroupNode)node).Type == BRESGroupType.Models)
                {
                    foreach (FMDL mdl in ((BFRESGroupNode)node).Nodes)
                        _models.Add(mdl);
                }
            }
        }

        public BFRES ResFileNode;

        public BFRESRender()
        {

        }
        private void GenerateBuffers()
        {
            GL.GenBuffers(1, out vbo_position);
            GL.GenBuffers(1, out ibo_elements);

            TransformBones();

            UpdateVertexData();
            UpdateTextureMaps();
        }

        public void Destroy()
        {
            bool buffersWereInitialized = ibo_elements != 0 && vbo_position != 0;

            if (!buffersWereInitialized)
                return;

            GL.DeleteBuffer(vbo_position);
            GL.DeleteBuffer(ibo_elements);

            Disposing = true;
        }

        private void TransformBones()
        {
            for (int mdl = 0; mdl < models.Count; mdl++)
            {
                for (int b = 0; b < models[mdl].Skeleton.bones.Count; b++)
                {
                    models[mdl].Skeleton.bones[b].ModelMatrix = ModelTransform;
                }
            }
        }

        #region Rendering


    //    public ShaderProgram BotwShaderProgram;
    //    public ShaderProgram normalsShaderProgram;
    //    public ShaderProgram debugShaderProgram;
     //   public ShaderProgram pbrShaderProgram;
      //  public ShaderProgram defaultShaderProgram;
     //   public ShaderProgram solidColorShaderProgram;

        public override void Prepare(GL_ControlModern control)
        {
    /*        string pathFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES.frag";
            string pathVert = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES.vert";


            string pathBotwFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES_Botw.frag";

            string pathPbrFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES_PBR.frag";

            string pathBfresUtiltyFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES_utility.frag";
            string pathBfresTurboShadow = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRESTurboShadow.frag";

            string pathUtiltyFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Utility") + "\\Utility.frag";



            string pathDebugFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\BFRES_Debug.frag";
            string pathNormalsFrag = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\Normals.frag";
            string pathNormalsVert = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\Normals.vert";
            string pathNormalGeom = System.IO.Path.Combine(Runtime.ExecutableDir, "Shader", "Bfres") + "\\Normals.geom";

            var defaultFrag = new FragmentShader(System.IO.File.ReadAllText(pathFrag));
            var defaultVert = new VertexShader(System.IO.File.ReadAllText(pathVert));

            var BotwtFrag = new FragmentShader(System.IO.File.ReadAllText(pathBotwFrag));

            var shadowMapAGL = new FragmentShader(System.IO.File.ReadAllText(pathBfresTurboShadow));

            var PbrFrag = new FragmentShader(System.IO.File.ReadAllText(pathPbrFrag));

            var debugFrag = new FragmentShader(System.IO.File.ReadAllText(pathDebugFrag));
            var normalsVert = new VertexShader(System.IO.File.ReadAllText(pathNormalsVert));
            var normalsFrag = new FragmentShader(System.IO.File.ReadAllText(pathNormalsFrag));
            var normalsGeom = new GeomertyShader(System.IO.File.ReadAllText(pathNormalGeom));

            var bfresUtiltyFrag = new FragmentShader(System.IO.File.ReadAllText(pathBfresUtiltyFrag));
            var utiltyFrag = new FragmentShader(System.IO.File.ReadAllText(pathUtiltyFrag));

            var solidColorFrag = new FragmentShader(
      @"#version 330
				uniform vec4 color;
				out vec4 fragColor;

				void main(){
					fragColor = color;
				}");

            var solidColorVert = new VertexShader(
          @"#version 330
                in vec3 vPosition;
                in vec3 vNormal;
                in vec3 vColor;

				uniform mat4 mtxMdl;
				uniform mat4 mtxCam;

                out vec3 normal;
                out vec3 color;
                out vec3 position;

				void main(){
                    normal = vNormal;
                    color = vColor;
	                position = vPosition;

                    gl_Position = mtxMdl * mtxCam * vec4(vPosition.xyz, 1.0);
				}");

            defaultShaderProgram = new ShaderProgram(new Shader[] { bfresUtiltyFrag, utiltyFrag, defaultFrag, defaultVert, utiltyFrag, shadowMapAGL });
            BotwShaderProgram = new ShaderProgram(new Shader[] { bfresUtiltyFrag, utiltyFrag, BotwtFrag, defaultVert, utiltyFrag, shadowMapAGL });

            normalsShaderProgram = new ShaderProgram(new Shader[] { normalsFrag, normalsVert, normalsGeom });
            debugShaderProgram = new ShaderProgram(new Shader[] { bfresUtiltyFrag, utiltyFrag, debugFrag, defaultVert, utiltyFrag, shadowMapAGL });
            pbrShaderProgram = new ShaderProgram(new Shader[] { bfresUtiltyFrag, utiltyFrag, PbrFrag, defaultVert, shadowMapAGL });
            solidColorShaderProgram = new ShaderProgram(solidColorFrag, solidColorVert);*/
        }

        public override void Prepare(GL_ControlLegacy control)
        {

        }

        public override void Draw(GL_ControlLegacy control, Pass pass)
        {
            if (Disposing) return;

            bool buffersWereInitialized = ibo_elements != 0 && vbo_position != 0;
            if (!buffersWereInitialized)
                GenerateBuffers();

            if (!Runtime.OpenTKInitialized)
                return;

            Matrix4 mvpMat = control.ModelMatrix * control.CameraMatrix * control.ProjectionMatrix;

            Matrix4 invertedCamera = Matrix4.Identity;
            if (invertedCamera.Determinant != 0)
                invertedCamera = mvpMat.Inverted();

            Vector3 lightDirection = new Vector3(0f, 0f, -1f);
            Vector3  difLightDirection  = Vector3.TransformNormal(lightDirection, invertedCamera).Normalized();

            GL.Enable(EnableCap.Texture2D);
            GL.Enable(EnableCap.DepthTest);

            foreach (var model in models)
            {
                foreach (var shape in model.shapes)
                {
                    if (Runtime.RenderModels && model.Checked && shape.Checked)
                    {
                        var mat = shape.GetMaterial();

                        List<int> faces = shape.lodMeshes[shape.DisplayLODIndex].getDisplayFace();

                        GL.Begin(PrimitiveType.Triangles);
                        foreach (var index in faces)
                        {
                            Vertex vert = shape.vertices[index];
                            float normal = Vector3.Dot(difLightDirection, vert.nrm) * 0.5f + 0.5f;
                            GL.Color3(new Vector3(normal));
                            GL.TexCoord2(vert.uv0);
                            GL.Vertex3(vert.pos);
                        }
                        GL.End();
                    }
                }
            }

            GL.Enable(EnableCap.Texture2D);
        }

        public void CenterCamera(GL_ControlModern control)
        {
            if (!Runtime.FrameCamera)
                return;

            var spheres = new List<Vector4>();
            for (int mdl = 0; mdl < models.Count; mdl++)
            {
                for (int shp = 0; shp < models[mdl].shapes.Count; shp++)
                {
                    var vertexPositions = models[mdl].shapes[shp].vertices.Select(x => x.pos).Distinct();
                    spheres.Add(control.GenerateBoundingSphere(vertexPositions));
                }
            }

            control.FrameSelect(spheres);
        }

        public static Vector4 GenerateBoundingSphere(IEnumerable<Vector4> boundingSpheres)
        {
            // The initial max/min should be the first point.
            Vector3 min = boundingSpheres.FirstOrDefault().Xyz - new Vector3(boundingSpheres.FirstOrDefault().W);
            Vector3 max = boundingSpheres.FirstOrDefault().Xyz + new Vector3(boundingSpheres.FirstOrDefault().W);

            // Calculate the end points using the center and radius
            foreach (var sphere in boundingSpheres)
            {
                min = Vector3.ComponentMin(min, sphere.Xyz - new Vector3(sphere.W));
                max = Vector3.ComponentMax(max, sphere.Xyz + new Vector3(sphere.W));
            }

            return GetBoundingSphereFromSpheres(min, max);
        }
        private static Vector4 GetBoundingSphereFromSpheres(Vector3 min, Vector3 max)
        {
            Vector3 lengths = max - min;
            float maxLength = Math.Max(lengths.X, Math.Max(lengths.Y, lengths.Z));
            Vector3 center = (max + min) / 2.0f;
            float radius = maxLength / 2.0f;
            return new Vector4(center, radius);
        }

        public override void Draw(GL_ControlModern control, Pass pass) {
            DrawBfres(control, pass);
        }

 /*       public override void Draw(GL_ControlModern control, Pass pass, EditorSceneBase editorScene) {
            DrawBfres(control, pass);
        }*/

        private void DrawBfres(GL_ControlModern control, Pass pass)
        {
            if (!Runtime.OpenTKInitialized || pass == Pass.TRANSPARENT || Disposing)
                return;

            bool buffersWereInitialized = ibo_elements != 0 && vbo_position != 0;
            if (!buffersWereInitialized)
                GenerateBuffers();

            if (Hovered == true)
                throw new Exception("model selected");

            //Temporarily revert to using this shader system as it is easy to port back
            //This is much quicker. Will change after shaders are handled faster
            SF.Shader shader = OpenTKSharedResources.shaders["BFRES"];

            if (Runtime.EnablePBR)
                shader = OpenTKSharedResources.shaders["BFRES_PBR"];

            if (models.Count > 0)
            {
                if (models[0].shapes.Count > 0)
                {
                    if (models[0].shapes[0].GetMaterial().shaderassign.ShaderModel == "uking_mat")
                    {
                        shader = OpenTKSharedResources.shaders["BFRES_Botw"];

                        //Botw uses small models so lower the bone size
                        Runtime.bonePointSize = 0.040f;
                    }
                }
            }

            if (Runtime.viewportShading != Runtime.ViewportShading.Default)
                shader = OpenTKSharedResources.shaders["BFRES_Debug"];

            if (Runtime.viewportShading == Runtime.ViewportShading.Lighting && Runtime.EnablePBR)
                shader = OpenTKSharedResources.shaders["BFRES_PBR"];

            shader.UseProgram();
            control.UpdateModelMatrix(Matrix4.CreateScale(Runtime.previewScale) * ModelTransform);

            Matrix4 camMat = control.CameraMatrix;
            Matrix4 mdlMat = control.ModelMatrix;
            Matrix4 projMat = control.ProjectionMatrix;
            Matrix4 computedCamMtx = camMat * projMat;
            Matrix4 mvpMat = control.ModelMatrix * control.CameraMatrix * control.ProjectionMatrix;

            Matrix4 sphereMatrix = mvpMat;

            if (sphereMatrix.Determinant != 0)
                sphereMatrix.Invert();

            sphereMatrix.Transpose();
            shader.SetMatrix4x4("sphereMatrix", ref sphereMatrix);

            shader.SetMatrix4x4("mtxCam", ref computedCamMtx);
            shader.SetMatrix4x4("mtxMdl", ref mdlMat);

            SetRenderSettings(shader);

            Vector4 pickingColor = control.NextPickingColor();

            shader.SetVector3("difLightColor", new Vector3(1));
            shader.SetVector3("ambLightColor", new Vector3(1));

            Matrix4 invertedCamera = Matrix4.Identity;
            if (invertedCamera.Determinant != 0)
                invertedCamera = mvpMat.Inverted();

            Vector3 lightDirection = new Vector3(0f, 0f, -1f);

            //Todo. Maybe change direction via AAMP file (configs shader data)
            shader.SetVector3("specLightDirection", Vector3.TransformNormal(lightDirection, invertedCamera).Normalized());
            shader.SetVector3("difLightDirection", Vector3.TransformNormal(lightDirection, invertedCamera).Normalized());


            GL.Enable(EnableCap.AlphaTest);
            GL.AlphaFunc(AlphaFunction.Gequal, 0.1f);

            DrawModels(shader, control);

            if (Runtime.renderNormalsPoints)
            {
                shader = OpenTKSharedResources.shaders["BFRES_Normals"];
                shader.UseProgram();

                shader.SetMatrix4x4("camMtx", ref camMat);
                shader.SetMatrix4x4("mtxProj", ref projMat);
                shader.SetMatrix4x4("mtxCam", ref computedCamMtx);
                shader.SetMatrix4x4("mtxMdl", ref mdlMat);

                shader.SetFloat("normalsLength", Runtime.normalsLineLength);

                DrawModels(shader, control);
            }

            GL.UseProgram(0);
            GL.Disable(EnableCap.DepthTest);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.CullFace);
        }

        private void DrawModels(SF.Shader shader, GL_ControlModern control)
        {
            shader.EnableVertexAttributes();
            for (int m = 0; m < models.Count; m++)
            {
                if (models[m].Checked)
                {
                    List<FSHP> opaque = new List<FSHP>();
                    List<FSHP> transparent = new List<FSHP>();

                    for (int shp = 0; shp < models[m].shapes.Count; shp++)
                    {
                        if (models[m].shapes[shp].GetMaterial().isTransparent)
                            transparent.Add(models[m].shapes[shp]);
                        else
                            opaque.Add(models[m].shapes[shp]);
                    }

                    for (int shp = 0; shp < transparent.Count; shp++)
                    {
                        DrawModel(transparent[shp], models[m], shader, models[m].IsSelected);
                    }

                    for (int shp = 0; shp < opaque.Count; shp++)
                    {
                        DrawModel(opaque[shp], models[m], shader, models[m].IsSelected);
                    }
                }
            }

            shader.DisableVertexAttributes();
        }

        public void DepthSortMeshes(Vector3 cameraPosition)
        {
            foreach (FMDL fmdl in models)
            {
                List<FSHP> unsortedMeshes = new List<FSHP>();

                foreach (FSHP m in fmdl.shapes)
                {
                    m.sortingDistance = m.CalculateSortingDistance(cameraPosition);
                    unsortedMeshes.Add(m);
                }

                fmdl.depthSortedMeshes = unsortedMeshes.OrderBy(o => (o.sortingDistance)).ToList();
            }


            // Order by the distance from the camera to the closest point on the bounding sphere. 
            // Positive values are usually closer to camera. Negative values are usually farther away. 
        }

        private void SetRenderSettings(SF.Shader shader)
        {
            shader.SetBoolToInt("renderVertColor", Runtime.renderVertColor);
            shader.SetBoolToInt("useNormalMap", Runtime.useNormalMap);
            shader.SetBoolToInt("renderR", Runtime.renderR);
            shader.SetBoolToInt("renderG", Runtime.renderG);
            shader.SetBoolToInt("renderB", Runtime.renderB);
            shader.SetBoolToInt("renderAlpha", Runtime.renderAlpha);

            shader.SetInt("renderType", (int)Runtime.viewportShading);
            shader.SetInt("uvChannel", (int)Runtime.uvChannel);

            shader.SetBoolToInt("renderFog", Runtime.renderFog);

            shader.SetBoolToInt("renderDiffuse", Runtime.renderDiffuse);
            shader.SetBoolToInt("renderSpecular", Runtime.renderSpecular);
            shader.SetBoolToInt("renderFresnel", Runtime.renderFresnel);
        }
        private static void SetDefaultTextureAttributes(FMAT mat, SF.Shader shader)
        {
            shader.SetBoolToInt("HasDiffuse", mat.HasDiffuseMap);
            shader.SetBoolToInt("HasDiffuseLayer", mat.HasDiffuseLayer);
            shader.SetBoolToInt("HasNormalMap", mat.HasNormalMap);
            shader.SetBoolToInt("HasEmissionMap", mat.HasEmissionMap);
            shader.SetBoolToInt("HasLightMap", mat.HasLightMap);
            shader.SetBoolToInt("HasShadowMap", mat.HasShadowMap);
            shader.SetBoolToInt("HasSpecularMap", mat.HasSpecularMap);
            shader.SetBoolToInt("HasTeamColorMap", mat.HasTeamColorMap);
            shader.SetBoolToInt("HasSphereMap", mat.HasSphereMap);
            shader.SetBoolToInt("HasSubSurfaceScatteringMap", mat.HasSubSurfaceScatteringMap);

            //Unused atm untill I do PBR shader
            shader.SetBoolToInt("HasMetalnessMap", mat.HasMetalnessMap);
            shader.SetBoolToInt("HasRoughnessMap", mat.HasRoughnessMap);
            shader.SetBoolToInt("HasMRA", mat.HasMRA);
        }
        private static void SetBoneUniforms(SF.Shader shader, FMDL fmdl, FSHP fshp)
        {
            for (int i = 0; i < fmdl.Skeleton.Node_Array.Length; i++)
            {
                GL.Uniform1(GL.GetUniformLocation(shader.Id, String.Format("boneIds[{0}]", i)), fmdl.Skeleton.Node_Array[i]);

                Matrix4 transform = fmdl.Skeleton.bones[fmdl.Skeleton.Node_Array[i]].invert * fmdl.Skeleton.bones[fmdl.Skeleton.Node_Array[i]].Transform;
                GL.UniformMatrix4(GL.GetUniformLocation(shader.Id, String.Format("bones[{0}]", i)), false, ref transform);
            }
        }

        private static void SetTextureUniforms(FMAT mat, FSHP m, SF.Shader shader)
        {
            SetDefaultTextureAttributes(mat, shader);

            GL.ActiveTexture(TextureUnit.Texture0 + 1);
            GL.BindTexture(TextureTarget.Texture2D, RenderTools.defaultTex.RenderableTex.TexID);

            GL.ActiveTexture(TextureUnit.Texture11);
            GL.Uniform1(shader.GetUniformLocation("weightRamp1"), 11);
            GL.BindTexture(TextureTarget.Texture2D, RenderTools.BoneWeightGradient.Id);

            GL.ActiveTexture(TextureUnit.Texture12);
            GL.Uniform1(shader.GetUniformLocation("weightRamp2"), 12);
            GL.BindTexture(TextureTarget.Texture2D, RenderTools.BoneWeightGradient2.Id);

            GL.Uniform1(shader.GetUniformLocation("debugOption"), 2);


            GL.ActiveTexture(TextureUnit.Texture10);
            GL.Uniform1(shader.GetUniformLocation("UVTestPattern"), 10);
            GL.BindTexture(TextureTarget.Texture2D, RenderTools.uvTestPattern.RenderableTex.TexID);

            GL.Uniform1(shader.GetUniformLocation("normalMap"), 0);
            GL.Uniform1(shader.GetUniformLocation("BakeShadowMap"), 0);

            shader.SetInt("RedChannel", 0);
            shader.SetInt("GreenChannel", 1);
            shader.SetInt("BlueChannel", 2);
            shader.SetInt("AlphaChannel", 3);

            LoadPBRMaps(shader);

            for (int t = 0; t < mat.TextureMaps.Count; t++)
            {
                MatTexture matex = (MatTexture)mat.TextureMaps[t];

                if (matex.Type == MatTexture.TextureType.Diffuse)
                    TextureUniform(shader, mat, mat.HasDiffuseMap, "DiffuseMap", matex);
                else if (matex.Type == MatTexture.TextureType.Normal)
                    TextureUniform(shader, mat, mat.HasNormalMap, "NormalMap", matex);
                else if (matex.Type == MatTexture.TextureType.Emission)
                    TextureUniform(shader, mat, mat.HasEmissionMap, "EmissionMap", matex);
                else if (matex.Type == MatTexture.TextureType.Specular)
                    TextureUniform(shader, mat, mat.HasSpecularMap, "SpecularMap", matex);
                else if (matex.Type == MatTexture.TextureType.Shadow)
                    TextureUniform(shader, mat, mat.HasShadowMap, "BakeShadowMap", matex);
                else if (matex.Type == MatTexture.TextureType.Light)
                    TextureUniform(shader, mat, mat.HasLightMap, "BakeLightMap", matex);
                else if (matex.Type == MatTexture.TextureType.Metalness)
                    TextureUniform(shader, mat, mat.HasMetalnessMap, "MetalnessMap", matex);
                else if (matex.Type == MatTexture.TextureType.Roughness)
                    TextureUniform(shader, mat, mat.HasRoughnessMap, "RoughnessMap", matex);
                else if (matex.Type == MatTexture.TextureType.TeamColor)
                    TextureUniform(shader, mat, mat.HasTeamColorMap, "TeamColorMap", matex);
                else if (matex.Type == MatTexture.TextureType.Transparency)
                    TextureUniform(shader, mat, mat.HasTransparencyMap, "TransparencyMap", matex);
                else if (matex.Type == MatTexture.TextureType.DiffuseLayer2)
                    TextureUniform(shader, mat, mat.HasDiffuseLayer, "DiffuseLayer", matex);
                else if (matex.Type == MatTexture.TextureType.SphereMap)
                    TextureUniform(shader, mat, mat.HasSphereMap, "SphereMap", matex);
                else if (matex.Type == MatTexture.TextureType.SubSurfaceScattering)
                    TextureUniform(shader, mat, mat.HasSubSurfaceScatteringMap, "SubSurfaceScatteringMap", matex);
                else if (matex.Type == MatTexture.TextureType.MRA)
                    TextureUniform(shader, mat, mat.HasMRA, "MRA", matex);
            }
        }

        private static void LoadPBRMaps(SF.Shader shader)
        {
            GL.ActiveTexture(TextureUnit.Texture0 + 26);
            RenderTools.specularPbr.Bind();
            GL.Uniform1(shader.GetUniformLocation("specularIbl"), 26);

            //    GL.GenerateMipmap(GenerateMipmapTarget.TextureCubeMap);

            // PBR IBL
            GL.ActiveTexture(TextureUnit.Texture0 + 25);
            RenderTools.diffusePbr.Bind();
            GL.Uniform1(shader.GetUniformLocation("irradianceMap"), 25);

            GL.ActiveTexture(TextureUnit.Texture0 + 27);
            RenderTools.brdfPbr.Bind();
            GL.Uniform1(shader.GetUniformLocation("brdfLUT"), 27);
        }

        private static void TextureUniform(SF.Shader shader, FMAT mat, bool hasTex, string name, MatTexture mattex)
        {
            if (mattex.textureState == STGenericMatTexture.TextureState.Binded)
                return;

            // Bind the texture and create the uniform if the material has the right textures. 
            if (hasTex)
            {
                GL.Uniform1(shader.GetUniformLocation(name), BindTexture(mattex, mat, shader, mat.GetResFileU() != null));
            }
        }
        public static int BindTexture(MatTexture tex, FMAT material, SF.Shader shader, bool IsWiiU)
        {
            BFRES bfres = (BFRES)material.Parent.Parent.Parent.Parent;

            GL.ActiveTexture(TextureUnit.Texture0 + tex.textureUnit + 1);
            GL.BindTexture(TextureTarget.Texture2D, RenderTools.defaultTex.RenderableTex.TexID);

            string activeTex = tex.Name;
            if (tex.animatedTexName != "")
                activeTex = tex.animatedTexName;

            if (IsWiiU)
            {
                if (bfres.HasTextures)
                {
                    var ftexCont = bfres.GetFTEXContainer;
                    if (ftexCont != null)
                    {
                        if (ftexCont.ResourceNodes.ContainsKey(activeTex))
                        {
                            BindFTEX(ftexCont, tex, shader, activeTex);
                            return tex.textureUnit + 1;
                        }
                    }
                }

                foreach (var ftexContainer in PluginRuntime.ftexContainers)
                {
                    if (ftexContainer.ResourceNodes.ContainsKey(activeTex))
                    {
                        BindFTEX(ftexContainer, tex, shader, activeTex);
                        return tex.textureUnit + 1;
                    }
                }
            }
            else
            {
                if (bfres.HasTextures)
                {
                    var bntx = bfres.GetBNTX;
                    if (bntx != null)
                    {
                        if (bntx.Textures.ContainsKey(activeTex))
                        {
                            BindBNTX(bntx, tex, shader, activeTex);
                            return tex.textureUnit + 1;
                        }
                    }
                }

                foreach (var bntx in PluginRuntime.bntxContainers)
                {
                    if (bntx.Textures.ContainsKey(activeTex))
                    {
                        BindBNTX(bntx, tex, shader, activeTex);
                        return tex.textureUnit + 1;
                    }
                }
            }
            return tex.textureUnit + 1;
        }

        private static void BindFTEX(BFRESGroupNode ftexContainer, MatTexture tex, SF.Shader shader, string activeTex)
        {
            FTEX ftex = (FTEX)ftexContainer.ResourceNodes[activeTex];

            if (ftex.RenderableTex == null || !ftex.RenderableTex.GLInitialized)
                ftex.LoadOpenGLTexture();

            BindGLTexture(tex, shader, ftex);
        }

        private static void BindBNTX(BNTX bntx, MatTexture tex, SF.Shader shader, string activeTex)
        {
            if (bntx.Textures[activeTex].RenderableTex == null ||
                     !bntx.Textures[activeTex].RenderableTex.GLInitialized)
            {
                bntx.Textures[activeTex].LoadOpenGLTexture();
            }

            BindGLTexture(tex, shader, bntx.Textures[activeTex]);
        }
            
        private static void BindGLTexture(MatTexture tex, SF.Shader shader, STGenericTexture texture)
        {
            if (tex.Type == STGenericMatTexture.TextureType.Diffuse)
            {
                shader.SetInt("RedChannel", (int)texture.RedChannel);
                shader.SetInt("GreenChannel", (int)texture.GreenChannel);
                shader.SetInt("BlueChannel", (int)texture.BlueChannel);
                shader.SetInt("AlphaChannel", (int)texture.AlphaChannel);
            }


            //     GL.ActiveTexture(TextureUnit.Texture0 + texid);
            GL.BindTexture(TextureTarget.Texture2D, texture.RenderableTex.TexID);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)MatTexture.wrapmode[tex.wrapModeS]);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)MatTexture.wrapmode[tex.wrapModeT]);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)MatTexture.minfilter[tex.minFilter]);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)MatTexture.magfilter[tex.magFilter]);
            GL.TexParameter(TextureTarget.Texture2D, (TextureParameterName)ExtTextureFilterAnisotropic.TextureMaxAnisotropyExt, 0.0f);
        }
        private void DrawModel(FSHP m, FMDL mdl, SF.Shader shader, bool ModelSelected)
        {
            if (m.lodMeshes[m.DisplayLODIndex].faces.Count <= 3)
                return;

            var mat = m.GetMaterial();

            if (shader != OpenTKSharedResources.shaders["BFRES_Normals"])
            {
                SetRenderPass(mat);
                SetUniforms(mat, shader, m, m.DisplayId);
                SetTextureUniforms(mat, m, shader);
            }
            SetBoneUniforms(shader, mdl, m);
            ApplyTransformFix(mdl, m, shader);
            SetVertexAttributes(m, shader);

            //Check the binded bone if it's visible from bone visual anims
       //     if (!mdl.Skeleton.bones[m.boneIndx].Visible)
       //         m.Checked = false;

            if (m.Checked && mdl.Skeleton.bones.Count > 0 && mdl.Skeleton.bones[m.BoneIndex].Visible && mat.Enabled)
            {
                shader.SetVector3("materialSelectColor", new Vector3(0));
                if (m.GetMaterial().IsSelected)
                {
                    shader.SetVector3("materialSelectColor", ColorUtility.ToVector3(Color.FromArgb(0,163,204)));
                    DrawModelSelection(m, shader);
                }
                else if (m.IsSelected || ModelSelected)
                {
                    DrawModelSelection(m, shader);
                }
                else
                {
                    if (Runtime.RenderModelWireframe)
                    {
                        DrawModelWireframe(m, shader);
                    }
                    if (Runtime.RenderModels)
                    {
                        DrawMdoelHoverSelection(m, shader, IsSelected(), Hovered);

                        PrimitiveType primitiveType = PrimitiveType.Triangles;

                        switch (m.lodMeshes[m.DisplayLODIndex].PrimitiveType)
                        {
                            case STPolygonType.Line:
                                primitiveType = PrimitiveType.Lines;
                                break;
                            case STPolygonType.LineStrip:
                                primitiveType = PrimitiveType.LineStrip;
                                break;
                            case STPolygonType.Point:
                                primitiveType = PrimitiveType.Points;
                                break;
                            case STPolygonType.Triangle:
                                primitiveType = PrimitiveType.Triangles;
                                break;
                        }

                        GL.DrawElements(primitiveType, m.lodMeshes[m.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, m.Offset);
                    }
                }
            }
        }
        private static void ApplyTransformFix(FMDL fmdl, FSHP m, SF.Shader shader)
        {
            Matrix4 idenity = Matrix4.Identity;
            shader.SetInt("NoSkinning", 0);
            shader.SetInt("RigidSkinning", 0);
            shader.SetInt("SingleBoneIndex", m.BoneIndex);

            shader.SetMatrix4x4("SingleBoneBindTransform", ref idenity);
            //Some objects will have no weights or indices. These will weigh to the bone index in the shape section.

            if (m.VertexSkinCount == 1)
            {
                shader.SetInt("RigidSkinning", 1);
            }
            if (m.VertexSkinCount == 0)
            {
                if (fmdl.Skeleton.bones.Count > 0)
                {
                    Matrix4 transform = fmdl.Skeleton.bones[m.BoneIndex].invert * fmdl.Skeleton.bones[m.BoneIndex].Transform;

                    shader.SetMatrix4x4("SingleBoneBindTransform", ref transform);
                    shader.SetInt("NoSkinning", 1);
                }
            }
        }

        static bool Loaded = false;
        public void UpdateVertexData()
        {
            if (!Runtime.OpenTKInitialized)
                return;

            UpdateModelList();
            Loaded = false;

            DisplayVertex[] Vertices;
            int[] Faces;

            int poffset = 0;
            int voffset = 0;
            List<DisplayVertex> Vs = new List<DisplayVertex>();
            List<int> Ds = new List<int>();

            int TotalShapeCount = models.Sum(b => b.shapes.Count);

            int value = 0;

            for (int m = 0; m < models.Count; m++)
            {
                //Reset min/max
                models[m].MaxPosition = new Vector3(0);
                models[m].MinPosition = new Vector3(0);

                for (int shp = 0; shp < models[m].shapes.Count; shp++)
                {
                    //Update render pass aswell
                    CheckRenderPass(models[m].shapes[shp].GetMaterial());

                    models[m].shapes[shp].Offset = poffset * 4;
                    List<DisplayVertex> pv = models[m].shapes[shp].CreateDisplayVertices(models[m]);
                    Vs.AddRange(pv);

                    for (int i = 0; i < models[m].shapes[shp].lodMeshes[models[m].shapes[shp].DisplayLODIndex].displayFaceSize; i++)
                    {
                        Ds.Add(models[m].shapes[shp].display[i] + voffset);
                    }
                    poffset += models[m].shapes[shp].lodMeshes[models[m].shapes[shp].DisplayLODIndex].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);

            LibraryGUI.UpdateViewport();

            Loaded = true;
        }
        public void UpdateSingleMaterialTextureMaps(FMAT mat)
        {
            if (!Runtime.OpenTKInitialized)
                return;

            foreach (BNTX bntx in PluginRuntime.bntxContainers)
            {
                foreach (var t in mat.TextureMaps)
                {
                    if (bntx.Textures.ContainsKey(t.Name))
                    {
                        if (!bntx.Textures[t.Name].RenderableTex.GLInitialized)
                            bntx.Textures[t.Name].LoadOpenGLTexture();
                    }
                }
            }

            LibraryGUI.UpdateViewport();
        }
        public void UpdateTextureMaps()
        {
            if (!Runtime.OpenTKInitialized)
                return;

            foreach (BNTX bntx in PluginRuntime.bntxContainers)
            {
                if (!bntx.AllGLInitialized)
                {
                    foreach (var tex in bntx.Textures)
                    {
                        if (!tex.Value.RenderableTex.GLInitialized)
                            tex.Value.LoadOpenGLTexture();
                    }
                }
            }
            foreach (BFRESGroupNode ftexCont in PluginRuntime.ftexContainers)
            {
                foreach (var tex in ftexCont.ResourceNodes)
                {
                    if (!((FTEX)tex.Value).RenderableTex.GLInitialized)
                        ((FTEX)tex.Value).LoadOpenGLTexture();
                }
            }
        }

        private static void SetRenderPass(FMAT mat)
        {
            bool NoCull = false;
            bool CullBack = false;
            bool CullFront = false;

            for (int i = 0; i < mat.renderinfo.Count; i++)
            {
                if (mat.renderinfo[i].Name == "display_face")
                {
                    NoCull = mat.renderinfo[i].ValueString.Contains("both");
                    CullFront = mat.renderinfo[i].ValueString.Contains("back");
                    CullBack = mat.renderinfo[i].ValueString.Contains("front");
                }

                if (mat.shaderassign.ShaderArchive == "Turbo_UBER")
                {
                    AglShaderTurbo aglShader = new AglShaderTurbo();
                    aglShader.LoadRenderInfo(mat.renderinfo[i]);
                }
            }

            if (NoCull)
            {
                GL.Disable(EnableCap.CullFace);
            }
            else if (CullFront)
            {
                GL.Enable(EnableCap.CullFace);
                GL.CullFace(CullFaceMode.Front);
            }
            else if (CullBack)
            {
                GL.Enable(EnableCap.CullFace);
                GL.CullFace(CullFaceMode.Back);
            }
        }

        private static void CheckRenderPass(FMAT mat)
        {
            if (mat.ImageKey != "material")
            {
                mat.ImageKey = "material";
                mat.SelectedImageKey = "material";
            }

            bool IsTranslucent = false;
            bool IsTransparentMask = false;


            for (int i = 0; i < mat.renderinfo.Count; i++)
            {
                if (mat.renderinfo[i].Name == "gsys_render_state_mode")
                {
                    IsTranslucent = mat.renderinfo[i].ValueString.Contains("translucent");
                    IsTransparentMask = mat.renderinfo[i].ValueString.Contains("mask");
                }
                if (mat.renderinfo[i].Name == "renderPass")
                {
                    IsTransparentMask = mat.renderinfo[i].ValueString.Contains("xlu");
                }
            }

            if (mat.shaderassign.options.ContainsKey("enable_translucent"))
                IsTranslucent = mat.shaderassign.options["enable_translucent"] == "1";
            if (mat.shaderassign.options.ContainsKey("enable_translucent"))
                IsTransparentMask = mat.shaderassign.options["enable_transparent"] == "1";

            if (mat.MaterialU != null)
            {
                IsTranslucent =  mat.MaterialU.RenderState.FlagsMode == ResU.RenderStateFlagsMode.Translucent;
                IsTransparentMask = mat.MaterialU.RenderState.FlagsMode == ResU.RenderStateFlagsMode.AlphaMask;
            }

            mat.isTransparent = IsTransparentMask || IsTranslucent;

            SetMaterialIcon(mat, IsTranslucent, "MaterialTranslucent");
            SetMaterialIcon(mat, IsTransparentMask, "MaterialTransparent");
        }

        private static void SetMaterialIcon(FMAT mat, bool IsEffect, string Key)
        {
            if (IsEffect)
            {
                mat.ImageKey = Key;
                mat.SelectedImageKey = Key;
            }
        }

        private static void SetUniforms(FMAT mat, SF.Shader shader, FSHP m, int id)
        {
             shader.SetBoolToInt("isTransparent", mat.isTransparent);

            shader.SetFloat("ao_density", 1);
            shader.SetFloat("shadow_density", 1);
            
            shader.SetFloat("normal_map_weight", 1);

            //Bake map UV coordinate ST
            shader.SetVector4("gsys_bake_st0", new Vector4(1, 1, 0, 0));
            shader.SetVector4("gsys_bake_st1", new Vector4(1, 1, 0, 0));

            shader.SetBoolToInt("UseSpecularColor",
                 (mat.GetOptionValue("specular_mask_is_color") == 1) ||
                 mat.GetOptionValue("enable_specular_color") == 1);

            shader.SetBoolToInt("UseMultiTexture", mat.GetOptionValue("enable_multi_texture") == 1);

            //Colors
            shader.SetVector4("const_color0", new Vector4(1, 1, 1, 1));
            shader.SetVector4("base_color_mul_color", new Vector4(1, 1, 1, 1));
            shader.SetVector3("albedo_tex_color", new Vector3(1, 1, 1));
            shader.SetVector3("emission_color", new Vector3(1, 1, 1));
            shader.SetVector3("specular_color", new Vector3(1, 1, 1));
            
            shader.SetFloat("fuv1_mtx", 0);

            //SRT
            shader.SetVector4("tex_mtx0", new Vector4(1, 1, 1, 1));
            shader.SetVector2("SRT_Scale", new Vector2(1, 1));
            shader.SetFloat("SRT_Rotate", 0);
            shader.SetVector2("SRT_Translate", new Vector2(0, 0));

            shader.SetInt("selectedBoneIndex", Runtime.SelectedBoneIndex);

            SetUniformData(mat, shader, "base_color_mul_color");

            shader.SetInt("enableCellShading", 0);
            bool HasTans = m.vertexAttributes.Any(x => x.Name == "_t0");
            shader.SetBoolToInt("hasTangents", HasTans);

            SetUniformData(mat, shader, "fuv1_mtx");

            SetUniformData(mat, shader, "gsys_bake_st0");
            SetUniformData(mat, shader, "gsys_bake_st1");

            SetUniformData(mat, shader, "ao_density");
            SetUniformData(mat, shader, "shadow_density");
            SetUniformData(mat, shader, "normal_map_weight");

            SetUniformData(mat, shader, "const_color0");
            SetUniformData(mat, shader, "base_color_mul_color");
            SetUniformData(mat, shader, "albedo_tex_color");
            SetUniformData(mat, shader, "emission_color");
            SetUniformData(mat, shader, "specular_color");


            //This uniform sets various maps for BOTW to use second UV channel
            SetUniformData(mat, shader, "uking_texture2_texcoord");

            SetUniformData(mat, shader, "cIsEnableNormalMap");

            SetUniformData(mat, shader, "tex_mtx0");

            //Sets shadow type
            //0 = Ambient occusion bake map
            //1 = Shadow 
            //2 = Shadow + Ambient occusion map
            SetUniformData(mat, shader, "bake_shadow_type");
            SetUniformData(mat, shader, "bake_light_type");
            SetUniformData(mat, shader, "gsys_bake_light_scale");

            SetUniformData(mat, shader, "enable_projection_light");
            SetUniformData(mat, shader, "enable_actor_light");

            SetUniformData(mat, shader, "bake_calc_type");
        }
        private static void SetUniformData(FMAT mat, SF.Shader shader, string propertyName)
        {
            if (mat.shaderassign.options.ContainsKey(propertyName))
            {
                float value = float.Parse(mat.shaderassign.options[propertyName]);
                shader.SetFloat(propertyName, value);
            }
            if (mat.matparam.ContainsKey(propertyName))
            {
                if (mat.matparam[propertyName].Type == ShaderParamType.Float)
                {
                    if (mat.anims.ContainsKey(propertyName))
                        mat.matparam[propertyName].ValueFloat[0] = mat.anims[propertyName][0];
                    shader.SetFloat(propertyName, mat.matparam[propertyName].ValueFloat[0]);
                }

                if (mat.matparam[propertyName].Type == ShaderParamType.Float2)
                {
                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.matparam[propertyName].ValueFloat = new float[2] {
                            mat.anims[propertyName][0], mat.anims[propertyName][1]};
                    }

                    shader.SetVector2(propertyName, Utils.ToVec2(mat.matparam[propertyName].ValueFloat));
                }

                if (mat.matparam[propertyName].Type == ShaderParamType.Float3)
                {
                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.matparam[propertyName].ValueFloat = new float[3] {
                            mat.anims[propertyName][0],
                            mat.anims[propertyName][1],
                            mat.anims[propertyName][2]};
                    }

                    shader.SetVector3(propertyName, Utils.ToVec3(mat.matparam[propertyName].ValueFloat));
                }
                if (mat.matparam[propertyName].Type == ShaderParamType.Float4)
                {
                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.matparam[propertyName].ValueFloat = new float[4] {
                                     mat.anims[propertyName][0], mat.anims[propertyName][1],
                                     mat.anims[propertyName][2], mat.anims[propertyName][3]};
                    }

                    shader.SetVector4(propertyName, Utils.ToVec4(mat.matparam[propertyName].ValueFloat));
                }
                if (mat.matparam[propertyName].Type == ShaderParamType.TexSrt)
                {
                    // Vector 2 Scale
                    // 1 roation float
                    // Vector2 translate
                    TexSrt texSRT = mat.matparam[propertyName].ValueTexSrt;

                    shader.SetVector2("SRT_Scale", Utils.ToVec2(texSRT.Scaling));
                    shader.SetFloat("SRT_Rotate", texSRT.Rotation);
                    shader.SetVector2("SRT_Translate", Utils.ToVec2(texSRT.Translation));
                }
            }

            if (mat.animatedMatParams.ContainsKey(propertyName))
            {
                if (mat.animatedMatParams[propertyName].Type == ShaderParamType.Float)
                {
                    if (mat.anims.ContainsKey(propertyName))
                        mat.animatedMatParams[propertyName].ValueFloat[0] = mat.anims[propertyName][0];
                    shader.SetFloat(propertyName, mat.animatedMatParams[propertyName].ValueFloat[0]);
                }

                if (mat.animatedMatParams[propertyName].Type == ShaderParamType.Float2)
                {
                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.animatedMatParams[propertyName].ValueFloat = new float[2] {
                            mat.anims[propertyName][0], mat.anims[propertyName][1]};
                    }

                    shader.SetVector2(propertyName, Utils.ToVec2(mat.animatedMatParams[propertyName].ValueFloat));
                }

                if (mat.animatedMatParams[propertyName].Type == ShaderParamType.Float3)
                {
                    Console.WriteLine(propertyName + " " + mat.animatedMatParams[propertyName].ValueFloat);

                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.animatedMatParams[propertyName].ValueFloat = new float[3] {
                            mat.anims[propertyName][0],
                            mat.anims[propertyName][1],
                            mat.anims[propertyName][2]};
                    }

                    shader.SetVector3(propertyName, Utils.ToVec3(mat.animatedMatParams[propertyName].ValueFloat));
                }
                if (mat.animatedMatParams[propertyName].Type == ShaderParamType.Float4)
                {
                    if (mat.anims.ContainsKey(propertyName))
                    {
                        mat.animatedMatParams[propertyName].ValueFloat = new float[4] {
                                     mat.anims[propertyName][0], mat.anims[propertyName][1],
                                     mat.anims[propertyName][2], mat.anims[propertyName][3]};
                    }

                    shader.SetVector4(propertyName, Utils.ToVec4(mat.animatedMatParams[propertyName].ValueFloat));
                }
                if (mat.animatedMatParams[propertyName].Type == ShaderParamType.TexSrt)
                {
                    // Vector 2 Scale
                    // 1 roation float
                    // Vector2 translate
                    TexSrt texSRT = mat.animatedMatParams[propertyName].ValueTexSrt;

                    shader.SetVector2("SRT_Scale", Utils.ToVec2(texSRT.Scaling));
                    shader.SetFloat("SRT_Rotate", texSRT.Rotation);
                    shader.SetVector2("SRT_Translate", Utils.ToVec2(texSRT.Translation));
                }
            }
        }
        private void SetVertexAttributes(FSHP m, SF.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("vTangent"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 24);
            GL.VertexAttribPointer(shader.GetAttribLocation("vBitangent"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 36);
            GL.VertexAttribPointer(shader.GetAttribLocation("vUV0"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 48);
            GL.VertexAttribPointer(shader.GetAttribLocation("vColor"), 4, VertexAttribPointerType.Float, false, DisplayVertex.Size, 56);
            GL.VertexAttribIPointer(shader.GetAttribLocation("vBone"), 4, VertexAttribIntegerType.Int, DisplayVertex.Size, new IntPtr(72));
            GL.VertexAttribPointer(shader.GetAttribLocation("vWeight"), 4, VertexAttribPointerType.Float, false, DisplayVertex.Size, 88);
            GL.VertexAttribPointer(shader.GetAttribLocation("vUV1"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 104);
            GL.VertexAttribPointer(shader.GetAttribLocation("vUV2"), 2, VertexAttribPointerType.Float, false, DisplayVertex.Size, 112);
            GL.VertexAttribPointer(shader.GetAttribLocation("vPosition2"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 124);
            GL.VertexAttribPointer(shader.GetAttribLocation("vPosition3"), 3, VertexAttribPointerType.Float, false, DisplayVertex.Size, 136);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements);
        }

        private static void DrawMdoelHoverSelection(STGenericObject p, SF.Shader shader,
            bool IsSelected, bool IsHovered)
        {
            if (IsHovered && IsSelected)
                shader.SetVector4("pickingColor", hoverColor);
            else if (IsHovered || IsSelected)
                shader.SetVector4("pickingColor", selectColor);
            else
                shader.SetVector4("pickingColor", new Vector4(1));
        }

        private static void DrawModelWireframe(STGenericObject p, SF.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.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
            shader.SetInt("colorOverride", 0);
        }
        private static void DrawModelSelection(STGenericObject p, SF.Shader shader)
        {
            //This part needs to be reworked for proper outline. Currently would make model disappear
            /*     GL.Enable(EnableCap.DepthTest);
                 GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Replace);

                 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);

                 GL.StencilMask(0x00);   

                 GL.StencilFunc(StencilFunction.Always, 1, 0xFF); // all fragments should update the stencil buffer
                 GL.StencilMask(0xFF); // enable writing to the stencil buffer
                 GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);

                 GL.StencilFunc(StencilFunction.Notequal, 1, 0xFF);
                 GL.StencilMask(0x00); // enable writing to the stencil buffer
                 GL.Disable(EnableCap.DepthTest);

                 shader.SetInt("colorOverride", 1);

                 GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
                 GL.LineWidth(2.0f);
                 GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);

                 shader.SetInt("colorOverride", 0);

                 GL.StencilMask(0xFF);
                 GL.Enable(EnableCap.DepthTest);*/

            // Override the model color with white in the shader.

            shader.SetInt("colorOverride", 1);
            GL.PolygonMode(MaterialFace.Front, PolygonMode.Line);
            GL.Enable(EnableCap.LineSmooth);
            GL.LineWidth(1.3f);
            GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
            shader.SetInt("colorOverride", 0);

            GL.DrawElements(PrimitiveType.Triangles, p.lodMeshes[p.DisplayLODIndex].displayFaceSize, DrawElementsType.UnsignedInt, p.Offset);
        }

  /*      public override BoundingBox GetSelectionBox()
        {
            Vector3 Min = new Vector3(0);
            Vector3 Max = new Vector3(0);

            foreach (var model in models)
            {
                foreach (var shape in model.shapes)
                {
                    foreach (var vertex in shape.vertices)
                    {
                        Min.X = Math.Min(Min.X, vertex.pos.X);
                        Min.Y = Math.Min(Min.Y, vertex.pos.Y);
                        Min.Z = Math.Min(Min.Z, vertex.pos.Z);
                        Max.X = Math.Max(Max.X, vertex.pos.X);
                        Max.Y = Math.Max(Max.Y, vertex.pos.Y);
                        Max.Z = Math.Max(Max.Z, vertex.pos.Z);
                    }
                }
            }

            return new BoundingBox()
            {
                minX = Min.X,
                minY = Min.Y,
                minZ = Min.Z,
                maxX = Max.X,
                maxY = Max.Y,
                maxZ = Max.Z,
            };
        }


        public override uint SelectAll(GL_ControlBase control)
        {
            Selected = true;
            return REDRAW;
        }

        public override uint SelectDefault(GL_ControlBase control)
        {
            Selected = true;
            return REDRAW;
        }

        public override uint Select(int partIndex, GL_ControlBase control)
        {
            Selected = true;
            return REDRAW;
        }

        public override uint Deselect(int partIndex, GL_ControlBase control)
        {
            Selected = false;
            return REDRAW;
        }

        public override LocalOrientation GetLocalOrientation(int partIndex)
        {
            return new LocalOrientation(position);
        }

        public override bool TryStartDragging(DragActionType actionType, int hoveredPart, out LocalOrientation localOrientation, out bool dragExclusively)
        {
            localOrientation = new LocalOrientation(position);
            dragExclusively = false;
            return Selected;
        }

        public override bool IsInRange(float range, float rangeSquared, Vector3 pos)
        {
            range = 20000; //Make the range large for now. Todo go back to this

            BoundingBox box = GetSelectionBox();

            if (pos.X < box.maxX + range && pos.X > box.minX - range &&
              pos.Y < box.maxY + range && pos.Y > box.minY - range &&
              pos.Z < box.maxZ + range && pos.Z > box.minZ - range)
                return true;

            return false;
        }

        public override uint DeselectAll(GL_ControlBase control)
        {
            Selected = false;
            return REDRAW;
        }

        public override Vector3 Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }*/

        #endregion
    }
}