private const float spotFalloff = 2.5f; ///<The outer range where spot light begins to falloff
void OnWizardCreate()
{
//Find all lights
Light[] lights = Object.FindObjectsOfType(typeof(Light)) as Light[];
//Find all renderers with a lightmap that should be lightmapped and all lightmaps that will be used (This is primarily done to show the progress bar)
List <Renderer> models = new List <Renderer>(); //List of all renderers with a lightmap
List <Texture2D> lightmaps = new List <Texture2D>(); //List of all lightmaps that will be used
foreach (Renderer renderer in Object.FindObjectsOfType(typeof(Renderer)))
{
if (renderer.sharedMaterial.HasProperty("_LightMap"))
{
if ((lightmapMode == LightmapMode.ALL) ||
(lightmapMode == LightmapMode.SELECTION && Selected(renderer.transform)) ||
(lightmapMode == LightmapMode.SELECTION_OR_CHILD && SelectedOrChild(renderer.transform))
)
{
Texture2D lightmap = renderer.sharedMaterial.GetTexture("_LightMap") as Texture2D;
if (lightmap)
{
models.Add(renderer);
if (!lightmaps.Contains(lightmap))
{
lightmaps.Add(lightmap);
}
}
}
}
}
//This is just for the progressbar
int totalSteps = lightmaps.Count + models.Count + lightmaps.Count; //Clear + Lightmap + Smoothing
int currentStep = 0;
//Clear all lightmaps
for (int lightmapIndex = 0; lightmapIndex < lightmaps.Count; lightmapIndex++)
{
Texture2D lightmap = lightmaps[lightmapIndex];
EditorUtility.DisplayProgressBar(
"Lightmapping",
string.Format("Clearing lightmaps: {0} ({1} of {2})", lightmap.name, lightmapIndex + 1, lightmaps.Count),
currentStep++ / (float)totalSteps
);
lightmap.SetPixels(new Color[lightmap.width * lightmap.height]); //Set the lightmap to all black
}
for (int rendererIndex = 0; rendererIndex < models.Count; rendererIndex++)
{
Renderer renderer = models[rendererIndex];
EditorUtility.DisplayProgressBar(
"Lightmapping",
string.Format("Calculating Light: {0} ({1} of {2})", renderer.name, rendererIndex + 1, models.Count),
currentStep++ / (float)totalSteps
);
Mesh mesh = ((MeshFilter)renderer.GetComponent(typeof(MeshFilter))).sharedMesh;
Texture2D lightmap = renderer.sharedMaterial.GetTexture("_LightMap") as Texture2D;
Texture2D mainTex = renderer.sharedMaterial.mainTexture as Texture2D; //For alpha lookup
if (lightmap.format == TextureFormat.Alpha8 || lightmap.format == TextureFormat.ARGB32 || lightmap.format == TextureFormat.RGB24)
{
int[] triangles = mesh.triangles;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
Vector2[] mainUV = mesh.uv; //Used for alphaLookup
Vector2[] lightUV = mesh.uv2.Length > 0 ? mesh.uv2 : mesh.uv;
//Transform vertices and normals to global coords
Transform t = renderer.transform;
for (int vert = 0; vert < vertices.Length; vert++)
{
vertices[vert] = t.TransformPoint(vertices[vert]);
}
for (int normal = 0; normal < normals.Length; normal++)
{
normals[normal] = t.TransformDirection(normals[normal]);
}
for (int i = 0; i < triangles.Length; i += 3)
{
//Vertice indexs
int vi0 = triangles[i];
int vi1 = triangles[i + 1];
int vi2 = triangles[i + 2];
//Lightmap UV coords [pixel]
Vector2 pUV0 = new Vector2(lightUV[vi0].x * lightmap.width, lightUV[vi0].y * lightmap.height);
Vector2 pUV1 = new Vector2(lightUV[vi1].x * lightmap.width, lightUV[vi1].y * lightmap.height);
Vector2 pUV2 = new Vector2(lightUV[vi2].x * lightmap.width, lightUV[vi2].y * lightmap.height);
Triangle2D pUVTriangle = new Triangle2D(pUV0, pUV1, pUV2);
//mainTex UV coords [pixel]. These are only used (and computed) if alphaLookup is true (AND there is a mainTex)
Vector2 pMainUV0 = alphaLookup && mainTex ? new Vector2(mainUV[vi0].x * mainTex.width, mainUV[vi0].y * mainTex.height) : Vector2.zero;
Vector2 pMainUV1 = alphaLookup && mainTex ? new Vector2(mainUV[vi1].x * mainTex.width, mainUV[vi1].y * mainTex.height) : Vector2.zero;
Vector2 pMainUV2 = alphaLookup && mainTex ? new Vector2(mainUV[vi2].x * mainTex.width, mainUV[vi2].y * mainTex.height) : Vector2.zero;
//Square of pixels around triangle
Vector4 bounds = new Vector4(
Mathf.Floor(Mathf.Min(Mathf.Min(pUV0.x, pUV1.x), pUV2.x)),
Mathf.Ceil(Mathf.Max(Mathf.Max(pUV0.x, pUV1.x), pUV2.x)),
Mathf.Floor(Mathf.Min(Mathf.Min(pUV0.y, pUV1.y), pUV2.y)),
Mathf.Ceil(Mathf.Max(Mathf.Max(pUV0.y, pUV1.y), pUV2.y))
);
for (float x = bounds.x; x < bounds.y; x++)
{
for (float y = bounds.z; y < bounds.w; y++)
{
Vector2 texel = new Vector2(x + 0.5f, y + 0.5f); //Use the middel of the texel
//Check if the texel is inside the triangle
if (pUVTriangle.IsPointInside(texel))
{
Vector3 bCoords = pUVTriangle.PointToBaryCentric(texel);
Vector3 worldPosition = vertices[vi0] * bCoords.x + vertices[vi1] * bCoords.y + vertices[vi2] * bCoords.z;
Vector3 worldNormal = normals[vi0] * bCoords.x + normals[vi1] * bCoords.y + normals[vi2] * bCoords.z;
Vector3 raycastOffset = worldNormal.normalized * 0.0001f; ///Vector added to worldposition when raycasting so that objects hit themself more consistently
Color texelColor = lightmap.GetPixel((int)x, (int)y);
//The modifier based on the surfaces alpha
float alpha = 1;
if (alphaLookup)
{
if (mainTex && (mainTex.format == TextureFormat.Alpha8 || mainTex.format == TextureFormat.ARGB32))
{
Vector2 mainTexel = bCoords.x * pMainUV0 + bCoords.y * pMainUV1 + bCoords.z * pMainUV2;
alpha = mainTex.GetPixel((int)mainTexel.x, (int)mainTexel.y).a;
}
}
foreach (Light light in lights)
{
Transform lightTransform = light.transform;
float alphaMod = alpha;
//POINT LIGHT
if (light.type == LightType.Point)
{
Vector3 toLight = lightTransform.position - worldPosition;
float distanceToLight = toLight.magnitude;
if (light.range > distanceToLight && !Obstructed(worldPosition + raycastOffset, toLight, distanceToLight, ref alphaMod))
{
float angleMod = AngleMod(toLight, worldNormal);
float attenuationMod = 2 * Mathf.Pow(Mathf.Exp(-distanceToLight / light.range), 5);
texelColor += light.color * light.intensity * angleMod * attenuationMod * alphaMod;
}
}
//SPOT LIGHT
else if (light.type == LightType.Spot)
{
Vector3 toLight = lightTransform.position - worldPosition;
float distanceToLight = toLight.magnitude;
float angleToSpot = Vector3.Angle(-toLight, lightTransform.forward);
if (angleToSpot < light.spotAngle / 2)
{
float projectedDistance = Mathf.Cos(angleToSpot * Mathf.Deg2Rad) * distanceToLight; //Distance to light (projected onto light.forward)
if (light.range > projectedDistance && !Obstructed(worldPosition + raycastOffset, toLight, distanceToLight, ref alphaMod))
{
//Edge falloff
float edgeMod = (spotFalloff - Mathf.Max(angleToSpot - ((light.spotAngle / 2) - spotFalloff), 0)) / spotFalloff;
float angleMod = AngleMod(toLight, worldNormal);
float attenuationMod = Mathf.Max(1 - Mathf.Pow(projectedDistance / light.range, 3), 0);
texelColor += light.color * light.intensity * edgeMod * angleMod * attenuationMod * alphaMod;
}
}
}
//DIRECTIONAL
else if (light.type == LightType.Directional)
{
if (Vector3.Angle(lightTransform.forward, worldNormal) > 90)
{
if (noDirectionalShadow || !Obstructed(worldPosition + raycastOffset, -lightTransform.forward, Mathf.Infinity, ref alphaMod))
{
float angleMod = AngleMod(-lightTransform.forward, worldNormal);
texelColor += light.color * light.intensity * angleMod * alphaMod;
}
}
}
} //END foreach light
lightmap.SetPixel((int)x, (int)y, texelColor);
}
}
}
}
} //END if (format)
else
{
Debug.LogError(string.Format("Unsupported format of lightmap in object {0}", renderer.name));
}
} //END foreach (renderer)
//Smooth and save lightmaps
for (int lightmapIndex = 0; lightmapIndex < lightmaps.Count; lightmapIndex++)
{
Texture2D lightmap = lightmaps[lightmapIndex];
EditorUtility.DisplayProgressBar(
"Lightmapping",
string.Format("Smoothing: {0} ({1} of {2})", lightmap.name, lightmapIndex + 1, lightmaps.Count),
currentStep++ / (float)totalSteps
);
SmoothLightMaps(lightmap);
lightmap.Apply();
//Save to disk
if (saveLightMaps)
{
File.WriteAllBytes(EditorUtility.GetAssetPath(lightmap), lightmap.EncodeToPNG());
}
}
EditorUtility.ClearProgressBar();
}