@script ExecuteInEditMode

var disablePixelLights:boolean = true;
var textureSize:int = 1024;
var renderingLayers:LayerMask = ~20;

private var camCount:int = 0;
private var isVisible:boolean = true;

private var filteredCameras:Hashtable = new Hashtable();
private var layerFilterTexture:RenderTexture = null;
private var oldLayerFilterTextureSize:int = 0;
private var myRenderer:Renderer;
private var myTransform:Transform;
private var shaderMat:Material;

private var renderProjectionShaderStr:String =
    "Shader \"FX/Render Projector\" {" +
    " Properties {" +
    "  _MainTex (\"Texture\", 2D) = \"white\" { TexGen ObjectLinear }" +
    " }" +
    " Subshader {" +
    "  Pass {" +
    "   Tags { \"Queue\" = \"Geometry+10\" }" +
    "   Offset -32,-32" +
    "   Cull Back" +
    
    "   SetTexture[_MainTex] {" +
    "    matrix[_ProjMatrix] combine texture" +
    "   }" +
    "  }" +
    " }" +
    "}";


function Awake() {
    myTransform = transform;
}

function Start() {
    if (renderer) {
        renderer.castShadows = false;
        renderer.receiveShadows = false;
    }
}

function OnBecameVisible() {
    isVisible = true;
}
function OnBecameInvisible() {
    isVisible = false;
}


function OnWillRenderObject() {
    if (!enabled || !renderer || !renderer.enabled || !isVisible) {return;}

    var cam:Camera = Camera.current;
    
    if (!cam) {return;}
    
    var filteringCamera:Camera = CreateCameraObjects(cam);
    var oldPixelLightCount:int = QualitySettings.pixelLightCount;
    if (disablePixelLights) {QualitySettings.pixelLightCount = 0;}
    UpdateCameraModes(cam, filteringCamera);
    filteringCamera.Render();

    if (!shaderMat) {
        shaderMat = Material(renderProjectionShaderStr);
        shaderMat.hideFlags = HideFlags.HideAndDontSave;
        shaderMat.mainTexture =  layerFilterTexture;
        
        renderer.castShadows = false;
        renderer.receiveShadows = false;
    }
    shaderMat.SetMatrix("_ProjMatrix", createProjectionMatrix(cam));
    renderer.sharedMaterial  = shaderMat;


    if (disablePixelLights) {
        QualitySettings.pixelLightCount = oldPixelLightCount;
    }       
}

function OnDisable() {
    shaderMat = null;
    if (layerFilterTexture) {
        DestroyImmediate(layerFilterTexture);
        layerFilterTexture = null;
    }
    for (var item in filteredCameras) {
        DestroyImmediate((item.Value as Camera).gameObject);
    }
    filteredCameras.Clear();
}


private function createProjectionMatrix(camera_:Camera):Matrix4x4 {
    var scaleOffset:Matrix4x4 = Matrix4x4.TRS(Vector3(0.5, 0.5, 0.5), Quaternion.identity, Vector3(0.5, 0.5, 0.5));
    var mtx:Matrix4x4 = Matrix4x4.Scale(Vector3(1.0/myTransform.lossyScale.x, 1.0/myTransform.lossyScale.y, 1.0/myTransform.lossyScale.z));
    return scaleOffset * camera_.projectionMatrix *camera_.worldToCameraMatrix *myTransform.localToWorldMatrix * mtx;
}

private function UpdateCameraModes(src:Camera, dest:Camera):void {
    if (dest == null) {return;}

    dest.clearFlags = src.clearFlags;
    dest.backgroundColor = src.backgroundColor;       
    if (src.clearFlags == CameraClearFlags.Skybox) {
        var sky:Skybox = src.GetComponent(Skybox);
        var mysky:Skybox = dest.GetComponent(Skybox);
        if (!sky || !sky.material) {
            mysky.enabled = false;
        } else {
            mysky.enabled = true;
            mysky.material = sky.material;
        }
    }
    dest.farClipPlane = src.farClipPlane;
    dest.nearClipPlane = src.nearClipPlane;
    dest.orthographic = src.orthographic;
    dest.fieldOfView = src.fieldOfView;
    dest.aspect = src.aspect;
    dest.orthographicSize = src.orthographicSize;
    
    dest.worldToCameraMatrix = src.worldToCameraMatrix;
    dest.cullingMask = renderingLayers.value;
    dest.targetTexture = layerFilterTexture;
}


private function CreateCameraObjects(currentCamera:Camera):Camera {
    var filteringCamera:Camera = null;
   
    if (!layerFilterTexture || oldLayerFilterTextureSize != textureSize) {
        if (layerFilterTexture) {DestroyImmediate(layerFilterTexture);}
        layerFilterTexture = RenderTexture(textureSize, textureSize, 16);
        layerFilterTexture.name = "__LayerFilteredTexture" +GetInstanceID();
        layerFilterTexture.isPowerOfTwo = true;
        layerFilterTexture.hideFlags = HideFlags.DontSave;
        oldLayerFilterTextureSize = textureSize;
    }
   
    filteringCamera = filteredCameras[currentCamera] as Camera;
    if (!filteringCamera) {
        var go:GameObject = new GameObject("Layer Filtering Camera id" +GetInstanceID() +" for " +currentCamera.GetInstanceID(), Camera, Skybox);
        filteringCamera = go.camera;
        filteringCamera.enabled = false;
        filteringCamera.transform.position = myTransform.position;
        filteringCamera.transform.rotation = myTransform.rotation;
        filteringCamera.gameObject.AddComponent("FlareLayer");
        go.hideFlags = HideFlags.HideAndDontSave;
        filteredCameras[currentCamera] = filteringCamera;
        camCount++;
    }
    return filteringCamera;
}