public bool DeleteFromRegistry(ModelMeshPart mesh, TransformMatrix worldMatrix)
 {
     for (var i = 0; i < Index; i++)
     {
         MeshLibrary meshLib = _meshLib[i];
         if (meshLib.HasMesh(mesh))
         {
             if (meshLib.DeleteFromRegistry(worldMatrix)) //if true, we can delete it from registry
             {
                 for (var j = i; j < Index - 1; j++)
                 {
                     //slide down one
                     _meshLib[j] = _meshLib[j + 1];
                 }
                 Index--;
             }
             break;
         }
     }
     if (Index <= 0)
     {
         return(true);            //this material is no longer needed.
     }
     return(false);
 }
        public void Register(ModelMeshPart mesh, TransformMatrix worldMatrix, BoundingSphere boundingSphere)
        {
            bool found = false;

            //Check if we already have a model like that, if yes put it in there!
            for (var i = 0; i < Index; i++)
            {
                MeshLibrary meshLib = _meshLib[i];
                if (meshLib.HasMesh(mesh))
                {
                    meshLib.Register(worldMatrix);
                    found = true;
                    break;
                }
            }

            //We have no Lib yet, make a new one.
            if (!found)
            {
                _meshLib[Index] = new MeshLibrary();
                _meshLib[Index].SetMesh(mesh);
                _meshLib[Index].SetBoundingSphere(boundingSphere);
                _meshLib[Index].Register(worldMatrix);
                Index++;
            }

            //If we exceeded our array length, make the array bigger.
            if (Index >= _meshLib.Length)
            {
                MeshLibrary[] tempLib = new MeshLibrary[Index + 1];
                _meshLib.CopyTo(tempLib, 0);
                _meshLib = tempLib;
            }
        }
        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);
        }