Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- // YOUTUBE LINK OF THIS CODE IN-GAME:
- // http://www.youtube.com/watch?v=KR7JQj0JetU&hd=1
- /*
- * Author: Christopher M. Casey
- * http://www.christophermcasey.com/
- * This class provides a lighting layer that cooperates with Futile.
- * The lighting mesh works by overlaying the screen with a variable-resolution
- * mesh that is mapped to a flat white texture with a multiplicative shader.
- * Every frame, vertex colors are updated based on the visible lights in the
- * scene. This allows for fast, nice-looking 2D lighting.
- */
- // We don't inherit any Futile classes because it is not necessary; we want to operate
- // independently from Futile's batching system. This layer will be the only one using
- // its material and it overlays everything else, so it is easier just to add on top of
- // Futile rather than inherit from it.
- public class HNSLightingLayer
- {
- // singleton (only one of these ever)
- public static HNSLightingLayer instance;
- // this is the color of all unlit space -- not totally black but very close
- // use a getter-only property because we can't have const Color, but this should never be set
- private static Color __darknessColor = new Color(0.05f, 0.05f, 0.05f, 1f);
- private static Color _darknessColor
- {
- get { return __darknessColor; }
- }
- // This is the size of each grid square or quad within the light mesh.
- // Smaller values look good but are slower, higher values look bad but are faster.
- // Tweak this for performance!
- private const float _lightGridRes = 16.0f;
- // This is used every update to calculate which lights are visible.
- private List<HNSLight> _visibleLights = new List<HNSLight>();
- // GameObject and its components...
- GameObject _gameObject;
- Transform _transform;
- MeshFilter _meshFilter;
- MeshRenderer _meshRenderer;
- Mesh _mesh;
- Material _material;
- // Use this to update vertex colors every frame
- Color32[] _vertColors;
- // width, height, and width * height of vertices in this mesh
- int _lightGridVertW;
- int _lightGridVertH;
- int _lightGridVertSz;
- // Offset for ensuring that this mesh's vertices always align with the same world space coordinates.
- // If we don't use this, artifacts occur as vertex coordinates change relative to world position of
- // lights and shadows.
- Vector2 _offVector = Vector2.zero;
- // private constructor to support singleton pattern
- private HNSLightingLayer()
- {
- // number of quads or grid squares that will be in the mesh
- int lightGridW = Mathf.CeilToInt(Futile.screen.width / _lightGridRes)+1;
- int lightGridH = Mathf.CeilToInt(Futile.screen.height / _lightGridRes)+1;
- int lightGridSz = lightGridW * lightGridH;
- // number of actual vertices that will be in the mesh
- _lightGridVertW = lightGridW+1;
- _lightGridVertH = lightGridH+1;
- _lightGridVertSz = _lightGridVertW * _lightGridVertH;
- // actual mesh data
- Vector3[] verts = new Vector3[_lightGridVertSz];
- Vector2[] uvs = new Vector2[_lightGridVertSz];
- _vertColors = new Color32[_lightGridVertSz];
- int[] tris = new int[lightGridSz*2*3];
- // fill vertex data - this will make a mesh that overlaps the whole screen
- for (int i=0; i<_lightGridVertSz; i++)
- {
- float col = i % _lightGridVertW;
- float row = i / _lightGridVertW;
- verts[i] = new Vector2(
- col * _lightGridRes - Futile.screen.halfWidth,
- row * _lightGridRes - Futile.screen.halfHeight);
- }
- // since we are just using a plain white texture, UVs don't matter, give it something safe.
- for (int i=0; i<_lightGridVertSz; i++)
- {
- uvs[i] = new Vector2(0.5f, 0.5f);
- }
- // these get updated every frame so they don't matter right now, give it something safe.
- for (int i=0; i<_lightGridVertSz; i++)
- {
- _vertColors[i] = _darknessColor;
- }
- // fill up triangles. clockwise winding order.
- for (int i=0; i<tris.Length; i+=6)
- {
- int vcol = (i/6) % lightGridW;
- int vrow = (i/6) / lightGridW;
- tris[i ] = vrow * _lightGridVertW + vcol;
- tris[i+1] = (vrow+1) * _lightGridVertW + vcol;
- tris[i+2] = (vrow+1) * _lightGridVertW + (vcol+1);
- tris[i+3] = vrow * _lightGridVertW + vcol;
- tris[i+4] = (vrow+1) * _lightGridVertW + (vcol+1);
- tris[i+5] = vrow * _lightGridVertW + (vcol+1);
- }
- // boilerplate GameObject setup: make a GameObject, give it a MeshFilter, MeshRenderer,
- // Material, etc.
- _gameObject = new GameObject("HNSLightingLayer");
- _transform = _gameObject.transform;
- _transform.parent = Futile.instance.gameObject.transform; // add _gameObject to the scene!
- _meshFilter = _gameObject.AddComponent<MeshFilter>();
- _meshRenderer = _gameObject.AddComponent<MeshRenderer>();
- _meshRenderer.castShadows = false;
- _meshRenderer.receiveShadows = false;
- _mesh = _meshFilter.mesh;
- _material = new Material(FShader.Multiplicative.shader); // special sauce, multiplicative blending
- _material.mainTexture = Futile.atlasManager.GetElementWithName("Futile_White").atlas.texture;
- _material.renderQueue = Futile.baseRenderQueueDepth+999999; // this will go on top of EVERYTHING.
- _meshRenderer.renderer.material = _material;
- // since we're uploading new colors every frame, this is a dynamic mesh
- _mesh.MarkDynamic();
- // -----
- // give the mesh all its data, which was calculated already above
- _mesh.vertices = verts;
- _mesh.uv = uvs;
- _mesh.colors32 = _vertColors;
- _mesh.triangles = tris;
- // -----
- // large values ensure this is never culled by Unity
- _mesh.bounds = new Bounds(Vector3.zero, new Vector3(9999999999, 9999999999, 9999999999));
- // this may not be strictly necessary, but doesn't seem to hurt.
- _mesh.Optimize();
- }
- // Singleton constructor (should only ever be one of these lighting meshes!)
- public static HNSLightingLayer Create()
- {
- if (instance == null)
- {
- return (instance = new HNSLightingLayer());
- }
- else
- {
- return instance;
- }
- }
- // This is called by the game update loop every time lights need to be updated (probably once per frame)
- public void Update()
- {
- // Set modulus offset for whole lighting layer (keeps mesh vertices at the same world space
- // location, avoid flickering artifacts)
- _offVector.Set(-(HNSGame.camera.x % _lightGridRes), -(HNSGame.camera.y % _lightGridRes));
- _gameObject.transform.position = _offVector;
- // Only process the lights that are currently visible (their radius intersects with camera)
- // AND are in the active room
- _visibleLights.Clear();
- foreach (HNSLight light in HNSLight.allInstances)
- {
- if (HNSRoom.activeRoom.bounds.Contains(light.position) &&
- light.circle.CheckIntersectWithRect(HNSGame.camera))
- {
- _visibleLights.Add(light);
- }
- }
- // Need some variables for lighting color loop...
- float distRatio, sum, avgR, avgG, avgB;
- Vector2 currentVert = Vector2.zero;
- float[] weights = new float[_visibleLights.Count];
- Rect smallRoomBounds = HNSRoom.activeRoom.bounds.CloneWithExpansion(-64f);
- // Lighting color loop, calculates color at each vertex of our lighting mesh
- for (int i=0; i<_lightGridVertSz; i++)
- {
- // This is the position of the current vertex in screen space
- currentVert.Set(
- (i % _lightGridVertW) * _lightGridRes + _offVector.x,
- (i / _lightGridVertW) * _lightGridRes + _offVector.y);
- // Here we iterate through every visible light and calculate color for this vertex
- // based on its location as a weighted average of its distance from every visible light!
- distRatio = 1f;
- sum = 0f;
- avgR = 0f;
- avgG = 0f;
- avgB = 0f;
- for (int j=0; j<_visibleLights.Count; ++j)
- {
- HNSLight light = _visibleLights[j];
- float tmp = Mathf.Clamp01(
- Vector2.Distance(
- light.position-HNSGame.camera.GetPosition(),currentVert) / light.radius);
- distRatio *= tmp;
- sum += 1f-tmp;
- weights[j] = 1f-tmp;
- }
- if (sum == 0f) sum = 0.000001f; // no dividing by zero!
- for (int j=0; j<weights.Length; ++j)
- {
- weights[j] /= sum;
- }
- for (int j=0; j<_visibleLights.Count; ++j)
- {
- Color clr = _visibleLights[j].color;
- avgR += clr.r * weights[j];
- avgG += clr.g * weights[j];
- avgB += clr.b * weights[j];
- }
- // We have a color for this vertex!
- _vertColors[i] = Color.Lerp(new Color(avgR,avgG,avgB,1f), _darknessColor, distRatio);
- // This ensures that areas outside of the currently active room are always darkened.
- // Can be turned into better-looking occlusion easily, but right now we only care about
- // the current room.
- Vector2 currentVertWorldSpace = currentVert + HNSGame.camera.GetPosition();
- if (!smallRoomBounds.Contains(currentVertWorldSpace))
- {
- Vector2 closestPt =
- smallRoomBounds.GetClosestInteriorPoint(currentVertWorldSpace);
- distRatio =
- Mathf.Clamp01(Vector2.Distance(closestPt,currentVertWorldSpace)/96.0f);
- _vertColors[i] = Color.Lerp(_vertColors[i], _darknessColor, distRatio);
- }
- }
- // All colors are now calculated, register them with the mesh.
- _mesh.colors32 = _vertColors;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement