//
|
// DrawTargetObject.cs
|
//
|
// Dynamic Shadow Projector
|
//
|
// Copyright 2015 NYAHOON GAMES PTE. LTD. All Rights Reserved.
|
//
|
|
using UnityEngine;
|
using System.Collections.Generic;
|
using UnityEngine.Rendering;
|
|
namespace DynamicShadowProjector {
|
[ExecuteAlways]
|
[RequireComponent(typeof(ShadowTextureRenderer))]
|
public class DrawTargetObject : MonoBehaviour {
|
[System.Serializable]
|
public struct ReplaceShader {
|
public string renderType;
|
public Shader shader;
|
}
|
public enum TextureAlignment {
|
None = 0,
|
TargetAxisX,
|
TargetAxisY,
|
TargetAxisZ,
|
}
|
public enum UpdateFunction {
|
OnPreCull = 0,
|
LateUpdate,
|
UpdateTransform,
|
}
|
// Serialize Fields
|
[SerializeField]
|
private Transform m_target;
|
[SerializeField]
|
private Transform m_targetDirection = null;
|
[SerializeField]
|
private LayerMask m_layerMask = -1;
|
[SerializeField]
|
private TextureAlignment m_textureAlignment = TextureAlignment.None;
|
[SerializeField]
|
private UpdateFunction m_updateFunction = UpdateFunction.OnPreCull;
|
[SerializeField]
|
private Material m_shadowShader;
|
[SerializeField]
|
private ReplaceShader[] m_replacementShaders;
|
[SerializeField]
|
private bool m_renderChildren = true;
|
[SerializeField]
|
private bool m_followTarget = true;
|
|
// public properties
|
public Transform target
|
{
|
get { return m_target; }
|
set {
|
if (m_target != value) {
|
m_target = value;
|
SetCommandBufferDirty();
|
}
|
}
|
}
|
|
public Transform targetDirection
|
{
|
get { return m_targetDirection; }
|
set { m_targetDirection = value; }
|
}
|
public bool renderChildren
|
{
|
get { return m_renderChildren; }
|
set {
|
if (m_renderChildren != value) {
|
m_renderChildren = value;
|
SetCommandBufferDirty();
|
}
|
}
|
}
|
public LayerMask layerMask
|
{
|
get { return m_layerMask; }
|
set {
|
if (m_layerMask != value) {
|
m_layerMask = value;
|
if (m_renderChildren) {
|
SetCommandBufferDirty();
|
}
|
}
|
}
|
}
|
public TextureAlignment textureAlignment
|
{
|
get { return m_textureAlignment; }
|
set { m_textureAlignment = value; }
|
}
|
public UpdateFunction updateFunction
|
{
|
get { return m_updateFunction; }
|
set { m_updateFunction = value; }
|
}
|
public bool followTarget
|
{
|
get { return m_followTarget; }
|
set { m_followTarget = value; }
|
}
|
public Material shadowShader
|
{
|
get { return m_shadowShader; }
|
set {
|
if (m_shadowShader != value) {
|
m_shadowShader = value;
|
SetCommandBufferDirty();
|
}
|
}
|
}
|
public ReplaceShader[] replacementShaders
|
{
|
get { return m_replacementShaders; }
|
set {
|
m_replacementShaders = value;
|
SetCommandBufferDirty();
|
}
|
}
|
|
// public functions
|
// Call SetCommandBufferDirty or UpdateCommandBuffer when child objects are added/deleted/disabled/enabled.
|
public void SetCommandBufferDirty()
|
{
|
m_isCommandBufferDirty = true;
|
}
|
|
public void UpdateCommandBuffer()
|
{
|
if (m_target == null) {
|
return;
|
}
|
|
m_commandBuffer.Clear();
|
int materialCount = m_replacementShaders == null ? 0 : m_replacementShaders.Length;
|
|
if (m_renderChildren) {
|
Renderer[] renderers = m_target.gameObject.GetComponentsInChildren<Renderer>();
|
for (int i = -1; i < materialCount; ++i) {
|
foreach (Renderer renderer in renderers) {
|
if ((m_layerMask & (1 << renderer.gameObject.layer)) != 0) {
|
AddDrawCommand(renderer, i);
|
}
|
}
|
}
|
}
|
else {
|
Renderer renderer = m_target.GetComponent<Renderer>();
|
if (renderer != null) {
|
for (int i = -1; i < materialCount; ++i) {
|
AddDrawCommand(renderer, i);
|
}
|
}
|
else if (Debug.isDebugBuild || Application.isEditor) {
|
Debug.LogError("The target object does not have a Renderer component!", m_target);
|
}
|
}
|
m_isCommandBufferDirty = false;
|
}
|
public void UpdateMaterial(Material mat)
|
{
|
if (m_replacedMaterialCache != null) {
|
Material replacedMaterial;
|
if (m_replacedMaterialCache.TryGetValue(mat, out replacedMaterial)) {
|
replacedMaterial.CopyPropertiesFromMaterial(mat);
|
}
|
}
|
}
|
public void UpdateTransform()
|
{
|
if (m_textureAlignment != TextureAlignment.None || m_targetDirection != null) {
|
// rotate projector to align texture parallel to target object.
|
Vector3 up;
|
switch (m_textureAlignment) {
|
case TextureAlignment.TargetAxisX:
|
up = m_target.right;
|
break;
|
case TextureAlignment.TargetAxisZ:
|
up = m_target.forward;
|
break;
|
default:
|
up = m_target.up;
|
break;
|
}
|
Vector3 z = m_targetDirection != null ? m_targetDirection.forward : transform.forward;
|
transform.LookAt(transform.position + z, up);
|
}
|
if (m_followTarget) {
|
Vector3 targetPos = transform.TransformPoint(m_localTargetPosition);
|
transform.position += m_target.position - targetPos;
|
}
|
}
|
|
// private fields
|
private bool m_isCommandBufferDirty;
|
private CommandBuffer m_commandBuffer;
|
private ShadowTextureRenderer m_shadowRenderer;
|
private Vector3 m_localTargetPosition;
|
private Dictionary<Material, Material> m_replacedMaterialCache;
|
|
// message handlers
|
void Awake()
|
{
|
m_shadowRenderer = GetComponent<ShadowTextureRenderer>();
|
if (m_target != null) {
|
m_localTargetPosition = transform.InverseTransformPoint(m_target.position);
|
}
|
CreateCommandBuffer();
|
}
|
|
void OnValidate()
|
{
|
if (m_commandBuffer != null) {
|
UpdateCommandBuffer();
|
}
|
}
|
|
void OnEnable()
|
{
|
if (m_commandBuffer == null) {
|
CreateCommandBuffer();
|
}
|
else if (m_shadowRenderer != null && m_shadowRenderer.isProjectorVisible) {
|
m_shadowRenderer.AddCommandBuffer(m_commandBuffer);
|
}
|
}
|
|
void OnDisable()
|
{
|
if (m_shadowRenderer != null && m_commandBuffer != null) {
|
m_shadowRenderer.RemoveCommandBuffer(m_commandBuffer);
|
}
|
}
|
|
void OnDestroy()
|
{
|
if (m_commandBuffer != null) {
|
m_commandBuffer.Dispose();
|
m_commandBuffer = null;
|
}
|
if (m_replacedMaterialCache != null) {
|
foreach (var pair in m_replacedMaterialCache) {
|
DestroyImmediate(pair.Value);
|
}
|
m_replacedMaterialCache.Clear();
|
}
|
}
|
|
void LateUpdate()
|
{
|
#if UNITY_EDITOR
|
if (!Application.isPlaying) {
|
return;
|
}
|
#endif
|
if (m_updateFunction == UpdateFunction.LateUpdate) {
|
UpdateTransform();
|
}
|
}
|
|
void OnPreCull()
|
{
|
if (m_isCommandBufferDirty) {
|
UpdateCommandBuffer();
|
}
|
#if UNITY_EDITOR
|
if (!Application.isPlaying) {
|
return;
|
}
|
#endif
|
if (m_updateFunction == UpdateFunction.OnPreCull) {
|
UpdateTransform();
|
}
|
}
|
|
void OnVisibilityChanged(bool isVisible)
|
{
|
#if UNITY_EDITOR
|
if (m_shadowRenderer == null) {
|
m_shadowRenderer = GetComponent<ShadowTextureRenderer>();
|
}
|
if (m_commandBuffer == null) {
|
CreateCommandBuffer();
|
UpdateCommandBuffer();
|
return;
|
}
|
if (m_isCommandBufferDirty) {
|
UpdateCommandBuffer();
|
}
|
#endif
|
if (isVisible) {
|
m_shadowRenderer.AddCommandBuffer(m_commandBuffer);
|
}
|
else {
|
m_shadowRenderer.RemoveCommandBuffer(m_commandBuffer);
|
}
|
}
|
|
// helper functions
|
void CreateCommandBuffer()
|
{
|
m_commandBuffer = new CommandBuffer();
|
if (m_shadowRenderer.isProjectorVisible) {
|
m_shadowRenderer.AddCommandBuffer(m_commandBuffer);
|
}
|
m_isCommandBufferDirty = true;
|
}
|
|
void AddDrawCommand(Renderer renderer, int renderTypeIndex)
|
{
|
Material[] materials = renderer.sharedMaterials;
|
for (int i = 0; i < materials.Length; ++i) {
|
Material m = materials[i];
|
if (m == null) {
|
Debug.LogWarning("The target object has a null material!", renderer);
|
continue;
|
}
|
string renderType = m.GetTag("RenderType", false);
|
if (m.shader.name == "Standard") {
|
if (m.IsKeywordEnabled("_ALPHABLEND_ON") || m.IsKeywordEnabled("_ALPHATEST_ON") || m.IsKeywordEnabled("_ALPHAPREMULTIPLY_ON")) {
|
renderType = "Transparent";
|
}
|
}
|
int foundIndex = -1;
|
if (m_replacementShaders != null && !string.IsNullOrEmpty(renderType)) {
|
for (int index = 0; index < m_replacementShaders.Length; ++index) {
|
if (renderType == m_replacementShaders[index].renderType) {
|
foundIndex = index;
|
Shader shader = m_replacementShaders[index].shader;
|
if (renderTypeIndex == index && shader != null) {
|
if (m_replacedMaterialCache == null) {
|
m_replacedMaterialCache = new Dictionary<Material, Material>();
|
}
|
Material replacedMaterial;
|
if (!m_replacedMaterialCache.TryGetValue(m, out replacedMaterial)) {
|
replacedMaterial = new Material(m);
|
replacedMaterial.shader = shader;
|
replacedMaterial.hideFlags = HideFlags.HideAndDontSave;
|
m_replacedMaterialCache.Add(m, replacedMaterial);
|
}
|
else {
|
replacedMaterial.CopyPropertiesFromMaterial(m);
|
replacedMaterial.shader = shader;
|
}
|
m_commandBuffer.DrawRenderer(renderer, replacedMaterial, i);
|
}
|
break;
|
}
|
}
|
}
|
if (foundIndex == -1 && renderTypeIndex == -1) {
|
m_commandBuffer.DrawRenderer(renderer, m_shadowShader, i);
|
}
|
}
|
}
|
|
}
|
}
|