using UnityEngine; using UnityEngine.Rendering; namespace ConformalDecals { public class ProjectionMeshTarget : IProjectionTarget { public const string NodeName = "MESH_TARGET"; // enabled flag public bool enabled; // Target object data public readonly Transform target; public readonly Transform root; public readonly Mesh mesh; public readonly MeshRenderer renderer; // Projection data private Matrix4x4 _decalMatrix; private Vector3 _decalNormal; private Vector3 _decalTangent; // property block private readonly MaterialPropertyBlock _decalMPB; public ProjectionMeshTarget(Transform target, Transform root, MeshRenderer renderer, Mesh mesh, bool useBaseNormal) { this.root = root; this.target = target; this.renderer = renderer; this.mesh = mesh; _decalMPB = new MaterialPropertyBlock(); SetNormalMap(renderer.sharedMaterial, useBaseNormal); } private void SetNormalMap(Material targetMaterial, bool useBaseNormal) { if (useBaseNormal && targetMaterial.HasProperty(DecalPropertyIDs._BumpMap)) { _decalMPB.SetTexture(DecalPropertyIDs._BumpMap, targetMaterial.GetTexture(DecalPropertyIDs._BumpMap)); var normalScale = targetMaterial.GetTextureScale(DecalPropertyIDs._BumpMap); var normalOffset = targetMaterial.GetTextureOffset(DecalPropertyIDs._BumpMap); _decalMPB.SetVector(DecalPropertyIDs._BumpMap_ST, new Vector4(normalScale.x, normalScale.y, normalOffset.x, normalOffset.y)); } else { _decalMPB.SetTexture(DecalPropertyIDs._BumpMap, DecalConfig.BlankNormal); } } public bool Project(Matrix4x4 orthoMatrix, Transform projector, Bounds projectionBounds) { if (projectionBounds.Intersects(renderer.bounds)) { enabled = true; var projectorToTargetMatrix = target.worldToLocalMatrix * projector.localToWorldMatrix; _decalMatrix = orthoMatrix * projectorToTargetMatrix.inverse; _decalNormal = projectorToTargetMatrix.MultiplyVector(Vector3.back).normalized; _decalTangent = projectorToTargetMatrix.MultiplyVector(Vector3.right).normalized; _decalMPB.SetMatrix(DecalPropertyIDs._ProjectionMatrix, _decalMatrix); _decalMPB.SetVector(DecalPropertyIDs._DecalNormal, _decalNormal); _decalMPB.SetVector(DecalPropertyIDs._DecalTangent, _decalTangent); } else { enabled = false; } return enabled; } public void Render(Material decalMaterial, MaterialPropertyBlock partMPB, Camera camera) { if (!enabled) return; _decalMPB.SetFloat(PropertyIDs._RimFalloff, partMPB.GetFloat(PropertyIDs._RimFalloff)); _decalMPB.SetColor(PropertyIDs._RimColor, partMPB.GetColor(PropertyIDs._RimColor)); Graphics.DrawMesh(mesh, target.localToWorldMatrix, decalMaterial, 0, camera, 0, _decalMPB, ShadowCastingMode.Off, true); } public static bool ValidateTarget(Transform target, MeshRenderer renderer, MeshFilter filter) { if (renderer == null) return false; if (filter == null) return false; if (!target.gameObject.activeInHierarchy) return false; var material = renderer.material; if (material == null) return false; if (DecalConfig.IsBlacklisted(material.shader)) return false; if (filter.sharedMesh == null) return false; return true; } } }