ArabDesert/Assets/Editor/x64/Bakery/scripts/ftBuildLights.cs

1146 lines
40 KiB
C#
Raw Normal View History

2024-05-25 09:10:35 +03:00
#if UNITY_EDITOR
// Disable 'obsolete' warnings
#pragma warning disable 0618
#define SRGBCONVERT
//#define OPTIMIZEDAREA // BSP
#define OPTIMIZEDAREA2 // efficient weighted sampling
#define LAUNCH_VIA_DLL
//#define DEBUGMESHDATA
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine.Rendering;
using System.Reflection;
public class ftBuildLights
{
static Dictionary<UnityEngine.Object, int> tex2hash;
static Dictionary<string, bool> lightSaved;
static bool allowOverwrite = false;
static System.Type texUtil;
static MethodInfo texUtil_GetUsage;
static public void InitMaps(bool overwrite)
{
allowOverwrite = overwrite;
tex2hash = new Dictionary<UnityEngine.Object, int>();
lightSaved = new Dictionary<string, bool>();
}
static public void BuildDirectLight(BakeryDirectLight obj, int SAMPLES, bool ignoreNormal = false, string outName = "direct.bin")
{
if (!allowOverwrite && lightSaved.ContainsKey(outName)) return;
lightSaved[outName] = true;
var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender";
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create));
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName);
f.Write(obj.transform.forward.x);
f.Write(obj.transform.forward.y);
f.Write(obj.transform.forward.z);
#if SRGBCONVERT
f.Write(obj.color.linear.r * obj.intensity);
f.Write(obj.color.linear.g * obj.intensity);
f.Write(obj.color.linear.b * obj.intensity);
#else
f.Write(obj.color.r * obj.intensity);
f.Write(obj.color.g * obj.intensity);
f.Write(obj.color.b * obj.intensity);
#endif
f.Write(obj.shadowSpread);
f.Write(SAMPLES);
f.Write(obj.cloudShadowTilingX);
f.Write(obj.cloudShadowTilingY);
f.Write(obj.cloudShadowOffsetX);
f.Write(obj.cloudShadowOffsetY);
f.Write(ignoreNormal);
f.Write((short)0);
if (obj.cloudShadow != null)
{
var tex = obj.cloudShadow;
int existingTexHash;
string texName = "";
if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1;
if (existingTexHash < 0)
{
int texHash = tex.GetHashCode();
tex2hash[tex] = texHash;
existingTexHash = texHash;
}
texName = "cookie_" + existingTexHash + ".dds";
// Save original texture to RGBA32F DDS
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveCookie((tex as Texture2D).GetNativeTexturePtr(),
folder + "/" + texName
);
GL.IssuePluginEvent(4);
}
else
{
var a = ftBuildGraphics.InputDataFromTex(tex, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
f.Write(texName);
}
else
{
f.Write((byte)0);
}
f.Close();
}
static public void BuildSkyLight(BakerySkyLight obj, int SAMPLES, bool texDirty, string outName = "sky.bin")
{
if (!allowOverwrite && lightSaved.ContainsKey(outName)) return;
lightSaved[outName] = true;
var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender";
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create));
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName);
#if SRGBCONVERT
f.Write(obj.color.linear.r * obj.intensity);
f.Write(obj.color.linear.g * obj.intensity);
f.Write(obj.color.linear.b * obj.intensity);
#else
f.Write(obj.color.r * obj.intensity);
f.Write(obj.color.g * obj.intensity);
f.Write(obj.color.b * obj.intensity);
#endif
f.Write(SAMPLES);
f.Write(obj.hemispherical);
var texName = obj.cubemap != null ? GetTempTexName(obj.cubemap, "sky") : "sky.dds";
f.Write(texName);
f.Close();
if (texDirty)
{
/*
// Disable cubemap compression, so texture is half-float
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj.cubemap)) as TextureImporter;
if (importer.textureCompression != TextureImporterCompression.Uncompressed)
{
importer.textureCompression = TextureImporterCompression.Uncompressed;
importer.SaveAndReimport();
}
*/
var tform = obj.transform;
var rh = tform.right;
var up = tform.up;
var fw = tform.forward;
bool isDoubleLDR = false;
bool isRGBM = false;
// Find out texture encoding
// Even if pixel format is the same, different encoding rules (dLDR, RGBM) can be used
if (texUtil == null) texUtil = Assembly.Load("UnityEditor.dll").GetType("UnityEditor.TextureUtil");
if (texUtil == null)
{
Debug.LogError("TextureUtil class cannot be found");
}
else
{
if (texUtil_GetUsage == null) texUtil_GetUsage = texUtil.GetMethod("GetUsageMode", BindingFlags.Static | BindingFlags.Public);
if (texUtil_GetUsage == null)
{
Debug.LogError("TextureUtil::GetUsage cannot be found");
}
else
{
int usage = (int)texUtil_GetUsage.Invoke(null, new object[]{obj.cubemap});
isDoubleLDR = usage == 1 // BakedLightmapDoubleLDR
|| usage == 7;// DoubleLDR
isRGBM = usage == 5; // RGBMEncoded
}
}
bool isLinear = PlayerSettings.colorSpace == ColorSpace.Linear;
if (obj.correctRotation)
{
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveSky(obj.cubemap.GetNativeTexturePtr(),
rh.x,
up.x,
fw.x,
rh.y,
up.y,
fw.y,
rh.z,
up.z,
fw.z,
folder + "/" + texName,
isLinear,
isDoubleLDR,
isRGBM
);
GL.IssuePluginEvent(3); // convert cubemap to small lat/lon DDS
}
else
{
var a = ftBuildGraphics.InputDataFromCubemap(obj.cubemap as Texture, Matrix4x4.TRS(Vector3.zero, obj.transform.rotation, Vector3.one).transpose, isLinear, isDoubleLDR, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
}
else
{
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveSky(obj.cubemap.GetNativeTexturePtr(),
obj.transform.right.x,
obj.transform.right.y,
obj.transform.right.z,
obj.transform.up.x,
obj.transform.up.y,
obj.transform.up.z,
obj.transform.forward.x,
obj.transform.forward.y,
obj.transform.forward.z,
folder + "/" + texName,
isLinear,
isDoubleLDR,
isRGBM
);
GL.IssuePluginEvent(3); // convert cubemap to small lat/lon DDS
}
else
{
var a = ftBuildGraphics.InputDataFromCubemap(obj.cubemap as Texture, Matrix4x4.TRS(Vector3.zero, obj.transform.rotation, Vector3.one), isLinear, isDoubleLDR, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
}
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
}
}
#if OPTIMIZEDAREA
class BSPNode
{
public BSPNode left, right;
public float probabilityDivide = 0.0f;
public List<int> leafIndices;
};
static int GetRandomTriFromBSP(BSPNode bspNode, float rndValue)
{
//Debug.LogError(bspNode.probabilityDivide);
if (bspNode.leafIndices != null)
{
return bspNode.leafIndices[Random.Range(0, bspNode.leafIndices.Count)];
}
if (rndValue < bspNode.probabilityDivide)
{
return GetRandomTriFromBSP(bspNode.left, rndValue);// != null ? bspNode.left : bspNode.right, rndValue);
}
else
{
return GetRandomTriFromBSP(bspNode.right, rndValue);// != null ? bspNode.right : bspNode.left, rndValue);
}
}
static BSPNode BuildProbabilityBSP(int[] triIndices, float[] area, int start, int end, int depth, float parentGlobalOffset, float parentGlobalEnd)
{
if (depth > 100) return null;
var bspNode = new BSPNode();
int startIndex = triIndices[start];
int endIndex = triIndices[end];
// Decide where to split
//float probabilityDivide = (area[startIndex] + area[endIndex]) * 0.5f;/// (end - start);// * 0.5f;
float probabilitySum = 0;
int divisor = start;
for(int i=start; i<=end; i++)
{
int triIndex = triIndices[i];
probabilitySum += area[triIndex];
//if (probabilitySum >= probabilityDivide)
//if (area[triIndex] >= probabilityDivide)
{
//divisor = i;
//break;
}
}
float probabilityDivide = probabilitySum / (end - start);
//probabilitySum = 0;
for(int i=start; i<=end; i++)
{
int triIndex = triIndices[i];
//probabilitySum += area[triIndex];
//if (probabilitySum >= probabilityDivide)
if (area[triIndex] >= probabilityDivide)
{
divisor = i - 1;
break;
}
}
if (divisor < 0) divisor = 0;
int beforeDivisorIndex = divisor > 0 ? triIndices[divisor-1] : 0;
int divisorIndex = triIndices[divisor];
//Debug.LogError(start+" "+end+" "+divisor+" "+probabilityDivide);
/*
// Create new BSP depth layer, if needed
if (layers.Count <= depth)
{
int numElements = triIndices.Length; // conservative?
layers[depth] = new int[numElements];
}
*/
//bspNode.probabilityDivide = probabilityDivide;
float probabilitySumLeft = 0;
float probabilitySumRight = 0;
for(int i=start; i<=end; i++)
{
int triIndex = triIndices[i];
if (i <= divisor)
{
probabilitySumLeft += area[triIndex];
}
else
{
probabilitySumRight += area[triIndex];
}
}
//probabilitySumLeft /= divisor - start + 1;
//probabilitySumRight /= end - divisor;
float probabilityLength = probabilitySumLeft + probabilitySumRight;
//bspNode.probabilityDivide = parentGlobalOffset + (probabilitySumLeft / probabilityLength) * parentGlobalPercent;
bspNode.probabilityDivide = Mathf.Lerp(parentGlobalOffset, parentGlobalEnd, probabilitySumLeft / probabilityLength);
//bspNode.leafIndex = startIndex;
bool isLeaf = true;
if (divisor != start && divisor != end)
{
//Debug.LogError("["+depth+"] Divide global " + bspNode.probabilityDivide+" "+start+" "+divisor+" "+end+" "+probabilitySumLeft + " "+probabilitySumRight+" "+parentGlobalOffset+" "+parentGlobalEnd);
// Split left part
int newStart = start;
int newEnd = divisor > 0 ? divisor : 0;
//Debug.LogError("left");
bspNode.left = BuildProbabilityBSP(triIndices, area, newStart, newEnd, depth + 1, parentGlobalOffset, bspNode.probabilityDivide);
// Split right part
newStart = divisor + 1;
newEnd = end;
//Debug.LogError("right");
bspNode.right = BuildProbabilityBSP(triIndices, area, newStart, newEnd, depth + 1, bspNode.probabilityDivide, parentGlobalEnd);
isLeaf = false;
}
if (isLeaf)
{
bspNode.leafIndices = new List<int>();
string l = "";
for(int i=start; i<=end; i++)
{
int triIndex = triIndices[i];
bspNode.leafIndices.Add(triIndex);
l += area[triIndex] + ", ";
}
//Debug.LogError("Leaf: " + l);
}
return bspNode;
}
#endif
static public float BuildLight(BakeryLightMesh obj, int SAMPLES, Vector3[] corners, string outName = "lights.bin",
List<Vector3> vplData = null)
{
if (!allowOverwrite && lightSaved.ContainsKey(outName)) return 0.0f;
lightSaved[outName] = true;
var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender";
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create));
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName);
f.Write(1);
Mesh mesh = null;
var tform = obj.transform;
Vector3[] verts;
Vector2[] uv = null;
int[] indices;
int tris;
if (corners == null)
{
mesh = ftBuildGraphics.GetSharedMeshBaked(obj.gameObject);
verts = mesh.vertices;
indices = mesh.triangles;
tris = indices.Length / 3;
if (obj.texture != null) uv = mesh.uv;
}
else
{
verts = corners;
indices = new int[6];
indices[0] = 2;
indices[1] = 1;
indices[2] = 0;
indices[3] = 0;
indices[4] = 3;
indices[5] = 2;
tris = 2;
if (obj.texture != null)
{
uv = new Vector2[4];
uv[0] = new Vector2(0,0);
uv[1] = new Vector2(0,1);
uv[2] = new Vector2(1,1);
uv[3] = new Vector2(1,0);
}
}
float[] area = new float[tris];
#if (OPTIMIZEDAREA || OPTIMIZEDAREA2)
#else
float minArea = float.MaxValue;
float maxArea = -float.MaxValue;
#endif
float totalWorldArea = 0;
//Vector2[] uv = null;
int downsampleRes = 0;
float[] pixels = null;
string texName = "";
if (obj.texture != null)
{
//uv = mesh.uv;
var tex = obj.texture;
// Save original texture to RGBA32F DDS
int existingTexHash;
if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1;
if (existingTexHash < 0)
{
int texHash = tex.GetHashCode();
tex2hash[tex] = texHash;
existingTexHash = texHash;
}
texName = "areatex_" + existingTexHash + ".dds";
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveCookie(tex.GetNativeTexturePtr(),
folder + "/" + texName
);
GL.IssuePluginEvent(4);
}
else
{
var a = ftBuildGraphics.InputDataFromTex(tex, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
// Get downsampled (via mips) texture
downsampleRes = (int)Mathf.Sqrt(SAMPLES);
if (downsampleRes == 0) downsampleRes = 1;
var downsampleRT = new RenderTexture(downsampleRes, downsampleRes, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var downsampleTex = new Texture2D(downsampleRes, downsampleRes, TextureFormat.RGBAFloat, false, true);
Graphics.Blit(tex, downsampleRT);
Graphics.SetRenderTarget(downsampleRT);
downsampleTex.ReadPixels(new Rect(0,0,downsampleRes,downsampleRes), 0, 0, false);
downsampleTex.Apply();
var bytes = downsampleTex.GetRawTextureData();
RenderTexture.active = null;
Object.DestroyImmediate(downsampleTex);
downsampleRT.Release();
pixels = new float[bytes.Length / 4];
System.Buffer.BlockCopy(bytes, 0, pixels, 0, bytes.Length);
}
for(int j=0; j<tris; j++)
{
var v1 = verts[indices[j*3]];
var v2 = verts[indices[j*3 + 1]];
var v3 = verts[indices[j*3 + 2]];
if (corners == null)
{
v1 = tform.TransformPoint(v1);
v2 = tform.TransformPoint(v2);
v3 = tform.TransformPoint(v3);
}
#if (OPTIMIZEDAREA || OPTIMIZEDAREA2)
area[j] = Vector3.Cross(v2 - v1, v3 - v1).magnitude;
if (area[j] > 0) totalWorldArea += area[j];
#else
area[j] = Vector3.Cross(v2 - v1, v3 - v1).magnitude;
if (area[j] > 0) totalWorldArea += area[j];
if (area[j] > 0)
{
minArea = Mathf.Min(minArea, area[j]);
maxArea = Mathf.Max(maxArea, area[j]);
}
#endif
}
#if OPTIMIZEDAREA2
// New 2
var randomTriIndices = new int[SAMPLES];
// When an area light is with a width or height of 0, this will avoid an OOR exception (this will keep the baking running instead of crashing it...)
float invTotalArea = totalWorldArea == 0 ? 0 : (1.0f / (totalWorldArea * 0.5f));
float sumWeights = 0.0f;
for(int j=0; j<tris; j++)
{
area[j] *= invTotalArea * 0.5f;
sumWeights += area[j];
}
float sampleWidth = sumWeights / SAMPLES;
int outputSampleIx = -1;
float weightSoFar = -Random.value * sampleWidth;
for(int i=0; i<SAMPLES; i++)
{
float sampleDist = i * sampleWidth;
while(sampleDist >= weightSoFar && outputSampleIx + 1 < tris)
{
weightSoFar += area[++outputSampleIx];
}
randomTriIndices[i] = outputSampleIx;
}
#elif OPTIMIZEDAREA
// New
// Collect indices to triangles
var triIndices = new int[tris];
float invTotalArea = 1.0f / (totalWorldArea * 0.5f);
for(int j=0; j<tris; j++)
{
area[j] *= invTotalArea * 0.5f;
triIndices[j] = j;
}
// Sort triangle indices by area (probability)
// Smaller -> Larger
System.Array.Sort(triIndices, delegate(int a, int b)
{
return area[a].CompareTo(area[b]);
});
// Put triangle indices into a BSP tree based on area
int start = 0;
int end = triIndices.Length - 1;
//var bspLayers = new List<int[]>(); // tri index array per depth level
var bspRoot = BuildProbabilityBSP(triIndices, area, start, end, 0, 0.0f, 1.0f);
#else
// Legacy
if (maxArea / minArea > 65535)
{
minArea = maxArea / 65535;
}
float invMinArea = 1.0f / minArea;
for(int j=0; j<tris; j++)
{
area[j] *= invMinArea;
area[j] = Mathf.Round(area[j]);
}
int skipped = 0;
var uniformTriList = new List<int>();
for(int j=0; j<tris; j++)
{
var tarea = area[j];
if (tarea > 0 && tarea < 65536)
{
for(int k=0; k<tarea; k++)
{
uniformTriList.Add(j);
}
}
else
{
skipped++;
}
}
if (skipped > 0) Debug.LogError("Skipped " + skipped + " invalid triangles out of " + tris + " on LightMesh " + obj.name + " (area is too big?)");
#endif
f.Write(obj.shadowmask ? 0 : obj.samples2);
f.Write(obj.shadowmaskFalloff ? -SAMPLES : SAMPLES);
Vector3 trinormal;
for(int sample=0; sample<SAMPLES; sample++)
{
#if OPTIMIZEDAREA2
int tri = randomTriIndices[sample];
#elif OPTIMIZEDAREA
int tri = GetRandomTriFromBSP(bspRoot, Random.value);
//Debug.LogError(tri);
#else
int rndTri = Random.Range(0, uniformTriList.Count);
int tri = uniformTriList.Count > 0 ? uniformTriList[rndTri] : 0;
#endif
var rndA = Random.value;
var rndB = Random.value;
var rndC = Random.value;
#if DEBUGMESHDATA
if ((tri*3+2) >= indices.Length)
{
Debug.LogError("tri*3+2 >= indices.Length for " + obj.name);
return 0.0f;
}
else if (indices[tri*3+2] >= verts.Length)
{
Debug.LogError("indices[tri*3+2] >= verts.Length for " + obj.name);
return 0.0f;
}
#endif
var A = verts[indices[tri*3]];
var B = verts[indices[tri*3+1]];
var C = verts[indices[tri*3+2]];
var point = (1.0f - Mathf.Sqrt(rndA)) * A + (Mathf.Sqrt(rndA) * (1.0f - rndB)) * B + (Mathf.Sqrt(rndA) * rndB) * C;
if (corners == null) point = tform.TransformPoint(point);
trinormal = Vector3.Cross(A - B, B - C);//.normalized;
float len = Mathf.Sqrt(trinormal.x*trinormal.x + trinormal.y*trinormal.y + trinormal.z*trinormal.z);
trinormal /= len;
if (corners == null) trinormal = tform.TransformDirection(trinormal);
point += trinormal * 0.001f;
f.Write(point.x);
f.Write(point.y);
f.Write(point.z);
if (vplData != null) vplData.Add(point);
f.Write(trinormal.x);
f.Write(trinormal.y);
f.Write(trinormal.z);
if (vplData != null) vplData.Add(trinormal);
if (obj.texture != null)
{
var tA = uv[indices[tri*3]];
var tB = uv[indices[tri*3+1]];
var tC = uv[indices[tri*3+2]];
var tpoint = (1.0f - Mathf.Sqrt(rndA)) * tA + (Mathf.Sqrt(rndA) * (1.0f - rndB)) * tB + (Mathf.Sqrt(rndA) * rndB) * tC;
int tx = (int)((tpoint.x - Mathf.Floor(tpoint.x)) * (downsampleRes - 1));
int ty = (int)((tpoint.y - Mathf.Floor(tpoint.y)) * (downsampleRes - 1));
int pixelIndex = ty * downsampleRes + tx;
if (pixelIndex*4+2 < pixels.Length)
{
float cr = pixels[pixelIndex * 4];
float cg = pixels[pixelIndex * 4 + 1];
float cb = pixels[pixelIndex * 4 + 2];
f.Write(cr);
f.Write(cg);
f.Write(cb);
if (vplData != null) vplData.Add(new Vector3(cr, cg, cb));
}
else
{
f.Write(0.0f);
f.Write(0.0f);
f.Write(0.0f);
if (vplData != null) vplData.Add(Vector3.one);
}
}
else if (vplData != null)
{
vplData.Add(Vector3.one);
}
//var g = GameObject.CreatePrimitive(PrimitiveType.Sphere);
//g.transform.position = point;
//g.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
}
f.Write(obj.cutoff);
f.Write(totalWorldArea * 0.5f);
#if SRGBCONVERT
f.Write(obj.color.linear.r * obj.intensity);
f.Write(obj.color.linear.g * obj.intensity);
f.Write(obj.color.linear.b * obj.intensity);
#else
f.Write(obj.color.r * obj.intensity);
f.Write(obj.color.g * obj.intensity);
f.Write(obj.color.b * obj.intensity);
#endif
f.Write(obj.lmid);
if (obj.texture != null)
{
f.Write(texName);
}
f.Close();
return totalWorldArea * 0.5f;
}
static public string GetTempTexName(Object tex, string prefix = "cookie_")
{
int existingTexHash;
if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1;
if (existingTexHash < 0)
{
int texHash = tex.GetHashCode();
tex2hash[tex] = texHash;
existingTexHash = texHash;
}
return prefix + existingTexHash + ".dds";
}
static public bool BuildLight(BakeryPointLight obj, int SAMPLES, bool texDirty, bool ignoreNormal = false, string outName = "pointlight.bin")
{
if (!allowOverwrite && lightSaved.ContainsKey(outName)) return false;
lightSaved[outName] = true;
bool isError = false;
var folder = ftBuildGraphics.scenePath;//Directory.GetParent(Application.dataPath).FullName + "/frender";
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
var f = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create));
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName);
f.Write(SAMPLES);
f.Write(obj.cutoff);
float fakeDistMult = 1.0f;
float falloffMinRadius = obj.falloffMinRadius;
if (!obj.realisticFalloff)
{
fakeDistMult = (1.0f / obj.cutoff) * 5.0f;
falloffMinRadius = 1;
}
f.Write(fakeDistMult);
f.Write(falloffMinRadius);
#if SRGBCONVERT
f.Write(obj.color.linear.r * obj.intensity);
f.Write(obj.color.linear.g * obj.intensity);
f.Write(obj.color.linear.b * obj.intensity);
#else
f.Write(obj.color.r * obj.intensity);
f.Write(obj.color.g * obj.intensity);
f.Write(obj.color.b * obj.intensity);
#endif
var pos = obj.transform.position;
f.Write(pos.x);
f.Write(pos.y);
f.Write(pos.z);
f.Write(obj.shadowSpread);
f.Write(ignoreNormal);
bool isCookie = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie != null;
bool isCone = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cone;
bool isCubemap = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap != null;
bool isIES = obj.projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile != null;
int existingTexHash;
string texName = "";
UnityEngine.Object tex = null;
if (isCookie || isCubemap || isIES)
{
if (isCookie)
{
tex = obj.cookie;
}
else if (isCubemap)
{
tex = obj.cubemap;
}
else
{
tex = obj.iesFile;
}
if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1;
if (existingTexHash < 0)
{
int texHash = tex.GetHashCode();
tex2hash[tex] = texHash;
existingTexHash = texHash;
}
texName = "cookie_" + existingTexHash + ".dds";
}
if (isCone)
{
f.Write(obj.transform.forward.x);
f.Write(obj.transform.forward.y);
f.Write(obj.transform.forward.z);
f.Write(obj.angle);
f.Write(obj.innerAngle / 100.0f);
}
if (isCookie || isCubemap || isIES)
{
if (isIES && obj.directionMode == BakeryPointLight.Direction.PositiveZ)
{
f.Write(obj.transform.right.x);
f.Write(obj.transform.right.y);
f.Write(obj.transform.right.z);
f.Write(-obj.transform.forward.x);
f.Write(-obj.transform.forward.y);
f.Write(-obj.transform.forward.z);
f.Write(obj.transform.up.x);
f.Write(obj.transform.up.y);
f.Write(obj.transform.up.z);
}
else
{
f.Write(obj.transform.right.x);
f.Write(obj.transform.right.y);
f.Write(obj.transform.right.z);
f.Write(obj.transform.up.x);
f.Write(obj.transform.up.y);
f.Write(obj.transform.up.z);
f.Write(obj.transform.forward.x);
f.Write(obj.transform.forward.y);
f.Write(obj.transform.forward.z);
}
f.Write(texName);
}
if (isCookie) f.Write(obj.angle);
if (texDirty)
{
if (!SavePointLightTexture(tex, folder, texName, isCookie, isCubemap, isIES)) isError = true;
}
f.Close();
return isError;
}
static void WriteNullTerminatedStringWithNewLine(BinaryWriter f, string s)
{
for(int i=0; i<s.Length; i++) f.Write(s[i]);
f.Write((byte)0);
f.Write((byte)'\n');
}
static bool SavePointLightTexture(UnityEngine.Object tex, string folder, string texName, bool isCookie, bool isCubemap, bool isIES)
{
if (File.Exists(folder + "/" + texName))
{
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
return true;
}
if (isCookie)
{
// Save original texture to RGBA32F DDS
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveCookie((tex as Texture2D).GetNativeTexturePtr(),
folder + "/" + texName
);
GL.IssuePluginEvent(4);
}
else
{
var a = ftBuildGraphics.InputDataFromTex(tex as Texture, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
}
else if (isCubemap)
{
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
{
ftBuildGraphics.InitShaders();
ftBuildGraphics.SaveSky((tex as Cubemap).GetNativeTexturePtr(),
0,
0,
1,
0,
1,
0,
1,
0,
0,
folder + "/" + texName,
true,
false,
false
);
GL.IssuePluginEvent(3); // convert cubemap
}
else
{
var a = ftBuildGraphics.InputDataFromCubemap(tex as Texture, Matrix4x4.identity, true, false, ftBuildGraphics.TexInputType.FloatColor);
ftBuildGraphics.SaveCookieFromRAM(a,
folder + "/" + texName
);
}
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
}
else if (isIES)
{
var startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.WorkingDirectory = folder;
startInfo.FileName = Application.dataPath + "/Editor/x64/Bakery/ies2tex.exe";
startInfo.CreateNoWindow = true;
//startInfo.Arguments = "../" + AssetDatabase.GetAssetPath(obj.iesFile) + " " + "cookie" + obj.UID + ".dds 1";
startInfo.Arguments = "\"" + Application.dataPath + "/../" + AssetDatabase.GetAssetPath(tex) + "\" " + texName + " 1";
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(texName);
#if LAUNCH_VIA_DLL
startInfo.Arguments = "\"" + Application.dataPath + "/../" + AssetDatabase.GetAssetPath(tex) + "\" \"" + folder + "/" + texName + "\" 1";
Debug.Log("Running ies2tex " + startInfo.Arguments);
var crt = ftRenderLightmap.ProcessCoroutine("ies2tex", startInfo.Arguments, false);
while(crt.MoveNext()) {}
int errCode = ftRenderLightmap.lastReturnValue;
#else
var exeProcess = System.Diagnostics.Process.Start(startInfo);
exeProcess.WaitForExit();
int errCode = exeProcess.ExitCode;
#endif
if (errCode!=0)
{
ftRenderLightmap.DebugLogError("ies2tex error: "+ ftErrorCodes.TranslateI2T(errCode));
Debug.LogError("ies2tex error: "+ ftErrorCodes.TranslateI2T(errCode) + " with args " + startInfo.Arguments);
return false;
}
}
return true;
}
static public bool BuildLights(BakeryPointLight[] allPoints, int start, int end, bool[] skipLight,
int sampleDiv, bool ignoreNormal = false, string outName = "batchpointlight.bin")
{
if (!allowOverwrite && lightSaved.ContainsKey(outName)) return false;
lightSaved[outName] = true;
//Debug.LogError("---Build:");
bool isError = false;
var folder = ftBuildGraphics.scenePath;
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
var flights = new BinaryWriter(File.Open(folder + "/" + outName, FileMode.Create));
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add(outName);
int numLights = 0;
for(int L=start; L<=end; L++)
{
if (!skipLight[L]) numLights++;
}
flights.Write(numLights);
for(int i=start; i<=end; i++)
{
if (skipLight[i]) continue;
//Debug.LogError(obj.name);
var obj = allPoints[i];
var tform = obj.transform;
var projMode = obj.projMode;
if (projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie == null)
{
Debug.LogError("No cookie texture is set for light " + obj.name);
projMode = BakeryPointLight.ftLightProjectionMode.Omni;
}
if (projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap == null)
{
Debug.LogError("No cubemap set for light " + obj.name);
projMode = BakeryPointLight.ftLightProjectionMode.Omni;
}
if (projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile == null)
{
Debug.LogError("No IES file is set for light " + obj.name);
projMode = BakeryPointLight.ftLightProjectionMode.Omni;
}
Vector3 right, up, forward;
if (projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.directionMode == BakeryPointLight.Direction.PositiveZ)
{
right = tform.right;
up = -tform.forward;
forward = tform.up;
}
else
{
right = tform.right;
up = tform.up;
forward = tform.forward;
}
float projParam1 = 0.0f;
float projParam2 = 0.0f;
if (projMode == BakeryPointLight.ftLightProjectionMode.Cone)
{
projParam1 = obj.angle;
projParam2 = obj.innerAngle / 100.0f;
}
else if (projMode == BakeryPointLight.ftLightProjectionMode.IES)
{
projMode = BakeryPointLight.ftLightProjectionMode.Cubemap;
}
else if (projMode == BakeryPointLight.ftLightProjectionMode.Cookie)
{
projParam2 = obj.angle;
}
flights.Write((float)projMode);
flights.Write(tform.position.x);
flights.Write(tform.position.y);
flights.Write(tform.position.z);
flights.Write(obj.shadowSpread);
flights.Write(obj.realisticFalloff ? 1.0f : ((1.0f / obj.cutoff) * 5.0f));
flights.Write(obj.realisticFalloff ? (obj.falloffMinRadius * obj.falloffMinRadius) : 1.0f);
flights.Write(1.0f / obj.cutoff);
flights.Write(obj.color.linear.r * obj.intensity);
flights.Write(obj.color.linear.g * obj.intensity);
flights.Write(obj.color.linear.b * obj.intensity);
flights.Write(projParam1);
flights.Write(right.x);
flights.Write(right.y);
flights.Write(right.z);
flights.Write(projParam2);
flights.Write(up.x);
flights.Write(up.y);
flights.Write(up.z);
flights.Write(obj.indirectIntensity * ftRenderLightmap.hackIndirectBoost);
flights.Write(forward.x);
flights.Write(forward.y);
flights.Write(forward.z);
int samples = obj.samples;
if (samples > 0)
{
samples = System.Math.Max(samples / sampleDiv, 1);
}
if (obj.shadowmaskFalloff) samples = -samples;
flights.Write(samples);
}
flights.Write(ignoreNormal ? (byte)1 : (byte)0);
for(int L=start; L<=end; L++)
{
if (skipLight[L]) continue;
var obj = allPoints[L];
bool isCookie = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cookie && obj.cookie != null;
bool isCubemap = obj.projMode == BakeryPointLight.ftLightProjectionMode.Cubemap && obj.cubemap != null;
bool isIES = obj.projMode == BakeryPointLight.ftLightProjectionMode.IES && obj.iesFile != null;
int existingTexHash;
string texName = "";
UnityEngine.Object tex = null;
if (isCookie || isCubemap || isIES)
{
if (isCookie)
{
tex = obj.cookie;
}
else if (isCubemap)
{
tex = obj.cubemap;
}
else
{
tex = obj.iesFile;
}
if (!tex2hash.TryGetValue(tex, out existingTexHash)) existingTexHash = -1;
if (existingTexHash < 0)
{
int texHash = tex.GetHashCode();
tex2hash[tex] = texHash;
existingTexHash = texHash;
}
texName = "cookie_" + existingTexHash + ".dds";
WriteNullTerminatedStringWithNewLine(flights, texName);
}
if (!SavePointLightTexture(tex, folder, texName, isCookie, isCubemap, isIES)) isError = true;
}
flights.Close();
return isError;
}
}
#endif