public void AddDecalProjector(Ray a_Ray, RaycastHit a_RaycastHit)
        {
            // Make sure there are not too many projectors.
            if (m_DecalProjectors.Count >= m_MaximumNumberOfProjectors)
            {
                // If there are more than the maximum number of projectors, we delete
                // the oldest one.
                DecalProjector l_DecalProjector = m_DecalProjectors [0];
                m_DecalProjectors.RemoveAt(0);
                m_DecalsMesh.RemoveProjector(l_DecalProjector);
            }

            // Calculate the position and rotation for the new decal projector.
            Vector3    l_ProjectorPosition = a_RaycastHit.point - (m_DecalProjectorOffset * a_Ray.direction.normalized);
            Quaternion l_ProjectorRotation = ProjectorRotationUtility.ProjectorRotation(Camera.main.transform.forward, Vector3.up);

            // Randomize the rotation.
            Quaternion l_RandomRotation = Quaternion.Euler(0.0f, Random.Range(0.0f, 360.0f), 0.0f);

            l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation;


            // We hit a collider. Next we have to find the mesh that belongs to the collider.
            // That step depends on how you set up your mesh filters and collider relative to
            // each other in the game objects. It is important to have a consistent way in order
            // to have a simpler implementation.

            MeshCollider l_MeshCollider = a_RaycastHit.collider.GetComponent <MeshCollider> ();
            MeshFilter   l_MeshFilter   = a_RaycastHit.collider.GetComponent <MeshFilter> ();

            if (l_MeshCollider != null || l_MeshFilter != null)
            {
                Mesh l_Mesh = null;
                if (l_MeshCollider != null)
                {
                    // Mesh collider was hit. Just use the mesh data from that one.
                    l_Mesh = l_MeshCollider.sharedMesh;
                }
                else if (l_MeshFilter != null)
                {
                    // Otherwise take the data from the shared mesh.
                    l_Mesh = l_MeshFilter.sharedMesh;
                }

                if (l_Mesh != null)
                {
                    // Create the decal projector.
                    DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex);

                    // Add the projector to our list and the decals mesh, such that both are
                    // synchronized. All the mesh data that is now added to the decals mesh
                    // will belong to this projector.
                    m_DecalProjectors.Add(l_DecalProjector);
                    m_DecalsMesh.AddProjector(l_DecalProjector);

                    // Get the required matrices.
                    Matrix4x4 l_WorldToMeshMatrix = a_RaycastHit.collider.renderer.transform.worldToLocalMatrix;
                    Matrix4x4 l_MeshToWorldMatrix = a_RaycastHit.collider.renderer.transform.localToWorldMatrix;

                    // Add the mesh data to the decals mesh, cut and offset it.
                    m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix);
                    m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh);
                    m_DecalsMesh.OffsetActiveProjectorVertices();

                    // The changes are only present in the decals mesh at the moment. We have
                    // to pass them to the decals instance to visualize them.
                    m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh);

                    NextUVRectangleIndex();
                }
            }
        }
        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.C))
            {
                // Remove all projectors.
                while (m_DecalProjectors.Count > 0)
                {
                    m_DecalsMesh.ClearAll();
                    m_DecalProjectors.Clear();

                    // Clearing of the decals mesh means we need to initialize it again.
                    m_DecalsMesh.Initialize(m_DecalsInstance);
                }
                m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh);
            }

            if (Input.GetButtonDown("Fire1"))
            {
                Ray        l_Ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f));
                RaycastHit l_RaycastHit;
                if (Physics.Raycast(l_Ray, out l_RaycastHit, Mathf.Infinity))
                {
                    // Collider hit.

                    // Make sure there are not too many projectors.
                    if (m_DecalProjectors.Count >= m_MaximumNumberOfProjectors)
                    {
                        // If there are more than the maximum number of projectors, we delete
                        // the oldest one.
                        DecalProjector l_DecalProjector = m_DecalProjectors [0];
                        m_DecalProjectors.RemoveAt(0);
                        m_DecalsMesh.RemoveProjector(l_DecalProjector);
                    }

                    // Calculate the position and rotation for the new decal projector.
                    Vector3    l_ProjectorPosition = l_RaycastHit.point - (m_DecalProjectorOffset * l_Ray.direction.normalized);
                    Quaternion l_ProjectorRotation = ProjectorRotationUtility.ProjectorRotation(Camera.main.transform.forward, Vector3.up);

                    // Randomize the rotation.
                    Quaternion l_RandomRotation = Quaternion.Euler(0.0f, Random.Range(0.0f, 360.0f), 0.0f);
                    l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation;

                    TerrainCollider l_TerrainCollider = l_RaycastHit.collider as TerrainCollider;
                    if (l_TerrainCollider != null)
                    {
                        // Terrain collider hit.

                        Terrain l_Terrain = l_TerrainCollider.GetComponent <Terrain> ();
                        if (l_Terrain != null)
                        {
                            // Create the decal projector with all the required information.
                            DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex);

                            // Add the projector to our list and the decals mesh, such that both are
                            // synchronized. All the mesh data that is now added to the decals mesh
                            // will belong to this projector.
                            m_DecalProjectors.Add(l_DecalProjector);
                            m_DecalsMesh.AddProjector(l_DecalProjector);

                            // The terrain data has to be converted to the decals instance's space.
                            Matrix4x4 l_TerrainToDecalsMatrix = m_WorldToDecalsMatrix * Matrix4x4.TRS(l_Terrain.transform.position, Quaternion.identity, Vector3.one);

                            // Pass the terrain data with the corresponding conversion to the decals mesh.
                            m_DecalsMesh.Add(l_Terrain, l_TerrainToDecalsMatrix);

                            // Cut and offset the decals mesh.
                            m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh);
                            m_DecalsMesh.OffsetActiveProjectorVertices();

                            // The changes are only present in the decals mesh at the moment. We have
                            // to pass them to the decals instance to visualize them.
                            m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh);

                            // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle
                            // based on the surface you have hit.
                            NextUVRectangleIndex();
                        }
                        else
                        {
                            Debug.LogError("Terrain is null!");
                        }
                    }
                    else
                    {
                        // We hit a collider. Next we have to find the mesh that belongs to the collider.
                        // That step depends on how you set up your mesh filters and collider relative to
                        // each other in the game objects. It is important to have a consistent way in order
                        // to have a simpler implementation.

                        MeshCollider l_MeshCollider = l_RaycastHit.collider.GetComponent <MeshCollider> ();
                        MeshFilter   l_MeshFilter   = l_RaycastHit.collider.GetComponent <MeshFilter> ();

                        if (l_MeshCollider != null || l_MeshFilter != null)
                        {
                            Mesh l_Mesh = null;
                            if (l_MeshCollider != null)
                            {
                                // Mesh collider was hit. Just use the mesh data from that one.
                                l_Mesh = l_MeshCollider.sharedMesh;
                            }
                            else if (l_MeshFilter != null)
                            {
                                // Otherwise take the data from the shared mesh.
                                l_Mesh = l_MeshFilter.sharedMesh;
                            }

                            if (l_Mesh != null)
                            {
                                // Create the decal projector.
                                DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex);

                                // Add the projector to our list and the decals mesh, such that both are
                                // synchronized. All the mesh data that is now added to the decals mesh
                                // will belong to this projector.
                                m_DecalProjectors.Add(l_DecalProjector);
                                m_DecalsMesh.AddProjector(l_DecalProjector);

                                // Get the required matrices.
                                Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix;
                                Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix;

                                // Add the mesh data to the decals mesh, cut and offset it.
                                m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix);
                                m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh);
                                m_DecalsMesh.OffsetActiveProjectorVertices();

                                // The changes are only present in the decals mesh at the moment. We have
                                // to pass them to the decals instance to visualize them.
                                m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh);

                                // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle
                                // based on the surface you have hit.
                                NextUVRectangleIndex();
                            }
                        }
                    }
                }
            }
        }
        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.C))
            {
                // Remove all decals instances.
                foreach (DS_Decals l_DecalsInstance in m_DecalsInstances)
                {
                    Destroy(l_DecalsInstance.gameObject);
                }
                m_DecalsInstances.Clear();
            }

            if (Input.GetButtonDown("Fire1"))
            {
                Ray        l_Ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f));
                RaycastHit l_RaycastHit;

                // Terrains have no uv2, so we just skip them.
                if
                (Physics.Raycast(l_Ray, out l_RaycastHit, Mathf.Infinity) &&
                 l_RaycastHit.collider as TerrainCollider == null)
                {
                    // Collider hit.

                    // Make sure there are not too many decals instances.
                    if (m_DecalsInstances.Count >= m_MaximumNumberOfDecals)
                    {
                        DS_Decals l_FirstDecalsInstance = m_DecalsInstances [0];
                        Destroy(l_FirstDecalsInstance);
                        m_DecalsInstances.RemoveAt(0);
                    }


                    // Instantiate the prefab and get its decals instance.
                    DS_Decals l_DecalsInstance = Instantiate(m_DecalsPrefab) as DS_Decals;

                    // Reuse the decals mesh, but be sure to initialize it always for the current
                    // decals instance.
                    m_DecalsMesh.Initialize(l_DecalsInstance);

                    // Calculate the position and rotation for the new decal projector.
                    Vector3    l_ProjectorPosition = l_RaycastHit.point - (m_DecalProjectorOffset * l_Ray.direction.normalized);
                    Quaternion l_ProjectorRotation = ProjectorRotationUtility.ProjectorRotation(Camera.main.transform.forward, Vector3.up);

                    // Randomize the rotation.
                    Quaternion l_RandomRotation = Quaternion.Euler(0.0f, Random.Range(0.0f, 360.0f), 0.0f);
                    l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation;

                    // We hit a collider. Next we have to find the mesh that belongs to the collider.
                    // That step depends on how you set up your mesh filters and collider relative to
                    // each other in the game objects. It is important to have a consistent way in order
                    // to have a simpler implementation.

                    MeshCollider l_MeshCollider = l_RaycastHit.collider.GetComponent <MeshCollider> ();
                    MeshFilter   l_MeshFilter   = l_RaycastHit.collider.GetComponent <MeshFilter> ();
                    MeshRenderer l_MeshRenderer = l_RaycastHit.collider.GetComponent <MeshRenderer> ();

                    if (l_MeshCollider != null || l_MeshFilter != null)
                    {
                        Mesh l_Mesh = null;
                        if (l_MeshCollider != null)
                        {
                            // Mesh collider was hit. Just use the mesh data from that one.
                            l_Mesh = l_MeshCollider.sharedMesh;
                        }
                        else if (l_MeshFilter != null)
                        {
                            // Otherwise take the data from the shared mesh.
                            l_Mesh = l_MeshFilter.sharedMesh;
                        }

                        if (l_Mesh != null)
                        {
                            // Create the decal projector.
                            DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex);

                            // Add the decals instance to our list and the projector
                            // to the decals mesh.
                            // All the mesh data that is now added to the decals mesh
                            // will belong to this projector.
                            m_DecalsInstances.Add(l_DecalsInstance);
                            m_DecalsMesh.AddProjector(l_DecalProjector);

                            // Get the required matrices.
                            Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix;
                            Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix;

                            // Add the mesh data to the decals mesh, cut and offset it.
                            m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix);
                            m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh);
                            m_DecalsMesh.OffsetActiveProjectorVertices();

                            // The changes are only present in the decals mesh at the moment. We have
                            // to pass them to the decals instance to visualize them.
                            l_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh);

                            // Lightmapping
                            l_DecalsInstance.DecalsMeshRenderers [0].MeshRenderer.lightmapIndex        = l_MeshRenderer.lightmapIndex;
                            l_DecalsInstance.DecalsMeshRenderers [0].MeshRenderer.lightmapTilingOffset = l_MeshRenderer.lightmapTilingOffset;

                            // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle
                            // based on the surface you have hit.
                            NextUVRectangleIndex(l_DecalsInstance);
                        }
                    }
                }
            }
        }