public void Register(MaterialEffect mat, ModelMeshPart mesh, TransformMatrix worldMatrix, BoundingSphere boundingSphere) //These should be ordered by likeness, so I don't get opaque -> transparent -> opaque
        {
            bool found = false;

            if (mat == null)
            {
                var effect = mesh.Effect as MaterialEffect;
                if (effect != null)
                {
                    mat = effect;
                }
                else
                {
                    mat = new MaterialEffect(mesh.Effect);
                }
            }

            //Check if we already have a material like that, if yes put it in there!
            for (var i = 0; i < Index; i++)
            {
                MaterialLibrary matLib = MaterialLib[i];
                if (matLib.HasMaterial(mat))
                {
                    matLib.Register(mesh, worldMatrix, boundingSphere);
                    found = true;
                    break;
                }
            }

            //We have no MatLib for that specific Material yet. Make a new one.
            if (!found)
            {
                MaterialLib[Index] = new MaterialLibrary();
                MaterialLib[Index].SetMaterial(ref mat);
                MaterialLib[Index].Register(mesh, worldMatrix, boundingSphere);
                Index++;
            }

            //If we exceeded our array length, make the array bigger.
            if (Index >= MaterialLib.Length)
            {
                MaterialLibrary[] tempLib = new MaterialLibrary[Index + 1];
                MaterialLib.CopyTo(tempLib, 0);
                MaterialLib = tempLib;

                MaterialLibPointer = new int[Index + 1];
                //sort from 0 to Index
                for (int j = 0; j < MaterialLibPointer.Length; j++)
                {
                    MaterialLibPointer[j] = j;
                }
                SortByDistance();
            }
        }
        /// <summary>
        /// Should be called when the frame is done.
        /// </summary>
        /// <param name="entities"></param>
        public void FrustumCullingFinalizeFrame(List <BasicEntity> entities)
        {
            //Set Changed to false
            for (int index1 = 0; index1 < entities.Count; index1++)
            {
                BasicEntity entity = entities[index1];
                entity.WorldTransform.HasChanged = false;
            }

            for (int index1 = 0; index1 < Index; index1++)
            {
                MaterialLibrary matLib = GameSettings.g_CPU_Sort ? MaterialLib[MaterialLibPointer[index1]] : MaterialLib[index1];

                matLib.HasChangedThisFrame = false;
            }
        }
        private void DeleteFromRegistry(MaterialEffect mat, ModelMeshPart mesh, TransformMatrix worldMatrix)
        {
            for (var i = 0; i < Index; i++)
            {
                MaterialLibrary matLib = MaterialLib[i];
                //if (matLib.HasMaterial(mat))
                //{
                if (matLib.DeleteFromRegistry(mesh, worldMatrix))
                {
                    for (var j = i; j < Index - 1; j++)
                    {
                        //slide down one
                        MaterialLib[j] = MaterialLib[j + 1];
                    }
                    Index--;

                    break;
                }
                //}
            }
        }
        public void Draw(RenderType renderType, GraphicsDevice graphicsDevice, Matrix viewProjection, bool lightViewPointChanged = false, bool hasAnyObjectMoved = false, bool outlined = false, int outlineId = 0, Matrix?view = null)
        {
            //if (renderType != RenderType.Alpha)
            //{
            graphicsDevice.BlendState = BlendState.Opaque;
            //    graphicsDevice.RasterizerState = RasterizerState.CullNone;
            //}

            //For shadowmaps we need to find out whether any object has moved and if so if it is rendered. If yes, redraw the whole frame, if no don't do anything
            if (!lightViewPointChanged && hasAnyObjectMoved)
            {
                bool discardFrame = true;

                for (int index1 = 0; index1 < Index; index1++)
                {
                    MaterialLibrary matLib = MaterialLib[index1];

                    //We determined beforehand whether something changed this frame
                    if (matLib.HasChangedThisFrame)
                    {
                        for (int i = 0; i < matLib.Index; i++)
                        {
                            //Now we have to check whether we have a rendered thing in here
                            MeshLibrary meshLib = matLib.GetMeshLibrary()[i];
                            for (int index = 0; index < meshLib.Index; index++)
                            {
                                //If it's set to "not rendered" skip
                                for (int j = 0; j < meshLib.Rendered.Length; j++)
                                {
                                    if (meshLib.Rendered[j])
                                    {
                                        discardFrame = false;
                                        break;
                                    }
                                }

                                if (!discardFrame)
                                {
                                    break;
                                }
                            }
                        }
                        if (!discardFrame)
                        {
                            break;
                        }
                    }
                }

                if (discardFrame)
                {
                    return;
                }

                //graphicsDevice.Clear(new Color(0.51f, 0.501f, 0, 0));
                graphicsDevice.Clear(Color.TransparentBlack);
            }

            for (int index1 = 0; index1 < Index; index1++)
            {
                MaterialLibrary matLib = MaterialLib[index1];

                if (matLib.Index < 1)
                {
                    continue;
                }

                //if none of this materialtype is drawn continue too!
                bool isUsed = false;

                for (int i = 0; i < matLib.Index; i++)
                {
                    MeshLibrary meshLib = matLib.GetMeshLibrary()[i];
                    for (int index = 0; index < meshLib.Index; index++)
                    {
                        //If it's set to "not rendered" skip
                        for (int j = 0; j < meshLib.Rendered.Length; j++)
                        {
                            if (meshLib.Rendered[j])
                            {
                                isUsed = true;
                                //if (meshLib.GetWorldMatrices()[j].HasChanged)
                                //    hasAnyObjectMoved = true;
                            }

                            if (isUsed)// && hasAnyObjectMoved)
                            {
                                break;
                            }
                        }

                        if (isUsed)// && hasAnyObjectMoved)
                        {
                            break;
                        }
                    }
                }

                if (!isUsed)
                {
                    continue;
                }

                //Count the draws of different materials!

                MaterialEffect material = /*GameSettings.DebugDrawUntextured==2 ? Art.DefaultMaterial :*/ matLib.GetMaterial();

                //Check if alpha or opaque!

                Effect shader = null;

                //todo: We only need textures for non shadow mapping, right? Not quite actually, for alpha textures we need materials
                if (renderType == RenderType.TextureBuffer)
                {
                    shader = Shaders.GBufferEffect;

                    if (GameSettings.d_defaultMaterial)
                    {
                        Shaders.GBufferEffectParameter_Material_DiffuseColor.SetValue(Color.Gray.ToVector3());
                        Shaders.GBufferEffectParameter_Material_Roughness.SetValue(GameSettings.m_defaultRoughness > 0
                                ? GameSettings.m_defaultRoughness
                                : 0.3f);
                        Shaders.GBufferEffectParameter_Material_Metallic.SetValue(0.0f);
                        Shaders.GBufferEffectParameter_Material_MaterialType.SetValue(0);
                        Shaders.GBufferEffect.CurrentTechnique = Shaders.GBufferEffectTechniques_DrawBasic;
                    }
                    else
                    {
                        if (material.HasDisplacement)
                        {
                            Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                            Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                            Shaders.GBufferEffectParameter_Material_DisplacementMap.SetValue(material.DisplacementMap);
                            Shaders.GBufferEffect.CurrentTechnique =
                                Shaders.GBufferEffectTechniques_DrawTextureDisplacement;
                        }
                        else if (material.HasMask) //Has diffuse for sure then
                        {
                            if (material.HasNormalMap && material.HasRoughnessMap)
                            {
                                Shaders.GBufferEffectParameter_Material_MaskMap.SetValue(material.Mask);
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularNormalMask;
                            }

                            else if (material.HasNormalMap)
                            {
                                Shaders.GBufferEffectParameter_Material_MaskMap.SetValue(material.Mask);
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureNormalMask;
                            }

                            else if (material.HasRoughnessMap)
                            {
                                Shaders.GBufferEffectParameter_Material_MaskMap.SetValue(material.Mask);
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularMask;
                            }
                            else
                            {
                                Shaders.GBufferEffectParameter_Material_MaskMap.SetValue(material.Mask);
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularMask;
                            }
                        }
                        else
                        {
                            if (material.HasNormalMap && material.HasRoughnessMap && material.HasDiffuse &&
                                material.HasMetallic)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffectParameter_Material_MetallicMap.SetValue(material.MetallicMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularNormalMetallic;
                            }

                            else if (material.HasNormalMap && material.HasRoughnessMap && material.HasDiffuse)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularNormal;
                            }

                            else if (material.HasNormalMap && material.HasDiffuse)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_NormalMap.SetValue(material.NormalMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureNormal;
                            }

                            else if (material.HasMetallic && material.HasRoughnessMap && material.HasDiffuse)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecularMetallic;
                            }

                            else if (material.HasRoughnessMap && material.HasDiffuse)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffectParameter_Material_RoughnessMap.SetValue(material.RoughnessMap);
                                Shaders.GBufferEffect.CurrentTechnique =
                                    Shaders.GBufferEffectTechniques_DrawTextureSpecular;
                            }

                            else if (material.HasDiffuse)
                            {
                                Shaders.GBufferEffectParameter_Material_Texture.SetValue(material.AlbedoMap);
                                Shaders.GBufferEffect.CurrentTechnique = Shaders.GBufferEffectTechniques_DrawTexture;
                            }

                            else
                            {
                                Shaders.GBufferEffect.CurrentTechnique = Shaders.GBufferEffectTechniques_DrawBasic;
                            }
                        }


                        if (!material.HasDiffuse)
                        {
                            if (material.Type == MaterialEffect.MaterialTypes.Emissive && material.EmissiveStrength > 0)
                            {
                                //{
                                Shaders.GBufferEffectParameter_Material_DiffuseColor.SetValue(material.DiffuseColor *
                                                                                              material.EmissiveStrength);
                            }
                            //* Math.Max(material.EmissiveStrength,1));
                            //}
                            else
                            {
                                //{
                                Shaders.GBufferEffectParameter_Material_DiffuseColor.SetValue(material.DiffuseColor);
                            }
                            //}
                        }

                        if (!material.HasRoughnessMap)
                        {
                            Shaders.GBufferEffectParameter_Material_Roughness.SetValue(GameSettings.m_defaultRoughness >
                                                                                       0
                                ? GameSettings.m_defaultRoughness
                                : material.Roughness);
                        }
                        Shaders.GBufferEffectParameter_Material_Metallic.SetValue(material.Metallic);
                        Shaders.GBufferEffectParameter_Material_MaterialType.SetValue(material.MaterialTypeNumber);
                    }
                }

                for (int i = 0; i < matLib.Index; i++)
                {
                    MeshLibrary meshLib = matLib.GetMeshLibrary()[i];

                    //Initialize the mesh VB and IB
                    graphicsDevice.SetVertexBuffer(meshLib.GetMesh().VertexBuffer);
                    graphicsDevice.Indices = (meshLib.GetMesh().IndexBuffer);
                    int primitiveCount = meshLib.GetMesh().PrimitiveCount;
                    int vertexOffset   = meshLib.GetMesh().VertexOffset;
                    //int vCount = meshLib.GetMesh().NumVertices;
                    int startIndex = meshLib.GetMesh().StartIndex;

                    //Now draw the local meshes!
                    for (int index = 0; index < meshLib.Index; index++)
                    {
                        //If it's set to "not rendered" skip
                        //if (!meshLib.GetWorldMatrices()[index].Rendered) continue;
                        if (!meshLib.Rendered[index])
                        {
                            continue;
                        }


                        Matrix localWorldMatrix = meshLib.GetWorldMatrices()[index].World;
                        if (renderType == RenderType.TextureBuffer)
                        {
                            Shaders.GBufferEffectParameter_World.SetValue(localWorldMatrix);
                            Shaders.GBufferEffectParameter_WorldViewProj.SetValue(localWorldMatrix * viewProjection);

                            Matrix world = Matrix.Transpose(Matrix.Invert(localWorldMatrix));
                            Shaders.GBufferEffectParameter_WorldIT.SetValue(world);

                            shader.CurrentTechnique.Passes[0].Apply();
                        }
                        else if (renderType == RenderType.FinalMesh)
                        {
                            Shaders.GBufferEffectParameter_WorldViewProj.SetValue(localWorldMatrix * viewProjection);

                            Shaders.GBufferEffectTechniques_DrawBasicMesh.Passes[0].Apply();
                        }

                        if (material.RenderCClockwise)
                        {
                            graphicsDevice.RasterizerState = RasterizerState.CullClockwise;
                        }

                        GameStats.MeshDraws++;

                        try
                        {
                            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, vertexOffset, startIndex, primitiveCount);
                        }
                        catch (Exception)
                        {
                            // do nothing
                        }

                        if (material.RenderCClockwise)
                        {
                            graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
                        }

                        //only once
                        if (renderType == RenderType.TextureBuffer)
                        {
                            return;
                        }
                    }
                }
            }

            //Update the drawcalls in our stats
        }
        /// <summary>
        /// Update whether or not Objects are in the viewFrustum and need to be rendered or not.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="boundingFrustrum"></param>
        /// <param name="hasCameraChanged"></param>
        public bool FrustumCulling(List <BasicEntity> entities, BoundingFrustum boundingFrustrum, bool hasCameraChanged, Vector3 cameraPosition)
        {
            //Check if the culling mode has changed
            if (_previousMode != GameSettings.g_CPU_Culling)
            {
                if (_previousMode)
                {
                    //If we previously did cull and now don't we need to set all the submeshes to render
                    for (int index1 = 0; index1 < Index; index1++)
                    {
                        MaterialLibrary matLib = MaterialLib[index1];
                        for (int i = 0; i < matLib.Index; i++)
                        {
                            MeshLibrary meshLib = matLib.GetMeshLibrary()[i];
                            for (int j = 0; j < meshLib.Rendered.Length; j++)
                            {
                                meshLib.Rendered[j] = _previousMode;
                            }
                        }
                    }
                }
                _previousMode = GameSettings.g_CPU_Culling;
            }

            if (!GameSettings.g_CPU_Culling)
            {
                return(false);
            }

            for (int index1 = 0; index1 < entities.Count; index1++)
            {
                BasicEntity entity = entities[index1];

                if (!hasCameraChanged && !entity.WorldTransform.HasChanged)// && entity.DynamicPhysicsObject == null)
                {
                    continue;
                }

                if (entity.WorldTransform.HasChanged)// || entity.DynamicPhysicsObject != null)
                {
                    entity.ApplyTransformation();
                }
            }

            bool hasAnythingChanged = false;

            //Ok we applied the transformation to all the entities, now update the submesh boundingboxes!
            // Parallel.For(0, Index, index1 =>
            for (int index1 = 0; index1 < Index; index1++)
            {
                float distance = 0;
                int   counter  = 0;


                MaterialLibrary matLib = GameSettings.g_CPU_Sort
                        ? MaterialLib[MaterialLibPointer[index1]]
                        : MaterialLib[index1];
                for (int i = 0; i < matLib.Index; i++)
                {
                    MeshLibrary meshLib    = matLib.GetMeshLibrary()[i];
                    float?      distanceSq = meshLib.UpdatePositionAndCheckRender(hasCameraChanged, boundingFrustrum,
                                                                                  cameraPosition, _defaultBoundingSphere);

                    //If we get a new distance, apply it to the material
                    if (distanceSq != null)
                    {
                        distance += (float)distanceSq;
                        counter++;
                        hasAnythingChanged = true;
                    }
                }

                if (Math.Abs(distance) > 0.00001f)
                {
                    distance /= counter;
                    matLib.DistanceSquared     = distance;
                    matLib.HasChangedThisFrame = true;
                }
            }    //);

            //finally sort the materials by distance. Bubble sort should in theory be fast here since little changes.
            if (hasAnythingChanged)
            {
                SortByDistance();
            }

            return(hasAnythingChanged);
        }