private void Update() { 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 >= 50) { // If there are more than 50 projectors, we remove the first one from // our list and certainly from the decals mesh (the intermediate mesh // format). All the mesh data that belongs to this projector will // be removed. DecalProjector l_DecalProjector = m_DecalProjectors [0]; // The vertex color list has to be updated as well. m_VertexColors.RemoveRange(0, l_DecalProjector.DecalsMeshUpperVertexIndex + 1); 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 - (decalProjectorOffset * l_Ray.direction.normalized); Vector3 l_ForwardDirection = Camera.main.transform.up; Vector3 l_UpDirection = -Camera.main.transform.forward; Quaternion l_ProjectorRotation = Quaternion.LookRotation(l_ForwardDirection, l_UpDirection); // 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, decalProjectorScale, cullingAngle, 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 = Matrix4x4.TRS(l_Terrain.transform.position, Quaternion.identity, Vector3.one) * m_WorldToDecalsMatrix; // Pass the terrain data with the corresponding conversion to the decals mesh. m_DecalsMesh.Add(l_Terrain, l_TerrainToDecalsMatrix); // Cut the data in the decals mesh accoring to the size and position of the decal projector. Offset the // vertices afterwards and pass the newly computed mesh to the decals instance, such that it becomes // visible. m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); m_Decals.UpdateDecalsMeshes(m_DecalsMesh); // Update the vertex colors too. Color l_VertexColor = CurrentColor; int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1; for (int i = 0; i < l_VertexCount; i = i + 1) { m_VertexColors.Add(l_VertexColor); } m_Decals.DecalsMeshRenderers [0].MeshFilter.mesh.colors = m_VertexColors.ToArray(); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); NextColorIndex(); } else { Debug.Log("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, decalProjectorScale, cullingAngle, 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 before we pass it // to the decals instance to be displayed. m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix); m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); m_Decals.UpdateDecalsMeshes(m_DecalsMesh); // Update the vertex colors too. Color l_VertexColor = CurrentColor; int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1; for (int i = 0; i < l_VertexCount; i = i + 1) { m_VertexColors.Add(l_VertexColor); } m_Decals.DecalsMeshRenderers [0].MeshFilter.mesh.colors = m_VertexColors.ToArray(); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); NextColorIndex(); } } } } } }
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(); } } } } } }
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(); } } }