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.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 >= 50) { m_DecalsInstances.RemoveAt(0); } // Instantiate the prefab and get its decals instance. GameObject l_Instance = Instantiate(decalsPrefab) as GameObject; DS_Decals l_Decals = l_Instance.GetComponentInChildren <DS_Decals> (); // Reuse the decals mesh, but be sure to initialize it always for the current // decals instance. m_DecalsMesh.Initialize(l_Decals); // 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; // 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, decalProjectorScale, cullingAngle, meshOffset, m_UVRectangleIndex, m_UVRectangleIndex); // All the mesh data that is now added to the decals mesh // will belong to this projector. 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(); l_Decals.UpdateDecalsMeshes(m_DecalsMesh); // Lightmapping l_Decals.DecalsMeshRenderers [0].MeshRenderer.lightmapIndex = l_MeshRenderer.lightmapIndex; l_Decals.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_Decals); } } } } }