8024 lines
334 KiB
C#
8024 lines
334 KiB
C#
#if UNITY_EDITOR
|
|
#define USE_TERRAINS
|
|
|
|
// Disable 'obsolete' warnings
|
|
#pragma warning disable 0618
|
|
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine.Rendering;
|
|
using System.Reflection;
|
|
|
|
public class ftBuildGraphics : ScriptableWizard
|
|
{
|
|
const int UVGBFLAG_NORMAL = 1;
|
|
const int UVGBFLAG_FACENORMAL = 2;
|
|
const int UVGBFLAG_ALBEDO = 4;
|
|
const int UVGBFLAG_EMISSIVE = 8;
|
|
const int UVGBFLAG_POS = 16;
|
|
const int UVGBFLAG_SMOOTHPOS = 32;
|
|
const int UVGBFLAG_TANGENT = 64;
|
|
const int UVGBFLAG_TERRAIN = 128;
|
|
const int UVGBFLAG_RESERVED = 256;
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void InitShaders();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void LoadScene(string path);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
private static extern void SetAlbedos(int count, IntPtr[] tex);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
private static extern int CopyAlbedos();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
private static extern int CopyHeightmapsFromRAM(int count, TexInput[] tex);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void FreeAlbedoCopies();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
private static extern void SetAlphas(int count, IntPtr[] tex, float[] alphaRefs, int[] alphaChannels, int numLODs, bool flip);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
private static extern void SetAlphasFromRAM(int count, System.IntPtr tex, float[] alphaRefs, int[] alphaChannels, int numLODs, bool flip);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SaveSky(IntPtr tex, float rx, float ry, float rz, float ux, float uy, float uz, float fx, float fy, float fz, string path, bool isLinear, bool doubleLDR, bool rgbm);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SaveCookie(IntPtr tex, string path);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SaveCookieFromRAM(TexInput tex, string path);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int ftRenderUVGBuffer();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SetUVGBFlags(int flags);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SetFixPos(bool enabled);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern void SetCompression(bool enabled);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int ftGenerateAlphaBuffer();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int SaveGBufferMap(IntPtr tex, string path, bool compressed);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int SaveGBufferMapFromRAM(byte[] tex, int size, string path, bool compressed);
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int GetABGErrorCode();
|
|
|
|
[DllImport ("frender", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int GetUVGBErrorCode();
|
|
|
|
[DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int uvrLoad(float[] inputUVs, int numVerts, int[] inputIndices, int numIndices);
|
|
|
|
[DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int uvrRepack(float padding, int resolution);
|
|
|
|
[DllImport ("uvrepack", CallingConvention=CallingConvention.Cdecl)]
|
|
public static extern int uvrUnload();
|
|
|
|
public enum TexInputType
|
|
{
|
|
Color,
|
|
FloatColor,
|
|
HalfColor
|
|
};
|
|
|
|
public struct TexInput
|
|
{
|
|
public byte[] data;
|
|
public ushort width, height;
|
|
};
|
|
|
|
static Material matCubemapToStripExport;
|
|
|
|
static int voffset, soffset, ioffset;
|
|
|
|
static public string scenePath = "";
|
|
|
|
static BufferedBinaryWriterFloat fvbfull, fvbtrace, fvbtraceTex, fvbtraceUV0;
|
|
static BufferedBinaryWriterInt fib;
|
|
static BinaryWriter fscene, fmesh, flmid, fseamfix, fsurf, fmatid, fmatide, fmatidh, fmatideb, falphaid, fib32, fhmaps;
|
|
static BinaryWriter[] fib32lod;
|
|
static BinaryWriter[] falphaidlod;
|
|
|
|
public static ftLightmapsStorage.ImplicitLightmapData tempStorage = new ftLightmapsStorage.ImplicitLightmapData();
|
|
|
|
public static float texelsPerUnit = 20;
|
|
public static int minAutoResolution = 16;
|
|
public static int maxAutoResolution = 4096;
|
|
public static bool mustBePOT = true;
|
|
public static bool autoAtlas = true;
|
|
public static bool unwrapUVs = true;
|
|
public static bool forceDisableUnwrapUVs = false;
|
|
public static bool exportShaderColors = true; // albedo and emission (sometimes normal) always come from the engine now
|
|
//public static int atlasPaddingPixels = ftAdditionalConfig.texelPaddingForDefaultAtlasPacker;
|
|
public static bool atlasCountPriority = false;
|
|
public static bool splitByScene = false;
|
|
public static bool splitByTag = true;
|
|
public static bool uvPaddingMax = false;
|
|
public static bool exportTerrainAsHeightmap = true;
|
|
public static bool exportTerrainTrees = false;
|
|
public static bool uvgbHeightmap = true;
|
|
|
|
public static bool texelsPerUnitPerMap = false;
|
|
public static float mainLightmapScale = 1;
|
|
public static float maskLightmapScale = 1;
|
|
public static float dirLightmapScale = 1;
|
|
|
|
const float atlasScaleUpValue = 1.01f;
|
|
const int atlasMaxTries = 100;
|
|
const float alphaInstanceThreshold = 5.0f / 255.0f;
|
|
|
|
const bool flipAlpha = true;
|
|
|
|
public static string overwriteExtensionCheck = ".hdr";
|
|
public static bool overwriteWarning = false;
|
|
public static bool overwriteWarningSelectedOnly = false;
|
|
public static bool memoryWarning = false;
|
|
public static bool modifyLightmapStorage = true;
|
|
public static bool validateLightmapStorageImmutability = true;
|
|
public static bool sceneNeedsToBeRebuilt = false;
|
|
//public static int unityVersionMajor = 0;
|
|
//public static int unityVersionMinor = 0;
|
|
|
|
static int areaLightCounter = -2;
|
|
public static int sceneLodsUsed = 0;
|
|
|
|
static GameObject lmgroupHolder;
|
|
static BakeryLightmapGroup lightProbeLMGroup = null;
|
|
static BakeryLightmapGroup volumeLMGroup = null;
|
|
|
|
static List<GameObject> terrainObjectList;
|
|
#if USE_TERRAINS
|
|
static List<Terrain> terrainObjectToActual;
|
|
#endif
|
|
static List<Texture> terrainObjectToHeightMap;
|
|
static List<TexInput> terrainObjectToHeightMapRAM;
|
|
static List<float> terrainObjectToBounds;
|
|
static List<int> terrainObjectToLMID;
|
|
static List<float> terrainObjectToBoundsUV;
|
|
static List<int> terrainObjectToFlags;
|
|
static List<List<float[]>> terrainObjectToHeightMips;
|
|
//static List<List<Vector3[]>> terrainObjectToNormalMips;
|
|
//static List<Vector3[]> terrainObjectToNormalMip0;
|
|
static List<GameObject> temporaryAreaLightMeshList;
|
|
static List<BakeryLightMesh> temporaryAreaLightMeshList2;
|
|
public static List<GameObject> temporaryGameObjects;
|
|
|
|
static Dictionary<GameObject, int> cmp_objToLodLevel;
|
|
static Dictionary<GameObject, float> cmp_holderObjArea;
|
|
|
|
public static Dictionary<int,List<int>> lodLevelsVisibleInLodLevel = new Dictionary<int,List<int>>(); // defines LOD levels visible in chosen LOD level
|
|
|
|
public static List<float> vbtraceTexPosNormalArray; // global vbTraceTex.bin positions/normals
|
|
public static List<float> vbtraceTexUVArray; // global vbTraceTex.bin UVs
|
|
public static float[] vbtraceTexUVArrayLOD; // global vbTraceTex.bin LOD UVs
|
|
|
|
public static List<Renderer> atlasOnlyObj;
|
|
public static List<Vector4> atlasOnlyScaleOffset;
|
|
public static List<int> atlasOnlySize;
|
|
public static List<int> atlasOnlyID;
|
|
public static List<BakeryLightmapGroup> atlasOnlyGroup;
|
|
|
|
public static ftGlobalStorage.AtlasPacker atlasPacker = ftGlobalStorage.AtlasPacker.xatlas;
|
|
|
|
public static bool forceAllAreaLightsSelfshadow = false;
|
|
|
|
public static bool postPacking = true;
|
|
public static bool holeFilling = false;
|
|
|
|
static int floatOverWarnCount = 0;
|
|
const int maxFloatOverWarn = 10;
|
|
|
|
static ftGlobalStorage gstorage;
|
|
static BakeryProjectSettings pstorage;
|
|
|
|
static ExportSceneData _tempData;
|
|
|
|
static public void DebugLogError(string text)
|
|
{
|
|
ftRenderLightmap.DebugLogError(text);
|
|
}
|
|
|
|
class FarSphereRenderData
|
|
{
|
|
public RenderTexture[] albedo, depth, normal;
|
|
public Matrix4x4[] viewProj;
|
|
public Vector3 pos;
|
|
public Mesh[] meshes;
|
|
public Texture2D[] textures;
|
|
}
|
|
|
|
static Shader farSphereRshader, farSphereRshaderOcc, farSphereSshader, farSphereProjClipShader, farSphereDilateShader;
|
|
static Material farSphereMat, farSphereMatOcc, farSphereProjClipMat, farSphereDilateMat;
|
|
static ComputeShader farSphereCSTransform, farSphereCSCull;
|
|
|
|
class AtlasNode
|
|
{
|
|
public AtlasNode child0, child1;
|
|
public Rect rc;
|
|
public GameObject obj;
|
|
bool leaf = true;
|
|
|
|
public AtlasNode Insert(GameObject o, Rect rect)
|
|
{
|
|
if (!leaf)
|
|
{
|
|
var newNode = child0.Insert(o, rect);
|
|
if (newNode != null) return newNode;
|
|
|
|
return child1.Insert(o, rect);
|
|
}
|
|
else
|
|
{
|
|
if (obj != null) return null;
|
|
|
|
var fits = (rect.width <= rc.width && rect.height <= rc.height);
|
|
if (!fits) return null;
|
|
|
|
var fitsExactly = (rect.width == rc.width && rect.height == rc.height);
|
|
if (fitsExactly)
|
|
{
|
|
obj = o;
|
|
return this;
|
|
}
|
|
|
|
child0 = new AtlasNode();
|
|
child1 = new AtlasNode();
|
|
|
|
var dw = rc.width - rect.width;
|
|
var dh = rc.height - rect.height;
|
|
|
|
if (dw > dh)
|
|
{
|
|
child0.rc = new Rect(rc.x, rc.y, rect.width, rc.height);
|
|
child1.rc = new Rect(rc.x + rect.width, rc.y, rc.width - rect.width, rc.height);
|
|
}
|
|
else
|
|
{
|
|
child0.rc = new Rect(rc.x, rc.y, rc.width, rect.height);
|
|
child1.rc = new Rect(rc.x, rc.y + rect.height, rc.width, rc.height - rect.height);
|
|
}
|
|
leaf = false;
|
|
|
|
return child0.Insert(o, rect);
|
|
}
|
|
}
|
|
|
|
public void GetMax(ref float maxx, ref float maxy)
|
|
{
|
|
if (obj != null)
|
|
{
|
|
if ((rc.x + rc.width) > maxx) maxx = rc.x + rc.width;
|
|
if ((rc.y + rc.height) > maxy) maxy = rc.y + rc.height;
|
|
}
|
|
if (child0 != null) child0.GetMax(ref maxx, ref maxy);
|
|
if (child1 != null) child1.GetMax(ref maxx, ref maxy);
|
|
}
|
|
|
|
public void Transform(float offsetx, float offsety, float scalex, float scaley)
|
|
{
|
|
rc.x *= scalex;
|
|
rc.y *= scaley;
|
|
rc.x += offsetx;
|
|
rc.y += offsety;
|
|
rc.width *= scalex;
|
|
rc.height *= scaley;
|
|
if (child0 != null) child0.Transform(offsetx, offsety, scalex, scaley);
|
|
if (child1 != null) child1.Transform(offsetx, offsety, scalex, scaley);
|
|
}
|
|
};
|
|
|
|
static ftBuildGraphics()
|
|
{
|
|
//ftRenderLightmap.PatchPath();
|
|
//var unityVer = Application.unityVersion.Split('.');
|
|
//unityVersionMajor = Int32.Parse(unityVer[0]);
|
|
//unityVersionMinor = Int32.Parse(unityVer[1]);
|
|
}
|
|
|
|
static void DebugLogInfo(string info)
|
|
{
|
|
if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings();
|
|
if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Info) != 0) Debug.Log(info);
|
|
}
|
|
|
|
static void DebugLogWarning(string info)
|
|
{
|
|
if (pstorage == null) pstorage = ftLightmaps.GetProjectSettings();
|
|
if ((pstorage.logLevel & (int)BakeryProjectSettings.LogLevel.Warning) != 0) Debug.LogWarning(info);
|
|
}
|
|
|
|
static string FilterNonASCII(string s)
|
|
{
|
|
if (Encoding.UTF8.GetByteCount(s) == s.Length) return s;
|
|
var bytes = Encoding.UTF8.GetBytes(s);
|
|
var newStr = "";
|
|
for(int i=0; i<bytes.Length; i++)
|
|
{
|
|
if (bytes[i] > 127)
|
|
{
|
|
newStr += "_" + (int)bytes[i];
|
|
}
|
|
else
|
|
{
|
|
newStr += Convert.ToChar(bytes[i]);
|
|
}
|
|
}
|
|
return newStr;
|
|
}
|
|
|
|
static void exportVBPos(BinaryWriter f, Transform t, Mesh m, Vector3[] vertices)
|
|
{
|
|
for(int i=0;i<vertices.Length;i++)
|
|
{
|
|
Vector3 pos = vertices[i];//t.(vertices[i]);
|
|
f.Write(pos.x);
|
|
f.Write(pos.y);
|
|
f.Write(pos.z);
|
|
}
|
|
}
|
|
|
|
static void exportVBTrace(BufferedBinaryWriterFloat f, Mesh m, Vector3[] vertices, Vector3[] normals)
|
|
{
|
|
for(int i=0;i<vertices.Length;i++)
|
|
{
|
|
Vector3 pos = vertices[i];//t.(vertices[i]);
|
|
f.Write(pos.x);
|
|
f.Write(pos.y);
|
|
f.Write(pos.z);
|
|
|
|
Vector3 normal = normals[i];//t.TransformDirection(normals[i]);
|
|
f.Write(normal.x);
|
|
f.Write(normal.y);
|
|
f.Write(normal.z);
|
|
}
|
|
}
|
|
|
|
public static Renderer GetValidRenderer(GameObject obj)
|
|
{
|
|
var mr = obj.GetComponent<Renderer>();
|
|
if (mr as MeshRenderer == null && mr as SkinnedMeshRenderer == null)
|
|
{
|
|
// possibly multiple renderers on one gameobject?
|
|
mr = obj.GetComponent<MeshRenderer>() as Renderer;
|
|
if (mr != null) return mr;
|
|
mr = obj.GetComponent<SkinnedMeshRenderer>() as Renderer;
|
|
if (mr != null) return mr;
|
|
return null;
|
|
}
|
|
return mr;
|
|
}
|
|
|
|
static BakeryLightmapGroup GetLMGroupFromObjectExplicit(GameObject obj, ExportSceneData data)
|
|
{
|
|
lmgroupHolder = null;
|
|
var lmgroupSelector = obj.GetComponent<BakeryLightmapGroupSelector>(); // if object has explicit lmgroup
|
|
if (lmgroupSelector == null)
|
|
{
|
|
// if parents have explicit lmgroup
|
|
var t2 = obj.transform.parent;
|
|
while(lmgroupSelector == null && t2 != null)
|
|
{
|
|
lmgroupSelector = t2.GetComponent<BakeryLightmapGroupSelector>();
|
|
t2 = t2.parent;
|
|
}
|
|
}
|
|
BakeryLightmapGroup lmgroup = null;
|
|
if (lmgroupSelector != null)
|
|
{
|
|
lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup;
|
|
lmgroupHolder = lmgroupSelector.gameObject;
|
|
|
|
var scaleInLm = data.objToScaleInLm[obj];
|
|
if (scaleInLm == 0.0f) lmgroup = data.autoVertexGroup;
|
|
//null; // ignore lightmaps when scaleInLightmap == 0
|
|
}
|
|
return lmgroup;
|
|
}
|
|
|
|
static BakeryLightmapGroup GetLMGroupFromObject(GameObject obj, ExportSceneData data)
|
|
{
|
|
UnityEngine.Object lmgroupObj = null;
|
|
BakeryLightmapGroup lmgroup = null;
|
|
lmgroupHolder = null;
|
|
|
|
var lmgroupSelector = obj.GetComponent<BakeryLightmapGroupSelector>(); // if object has explicit lmgroup
|
|
tempStorage.implicitGroupMap.TryGetValue(obj, out lmgroupObj); // or implicit
|
|
lmgroup = (BakeryLightmapGroup)lmgroupObj;
|
|
lmgroupHolder = obj;
|
|
if (lmgroupSelector == null && lmgroup == null)
|
|
{
|
|
// if parents have explicit/implicit lmgroup
|
|
var t2 = obj.transform.parent;
|
|
while(lmgroupSelector == null && lmgroup == null && t2 != null)
|
|
{
|
|
lmgroupSelector = t2.GetComponent<BakeryLightmapGroupSelector>();
|
|
tempStorage.implicitGroupMap.TryGetValue(t2.gameObject, out lmgroupObj);
|
|
lmgroup = (BakeryLightmapGroup)lmgroupObj;
|
|
lmgroupHolder = t2.gameObject;
|
|
t2 = t2.parent;
|
|
}
|
|
}
|
|
if (lmgroupSelector != null)
|
|
{
|
|
lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup;
|
|
}
|
|
|
|
if (lmgroup != null)
|
|
{
|
|
var r = GetValidRenderer(obj);
|
|
if (r)
|
|
{
|
|
var scaleInLm = data.objToScaleInLm[obj];
|
|
if (scaleInLm == 0.0f) lmgroup = data.autoVertexGroup;
|
|
// null; // ignore lightmaps when scaleInLightmap == 0
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lmgroupHolder = null;
|
|
}
|
|
|
|
return lmgroup;
|
|
}
|
|
|
|
// use by ftRenderLightmap
|
|
public static BakeryLightmapGroup GetLMGroupFromObject(GameObject obj)
|
|
{
|
|
UnityEngine.Object lmgroupObj = null;
|
|
BakeryLightmapGroup lmgroup = null;
|
|
lmgroupHolder = null;
|
|
|
|
var lmgroupSelector = obj.GetComponent<BakeryLightmapGroupSelector>(); // if object has explicit lmgroup
|
|
tempStorage.implicitGroupMap.TryGetValue(obj, out lmgroupObj); // or implicit
|
|
lmgroup = (BakeryLightmapGroup)lmgroupObj;
|
|
lmgroupHolder = obj;
|
|
if (lmgroupSelector == null && lmgroup == null)
|
|
{
|
|
// if parents have explicit/implicit lmgroup
|
|
var t2 = obj.transform.parent;
|
|
while(lmgroupSelector == null && lmgroup == null && t2 != null)
|
|
{
|
|
lmgroupSelector = t2.GetComponent<BakeryLightmapGroupSelector>();
|
|
tempStorage.implicitGroupMap.TryGetValue(t2.gameObject, out lmgroupObj);
|
|
lmgroup = (BakeryLightmapGroup)lmgroupObj;
|
|
lmgroupHolder = t2.gameObject;
|
|
t2 = t2.parent;
|
|
}
|
|
}
|
|
if (lmgroupSelector != null)
|
|
{
|
|
lmgroup = lmgroupSelector.lmgroupAsset as BakeryLightmapGroup;
|
|
}
|
|
|
|
if (lmgroup != null)
|
|
{
|
|
var r = GetValidRenderer(obj);
|
|
if (r)
|
|
{
|
|
var so = new SerializedObject(r);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = r as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
//var scaleInLm = data.objToScaleInLm[obj];
|
|
if (scaleInLm == 0.0f) lmgroup = null;
|
|
// null; // ignore lightmaps when scaleInLightmap == 0
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lmgroupHolder = null;
|
|
}
|
|
|
|
return lmgroup;
|
|
}
|
|
|
|
public static void exportVBTraceTexAttribs(List<float> arrPosNormal, List<float> arrUV,
|
|
Vector3[] vertices, Vector3[] normals, Vector2[] uv2, int lmid, bool vertexBake, GameObject obj)
|
|
{
|
|
for(int i=0;i<vertices.Length;i++)
|
|
{
|
|
Vector3 pos = vertices[i];//t.(vertices[i]);
|
|
arrPosNormal.Add(pos.x);
|
|
arrPosNormal.Add(pos.y);
|
|
arrPosNormal.Add(pos.z);
|
|
|
|
Vector3 normal = normals[i];//t.TransformDirection(normals[i]);
|
|
arrPosNormal.Add(normal.x);
|
|
arrPosNormal.Add(normal.y);
|
|
arrPosNormal.Add(normal.z);
|
|
|
|
float u = 0;
|
|
float v = 0;
|
|
|
|
if (lmid < 0)
|
|
{
|
|
//u = lmid * 10;
|
|
if (uv2.Length>0)
|
|
{
|
|
u = Mathf.Clamp(uv2[i].x, 0, 0.99999f);
|
|
v = Mathf.Clamp(1.0f - uv2[i].y, 0, 0.99999f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (uv2.Length>0 && !vertexBake)
|
|
{
|
|
u = Mathf.Clamp(uv2[i].x, 0, 0.99999f);
|
|
v = Mathf.Clamp(uv2[i].y, 0, 0.99999f);
|
|
}
|
|
else if (vertexBake)
|
|
{
|
|
u = uv2[i].x;
|
|
v = uv2[i].y - 1.0f;
|
|
}
|
|
}
|
|
|
|
float origU = u;
|
|
if (lmid >= 0)
|
|
{
|
|
u += lmid * 10;
|
|
if ((int)u > lmid*10)
|
|
{
|
|
// attempt fixing float overflow
|
|
u = (lmid*10+1) - (lmid*10+1)*0.0000002f;
|
|
if ((int)u > lmid*10)
|
|
{
|
|
if (floatOverWarnCount < maxFloatOverWarn)
|
|
{
|
|
Debug.LogError("Float overflow for " + obj.name + " (U: " + origU + ", LMID: " + lmid + ")");
|
|
floatOverWarnCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u = lmid * 10 - u;
|
|
if ((int)u != lmid*10)
|
|
{
|
|
u = -u;
|
|
lmid = -lmid;
|
|
u = (lmid*10+1) - (lmid*10+1)*0.0000002f;
|
|
if ((int)u > lmid*10)
|
|
{
|
|
if (floatOverWarnCount < maxFloatOverWarn)
|
|
{
|
|
Debug.LogError("Float overflow for " + obj.name + " (U: " + origU + ", LMID: " + lmid + ")");
|
|
floatOverWarnCount++;
|
|
}
|
|
}
|
|
u = -u;
|
|
lmid = -lmid;
|
|
}
|
|
}
|
|
|
|
arrUV.Add(u);
|
|
arrUV.Add(v);
|
|
}
|
|
}
|
|
|
|
static void exportVBTraceUV0(BufferedBinaryWriterFloat f, Vector2[] uvs, int vertCount)
|
|
{
|
|
if (uvs.Length == 0)
|
|
{
|
|
for(int i=0;i<vertCount;i++)
|
|
{
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i=0;i<vertCount;i++)
|
|
{
|
|
f.Write(uvs[i].x);
|
|
f.Write(uvs[i].y);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void exportVBBasic(BinaryWriter f, Transform t, Mesh m, Vector3[] vertices, Vector3[] normals, Vector2[] uv2)
|
|
{
|
|
for(int i=0;i<vertices.Length;i++)
|
|
{
|
|
Vector3 pos = vertices[i];//t.(vertices[i]);
|
|
f.Write(pos.x);
|
|
f.Write(pos.y);
|
|
f.Write(pos.z);
|
|
|
|
Vector3 normal = normals[i];//t.TransformDirection(normals[i]);
|
|
f.Write(normal.x);
|
|
f.Write(normal.y);
|
|
f.Write(normal.z);
|
|
|
|
if (uv2.Length>0)
|
|
{
|
|
f.Write(uv2[i].x);
|
|
f.Write(uv2[i].y);
|
|
}
|
|
else
|
|
{
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Either I'm dumb, or it's impossible to make generics work with it (only worked in .NET 3.5)
|
|
class BufferedBinaryWriterFloat
|
|
{
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
public class ReinterpretBuffer
|
|
{
|
|
[FieldOffset(0)]
|
|
public byte[] bytes;
|
|
[FieldOffset(0)]
|
|
public float[] elements;
|
|
}
|
|
|
|
BinaryWriter f;
|
|
ReinterpretBuffer buffer;
|
|
int bufferPtr;
|
|
int bufferSize;
|
|
int elementSize;
|
|
|
|
public BufferedBinaryWriterFloat(BinaryWriter b, int elemSize = 4, int buffSizeInFloats = 1024*1024)
|
|
{
|
|
f = b;
|
|
buffer = new ReinterpretBuffer();
|
|
buffer.bytes = new byte[buffSizeInFloats * elemSize];
|
|
bufferPtr = 0;
|
|
bufferSize = buffSizeInFloats;
|
|
elementSize = elemSize;
|
|
}
|
|
|
|
void Flush()
|
|
{
|
|
if (bufferPtr == 0) return;
|
|
f.Write(buffer.bytes, 0, bufferPtr * elementSize);
|
|
bufferPtr = 0;
|
|
}
|
|
|
|
public void Write(float x)
|
|
{
|
|
buffer.elements[bufferPtr] = x;
|
|
bufferPtr++;
|
|
if (bufferPtr == bufferSize) Flush();
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
Flush();
|
|
f.Close();
|
|
}
|
|
}
|
|
class BufferedBinaryWriterInt
|
|
{
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
public class ReinterpretBuffer
|
|
{
|
|
[FieldOffset(0)]
|
|
public byte[] bytes;
|
|
[FieldOffset(0)]
|
|
public int[] elements;
|
|
}
|
|
|
|
BinaryWriter f;
|
|
ReinterpretBuffer buffer;
|
|
int bufferPtr;
|
|
int bufferSize;
|
|
int elementSize;
|
|
|
|
public BufferedBinaryWriterInt(BinaryWriter b, int elemSize = 4, int buffSizeInFloats = 1024*1024)
|
|
{
|
|
f = b;
|
|
buffer = new ReinterpretBuffer();
|
|
buffer.bytes = new byte[buffSizeInFloats * elemSize];
|
|
bufferPtr = 0;
|
|
bufferSize = buffSizeInFloats;
|
|
elementSize = elemSize;
|
|
}
|
|
|
|
void Flush()
|
|
{
|
|
if (bufferPtr == 0) return;
|
|
f.Write(buffer.bytes, 0, bufferPtr * elementSize);
|
|
bufferPtr = 0;
|
|
}
|
|
|
|
public void Write(int x)
|
|
{
|
|
buffer.elements[bufferPtr] = x;
|
|
bufferPtr++;
|
|
if (bufferPtr == bufferSize) Flush();
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
Flush();
|
|
f.Close();
|
|
}
|
|
}
|
|
|
|
static void exportVBFull(BufferedBinaryWriterFloat f, Vector3[] vertices, Vector3[] normals, Vector4[] tangents, Vector2[] uv, Vector2[] uv2)
|
|
{
|
|
bool hasUV = uv.Length > 0;
|
|
bool hasUV2 = uv2.Length > 0;
|
|
|
|
for(int i=0;i<vertices.Length;i++)
|
|
{
|
|
Vector3 pos = vertices[i];//t.(vertices[i]);
|
|
f.Write(pos.x);
|
|
f.Write(pos.y);
|
|
f.Write(pos.z);
|
|
|
|
Vector3 normal = normals[i];//t.TransformDirection(normals[i]);
|
|
f.Write(normal.x);
|
|
f.Write(normal.y);
|
|
f.Write(normal.z);
|
|
|
|
if (tangents == null)
|
|
{
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
}
|
|
else
|
|
{
|
|
Vector4 tangent = tangents[i];
|
|
f.Write(tangent.x);
|
|
f.Write(tangent.y);
|
|
f.Write(tangent.z);
|
|
f.Write(tangent.w);
|
|
}
|
|
|
|
if (hasUV)
|
|
{
|
|
f.Write(uv[i].x);
|
|
f.Write(uv[i].y);
|
|
}
|
|
else
|
|
{
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
}
|
|
|
|
if (hasUV2)
|
|
{
|
|
f.Write(uv2[i].x);
|
|
f.Write(uv2[i].y);
|
|
}
|
|
else
|
|
{
|
|
f.Write(0.0f);
|
|
f.Write(0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int exportIB(BufferedBinaryWriterInt f, int[] indices, bool isFlipped, bool is32Bit, int offset, BinaryWriter falphaid, ushort alphaID)
|
|
{
|
|
//var indices = m.GetTriangles(i);
|
|
for(int j=0;j<indices.Length;j+=3)
|
|
{
|
|
if (!isFlipped)
|
|
{
|
|
if (is32Bit)
|
|
{
|
|
f.Write(indices[j] + offset);
|
|
f.Write(indices[j+1] + offset);
|
|
f.Write(indices[j+2] + offset);
|
|
}
|
|
else
|
|
{
|
|
f.Write(indices[j]);
|
|
f.Write(indices[j+1]);
|
|
f.Write(indices[j+2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is32Bit)
|
|
{
|
|
f.Write(indices[j+2] + offset);
|
|
f.Write(indices[j+1] + offset);
|
|
f.Write(indices[j] + offset);
|
|
}
|
|
else
|
|
{
|
|
f.Write(indices[j+2]);
|
|
f.Write(indices[j+1]);
|
|
f.Write(indices[j]);
|
|
}
|
|
}
|
|
|
|
if (falphaid!=null) falphaid.Write(alphaID);
|
|
}
|
|
return indices.Length;
|
|
}
|
|
|
|
// returns mesh area if requested
|
|
static void exportIB32(List<int> indicesOpaque, List<int> indicesTransparent, List<int> indicesLMID,
|
|
int[] indices, bool isFlipped, int offset, int indexOffsetLMID, BinaryWriter falphaid,
|
|
ushort alphaID)
|
|
{
|
|
//var indices = m.GetTriangles(i);
|
|
var indicesOut = alphaID == 0xFFFF ? indicesOpaque : indicesTransparent;
|
|
|
|
int indexA, indexB, indexC;
|
|
|
|
for(int j=0;j<indices.Length;j+=3)
|
|
{
|
|
if (!isFlipped)
|
|
{
|
|
indexA = indices[j];
|
|
indexB = indices[j + 1];
|
|
indexC = indices[j + 2];
|
|
}
|
|
else
|
|
{
|
|
indexA = indices[j + 2];
|
|
indexB = indices[j + 1];
|
|
indexC = indices[j];
|
|
}
|
|
|
|
indicesOut.Add(indexA + offset);
|
|
indicesOut.Add(indexB + offset);
|
|
indicesOut.Add(indexC + offset);
|
|
|
|
if (indicesLMID != null)
|
|
{
|
|
indicesLMID.Add(indexA + indexOffsetLMID);
|
|
indicesLMID.Add(indexB + indexOffsetLMID);
|
|
indicesLMID.Add(indexC + indexOffsetLMID);
|
|
}
|
|
|
|
if (alphaID != 0xFFFF) falphaid.Write(alphaID);
|
|
}
|
|
}
|
|
|
|
|
|
static void exportSurfs(BinaryWriter f, int[][] indices, int subMeshCount)// Mesh m)
|
|
{
|
|
int offset = ioffset;
|
|
for(int i=0;i<subMeshCount;i++) {
|
|
int size = indices[i].Length;//m.GetTriangles(i).Length;
|
|
f.Write(offset);
|
|
f.Write(size);
|
|
offset += size;// * 2;
|
|
}
|
|
soffset += subMeshCount;
|
|
}
|
|
|
|
static void exportMesh(BinaryWriter f, Mesh m)
|
|
{
|
|
f.Write(voffset);
|
|
f.Write(soffset);
|
|
f.Write((ushort)m.subMeshCount);
|
|
f.Write((ushort)0);
|
|
}
|
|
|
|
static int exportLMID(BinaryWriter f, GameObject obj, BakeryLightmapGroup lmgroup)
|
|
{
|
|
var areaLight = obj.GetComponent<BakeryLightMesh>();
|
|
if (areaLight == null)
|
|
{
|
|
int index = temporaryAreaLightMeshList.IndexOf(obj);
|
|
if (index >= 0)
|
|
{
|
|
areaLight = temporaryAreaLightMeshList2[index];
|
|
}
|
|
}
|
|
if (areaLight != null)
|
|
{
|
|
f.Write(areaLightCounter);
|
|
areaLight.lmid = areaLightCounter;
|
|
areaLightCounter--;
|
|
return areaLightCounter;
|
|
}
|
|
else if (lmgroup != null)
|
|
{
|
|
f.Write(lmgroup.id);
|
|
return lmgroup.id;
|
|
}
|
|
else
|
|
{
|
|
f.Write(0xFFFFFFFF);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static Vector2[] GenerateVertexBakeUVs(int voffset, int vlength, int totalVertexCount)
|
|
{
|
|
int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)totalVertexCount));
|
|
atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize;
|
|
var uvs = new Vector2[vlength];
|
|
float mul = 1.0f / atlasTexSize;
|
|
float add = mul * 0.5f;
|
|
for(int i=0; i<vlength; i++)
|
|
{
|
|
int x = (i + voffset) % atlasTexSize;
|
|
int y = (i + voffset) / atlasTexSize;
|
|
uvs[i] = new Vector2(x * mul + add, y * mul + add);// - 1.0f);
|
|
}
|
|
return uvs;
|
|
}
|
|
|
|
public static Mesh GetSharedMesh(Renderer mr)
|
|
{
|
|
if (mr == null) return null;
|
|
var mrSkin = mr as SkinnedMeshRenderer;
|
|
var mf = mr.gameObject.GetComponent<MeshFilter>();
|
|
return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null);
|
|
}
|
|
|
|
public static Mesh GetSharedMeshBaked(GameObject obj)
|
|
{
|
|
var mrSkin = obj.GetComponent<SkinnedMeshRenderer>();
|
|
if (mrSkin != null)
|
|
{
|
|
var baked = new Mesh();
|
|
mrSkin.BakeMesh(baked);
|
|
return baked;
|
|
}
|
|
var mf = obj.GetComponent<MeshFilter>();
|
|
return (mf != null ? mf.sharedMesh : null);
|
|
}
|
|
|
|
public static Mesh GetSharedMesh(GameObject obj)
|
|
{
|
|
var mrSkin = obj.GetComponent<SkinnedMeshRenderer>();
|
|
var mf = obj.GetComponent<MeshFilter>();
|
|
return mrSkin != null ? mrSkin.sharedMesh : (mf != null ? mf.sharedMesh : null);
|
|
}
|
|
|
|
public static Mesh GetSharedMeshSkinned(GameObject obj, out bool isSkin)
|
|
{
|
|
var mrSkin = obj.GetComponent<SkinnedMeshRenderer>();
|
|
Mesh mesh;
|
|
if (mrSkin != null)
|
|
{
|
|
mesh = new Mesh();
|
|
mrSkin.BakeMesh(mesh);
|
|
isSkin = mrSkin.bones.Length > 0; // blendshape-only don't need scale?
|
|
}
|
|
else
|
|
{
|
|
isSkin = false;
|
|
var mf = obj.GetComponent<MeshFilter>();
|
|
if (mf == null) return null;
|
|
mesh = mf.sharedMesh;
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
static GameObject TestPackAsSingleSquare(GameObject holder)
|
|
{
|
|
var t = holder.transform;
|
|
var p = t.parent;
|
|
while(p != null)
|
|
{
|
|
if (p.GetComponent<BakeryPackAsSingleSquare>() != null) return p.gameObject;
|
|
p = p.parent;
|
|
}
|
|
return holder;
|
|
}
|
|
|
|
static bool ModelUVsOverlap(ModelImporter importer, ftGlobalStorage store)
|
|
{
|
|
if (importer.generateSecondaryUV) return true;
|
|
|
|
var path = importer.assetPath;
|
|
/*for(int i=0; i<storages.Length; i++)
|
|
{
|
|
var store = storages[i];
|
|
if (store == null) continue;
|
|
var index = store.assetList.IndexOf(path);
|
|
if (index < 0) continue;
|
|
|
|
if (store.uvOverlapAssetList[index] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}*/
|
|
var index = store.assetList.IndexOf(path);
|
|
if (index >= 0 && index < store.uvOverlapAssetList.Count)
|
|
{
|
|
if (store.uvOverlapAssetList[index] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
var newAsset = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject;
|
|
ftModelPostProcessor.CheckUVOverlap(newAsset, path);
|
|
|
|
/*for(int i=0; i<storages.Length; i++)
|
|
{
|
|
var store = storages[i];
|
|
var index = store.assetList.IndexOf(path);
|
|
if (index < 0) continue;
|
|
|
|
if (store.uvOverlapAssetList[index] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}*/
|
|
index = store.assetList.IndexOf(path);
|
|
if (index >= 0)
|
|
{
|
|
if (store.uvOverlapAssetList[index] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool NeedsTangents(BakeryLightmapGroup lmgroup, bool tangentSHLights)
|
|
{
|
|
// RNM requires tangents
|
|
if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.RNM ||
|
|
(lmgroup!=null && lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.RNM)) return true;
|
|
|
|
// SH requires tangents only if there is a SH skylight
|
|
if (!tangentSHLights) return false;
|
|
|
|
if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.SH || ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.MonoSH ||
|
|
(lmgroup!=null && (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.SH || lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.MonoSH))) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static long GetTime()
|
|
{
|
|
return System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond;
|
|
}
|
|
|
|
static public string progressBarText;
|
|
static public float progressBarPercent = 0;
|
|
static public bool userCanceled = false;
|
|
//static IEnumerator progressFunc;
|
|
static EditorWindow activeWindow;
|
|
static void ProgressBarInit(string startText, EditorWindow window = null)
|
|
{
|
|
progressBarText = startText;
|
|
if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, 0, false);
|
|
}
|
|
static void ProgressBarShow(string text, float percent, bool onTop)
|
|
{
|
|
progressBarText = text;
|
|
progressBarPercent = percent;
|
|
if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarShow("Bakery", progressBarText, progressBarPercent, 0, onTop);
|
|
userCanceled = ftRenderLightmap.simpleProgressBarCancelled();
|
|
}
|
|
|
|
public static void FreeTemporaryAreaLightMeshes()
|
|
{
|
|
if (temporaryAreaLightMeshList != null)
|
|
{
|
|
for(int i=0; i<temporaryAreaLightMeshList.Count; i++)
|
|
{
|
|
if (temporaryAreaLightMeshList[i] != null)
|
|
{
|
|
//var mr = temporaryAreaLightMeshList[i].GetComponent<Renderer>();
|
|
//if (mr != null) DestroyImmediate(mr);
|
|
//var mf = temporaryAreaLightMeshList[i].GetComponent<MeshFilter>();
|
|
//if (mf != null) DestroyImmediate(mf);
|
|
DestroyImmediate(temporaryAreaLightMeshList[i]);
|
|
}
|
|
}
|
|
temporaryAreaLightMeshList = null;
|
|
}
|
|
}
|
|
|
|
public static void ProgressBarEnd(bool removeTempObjects)// bool isError = true)
|
|
{
|
|
if (removeTempObjects)
|
|
{
|
|
if (terrainObjectList != null)
|
|
{
|
|
for(int i=0; i<terrainObjectList.Count; i++)
|
|
{
|
|
if (terrainObjectList[i] != null) DestroyImmediate(terrainObjectList[i]);
|
|
}
|
|
terrainObjectList = null;
|
|
}
|
|
|
|
if (temporaryGameObjects != null)
|
|
{
|
|
for(int i=0; i<temporaryGameObjects.Count; i++)
|
|
{
|
|
if (temporaryGameObjects[i] != null) DestroyImmediate(temporaryGameObjects[i]);
|
|
}
|
|
temporaryGameObjects = null;
|
|
}
|
|
|
|
//if (isError)
|
|
{
|
|
FreeTemporaryAreaLightMeshes();
|
|
}
|
|
}
|
|
|
|
if (ftRenderLightmap.showProgressBar) ftRenderLightmap.simpleProgressBarEnd();
|
|
}
|
|
void OnInspectorUpdate()
|
|
{
|
|
Repaint();
|
|
}
|
|
|
|
static void CloseAllFiles()
|
|
{
|
|
if (fscene != null) fscene.Close();
|
|
if (fmesh != null) fmesh.Close();
|
|
if (flmid != null) flmid.Close();
|
|
if (fseamfix != null) fseamfix.Close();
|
|
if (fsurf != null) fsurf.Close();
|
|
if (fmatid != null) fmatid.Close();
|
|
if (fmatide != null) fmatide.Close();
|
|
if (fmatideb != null) fmatideb.Close();
|
|
if (fmatidh != null) fmatidh.Close();
|
|
if (falphaid != null) falphaid.Close();
|
|
if (fvbfull != null) fvbfull.Close();
|
|
if (fvbtrace != null) fvbtrace.Close();
|
|
if (fvbtraceTex != null) fvbtraceTex.Close();
|
|
if (fvbtraceUV0 != null) fvbtraceUV0.Close();
|
|
if (fib != null) fib.Close();
|
|
if (fib32 != null) fib32.Close();
|
|
if (fhmaps != null) fhmaps.Close();
|
|
if (fib32lod != null)
|
|
{
|
|
for(int i=0; i<fib32lod.Length; i++) fib32lod[i].Close();
|
|
}
|
|
if (falphaidlod != null)
|
|
{
|
|
for(int i=0; i<falphaidlod.Length; i++) falphaidlod[i].Close();
|
|
}
|
|
fvbfull = fvbtrace = fvbtraceTex = fvbtraceUV0 = null;
|
|
fib = null;
|
|
fscene = fmesh = flmid = fsurf = fmatid = fmatide = fmatideb = falphaid = fib32 = fseamfix = fmatidh = fhmaps = null;
|
|
fib32lod = falphaidlod = null;
|
|
}
|
|
|
|
static bool CheckUnwrapError()
|
|
{
|
|
if (ftModelPostProcessor.unwrapError)
|
|
{
|
|
|
|
DebugLogError("Unity failed unwrapping some models. See console for details. Last failed model: " + ftModelPostProcessor.lastUnwrapErrorAsset+"\nModel will now be reverted to original state.");
|
|
|
|
int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(ftModelPostProcessor.lastUnwrapErrorAsset);
|
|
if (mstoreIndex < 0)
|
|
{
|
|
Debug.LogError("Failed to find failed asset?");
|
|
}
|
|
else
|
|
{
|
|
gstorage.ClearAssetModifications(mstoreIndex);
|
|
}
|
|
|
|
ftModelPostProcessor.unwrapError = false;
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static Mesh BuildAreaLightMesh(Light areaLight)
|
|
{
|
|
var mesh = new Mesh();
|
|
|
|
var verts = new Vector3[4];
|
|
|
|
Vector2 areaSize = ftLightMeshInspector.GetAreaLightSize(areaLight);
|
|
|
|
verts[0] = new Vector3(-0.5f * areaSize.x, -0.5f * areaSize.y, 0);
|
|
verts[1] = new Vector3(0.5f * areaSize.x, -0.5f * areaSize.y, 0);
|
|
verts[2] = new Vector3(0.5f * areaSize.x, 0.5f * areaSize.y, 0);
|
|
verts[3] = new Vector3(-0.5f * areaSize.x, 0.5f * areaSize.y, 0);
|
|
|
|
var uvs = new Vector2[4];
|
|
uvs[0] = new Vector2(0, 0);
|
|
uvs[1] = new Vector2(1, 0);
|
|
uvs[2] = new Vector2(1, 1);
|
|
uvs[3] = new Vector2(0, 1);
|
|
|
|
var indices = new int[6];
|
|
|
|
indices[0] = 0;
|
|
indices[1] = 1;
|
|
indices[2] = 2;
|
|
|
|
indices[3] = 0;
|
|
indices[4] = 2;
|
|
indices[5] = 3;
|
|
|
|
var normals = new Vector3[4];
|
|
var n = Vector3.forward;// -areaLight.transform.forward; // transformation will be applied later
|
|
for(int i=0; i<4; i++) normals[i] = n;
|
|
|
|
mesh.vertices = verts;
|
|
mesh.triangles = indices;
|
|
mesh.normals = normals;
|
|
mesh.uv = uvs;
|
|
|
|
return mesh;
|
|
}
|
|
|
|
static bool CheckForTangentSHLights()
|
|
{
|
|
var All2 = FindObjectsOfType(typeof(BakerySkyLight));
|
|
for(int i=0; i<All2.Length; i++)
|
|
{
|
|
var obj = All2[i] as BakerySkyLight;
|
|
if (!obj.enabled) continue;
|
|
if (!obj.gameObject.activeInHierarchy) continue;
|
|
if (obj.tangentSH)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static void CreateSceneFolder()
|
|
{
|
|
if (scenePath == "" || !Directory.Exists(scenePath))
|
|
{
|
|
// Default scene path is TEMP/frender
|
|
var tempDir = System.Environment.GetEnvironmentVariable("TEMP", System.EnvironmentVariableTarget.Process);
|
|
scenePath = tempDir + "\\frender";
|
|
if (!Directory.Exists(scenePath)) Directory.CreateDirectory(scenePath);
|
|
}
|
|
}
|
|
|
|
static int CorrectLMGroupID(int id, BakeryLightmapGroup lmgroup, List<BakeryLightmapGroup> groupList)
|
|
{
|
|
id = id < 0 ? -1 : id;
|
|
if (lmgroup != null && lmgroup.parentName != null && lmgroup.parentName.Length > 0 && lmgroup.parentName != "|")
|
|
{
|
|
for(int g=0; g<groupList.Count; g++)
|
|
{
|
|
if (groupList[g].name == lmgroup.parentName)
|
|
{
|
|
id = g;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static void InitSceneStorage(ExportSceneData data)
|
|
{
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
|
|
bool first = true;
|
|
for(int i=0; i<storages.Length; i++)
|
|
{
|
|
var scene = SceneManager.GetSceneAt(i);
|
|
if (!scene.isLoaded) continue;
|
|
var gg = ftLightmaps.FindInScene("!ftraceLightmaps", scene);
|
|
|
|
if (gg == null)
|
|
{
|
|
gg = new GameObject();
|
|
SceneManager.MoveGameObjectToScene(gg, scene);
|
|
gg.name = "!ftraceLightmaps";
|
|
gg.hideFlags = HideFlags.HideInHierarchy;
|
|
}
|
|
|
|
storages[i] = gg.GetComponent<ftLightmapsStorage>();
|
|
if (storages[i] == null)
|
|
{
|
|
storages[i] = gg.AddComponent<ftLightmapsStorage>();
|
|
}
|
|
|
|
if (modifyLightmapStorage)
|
|
{
|
|
/*
|
|
storages[i].bakedRenderers = new List<Renderer>();
|
|
storages[i].bakedIDs = new List<int>();
|
|
storages[i].bakedScaleOffset = new List<Vector4>();
|
|
storages[i].bakedVertexOffset = new List<int>();
|
|
storages[i].bakedVertexColorMesh = new List<Mesh>();
|
|
storages[i].bakedRenderersTerrain = new List<Terrain>();
|
|
storages[i].bakedIDsTerrain = new List<int>();
|
|
storages[i].bakedScaleOffsetTerrain = new List<Vector4>();
|
|
*/
|
|
storages[i].hasEmissive = new List<bool>();
|
|
storages[i].lmGroupLODResFlags = null;
|
|
storages[i].lmGroupMinLOD = null;
|
|
storages[i].lmGroupLODMatrix = null;
|
|
storages[i].nonBakedRenderers = new List<Renderer>();
|
|
}
|
|
if (first)
|
|
{
|
|
data.firstNonNullStorage = i;
|
|
first = false;
|
|
}
|
|
storages[i].implicitGroups = new List<UnityEngine.Object>();
|
|
storages[i].implicitGroupedObjects = new List<GameObject>();
|
|
sceneToID[scene] = i;
|
|
}
|
|
|
|
//var go = GameObject.Find("!ftraceLightmaps");
|
|
//data.settingsStorage = go.GetComponent<ftLightmapsStorage>();
|
|
}
|
|
|
|
static void InitSceneStorage2(ExportSceneData data)
|
|
{
|
|
var storages = data.storages;
|
|
for(int i=0; i<storages.Length; i++)
|
|
{
|
|
var scene = SceneManager.GetSceneAt(i);
|
|
if (!scene.isLoaded) continue;
|
|
if (modifyLightmapStorage)
|
|
{
|
|
storages[i].bakedRenderers = new List<Renderer>();
|
|
storages[i].bakedIDs = new List<int>();
|
|
storages[i].bakedScaleOffset = new List<Vector4>();
|
|
storages[i].bakedVertexOffset = new List<int>();
|
|
storages[i].bakedVertexColorMesh = new List<Mesh>();
|
|
#if USE_TERRAINS
|
|
storages[i].bakedRenderersTerrain = new List<Terrain>();
|
|
storages[i].bakedIDsTerrain = new List<int>();
|
|
storages[i].bakedScaleOffsetTerrain = new List<Vector4>();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Used to pass maps to alphaBufferGen/frender via RAM (non-DX11)
|
|
public static TexInput InputDataFromTex(Texture tex, TexInputType ttype = TexInputType.Color)
|
|
{
|
|
RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32;
|
|
TextureFormat texFormat = TextureFormat.RGBA32;
|
|
if (ttype == TexInputType.FloatColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBFloat;
|
|
texFormat = TextureFormat.RGBAFloat;
|
|
}
|
|
else if (ttype == TexInputType.HalfColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBHalf;
|
|
texFormat = TextureFormat.RGBAHalf;
|
|
}
|
|
var rt = new RenderTexture(tex.width, tex.height, 0, rtFormat);
|
|
var readableTex = new Texture2D(tex.width, tex.height, texFormat, false);
|
|
Graphics.Blit(tex, rt);
|
|
Graphics.SetRenderTarget(rt);
|
|
readableTex.ReadPixels(new Rect(0,0,tex.width,tex.height), 0, 0, false);
|
|
readableTex.Apply();
|
|
var bytes = readableTex.GetRawTextureData();
|
|
|
|
var a = new TexInput();
|
|
a.data = bytes;
|
|
a.width = (ushort)tex.width;
|
|
a.height = (ushort)tex.height;
|
|
|
|
DestroyImmediate(readableTex);
|
|
rt.Release();
|
|
|
|
return a;
|
|
}
|
|
|
|
public static byte[] InputBytesFromTex(Texture tex, TexInputType ttype = TexInputType.Color)
|
|
{
|
|
RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32;
|
|
TextureFormat texFormat = TextureFormat.RGBA32;
|
|
byte[] header = ftDDS.ddsHeaderRGBA8;
|
|
if (ttype == TexInputType.FloatColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBFloat;
|
|
texFormat = TextureFormat.RGBAFloat;
|
|
header = ftDDS.ddsHeaderFloat4;
|
|
}
|
|
else if (ttype == TexInputType.HalfColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBHalf;
|
|
texFormat = TextureFormat.RGBAHalf;
|
|
header = ftDDS.ddsHeaderHalf4;
|
|
}
|
|
var rt = new RenderTexture(tex.width, tex.height, 0, rtFormat);
|
|
var readableTex = new Texture2D(tex.width, tex.height, texFormat, false);
|
|
Graphics.Blit(tex, rt);
|
|
Graphics.SetRenderTarget(rt);
|
|
readableTex.ReadPixels(new Rect(0,0,tex.width,tex.height), 0, 0, false);
|
|
readableTex.Apply();
|
|
var bytes = readableTex.GetRawTextureData();
|
|
|
|
var fbytes = new byte[128 + bytes.Length];
|
|
System.Buffer.BlockCopy(header, 0, fbytes, 0, 128);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(tex.height), 0, fbytes, 12, 4);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(tex.width), 0, fbytes, 16, 4);
|
|
System.Buffer.BlockCopy(bytes, 0, fbytes, 128, bytes.Length);
|
|
|
|
DestroyImmediate(readableTex);
|
|
rt.Release();
|
|
|
|
return fbytes;
|
|
}
|
|
|
|
public static TexInput InputDataFromCubemap(Texture tex, Matrix4x4 mtx, bool isLinear, bool isDoubleLDR, TexInputType ttype = TexInputType.Color)
|
|
{
|
|
RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32;
|
|
TextureFormat texFormat = TextureFormat.RGBA32;
|
|
if (ttype == TexInputType.FloatColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBFloat;
|
|
texFormat = TextureFormat.RGBAFloat;
|
|
}
|
|
else if (ttype == TexInputType.HalfColor)
|
|
{
|
|
rtFormat = RenderTextureFormat.ARGBHalf;
|
|
texFormat = TextureFormat.RGBAHalf;
|
|
}
|
|
|
|
int outWidth = System.Math.Min(tex.width, 512);
|
|
int outHeight = System.Math.Min(tex.height, 512);
|
|
|
|
var rt = new RenderTexture(outWidth, outHeight * 6, 0, rtFormat);
|
|
var readableTex = new Texture2D(outWidth, outHeight * 6, texFormat, false);
|
|
|
|
if (matCubemapToStripExport == null) matCubemapToStripExport = new Material(Shader.Find("Hidden/ftCubemap2StripExport"));
|
|
matCubemapToStripExport.SetMatrix("transform", mtx);
|
|
matCubemapToStripExport.SetVector("isLinear_isDoubleLDR", new Vector4(isLinear ? 1 : 0, isDoubleLDR ? 1 : 0, 0, 0));
|
|
|
|
Graphics.Blit(tex, rt, matCubemapToStripExport);
|
|
Graphics.SetRenderTarget(rt);
|
|
readableTex.ReadPixels(new Rect(0,0,outWidth, outHeight * 6), 0, 0, false);
|
|
readableTex.Apply();
|
|
var bytes = readableTex.GetRawTextureData();
|
|
|
|
var a = new TexInput();
|
|
a.data = bytes;
|
|
a.width = (ushort)outWidth;
|
|
a.height = (ushort)(outHeight * 6);
|
|
|
|
DestroyImmediate(readableTex);
|
|
rt.Release();
|
|
|
|
return a;
|
|
}
|
|
|
|
static IEnumerator CreateLightProbeLMGroup(ExportSceneData data)
|
|
{
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
var lmBounds = data.lmBounds;
|
|
var groupList = data.groupList;
|
|
|
|
var probes = LightmapSettings.lightProbes;
|
|
if (probes == null)
|
|
{
|
|
DebugLogError("No probes in LightingDataAsset");
|
|
yield break;
|
|
}
|
|
var positions = probes.positions;
|
|
|
|
int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)probes.count));
|
|
atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize;
|
|
var uvpos = new float[atlasTexSize * atlasTexSize * 4];
|
|
var uvnormal = new byte[atlasTexSize * atlasTexSize * 4];
|
|
|
|
for(int i=0; i<probes.count; i++)
|
|
{
|
|
int x = i % atlasTexSize;
|
|
int y = i / atlasTexSize;
|
|
int index = y * atlasTexSize + x;
|
|
uvpos[index * 4] = positions[i].x;
|
|
uvpos[index * 4 + 1] = positions[i].y;
|
|
uvpos[index * 4 + 2] = positions[i].z;
|
|
uvpos[index * 4 + 3] = 1.0f;
|
|
uvnormal[index * 4 + 1] = 255;
|
|
uvnormal[index * 4 + 3] = 255;
|
|
}
|
|
|
|
var posFile = new byte[128 + uvpos.Length * 4];
|
|
System.Buffer.BlockCopy(ftDDS.ddsHeaderFloat4, 0, posFile, 0, 128);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 12, 4);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 16, 4);
|
|
System.Buffer.BlockCopy(uvpos, 0, posFile, 128, uvpos.Length * 4);
|
|
SaveGBufferMapFromRAM(posFile, posFile.Length, scenePath + "/uvpos_probes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer);
|
|
//GL.IssuePluginEvent(8);
|
|
//yield return null;
|
|
|
|
var posNormal = new byte[128 + uvnormal.Length];
|
|
System.Buffer.BlockCopy(ftDDS.ddsHeaderRGBA8, 0, posNormal, 0, 128);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 12, 4);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 16, 4);
|
|
System.Buffer.BlockCopy(uvnormal, 0, posNormal, 128, uvnormal.Length);
|
|
SaveGBufferMapFromRAM(posNormal, posNormal.Length, scenePath + "/uvnormal_probes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer);
|
|
//GL.IssuePluginEvent(8);
|
|
//yield return null;
|
|
|
|
lightProbeLMGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
lightProbeLMGroup.name = "probes";
|
|
lightProbeLMGroup.probes = true;
|
|
lightProbeLMGroup.isImplicit = true;
|
|
lightProbeLMGroup.resolution = 256;
|
|
lightProbeLMGroup.bitmask = 1;
|
|
lightProbeLMGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex;
|
|
lightProbeLMGroup.id = data.lmid;
|
|
lightProbeLMGroup.totalVertexCount = probes.count;
|
|
lightProbeLMGroup.vertexCounter = 0;
|
|
lightProbeLMGroup.renderDirMode = BakeryLightmapGroup.RenderDirMode.ProbeSH;
|
|
lightProbeLMGroup.renderMode = (ftRenderLightmap.instance.userRenderMode == ftRenderLightmap.RenderMode.Subtractive && ftRenderLightmap.useUnityForOcclsusionProbes) ? BakeryLightmapGroup.RenderMode.Indirect : BakeryLightmapGroup.RenderMode.Auto;
|
|
groupList.Add(lightProbeLMGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(10000,10000,10000)));
|
|
data.lmid++;
|
|
|
|
storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroups.Add(lightProbeLMGroup);
|
|
storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroupedObjects.Add(null);
|
|
}
|
|
|
|
static IEnumerator CreateVolumeLMGroup(ExportSceneData data)
|
|
{
|
|
ftRenderLightmap.hasAnyVolumes = false;
|
|
|
|
var vols = ftRenderLightmap.FindBakeableVolumes();
|
|
if (vols.Length == 0) yield break;
|
|
|
|
ftRenderLightmap.hasAnyVolumes = true;
|
|
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
var lmBounds = data.lmBounds;
|
|
var groupList = data.groupList;
|
|
|
|
int numTotalProbes = 0;
|
|
for(int v=0; v<vols.Length; v++)
|
|
{
|
|
numTotalProbes += ftRenderLightmap.VolumeDimension(vols[v].resolutionX) * ftRenderLightmap.VolumeDimension(vols[v].resolutionY) * ftRenderLightmap.VolumeDimension(vols[v].resolutionZ);
|
|
}
|
|
|
|
var positions = new Vector3[numTotalProbes];
|
|
int i = 0;
|
|
Vector3 halfVoxelSize = Vector3.one;
|
|
for(int v=0; v<vols.Length; v++)
|
|
{
|
|
var vol = vols[v];
|
|
int rx = ftRenderLightmap.VolumeDimension(vol.resolutionX);
|
|
int ry = ftRenderLightmap.VolumeDimension(vol.resolutionY);
|
|
int rz = ftRenderLightmap.VolumeDimension(vol.resolutionZ);
|
|
vol.UpdateBounds();
|
|
var bmin = vol.bounds.min;
|
|
var bmax = vol.bounds.max;
|
|
halfVoxelSize = bmax - bmin;
|
|
halfVoxelSize = new Vector3(halfVoxelSize.x/rx, halfVoxelSize.y/ry, halfVoxelSize.z/rz) * 0.5f;
|
|
float lx, ly, lz;
|
|
for(int z=0; z<rz; z++)
|
|
{
|
|
lz = Mathf.Lerp(bmin.z, bmax.z, z/(float)rz) + halfVoxelSize.z;
|
|
for(int y=0; y<ry; y++)
|
|
{
|
|
ly = Mathf.Lerp(bmin.y, bmax.y, y/(float)ry) + halfVoxelSize.y;
|
|
for(int x=0; x<rx; x++)
|
|
{
|
|
lx = Mathf.Lerp(bmin.x, bmax.x, x/(float)rx) + halfVoxelSize.x;
|
|
positions[i] = new Vector3(lx, ly, lz);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)numTotalProbes));
|
|
atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize;
|
|
var uvpos = new float[atlasTexSize * atlasTexSize * 4];
|
|
var uvnormal = new byte[atlasTexSize * atlasTexSize * 4];
|
|
|
|
for(i=0; i<numTotalProbes; i++)
|
|
{
|
|
int x = i % atlasTexSize;
|
|
int y = i / atlasTexSize;
|
|
int index = y * atlasTexSize + x;
|
|
uvpos[index * 4] = positions[i].x;
|
|
uvpos[index * 4 + 1] = positions[i].y;
|
|
uvpos[index * 4 + 2] = positions[i].z;
|
|
uvpos[index * 4 + 3] = 1.0f;
|
|
uvnormal[index * 4 + 1] = 255;
|
|
uvnormal[index * 4 + 3] = 255;
|
|
}
|
|
|
|
var posFile = new byte[128 + uvpos.Length * 4];
|
|
System.Buffer.BlockCopy(ftDDS.ddsHeaderFloat4, 0, posFile, 0, 128);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 12, 4);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posFile, 16, 4);
|
|
System.Buffer.BlockCopy(uvpos, 0, posFile, 128, uvpos.Length * 4);
|
|
SaveGBufferMapFromRAM(posFile, posFile.Length, scenePath + "/uvpos_volumes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer);
|
|
//GL.IssuePluginEvent(8);
|
|
//yield return null;
|
|
|
|
var posNormal = new byte[128 + uvnormal.Length];
|
|
System.Buffer.BlockCopy(ftDDS.ddsHeaderRGBA8, 0, posNormal, 0, 128);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 12, 4);
|
|
System.Buffer.BlockCopy(BitConverter.GetBytes(atlasTexSize), 0, posNormal, 16, 4);
|
|
System.Buffer.BlockCopy(uvnormal, 0, posNormal, 128, uvnormal.Length);
|
|
SaveGBufferMapFromRAM(posNormal, posNormal.Length, scenePath + "/uvnormal_volumes" + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"), ftRenderLightmap.compressedGBuffer);
|
|
//GL.IssuePluginEvent(8);
|
|
//yield return null;
|
|
|
|
volumeLMGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
volumeLMGroup.name = "volumes";
|
|
volumeLMGroup.probes = true;
|
|
volumeLMGroup.fixPos3D = true;
|
|
volumeLMGroup.voxelSize = halfVoxelSize * 2; // incorrect... should be different for every probe
|
|
volumeLMGroup.isImplicit = true;
|
|
volumeLMGroup.resolution = 256;
|
|
volumeLMGroup.bitmask = 1;
|
|
volumeLMGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex;
|
|
volumeLMGroup.id = data.lmid;
|
|
volumeLMGroup.totalVertexCount = numTotalProbes;
|
|
volumeLMGroup.vertexCounter = 0;
|
|
volumeLMGroup.renderMode = (BakeryLightmapGroup.RenderMode)pstorage.volumeRenderMode;
|
|
volumeLMGroup.renderDirMode = BakeryLightmapGroup.RenderDirMode.ProbeSH;
|
|
groupList.Add(volumeLMGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(10000,10000,10000)));
|
|
data.lmid++;
|
|
|
|
storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroups.Add(volumeLMGroup);
|
|
storages[sceneToID[EditorSceneManager.GetActiveScene()]].implicitGroupedObjects.Add(null);
|
|
}
|
|
|
|
static void CollectExplicitLMGroups(ExportSceneData data)
|
|
{
|
|
var groupList = data.groupList;
|
|
var lmBounds = data.lmBounds;
|
|
|
|
// Find explicit LMGroups
|
|
// (Also init lmBounds and LMID)
|
|
var groupSelectors = new List<BakeryLightmapGroupSelector>(FindObjectsOfType(typeof(BakeryLightmapGroupSelector)) as BakeryLightmapGroupSelector[]);
|
|
for(int i=0; i<groupSelectors.Count; i++)
|
|
{
|
|
var lmgroup = groupSelectors[i].lmgroupAsset as BakeryLightmapGroup;
|
|
if (lmgroup == null) continue;
|
|
if (!groupList.Contains(lmgroup))
|
|
{
|
|
lmgroup.name = FilterNonASCII(lmgroup.name);
|
|
lmgroup.id = data.lmid;
|
|
lmgroup.sceneLodLevel = -1;
|
|
lmgroup.sceneName = "";
|
|
groupList.Add(lmgroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
}
|
|
EditorUtility.SetDirty(lmgroup);
|
|
}
|
|
}
|
|
|
|
static bool CheckForMultipleSceneStorages(GameObject obj, ExportSceneData data)
|
|
{
|
|
var sceneHasStorage = data.sceneHasStorage;
|
|
|
|
// Check for storage count in each scene
|
|
if (obj.name == "!ftraceLightmaps")
|
|
{
|
|
if (!sceneHasStorage.ContainsKey(obj.scene))
|
|
{
|
|
sceneHasStorage[obj.scene] = true;
|
|
}
|
|
else
|
|
{
|
|
return ExportSceneValidationMessage("Scene " + obj.scene.name + " has multiple lightmap storage objects. This is not currently supported. Make sure you don't bake a scene with already baked prefabs.");
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if USE_TERRAINS
|
|
static void ConvertTerrain(GameObject obj)
|
|
{
|
|
var terr = obj.GetComponent<Terrain>();
|
|
if (terr == null) return;
|
|
if (!terr.enabled) return;
|
|
|
|
if (!obj.activeInHierarchy) return;
|
|
if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) return; // skip temp objects
|
|
if (obj.tag == "EditorOnly") return; // skip temp objects
|
|
if ((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) return; // skip dynamic
|
|
|
|
var so = new SerializedObject(terr);
|
|
var scaleInLmTerr = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
|
|
var terrParent = new GameObject();
|
|
SceneManager.MoveGameObjectToScene(terrParent, obj.scene);
|
|
terrParent.transform.parent = obj.transform;//.parent;
|
|
var expGroup = obj.GetComponent<BakeryLightmapGroupSelector>();
|
|
if (expGroup != null)
|
|
{
|
|
var expGroup2 = terrParent.AddComponent<BakeryLightmapGroupSelector>();
|
|
expGroup2.lmgroupAsset = expGroup.lmgroupAsset;
|
|
expGroup2.instanceResolutionOverride = expGroup.instanceResolutionOverride;
|
|
expGroup2.instanceResolution = expGroup.instanceResolution;
|
|
}
|
|
terrParent.name = "__ExportTerrainParent";
|
|
terrainObjectList.Add(terrParent);
|
|
terrainObjectToActual.Add(terr);
|
|
|
|
var tdata = terr.terrainData;
|
|
int res = tdata.heightmapResolution;
|
|
var heightmap = tdata.GetHeights(0, 0, res, res);
|
|
var uvscale = new Vector2(1,1) / (res-1);
|
|
var uvoffset = new Vector2(0,0);
|
|
var gposOffset = obj.transform.position;
|
|
float scaleX = tdata.size.x / (res-1);
|
|
float scaleY = tdata.size.y;
|
|
float scaleZ = tdata.size.z / (res-1);
|
|
|
|
int patchRes = res;
|
|
#if UNITY_2017_3_OR_NEWER
|
|
// supports 32-bit indices
|
|
#else
|
|
while(patchRes > 254) patchRes = 254;//patchRes /= 2;
|
|
#endif
|
|
int numVerts = patchRes * patchRes;
|
|
int numPatches = (int)Mathf.Ceil(res / (float)patchRes);
|
|
|
|
// Gen terrain texture
|
|
var oldMat = terr.materialTemplate;
|
|
var oldMatType = terr.materialType;
|
|
var oldPos = obj.transform.position;
|
|
#if UNITY_2018_3_OR_NEWER
|
|
var oldInstanced = terr.drawInstanced;
|
|
#endif
|
|
var unlitTerrainMat = new Material(Shader.Find("Hidden/ftUnlitTerrain"));
|
|
//unlitTerrainMat = AssetDatabase.LoadAssetAtPath("Assets/Bakery/ftUnlitTerrain.mat", typeof(Material)) as Material;
|
|
terr.materialTemplate = unlitTerrainMat;
|
|
terr.materialType = Terrain.MaterialType.Custom;
|
|
#if UNITY_2018_3_OR_NEWER
|
|
terr.drawInstanced = false;
|
|
#endif
|
|
|
|
int baseMapResolution = tdata.baseMapResolution;
|
|
#if UNITY_2019_3_OR_NEWER
|
|
int holesResolution = tdata.holesResolution;
|
|
if (holesResolution > baseMapResolution) baseMapResolution = holesResolution;
|
|
#endif
|
|
|
|
obj.transform.position = new Vector3(-10000, -10000, -10000); // let's hope it's not the worst idea
|
|
var tempCamGO = new GameObject();
|
|
tempCamGO.transform.parent = obj.transform;
|
|
tempCamGO.transform.localPosition = new Vector3(tdata.size.x * 0.5f, scaleY + 1, tdata.size.z * 0.5f);
|
|
tempCamGO.transform.eulerAngles = new Vector3(90,0,0);
|
|
var tempCam = tempCamGO.AddComponent<Camera>();
|
|
tempCam.orthographic = true;
|
|
tempCam.orthographicSize = Mathf.Max(tdata.size.x, tdata.size.z) * 0.5f;
|
|
tempCam.aspect = Mathf.Max(tdata.size.x, tdata.size.z) / Mathf.Min(tdata.size.x, tdata.size.z);
|
|
tempCam.enabled = false;
|
|
tempCam.clearFlags = CameraClearFlags.SolidColor;
|
|
tempCam.backgroundColor = new Color(0,0,0,0);
|
|
tempCam.targetTexture =
|
|
new RenderTexture(baseMapResolution, baseMapResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
|
var tex = new Texture2D(baseMapResolution, baseMapResolution, TextureFormat.ARGB32, true, false);
|
|
RenderTexture.active = tempCam.targetTexture;
|
|
tempCam.Render();
|
|
terr.materialTemplate = oldMat;
|
|
terr.materialType = oldMatType;
|
|
#if UNITY_2018_3_OR_NEWER
|
|
terr.drawInstanced = oldInstanced;
|
|
#endif
|
|
obj.transform.position = oldPos;
|
|
RenderTexture.active = tempCam.targetTexture;
|
|
tex.ReadPixels(new Rect(0,0,baseMapResolution, baseMapResolution), 0, 0, true);
|
|
tex.Apply();
|
|
unlitTerrainMat.mainTexture = tex;
|
|
Graphics.SetRenderTarget(null);
|
|
DestroyImmediate(tempCamGO);
|
|
|
|
bool isDX11 = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11;
|
|
|
|
if (exportTerrainAsHeightmap)
|
|
{
|
|
var hmap = new BinaryWriter(File.Open(scenePath + "/height" + terrainObjectToHeightMap.Count + ".dds", FileMode.Create));
|
|
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("height" + terrainObjectToHeightMap.Count + ".dds");
|
|
|
|
hmap.Write(ftDDS.ddsHeaderR32F);
|
|
var bytes = new byte[res * res * 4];
|
|
|
|
// Normalize heights
|
|
float maxHeight = 0;
|
|
float height;
|
|
for(int y=0; y<res; y++)
|
|
{
|
|
for(int x=0; x<res; x++)
|
|
{
|
|
height = heightmap[x,y];
|
|
if (height > maxHeight) maxHeight = height;
|
|
}
|
|
}
|
|
maxHeight = Mathf.Max(maxHeight, 0.0001f);
|
|
float invMaxHeight = 1.0f / maxHeight;
|
|
for(int y=0; y<res; y++)
|
|
{
|
|
for(int x=0; x<res; x++)
|
|
{
|
|
heightmap[x,y] *= invMaxHeight;
|
|
}
|
|
}
|
|
float aabbHeight = maxHeight * scaleY;
|
|
|
|
// First mip is the real heightmap
|
|
System.Buffer.BlockCopy(heightmap, 0, bytes, 0, bytes.Length);
|
|
hmap.Write(bytes);
|
|
|
|
if (isDX11)
|
|
{
|
|
var htex = new Texture2D(res, res, TextureFormat.RFloat, false, true);
|
|
htex.LoadRawTextureData(bytes);
|
|
htex.Apply();
|
|
terrainObjectToHeightMap.Add(htex);
|
|
}
|
|
else
|
|
{
|
|
var a = new TexInput();
|
|
a.data = bytes;
|
|
a.width = (ushort)res;
|
|
a.height = a.width;
|
|
terrainObjectToHeightMapRAM.Add(a);
|
|
terrainObjectToHeightMap.Add(null);
|
|
}
|
|
|
|
// min
|
|
terrainObjectToBounds.Add(obj.transform.position.x);
|
|
terrainObjectToBounds.Add(obj.transform.position.y);
|
|
terrainObjectToBounds.Add(obj.transform.position.z);
|
|
// max
|
|
terrainObjectToBounds.Add(obj.transform.position.x + tdata.size.x);
|
|
terrainObjectToBounds.Add(obj.transform.position.y + aabbHeight);
|
|
terrainObjectToBounds.Add(obj.transform.position.z + tdata.size.z);
|
|
|
|
// filled later
|
|
terrainObjectToLMID.Add(0);
|
|
terrainObjectToBoundsUV.Add(0);
|
|
terrainObjectToBoundsUV.Add(0);
|
|
terrainObjectToBoundsUV.Add(0);
|
|
terrainObjectToBoundsUV.Add(0);
|
|
|
|
terrainObjectToFlags.Add(terr.castShadows ? 1 : 0);
|
|
|
|
// Second mip is max() of 3x3 corners
|
|
float[] floats = null;
|
|
float[] floatsPrev = null;
|
|
float[] floatsTmp;
|
|
int mipCount = 1;
|
|
int mipRes = res / 2;
|
|
//int prevRes = res;
|
|
float h00, h10, h01, h11;
|
|
/*Vector3 n00, n10, n01, n11;
|
|
Vector3[] normals = null;
|
|
Vector3[] normalsPrev = null;
|
|
|
|
var origNormals = new Vector3[res * res];
|
|
for(int y=0; y<res; y++)
|
|
{
|
|
for(int x=0; x<res; x++)
|
|
{
|
|
origNormals[y*res+x] = data.GetInterpolatedNormal(x / (float)res, y / (float)res);
|
|
}
|
|
}*/
|
|
int terrIndex = terrainObjectToHeightMap.Count - 1;//terrainObjectToNormalMip0.Count;
|
|
//terrainObjectToNormalMip0.Add(origNormals);
|
|
//normalsPrev = origNormals;
|
|
terrainObjectToHeightMips.Add(new List<float[]>());
|
|
//terrainObjectToNormalMips.Add(new List<Vector3[]>());
|
|
|
|
if (mipRes > 0)
|
|
{
|
|
floats = new float[mipRes * mipRes];
|
|
//normals = new Vector3[mipRes * mipRes];
|
|
for(int y=0; y<mipRes; y++)
|
|
{
|
|
for(int x=0; x<mipRes; x++)
|
|
{
|
|
/*h00 = heightmap[y*2,x*2];
|
|
h10 = heightmap[y*2,x*2+1];
|
|
h01 = heightmap[y*2+1,x*2];
|
|
h11 = heightmap[y*2+1,x*2+1];
|
|
height = h00 > h10 ? h00 : h10;
|
|
height = height > h01 ? height : h01;
|
|
height = height > h11 ? height : h11;
|
|
floats[y*mipRes+x] = height;*/
|
|
|
|
float maxVal = 0;
|
|
for(int yy=0; yy<3; yy++)
|
|
{
|
|
for(int xx=0; xx<3; xx++)
|
|
{
|
|
float val = heightmap[y*2+yy, x*2+xx];
|
|
if (val > maxVal) maxVal = val;
|
|
}
|
|
}
|
|
floats[y*mipRes+x] = maxVal;
|
|
|
|
//n00 = normalsPrev[y*2 * res + x*2];
|
|
//n10 = normalsPrev[y*2 * res + x*2+1];
|
|
//n01 = normalsPrev[(y*2+1) * res + x*2];
|
|
//n11 = normalsPrev[(y*2+1) * res + x*2+1];
|
|
//normals[y*mipRes+x] = (n00 + n10 + n01 + n11);
|
|
}
|
|
}
|
|
|
|
System.Buffer.BlockCopy(floats, 0, bytes, 0, mipRes*mipRes*4);
|
|
hmap.Write(bytes, 0, mipRes*mipRes*4);
|
|
|
|
float[] storedMip = new float[mipRes*mipRes];
|
|
System.Buffer.BlockCopy(floats, 0, storedMip, 0, mipRes*mipRes*4);
|
|
terrainObjectToHeightMips[terrIndex].Add(storedMip);
|
|
//terrainObjectToNormalMips[terrIndex].Add(normals);
|
|
|
|
mipCount++;
|
|
mipRes /= 2;
|
|
}
|
|
|
|
// Next mips are regular max() of 4 texels
|
|
while(mipRes > 1)
|
|
{
|
|
if (floatsPrev == null)
|
|
{
|
|
floatsPrev = floats;
|
|
floats = new float[mipRes * mipRes];
|
|
}
|
|
|
|
//normalsPrev = normals;
|
|
//normals = new Vector3[mipRes * mipRes];
|
|
|
|
for(int y=0; y<mipRes; y++)
|
|
{
|
|
for(int x=0; x<mipRes; x++)
|
|
{
|
|
h00 = floatsPrev[y*2 * mipRes*2 + x*2];
|
|
h10 = floatsPrev[y*2 * mipRes*2 + x*2+1];
|
|
h01 = floatsPrev[(y*2+1) * mipRes*2 + x*2];
|
|
h11 = floatsPrev[(y*2+1) * mipRes*2 + x*2+1];
|
|
height = h00 > h10 ? h00 : h10;
|
|
height = height > h01 ? height : h01;
|
|
height = height > h11 ? height : h11;
|
|
floats[y*mipRes+x] = height;
|
|
|
|
//n00 = normalsPrev[y*2 * mipRes*2 + x*2];
|
|
//n10 = normalsPrev[y*2 * mipRes*2 + x*2+1];
|
|
//n01 = normalsPrev[(y*2+1) * mipRes*2 + x*2];
|
|
//n11 = normalsPrev[(y*2+1) * mipRes*2 + x*2+1];
|
|
//normals[y*mipRes+x] = (n00 + n10 + n01 + n11);
|
|
}
|
|
}
|
|
|
|
System.Buffer.BlockCopy(floats, 0, bytes, 0, mipRes*mipRes*4);
|
|
hmap.Write(bytes, 0, mipRes*mipRes*4);
|
|
|
|
float[] storedMip = new float[mipRes*mipRes];
|
|
System.Buffer.BlockCopy(floats, 0, storedMip, 0, mipRes*mipRes*4);
|
|
terrainObjectToHeightMips[terrIndex].Add(storedMip);
|
|
//terrainObjectToNormalMips[terrIndex].Add(normals);
|
|
|
|
mipCount++;
|
|
mipRes /= 2;
|
|
|
|
floatsTmp = floatsPrev;
|
|
floatsPrev = floats;
|
|
floats = floatsTmp;
|
|
}
|
|
|
|
hmap.BaseStream.Seek(12, SeekOrigin.Begin);
|
|
hmap.Write(res);
|
|
hmap.Write(res);
|
|
hmap.BaseStream.Seek(28, SeekOrigin.Begin);
|
|
hmap.Write(mipCount);
|
|
hmap.Close();
|
|
|
|
// Create dummy plane for packing/albedo/emissive purposes
|
|
var mesh = new Mesh();
|
|
mesh.vertices = new Vector3[] { gposOffset,
|
|
gposOffset + new Vector3(tdata.size.x, 0, 0),
|
|
gposOffset + new Vector3(tdata.size.x, 0, tdata.size.z),
|
|
gposOffset + new Vector3(0, 0, tdata.size.z) };
|
|
mesh.triangles = new int[]{0,1,2, 2,3,0};
|
|
mesh.normals = new Vector3[]{Vector3.up, Vector3.up, Vector3.up, Vector3.up};
|
|
mesh.uv = new Vector2[]{new Vector2(0,0), new Vector2(1,0), new Vector2(1,1), new Vector2(0,1)};
|
|
mesh.uv2 = mesh.uv;
|
|
|
|
var terrGO = new GameObject();
|
|
terrGO.name = "__ExportTerrain";
|
|
GameObjectUtility.SetStaticEditorFlags(terrGO, StaticEditorFlags.LightmapStatic);
|
|
terrGO.transform.parent = terrParent.transform;
|
|
var mf = terrGO.AddComponent<MeshFilter>();
|
|
var mr = terrGO.AddComponent<MeshRenderer>();
|
|
mf.sharedMesh = mesh;
|
|
#if UNITY_2019_3_OR_NEWER
|
|
// using standard materialTemplates in 2019.3 doesn't work
|
|
mr.sharedMaterial = unlitTerrainMat;
|
|
#else
|
|
mr.sharedMaterial = (terr.materialTemplate == null) ? unlitTerrainMat : terr.materialTemplate;
|
|
#endif
|
|
|
|
terrGO.transform.position = obj.transform.position;
|
|
|
|
var so2 = new SerializedObject(mr);
|
|
so2.FindProperty("m_ScaleInLightmap").floatValue = scaleInLmTerr;
|
|
so2.ApplyModifiedProperties();
|
|
|
|
//terrainObjectList.Add(terrGO);
|
|
//terrainObjectToActual.Add(terr);
|
|
}
|
|
else
|
|
{
|
|
for (int patchX=0; patchX<numPatches; patchX++)
|
|
{
|
|
for (int patchY=0; patchY<numPatches; patchY++)
|
|
{
|
|
int patchResX = patchX < numPatches-1 ? patchRes : (res - patchRes*(numPatches-1));
|
|
int patchResY = patchY < numPatches-1 ? patchRes : (res - patchRes*(numPatches-1));
|
|
if (patchX < numPatches-1) patchResX += 1;
|
|
if (patchY < numPatches-1) patchResY += 1;
|
|
numVerts = patchResX * patchResY;
|
|
var posOffset = gposOffset + new Vector3(patchX*patchRes*scaleX, 0, patchY*patchRes*scaleZ);
|
|
|
|
var positions = new Vector3[numVerts];
|
|
var uvs = new Vector2[numVerts];
|
|
var normals = new Vector3[numVerts];
|
|
var indices = new int[(patchResX-1) * (patchResY-1) * 2 * 3];
|
|
int vertOffset = 0;
|
|
int indexOffset = 0;
|
|
|
|
for (int x=0;x<patchResX;x++)
|
|
{
|
|
for (int y=0;y<patchResY;y++)
|
|
{
|
|
int gx = x + patchX * patchRes;
|
|
int gy = y + patchY * patchRes;
|
|
|
|
int index = x * patchResY + y;
|
|
float height = heightmap[gy,gx];
|
|
|
|
positions[index] = new Vector3(x * scaleX, height * scaleY, y * scaleZ) + posOffset;
|
|
uvs[index] = new Vector2(gx * uvscale.x + uvoffset.x, gy * uvscale.y + uvoffset.y);
|
|
|
|
normals[index] = tdata.GetInterpolatedNormal(gx / (float)res, gy / (float)res);
|
|
|
|
if (x < patchResX-1 && y < patchResY-1)
|
|
{
|
|
indices[indexOffset] = vertOffset + patchResY + 1;
|
|
indices[indexOffset + 1] = vertOffset + patchResY;
|
|
indices[indexOffset + 2] = vertOffset;
|
|
|
|
indices[indexOffset + 3] = vertOffset + 1;
|
|
indices[indexOffset + 4] = vertOffset + patchResY + 1;
|
|
indices[indexOffset + 5] = vertOffset;
|
|
|
|
indexOffset += 6;
|
|
}
|
|
|
|
vertOffset++;
|
|
}
|
|
}
|
|
|
|
var mesh = new Mesh();
|
|
#if UNITY_2017_3_OR_NEWER
|
|
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
|
#endif
|
|
mesh.vertices = positions;
|
|
mesh.triangles = indices;
|
|
mesh.normals = normals;
|
|
mesh.uv = uvs;
|
|
mesh.uv2 = uvs;
|
|
|
|
var terrGO = new GameObject();
|
|
terrGO.name = "__ExportTerrain";
|
|
GameObjectUtility.SetStaticEditorFlags(terrGO, StaticEditorFlags.LightmapStatic);
|
|
terrGO.transform.parent = terrParent.transform;
|
|
var mf = terrGO.AddComponent<MeshFilter>();
|
|
var mr = terrGO.AddComponent<MeshRenderer>();
|
|
mf.sharedMesh = mesh;
|
|
#if UNITY_2019_3_OR_NEWER
|
|
// using standard materialTemplates in 2019.3 doesn't work
|
|
mr.sharedMaterial = unlitTerrainMat;
|
|
#else
|
|
mr.sharedMaterial = (terr.materialTemplate == null) ? unlitTerrainMat : terr.materialTemplate;
|
|
#endif
|
|
|
|
var so2 = new SerializedObject(mr);
|
|
so2.FindProperty("m_ScaleInLightmap").floatValue = scaleInLmTerr;
|
|
so2.ApplyModifiedProperties();
|
|
|
|
mr.shadowCastingMode = terr.castShadows ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.Off;
|
|
|
|
terrainObjectList.Add(terrGO);
|
|
terrainObjectToActual.Add(terr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exportTerrainTrees && terr.drawTreesAndFoliage)
|
|
{
|
|
var trees = tdata.treeInstances;
|
|
for (int t = 0; t < trees.Length; t++)
|
|
{
|
|
Vector3 pos = Vector3.Scale(trees[t].position, tdata.size) + obj.transform.position;
|
|
|
|
var treeProt = tdata.treePrototypes[trees[t].prototypeIndex];
|
|
var prefab = treeProt.prefab;
|
|
|
|
var newObj = GameObject.Instantiate(prefab, pos, Quaternion.AngleAxis(trees[t].rotation, Vector3.up)) as GameObject;
|
|
newObj.name = "__Export" + newObj.name;
|
|
temporaryGameObjects.Add(newObj);
|
|
|
|
var lodGroup = newObj.GetComponent<LODGroup>();
|
|
if (lodGroup == null)
|
|
{
|
|
var renderers = newObj.GetComponentsInChildren<Renderer>();
|
|
for(int r=0; r<renderers.Length; r++)
|
|
{
|
|
GameObjectUtility.SetStaticEditorFlags(renderers[r].gameObject, StaticEditorFlags.LightmapStatic);
|
|
var s = new SerializedObject(renderers[r]);
|
|
s.FindProperty("m_ScaleInLightmap").floatValue = 0;
|
|
s.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var lods = lodGroup.GetLODs();
|
|
for (int tl = 0; tl < lods.Length; tl++)
|
|
{
|
|
for (int h = 0; h < lods[tl].renderers.Length; h++)
|
|
{
|
|
GameObjectUtility.SetStaticEditorFlags(lods[tl].renderers[h].gameObject, tl==0 ? StaticEditorFlags.LightmapStatic : 0);
|
|
if (tl == 0)
|
|
{
|
|
var s = new SerializedObject(lods[tl].renderers[h]);
|
|
s.FindProperty("m_ScaleInLightmap").floatValue = 0;
|
|
s.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var xform = newObj.transform;
|
|
xform.localScale = new Vector3(trees[t].widthScale, trees[t].heightScale, trees[t].widthScale);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool ConvertUnityAreaLight(GameObject obj)
|
|
{
|
|
// Add temporary meshes to area lights
|
|
var areaLightMesh = obj.GetComponent<BakeryLightMesh>();
|
|
if (areaLightMesh != null)
|
|
{
|
|
var areaLight = obj.GetComponent<Light>();
|
|
var mr = GetValidRenderer(obj);
|
|
var mf = obj.GetComponent<MeshFilter>();
|
|
|
|
if (!forceAllAreaLightsSelfshadow)
|
|
{
|
|
if (!areaLightMesh.selfShadow) return true; // no selfshadow - ignore mesh export
|
|
}
|
|
if (areaLight != null && ftLightMeshInspector.IsArea(areaLight) && (mr == null || mf == null))
|
|
{
|
|
var areaObj = new GameObject();
|
|
mf = areaObj.AddComponent<MeshFilter>();
|
|
mf.sharedMesh = BuildAreaLightMesh(areaLight);
|
|
mr = areaObj.AddComponent<MeshRenderer>();
|
|
|
|
var props = new MaterialPropertyBlock();
|
|
props.SetColor("_Color", areaLightMesh.color);
|
|
props.SetFloat("intensity", areaLightMesh.intensity);
|
|
if (areaLightMesh.texture != null) props.SetTexture("_MainTex", areaLightMesh.texture);
|
|
mr.SetPropertyBlock(props);
|
|
GameObjectUtility.SetStaticEditorFlags(areaObj, StaticEditorFlags.LightmapStatic);
|
|
temporaryAreaLightMeshList.Add(areaObj);
|
|
temporaryAreaLightMeshList2.Add(areaLightMesh);
|
|
|
|
var xformSrc = obj.transform;
|
|
var xformDest = areaObj.transform;
|
|
xformDest.position = xformSrc.position;
|
|
xformDest.rotation = xformSrc.rotation;
|
|
var srcMtx = xformSrc.localToWorldMatrix;
|
|
xformDest.localScale = new Vector3(srcMtx.GetColumn(0).magnitude, srcMtx.GetColumn(1).magnitude, srcMtx.GetColumn(2).magnitude);
|
|
|
|
return true; // mesh created
|
|
}
|
|
}
|
|
return false; // not Light Mesh
|
|
}
|
|
|
|
static void MapObjectsToSceneLODs(ExportSceneData data, UnityEngine.Object[] objects)
|
|
{
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var objToLodLevelVisible = data.objToLodLevelVisible;
|
|
lodLevelsVisibleInLodLevel = new Dictionary<int, List<int>>();
|
|
|
|
const int maxSceneLodLevels = 100;
|
|
var sceneLodUsed = new int[maxSceneLodLevels];
|
|
for(int i=0; i<maxSceneLodLevels; i++) sceneLodUsed[i] = -1;
|
|
var lodGroups = Resources.FindObjectsOfTypeAll(typeof(LODGroup));
|
|
var lodLevelsInLodGroup = new List<int>[lodGroups.Length];
|
|
var localLodLevelsInLodGroup = new List<int>[lodGroups.Length];
|
|
int lcounter = -1;
|
|
foreach(LODGroup lodgroup in lodGroups)
|
|
{
|
|
lcounter++;
|
|
if (!lodgroup.enabled) continue;
|
|
var obj = lodgroup.gameObject;
|
|
if (obj == null) continue;
|
|
if (!obj.activeInHierarchy) continue;
|
|
var path = AssetDatabase.GetAssetPath(obj);
|
|
if (path != "") continue; // must belond to scene
|
|
if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects
|
|
if (obj.tag == "EditorOnly") continue; // skip temp objects
|
|
|
|
var lods = lodgroup.GetLODs();
|
|
if (lods.Length == 0) continue;
|
|
|
|
for(int i=0; i<lods.Length; i++)
|
|
{
|
|
var lodRenderers = lods[i].renderers;
|
|
if (lodRenderers.Length == 0) continue;
|
|
|
|
bool lightmappedLOD = false;
|
|
for(int j=0; j<lodRenderers.Length; j++)
|
|
{
|
|
var r = lodRenderers[j];
|
|
if (r == null) continue;
|
|
if (!r.enabled) continue;
|
|
if (!r.gameObject.activeInHierarchy) continue;
|
|
if ((r.gameObject.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects
|
|
if (r.gameObject.tag == "EditorOnly") continue; // skip temp objects
|
|
if ((GameObjectUtility.GetStaticEditorFlags(r.gameObject) & StaticEditorFlags.LightmapStatic) == 0) continue; // skip dynamic
|
|
var mr = GetValidRenderer(r.gameObject);
|
|
var sharedMesh = GetSharedMesh(mr);
|
|
if (mr == null || sharedMesh == null) continue; // must have visible mesh
|
|
var mrEnabled = mr.enabled || r.gameObject.GetComponent<BakeryAlwaysRender>() != null;
|
|
if (!mrEnabled) continue;
|
|
//if (mf.sharedMesh == null) continue;
|
|
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue; // (before FilterObjects)
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = mr as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
if (scaleInLm == 0) continue;
|
|
|
|
lightmappedLOD = true;
|
|
break;
|
|
}
|
|
if (!lightmappedLOD) continue;
|
|
|
|
var lodDist = i == 0 ? 0 : (int)Mathf.Clamp((1.0f-lods[i-1].screenRelativeTransitionHeight) * (maxSceneLodLevels-1), 0, maxSceneLodLevels-1);
|
|
if (sceneLodUsed[lodDist] < 0)
|
|
{
|
|
sceneLodUsed[lodDist] = sceneLodsUsed;
|
|
sceneLodsUsed++;
|
|
}
|
|
int newLodLevel = sceneLodUsed[lodDist];
|
|
|
|
if (lodLevelsInLodGroup[lcounter] == null)
|
|
{
|
|
lodLevelsInLodGroup[lcounter] = new List<int>();
|
|
localLodLevelsInLodGroup[lcounter] = new List<int>();
|
|
}
|
|
if (lodLevelsInLodGroup[lcounter].IndexOf(newLodLevel) < 0)
|
|
{
|
|
lodLevelsInLodGroup[lcounter].Add(newLodLevel);
|
|
localLodLevelsInLodGroup[lcounter].Add(i);
|
|
}
|
|
|
|
for(int j=0; j<lodRenderers.Length; j++)
|
|
{
|
|
var r = lodRenderers[j];
|
|
if (r == null) continue;
|
|
int existingLodLevel = -1;
|
|
if (objToLodLevel.ContainsKey(r.gameObject)) existingLodLevel = objToLodLevel[r.gameObject];
|
|
if (existingLodLevel < newLodLevel)
|
|
{
|
|
objToLodLevel[r.gameObject] = existingLodLevel < 0 ? newLodLevel : existingLodLevel; // set to lowest LOD
|
|
|
|
// Collect LOD levels where this object is visible
|
|
List<int> visList;
|
|
if (!objToLodLevelVisible.TryGetValue(r.gameObject, out visList)) objToLodLevelVisible[r.gameObject] = visList = new List<int>();
|
|
visList.Add(newLodLevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort scene LOD levels
|
|
int counter = 0;
|
|
var unsortedLodToSortedLod = new int[maxSceneLodLevels];
|
|
for(int i=0; i<maxSceneLodLevels; i++)
|
|
{
|
|
int unsorted = sceneLodUsed[i];
|
|
if (unsorted >= 0)
|
|
{
|
|
unsortedLodToSortedLod[unsorted] = counter;
|
|
sceneLodUsed[i] = counter;
|
|
counter++;
|
|
}
|
|
}
|
|
var keys = new GameObject[objToLodLevel.Count];
|
|
counter = 0;
|
|
foreach(var pair in objToLodLevel)
|
|
{
|
|
keys[counter] = pair.Key;
|
|
counter++;
|
|
}
|
|
foreach(var key in keys)
|
|
{
|
|
int unsorted = objToLodLevel[key];
|
|
objToLodLevel[key] = unsortedLodToSortedLod[unsorted];
|
|
var visList = objToLodLevelVisible[key];
|
|
for(int j=0; j<visList.Count; j++)
|
|
{
|
|
visList[j] = unsortedLodToSortedLod[visList[j]];
|
|
}
|
|
}
|
|
for(int i=0; i<lodLevelsInLodGroup.Length; i++)
|
|
{
|
|
if (lodLevelsInLodGroup[i] == null) continue;
|
|
var levels = lodLevelsInLodGroup[i];
|
|
for(int j=0; j<levels.Count; j++)
|
|
{
|
|
levels[j] = unsortedLodToSortedLod[levels[j]];
|
|
}
|
|
}
|
|
|
|
// Fill LOD gaps
|
|
for(int i=0; i<lodLevelsInLodGroup.Length; i++)
|
|
{
|
|
if (lodLevelsInLodGroup[i] == null) continue;
|
|
var levels = lodLevelsInLodGroup[i];
|
|
var localLevels = localLodLevelsInLodGroup[i];
|
|
var lgroup = lodGroups[i] as LODGroup;
|
|
var lods = lgroup.GetLODs();
|
|
for(int j=0; j<levels.Count; j++)
|
|
{
|
|
int level = levels[j];
|
|
int localLevel = localLevels[j];
|
|
int nextLevel = (j == levels.Count-1) ? (sceneLodsUsed-1) : levels[j+1];
|
|
if (nextLevel - level > 1)
|
|
{
|
|
var lodRenderers = lods[localLevel].renderers;
|
|
for(int k=0; k<lodRenderers.Length; k++)
|
|
{
|
|
var r = lodRenderers[k];
|
|
if (r == null) continue;
|
|
|
|
var visList = objToLodLevelVisible[r.gameObject];
|
|
for(int l=level+1; l<nextLevel; l++)
|
|
{
|
|
visList.Add(l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute which LOD levels are visible in other LOD levels
|
|
foreach(var objToVisible in objToLodLevelVisible)
|
|
{
|
|
var obj = objToVisible.Key;
|
|
var objAffects = objToVisible.Value;
|
|
if (objAffects == null) continue;
|
|
|
|
int objOwnLOD = -1;
|
|
if (!objToLodLevel.TryGetValue(obj, out objOwnLOD)) continue;
|
|
if (objOwnLOD < 0) continue;
|
|
|
|
for(int i=0; i<objAffects.Count; i++)
|
|
{
|
|
int affectedLOD = objAffects[i];
|
|
|
|
List<int> visList;
|
|
if (!lodLevelsVisibleInLodLevel.TryGetValue(affectedLOD, out visList)) lodLevelsVisibleInLodLevel[affectedLOD] = visList = new List<int>();
|
|
if (visList.IndexOf(objOwnLOD) < 0) visList.Add(objOwnLOD);
|
|
}
|
|
}
|
|
/*foreach(var pair in lodLevelsVisibleInLodLevel)
|
|
{
|
|
string str = "LOD " + pair.Key + " sees: ";
|
|
for(int i=0; i<pair.Value.Count; i++)
|
|
{
|
|
str += pair.Value[i] + ", ";
|
|
}
|
|
Debug.LogError(str);
|
|
}*/
|
|
|
|
DebugLogInfo("Scene LOD levels: " + sceneLodsUsed);
|
|
|
|
// Init scene LOD index buffers
|
|
data.indicesOpaqueLOD = new List<int>[sceneLodsUsed];
|
|
data.indicesTransparentLOD = new List<int>[sceneLodsUsed];
|
|
for(int i=0; i<sceneLodsUsed; i++)
|
|
{
|
|
data.indicesOpaqueLOD[i] = new List<int>();
|
|
data.indicesTransparentLOD[i] = new List<int>();
|
|
}
|
|
|
|
// Sort objects by scene-wide LOD level
|
|
if (sceneLodsUsed > 0)
|
|
{
|
|
Array.Sort(objects, delegate(UnityEngine.Object a, UnityEngine.Object b)
|
|
{
|
|
if (a == null || b == null) return 0;
|
|
int lodLevelA = -1;
|
|
int lodLevelB = -1;
|
|
if (!objToLodLevel.TryGetValue((GameObject)a, out lodLevelA)) lodLevelA = -1;
|
|
if (!objToLodLevel.TryGetValue((GameObject)b, out lodLevelB)) lodLevelB = -1;
|
|
return lodLevelA.CompareTo(lodLevelB);
|
|
});
|
|
}
|
|
}
|
|
|
|
public static int IsInsideSector(Transform tform, Transform sectorTform, Bounds b, BakerySector s)
|
|
{
|
|
var parent = tform;
|
|
while(parent != null)
|
|
{
|
|
if (parent == sectorTform) return 1; // belongs to this sector
|
|
parent = parent.parent;
|
|
}
|
|
|
|
return 0; // far
|
|
}
|
|
|
|
static bool ClipFarSphere(FarSphereRenderData clipee, FarSphereRenderData clipper)
|
|
{
|
|
if (farSphereProjClipShader == null)
|
|
{
|
|
farSphereProjClipShader = Shader.Find("Hidden/ftFarSphereProjClip");
|
|
if (farSphereProjClipShader == null)
|
|
{
|
|
Debug.LogError("Can't find ftFarSphereProjClip shader");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (farSphereProjClipMat == null)
|
|
{
|
|
farSphereProjClipMat = new Material(farSphereProjClipShader);
|
|
}
|
|
|
|
// Project clipper depth to clipee
|
|
// If projected pixel is on screen and is closer to the projector
|
|
// draw empty pixel
|
|
// else
|
|
// discard
|
|
var cmd = new CommandBuffer();
|
|
Shader.SetGlobalFloat("_InvCubeSize", 1.0f / ftAdditionalConfig.sectorFarSphereResolution);
|
|
for(int i=0; i<6; i++)
|
|
{
|
|
Shader.SetGlobalTexture("_CurDepth", clipee.depth[i]);
|
|
Shader.SetGlobalMatrix("_CurInvViewProj", Matrix4x4.Inverse(clipee.viewProj[i]));
|
|
Shader.SetGlobalVector("_CurPos", clipee.pos);
|
|
Shader.SetGlobalTexture("_CurNormal", clipee.normal[i]);
|
|
|
|
for(int j=0; j<6; j++)
|
|
{
|
|
Shader.SetGlobalTexture("_ProjDepth", clipper.depth[j]);
|
|
Shader.SetGlobalMatrix("_ProjViewProj", clipper.viewProj[j]);
|
|
Shader.SetGlobalVector("_ProjPos", clipper.pos);
|
|
|
|
//Graphics.Blit(null, clipee.albedo[i], farSphereProjClipMat); // can flip
|
|
|
|
cmd.Clear();
|
|
cmd.SetRenderTarget(new RenderTargetIdentifier(clipee.albedo[i]), 0, CubemapFace.Unknown, 0);
|
|
cmd.DrawProcedural(Matrix4x4.identity, farSphereProjClipMat, 0, MeshTopology.Triangles, 6, 1, null);
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static FarSphereRenderData GenerateFarSphereData(ExportSceneData data, Vector3 capturePoint, int objCount, bool genProjMatrix)
|
|
{
|
|
// Constants
|
|
const int cubeRes = ftAdditionalConfig.sectorFarSphereResolution;
|
|
|
|
var rot = new Vector3[6];
|
|
rot[0] = new Vector3(0, -90, 0);
|
|
rot[1] = new Vector3(0, 90, 0);
|
|
rot[2] = new Vector3(90, 0, 0);
|
|
rot[3] = new Vector3(-90, 0, 0);
|
|
rot[4] = new Vector3(0, 180, 180);
|
|
rot[5] = new Vector3(0, 0, 180);
|
|
|
|
var wnormal = new Vector3[6];
|
|
wnormal[0] = -Vector3.right;
|
|
wnormal[1] = Vector3.right;
|
|
wnormal[2] = -Vector3.up;
|
|
wnormal[3] = Vector3.up;
|
|
wnormal[4] = -Vector3.forward;
|
|
wnormal[5] = Vector3.forward;
|
|
|
|
var objsToWrite = data.objsToWrite;
|
|
//var sector = ftRenderLightmap.curSector;
|
|
|
|
// Load shaders
|
|
if (farSphereRshader == null)
|
|
{
|
|
farSphereRshader = Shader.Find("Hidden/ftFarSphereRender");
|
|
if (farSphereRshader == null)
|
|
{
|
|
Debug.LogError("Can't find ftFarSphereRender shader");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (farSphereRshaderOcc == null)
|
|
{
|
|
farSphereRshaderOcc = Shader.Find("Hidden/ftFarSphereRenderOccluder");
|
|
if (farSphereRshaderOcc == null)
|
|
{
|
|
Debug.LogError("Can't find ftFarSphereRenderOccluder shader");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (farSphereMatOcc == null)
|
|
{
|
|
farSphereMatOcc = new Material(farSphereRshaderOcc);
|
|
}
|
|
|
|
// Create RTs
|
|
FarSphereRenderData outData = new FarSphereRenderData();
|
|
outData.albedo = new RenderTexture[6];
|
|
outData.depth = new RenderTexture[6];
|
|
outData.normal = new RenderTexture[6];
|
|
outData.viewProj = new Matrix4x4[6];
|
|
outData.pos = capturePoint;
|
|
for(int i=0; i<6; i++)
|
|
{
|
|
outData.albedo[i] = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
|
outData.albedo[i].wrapMode = TextureWrapMode.Clamp;
|
|
outData.albedo[i].Create();
|
|
outData.depth[i] = new RenderTexture(cubeRes, cubeRes, 32, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear);
|
|
outData.depth[i].filterMode = FilterMode.Point;
|
|
outData.depth[i].wrapMode = TextureWrapMode.Clamp;
|
|
outData.depth[i].Create();
|
|
outData.normal[i] = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
|
outData.normal[i].filterMode = FilterMode.Point;
|
|
outData.normal[i].wrapMode = TextureWrapMode.Clamp;
|
|
outData.normal[i].Create();
|
|
}
|
|
|
|
bool useCamera = true;
|
|
List<Renderer> outsideRenderers = null;
|
|
//#if UNITY_2019_1_OR_NEWER
|
|
// if (GraphicsSettings.renderPipelineAsset != null)
|
|
// {
|
|
useCamera = false;
|
|
outsideRenderers = data.outsideRenderers;
|
|
if (farSphereMat == null)
|
|
{
|
|
farSphereMat = new Material(farSphereRshader);
|
|
}
|
|
// }
|
|
//#endif
|
|
|
|
GameObject tempCamGO = null;
|
|
Camera tempCam = null;
|
|
Transform camTform = null;
|
|
Matrix4x4 uProj;
|
|
if (useCamera)
|
|
{
|
|
// Create temp camera
|
|
tempCamGO = new GameObject();
|
|
tempCamGO.name = "BakerySectorFarCamera";
|
|
camTform = tempCamGO.transform;
|
|
camTform.localPosition = capturePoint;// sectorCenter;
|
|
tempCam = tempCamGO.AddComponent<Camera>();
|
|
tempCam.orthographic = false;
|
|
tempCam.aspect = 1.0f;
|
|
tempCam.fieldOfView = 90.0f;
|
|
tempCam.enabled = false;
|
|
tempCam.clearFlags = CameraClearFlags.Nothing;
|
|
tempCam.backgroundColor = new Color(0,0,0,0);
|
|
tempCam.SetReplacementShader(farSphereRshader, "RenderType");
|
|
tempCam.nearClipPlane = 0.1f;
|
|
tempCam.farClipPlane = 1000.0f;
|
|
tempCam.renderingPath = RenderingPath.Forward;
|
|
|
|
uProj = tempCam.projectionMatrix;
|
|
}
|
|
else
|
|
{
|
|
uProj = Matrix4x4.Perspective(90, 1, 0.1f, 1000.0f);
|
|
}
|
|
|
|
var proj = GL.GetGPUProjectionMatrix(uProj, true);
|
|
|
|
/*// Set culling matrices for the render (cull things inside the sector + near distance)
|
|
if (sector.tforms.Count == 0)
|
|
{
|
|
Shader.SetGlobalFloat("cullMatricesCount", 0);
|
|
}
|
|
else
|
|
{
|
|
var cullMatrices = new Matrix4x4[sector.tforms.Count];
|
|
for(int i=0; i<sector.tforms.Count; i++)
|
|
{
|
|
cullMatrices[i] = Matrix4x4.TRS(sector.tforms[i].position, sector.tforms[i].rotation, sector.tforms[i].localScale).inverse;// + Vector3.one * sector.nearDistance).inverse;//sector.tforms[i].worldToLocalMatrix;
|
|
//cullMatrices[i] = sector.tforms[i].worldToLocalMatrix;
|
|
}
|
|
Shader.SetGlobalMatrixArray("cullMatrices", cullMatrices);
|
|
Shader.SetGlobalFloat("cullMatricesCount", cullMatrices.Length);
|
|
}*/
|
|
|
|
// Iterate over cube faces
|
|
for(int i=0; i<6; i++)
|
|
{
|
|
// Render cube face
|
|
|
|
// Render far objects
|
|
var mrt = new RenderBuffer[2];
|
|
mrt[0] = outData.albedo[i].colorBuffer;
|
|
mrt[1] = outData.normal[i].colorBuffer;
|
|
Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer);
|
|
GL.Clear(true, true, new Color(0,0,0, 1.0f / 255.0f)); // 0 is reserverd for projClip
|
|
|
|
// Get view matrix for this cube face
|
|
Matrix4x4 v;
|
|
if (useCamera)
|
|
{
|
|
camTform.localEulerAngles = rot[i];
|
|
v = tempCam.worldToCameraMatrix;
|
|
}
|
|
else
|
|
{
|
|
v = Matrix4x4.TRS(capturePoint, Quaternion.Euler(rot[i]), new Vector3(1,1,-1)).inverse;
|
|
}
|
|
// Compute/set viewProj
|
|
var vp = uProj * v;
|
|
if (Camera.current != null) vp *= Camera.current.worldToCameraMatrix.inverse;
|
|
GL.LoadProjectionMatrix(vp);
|
|
outData.viewProj[i] = proj * v;
|
|
|
|
// Render near objects to depth
|
|
Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer);
|
|
farSphereMatOcc.SetPass(0);
|
|
for(int o=0; o<objCount; o++)
|
|
{
|
|
var m = objsToWrite[o].GetComponent<MeshFilter>().sharedMesh;
|
|
var worldMatrix = objsToWrite[o].transform.localToWorldMatrix;
|
|
for(int s=0; s<m.subMeshCount; s++)
|
|
{
|
|
Graphics.DrawMeshNow(m, worldMatrix, s);
|
|
}
|
|
}
|
|
|
|
var uvOffset = ftUVGBufferGen.uvOffset;
|
|
float texelSize = 1.0f / cubeRes;
|
|
var pTo = Shader.PropertyToID("texelOffset");
|
|
|
|
if (useCamera)
|
|
{
|
|
// Render far objects via camera
|
|
tempCam.SetTargetBuffers(mrt, outData.depth[i].depthBuffer);
|
|
tempCam.Render();
|
|
}
|
|
else
|
|
{
|
|
// ... or via individual draws (SRP)
|
|
Graphics.SetRenderTarget(mrt, outData.depth[i].depthBuffer);
|
|
GL.sRGBWrite = true;
|
|
GL.LoadProjectionMatrix(vp);
|
|
for(int o=0; o<outsideRenderers.Count; o++)
|
|
{
|
|
var m = outsideRenderers[o].GetComponent<MeshFilter>().sharedMesh;
|
|
var worldMatrix = outsideRenderers[o].transform.localToWorldMatrix;
|
|
var mats = outsideRenderers[o].sharedMaterials;
|
|
for(int s=0; s<m.subMeshCount; s++)
|
|
{
|
|
int pass = 0;
|
|
if (mats.Length > s && mats[s] != null && mats[s].HasProperty("_MainTex"))
|
|
{
|
|
var mat = mats[s];
|
|
Texture tex = null;
|
|
|
|
if (mat.HasProperty("_MainTex"))
|
|
{
|
|
tex = mat.mainTexture;
|
|
}
|
|
else if (mat.HasProperty("_BaseColorMap"))
|
|
{
|
|
// HDRP
|
|
tex = mat.GetTexture("_BaseColorMap");
|
|
}
|
|
else if (mat.HasProperty("_BaseMap"))
|
|
{
|
|
// URP
|
|
tex = mat.GetTexture("_BaseMap");
|
|
}
|
|
|
|
farSphereMat.SetTexture("_MainTex", tex);
|
|
|
|
pass = 0; // opaque
|
|
if (tex != null)
|
|
{
|
|
var matTag = mat.GetTag("RenderType", true);
|
|
if (matTag == "TransparentCutout" || matTag == "Transparent" || matTag == "TreeLeaf")
|
|
{
|
|
pass = 2; // transparent cutout
|
|
}
|
|
}
|
|
|
|
farSphereMat.SetPass(pass);
|
|
}
|
|
|
|
for(int j=0; j<uvOffset.Length/2; j++)
|
|
{
|
|
var to = new Vector4(uvOffset[j*2] * texelSize, uvOffset[j*2+1] * texelSize, 0, 0);
|
|
farSphereMat.SetVector(pTo, to);
|
|
farSphereMat.SetPass(pass);
|
|
Graphics.DrawMeshNow(m, worldMatrix, s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (useCamera)
|
|
{
|
|
DestroyImmediate(tempCamGO);
|
|
}
|
|
|
|
return outData;
|
|
}
|
|
|
|
static bool GenerateFarSpheres(ExportSceneData data, FarSphereRenderData[] fdatas, List<Transform> capturePoints)
|
|
{
|
|
if (fdatas.Length == 0) return true;
|
|
|
|
// Constants
|
|
const int cubeRes = ftAdditionalConfig.sectorFarSphereResolution;
|
|
const int threadWidth = 16;
|
|
const int vertWidth = cubeRes;
|
|
const int dispatchWidth = vertWidth / threadWidth;
|
|
const int triCount = (vertWidth-1)*(vertWidth-1)*2;
|
|
int dispatchIndexGroups = (int)Mathf.Ceil(triCount / (float)(threadWidth * threadWidth));
|
|
|
|
var wnormal = new Vector3[6];
|
|
wnormal[0] = -Vector3.right;
|
|
wnormal[1] = Vector3.right;
|
|
wnormal[2] = -Vector3.up;
|
|
wnormal[3] = Vector3.up;
|
|
wnormal[4] = -Vector3.forward;
|
|
wnormal[5] = Vector3.forward;
|
|
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteNames = data.objsToWriteNames;
|
|
var objsToWriteLightmapped = data.objsToWriteLightmapped;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWriteVerticesUV = data.objsToWriteVerticesUV;
|
|
var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objToScaleInLm = data.objToScaleInLm;
|
|
if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, data.objsToWrite.Count > 0 ? data.objsToWrite[0] : null);
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
|
|
if (farSphereSshader == null)
|
|
{
|
|
farSphereSshader = Shader.Find("Hidden/ftFarSphere");
|
|
if (farSphereSshader == null)
|
|
{
|
|
Debug.LogError("Can't find ftFarSphere shader");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var bakeryEditorPath = ftLightmaps.GetEditorPath();
|
|
|
|
if (farSphereCSTransform == null)
|
|
{
|
|
farSphereCSTransform = AssetDatabase.LoadAssetAtPath(bakeryEditorPath + "shaderSrc/ftTransformFarSphere.compute", typeof(ComputeShader)) as ComputeShader;
|
|
if (farSphereCSTransform == null)
|
|
{
|
|
Debug.LogError("Can't find ftTransformFarSphere");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (farSphereCSCull == null)
|
|
{
|
|
farSphereCSCull = AssetDatabase.LoadAssetAtPath(bakeryEditorPath + "shaderSrc/ftCullFarSphere.compute", typeof(ComputeShader)) as ComputeShader;
|
|
if (farSphereCSCull == null)
|
|
{
|
|
Debug.LogError("Can't find ftCullFarSphere");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Create temp buffers
|
|
var rwBuff = new ComputeBuffer(vertWidth * vertWidth, 12); // vertices in/out
|
|
var indBuff = new ComputeBuffer(triCount, 3*4); // indices in
|
|
var appendBuff = new ComputeBuffer(triCount, 3*4, ComputeBufferType.Append); // indices out
|
|
var countBuff = new ComputeBuffer(1, 4, ComputeBufferType.Raw); // out index count
|
|
var countArray = new int[1];
|
|
var uvBuff = new ComputeBuffer(vertWidth * vertWidth, 8); // vertex UVs
|
|
|
|
// Create shared input index buffer
|
|
var tris = new int[(vertWidth-1) * (vertWidth-1) * 2 * 3];
|
|
int indCount = 0;
|
|
for(int y=0; y<vertWidth-1; y++)
|
|
{
|
|
for(int x=0; x<vertWidth-1; x++)
|
|
{
|
|
tris[indCount] = y*vertWidth+x;
|
|
tris[indCount+1] = y*vertWidth+x+1;
|
|
tris[indCount+2] = (y+1)*vertWidth+x;
|
|
indCount += 3;
|
|
tris[indCount] = y*vertWidth+x+1;
|
|
tris[indCount+1] = (y+1)*vertWidth+x+1;
|
|
tris[indCount+2] = (y+1)*vertWidth+x;
|
|
indCount += 3;
|
|
}
|
|
}
|
|
|
|
// Create shared UVs
|
|
var uv = new Vector2[vertWidth * vertWidth];
|
|
float invDiv = 1.0f / (vertWidth-1);
|
|
for(int y=0; y<vertWidth; y++)
|
|
{
|
|
int yoffset = y*vertWidth;
|
|
for(int x=0; x<vertWidth; x++)
|
|
{
|
|
uv[yoffset+x] = new Vector2(x*invDiv, 1.0f - y*invDiv);
|
|
}
|
|
}
|
|
|
|
ftLightmapsStorage sceneST = null;
|
|
int totalTris = 0;
|
|
|
|
var tempTex = new RenderTexture(cubeRes, cubeRes, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
|
tempTex.Create();
|
|
var cmd = new CommandBuffer();
|
|
var rtiTemp = new RenderTargetIdentifier(tempTex);
|
|
var mblock = new MaterialPropertyBlock();
|
|
if (farSphereDilateShader == null)
|
|
{
|
|
farSphereDilateShader = Shader.Find("Hidden/ftFarSphereDilate");
|
|
if (farSphereDilateShader == null)
|
|
{
|
|
Debug.LogError("Can't find farSphereDilate shader");
|
|
return false;
|
|
}
|
|
}
|
|
if (farSphereDilateMat == null)
|
|
{
|
|
farSphereDilateMat = new Material(farSphereDilateShader);
|
|
}
|
|
var pMainTex = Shader.PropertyToID("_MainTex");
|
|
|
|
for(int f=0; f<fdatas.Length; f++)
|
|
{
|
|
var fdata = fdatas[f];
|
|
fdata.meshes = new Mesh[6];
|
|
fdata.textures = new Texture2D[6];
|
|
var capturePoint = capturePoints[f].position;
|
|
|
|
// Iterate over cube faces
|
|
for(int i=0; i<6; i++)
|
|
{
|
|
// Create tex readable by the lightmapper
|
|
var texAlbedo = new Texture2D(cubeRes, cubeRes, TextureFormat.RGBA32, false, false);
|
|
texAlbedo.wrapMode = TextureWrapMode.Clamp;
|
|
texAlbedo.filterMode = FilterMode.Point;
|
|
fdata.textures[i] = texAlbedo;
|
|
|
|
// Dilate
|
|
mblock.SetTexture(pMainTex, fdata.albedo[i]);
|
|
cmd.Clear();
|
|
cmd.SetRenderTarget(rtiTemp, 0, CubemapFace.Unknown, 0);
|
|
cmd.DrawProcedural(Matrix4x4.identity, farSphereDilateMat, 0, MeshTopology.Triangles, 6, 1, mblock);
|
|
Graphics.ExecuteCommandBuffer(cmd);
|
|
|
|
// Copy tex
|
|
//Graphics.SetRenderTarget(fdata.albedo[i]);
|
|
Graphics.SetRenderTarget(tempTex);
|
|
texAlbedo.ReadPixels(new Rect(0,0,cubeRes,cubeRes), 0, 0, false);
|
|
texAlbedo.Apply();
|
|
Graphics.SetRenderTarget(null);
|
|
|
|
// Create verts
|
|
var verts = new Vector3[vertWidth * vertWidth];
|
|
for(int y=0; y<vertWidth; y++)
|
|
{
|
|
int yoffset = y*vertWidth;
|
|
float fy = -0.5f + y * invDiv;
|
|
for(int x=0; x<vertWidth; x++)
|
|
{
|
|
float fx = -0.5f + x * invDiv;
|
|
uv[yoffset+x] = new Vector2(x*invDiv, 1.0f - y*invDiv);
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
verts[yoffset+x] = new Vector3(-0.5f, -fy, fx);
|
|
break;
|
|
case 1:
|
|
verts[yoffset+x] = new Vector3(0.5f, -fy, -fx);
|
|
break;
|
|
case 2:
|
|
verts[yoffset+x] = new Vector3(fx, -0.5f, -fy);
|
|
break;
|
|
case 3:
|
|
verts[yoffset+x] = new Vector3(fx, 0.5f, fy);
|
|
break;
|
|
case 4:
|
|
verts[yoffset+x] = new Vector3(fx, fy, -0.5f);
|
|
break;
|
|
case 5:
|
|
verts[yoffset+x] = new Vector3(-fx, fy, 0.5f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Displace verts
|
|
rwBuff.SetData(verts);
|
|
farSphereCSTransform.SetBuffer(0, "verts", rwBuff);
|
|
farSphereCSTransform.SetTexture(0, "_DepthTex", fdata.depth[i]);
|
|
farSphereCSTransform.SetVector("objectCenter", capturePoint);
|
|
farSphereCSTransform.SetInt("vertWidth", vertWidth);
|
|
var ivp = fdata.viewProj[i].inverse;
|
|
farSphereCSTransform.SetVector("_InvProj0", ivp.GetRow(0));
|
|
farSphereCSTransform.SetVector("_InvProj1", ivp.GetRow(1));
|
|
farSphereCSTransform.SetVector("_InvProj2", ivp.GetRow(2));
|
|
farSphereCSTransform.SetVector("_InvProj3", ivp.GetRow(3));
|
|
//farSphereCSTransform.SetVector("wnormal", wnormal[i]);
|
|
farSphereCSTransform.Dispatch(0, dispatchWidth, dispatchWidth, 1);
|
|
rwBuff.GetData(verts);
|
|
|
|
// Cull tris
|
|
indBuff.SetData(tris);
|
|
appendBuff.SetCounterValue(0);
|
|
uvBuff.SetData(uv);
|
|
farSphereCSCull.SetBuffer(0, "verts", rwBuff);
|
|
farSphereCSCull.SetBuffer(0, "uvs", uvBuff);
|
|
farSphereCSCull.SetBuffer(0, "indices", indBuff);
|
|
farSphereCSCull.SetBuffer(0, "newIndices", appendBuff);
|
|
farSphereCSCull.SetInt("triCount", tris.Length/3);
|
|
farSphereCSCull.SetTexture(0, "alphaTex", texAlbedo);
|
|
farSphereCSCull.SetFloat("cubeSize", ftAdditionalConfig.sectorFarSphereResolution);
|
|
farSphereCSCull.Dispatch(0, dispatchIndexGroups, 1, 1);
|
|
|
|
// Get culled count
|
|
ComputeBuffer.CopyCount(appendBuff, countBuff, 0);
|
|
countBuff.GetData(countArray);
|
|
|
|
// Get new tris
|
|
var arr = new int[countArray[0]*3];
|
|
appendBuff.GetData(arr);
|
|
|
|
// Create temp material
|
|
var mat = new Material(farSphereSshader);
|
|
mat.mainTexture = texAlbedo;
|
|
|
|
// Create temp object
|
|
var faceMesh = new GameObject();
|
|
faceMesh.name = "f_" + f + "_" + i;
|
|
faceMesh.transform.position = capturePoint;
|
|
temporaryGameObjects.Add(faceMesh);
|
|
|
|
var mr = faceMesh.AddComponent<MeshRenderer>();
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap");
|
|
scaleInLm.floatValue = 0;
|
|
so.ApplyModifiedProperties();
|
|
mr.sharedMaterial = mat;
|
|
GameObjectUtility.SetStaticEditorFlags(faceMesh, StaticEditorFlags.LightmapStatic);
|
|
|
|
var mf = faceMesh.AddComponent<MeshFilter>();
|
|
|
|
var mesh = new Mesh();
|
|
mesh.vertices = verts;
|
|
mesh.uv = uv;
|
|
mesh.triangles = arr;
|
|
totalTris += arr.Length;
|
|
mesh.RecalculateNormals();
|
|
mf.sharedMesh = mesh;
|
|
fdata.meshes[i] = mesh;
|
|
|
|
objsToWrite.Add(faceMesh);
|
|
objsToWriteNames.Add("");
|
|
objsToWriteLightmapped.Add(false);
|
|
objsToWriteGroup.Add(data.autoVertexGroup);
|
|
objsToWriteHolder.Add(faceMesh);
|
|
objsToWriteVerticesUV.Add(uv);
|
|
objsToWriteVerticesUV2.Add(uv);
|
|
var inds = new int[1][];
|
|
inds[0] = arr;
|
|
objsToWriteIndices.Add(inds);
|
|
objToScaleInLm[faceMesh] = 0;
|
|
|
|
if (sceneST == null) sceneST = storages[sceneToID[faceMesh.scene]];
|
|
sceneST.implicitGroupedObjects.Add(faceMesh);
|
|
sceneST.implicitGroups.Add(data.autoVertexGroup);
|
|
tempStorage.implicitGroupMap[faceMesh] = data.autoVertexGroup;
|
|
|
|
if (modifyLightmapStorage) sceneST.nonBakedRenderers.Add(mr);
|
|
|
|
fdata.albedo[i].Release();
|
|
fdata.depth[i].Release();
|
|
fdata.normal[i].Release();
|
|
}
|
|
}
|
|
|
|
Debug.Log("Total out-of-sector tris: " + totalTris);
|
|
|
|
tempTex.Release();
|
|
rwBuff.Release();
|
|
indBuff.Release();
|
|
appendBuff.Release();
|
|
countBuff.Release();
|
|
uvBuff.Release();
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool LoadSectorCapture(ExportSceneData data, BakerySectorCapture capture, Transform sectorTform)
|
|
{
|
|
if (capture.meshes == null)
|
|
{
|
|
Debug.LogError("No meshes in capture");
|
|
return false;
|
|
}
|
|
if (capture.positions == null)
|
|
{
|
|
Debug.LogError("No positions in capture");
|
|
return false;
|
|
}
|
|
if (capture.textures == null)
|
|
{
|
|
Debug.LogError("No textures in capture");
|
|
return false;
|
|
}
|
|
|
|
if (farSphereSshader == null)
|
|
{
|
|
farSphereSshader = Shader.Find("Hidden/ftFarSphere");
|
|
if (farSphereSshader == null)
|
|
{
|
|
Debug.LogError("Can't find ftFarSphere shader");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteNames = data.objsToWriteNames;
|
|
var objsToWriteLightmapped = data.objsToWriteLightmapped;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWriteVerticesUV = data.objsToWriteVerticesUV;
|
|
var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objToScaleInLm = data.objToScaleInLm;
|
|
if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, data.objsToWrite.Count > 0 ? data.objsToWrite[0] : null);
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
|
|
var parent = new GameObject();
|
|
parent.name = "fp";
|
|
var parentTform = parent.transform;
|
|
parentTform.position = capture.sectorPos;
|
|
parentTform.rotation = capture.sectorRot;
|
|
temporaryGameObjects.Add(parent);
|
|
|
|
ftLightmapsStorage sceneST = null;
|
|
|
|
for(int i=0; i<capture.meshes.Count; i++)
|
|
{
|
|
var capturePoint = capture.positions[i];
|
|
|
|
// Create temp material
|
|
var mat = new Material(farSphereSshader);
|
|
mat.mainTexture = capture.textures[i];
|
|
|
|
// Create temp object
|
|
var faceMesh = new GameObject();
|
|
faceMesh.name = "f_" + i;
|
|
var faceTform = faceMesh.transform;
|
|
faceTform.position = capturePoint;
|
|
faceTform.parent = parentTform;
|
|
temporaryGameObjects.Add(faceMesh);
|
|
|
|
var mr = faceMesh.AddComponent<MeshRenderer>();
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap");
|
|
scaleInLm.floatValue = 0;
|
|
so.ApplyModifiedProperties();
|
|
mr.sharedMaterial = mat;
|
|
GameObjectUtility.SetStaticEditorFlags(faceMesh, StaticEditorFlags.LightmapStatic);
|
|
|
|
var mf = faceMesh.AddComponent<MeshFilter>();
|
|
mf.sharedMesh = capture.meshes[i];
|
|
|
|
objsToWrite.Add(faceMesh);
|
|
objsToWriteNames.Add("");
|
|
objsToWriteLightmapped.Add(false);
|
|
objsToWriteGroup.Add(data.autoVertexGroup);
|
|
objsToWriteHolder.Add(faceMesh);
|
|
|
|
var uv = mf.sharedMesh.uv;
|
|
objsToWriteVerticesUV.Add(uv);
|
|
objsToWriteVerticesUV2.Add(uv);
|
|
|
|
var inds = new int[1][];
|
|
inds[0] = mf.sharedMesh.triangles;
|
|
objsToWriteIndices.Add(inds);
|
|
objToScaleInLm[faceMesh] = 0;
|
|
|
|
if (sceneST == null) sceneST = storages[sceneToID[faceMesh.scene]];
|
|
sceneST.implicitGroupedObjects.Add(faceMesh);
|
|
sceneST.implicitGroups.Add(data.autoVertexGroup);
|
|
tempStorage.implicitGroupMap[faceMesh] = data.autoVertexGroup;
|
|
|
|
if (modifyLightmapStorage) sceneST.nonBakedRenderers.Add(mr);
|
|
}
|
|
|
|
parentTform.rotation = sectorTform.rotation;
|
|
parentTform.position = sectorTform.position;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void CreateAutoVertexGroup(ExportSceneData data, GameObject anyObj)
|
|
{
|
|
var groupList = data.groupList;
|
|
var lmBounds = data.lmBounds;
|
|
|
|
data.autoVertexGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
data.autoVertexGroup.name = (anyObj == null ? "scene" : FilterNonASCII(anyObj.scene.name)) + "_VLM";
|
|
data.autoVertexGroup.isImplicit = true;
|
|
data.autoVertexGroup.resolution = 256;
|
|
data.autoVertexGroup.bitmask = 1;
|
|
data.autoVertexGroup.mode = BakeryLightmapGroup.ftLMGroupMode.Vertex;
|
|
data.autoVertexGroup.id = data.lmid;
|
|
groupList.Add(data.autoVertexGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
}
|
|
|
|
static int GetLightmapTag(GameObject obj, ExportSceneData data)
|
|
{
|
|
var objToBakeTag = data.objToBakeTag;
|
|
if (objToBakeTag == null) objToBakeTag = data.objToBakeTag = new Dictionary<GameObject, int>();
|
|
|
|
int tag;
|
|
if (objToBakeTag.TryGetValue(obj, out tag)) return tag;
|
|
tag = -1;
|
|
|
|
var mr = obj.GetComponent<MeshRenderer>();
|
|
if (mr != null)
|
|
{
|
|
var so = new SerializedObject(mr);
|
|
var param = so.FindProperty("m_LightmapParameters").objectReferenceValue;
|
|
if (param != null)
|
|
{
|
|
var param2 = param as LightmapParameters;
|
|
if (param2 != null)
|
|
{
|
|
tag = param2.bakedLightmapTag;
|
|
}
|
|
}
|
|
}
|
|
objToBakeTag[obj] = tag;
|
|
return tag;
|
|
}
|
|
|
|
static bool FilterObjects(ExportSceneData data, UnityEngine.Object[] objects)
|
|
{
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteNames = data.objsToWriteNames;
|
|
var objsToWriteLightmapped = data.objsToWriteLightmapped;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWriteVerticesUV = data.objsToWriteVerticesUV;
|
|
var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objToScaleInLm = data.objToScaleInLm;
|
|
List<Renderer> outsideRenderers = null;
|
|
|
|
outsideRenderers = data.outsideRenderers;
|
|
/*#if UNITY_2019_1_OR_NEWER
|
|
if (GraphicsSettings.renderPipelineAsset != null)
|
|
{
|
|
outsideRenderers = data.outsideRenderers;
|
|
}
|
|
#endif*/
|
|
Transform sectorTform = null;
|
|
if (ftRenderLightmap.fullSectorRender)
|
|
{
|
|
sectorTform = ftRenderLightmap.curSector.transform;
|
|
}
|
|
|
|
var prop = new MaterialPropertyBlock();
|
|
foreach(GameObject obj in objects)
|
|
{
|
|
if (obj == null) continue;
|
|
if (!obj.activeInHierarchy) continue;
|
|
var path = AssetDatabase.GetAssetPath(obj);
|
|
if (path != "") continue; // must belond to scene
|
|
if ((obj.hideFlags & (HideFlags.DontSave|HideFlags.HideAndDontSave)) != 0) continue; // skip temp objects
|
|
if (obj.tag == "EditorOnly") continue; // skip temp objects
|
|
|
|
var areaLight = obj.GetComponent<BakeryLightMesh>();
|
|
if (areaLight == null)
|
|
{
|
|
int areaIndex = temporaryAreaLightMeshList.IndexOf(obj);
|
|
if (areaIndex >= 0) areaLight = temporaryAreaLightMeshList2[areaIndex];
|
|
}
|
|
|
|
if (areaLight != null)
|
|
{
|
|
if (!forceAllAreaLightsSelfshadow)
|
|
{
|
|
if (!areaLight.selfShadow) continue;
|
|
}
|
|
}
|
|
var mr = GetValidRenderer(obj);
|
|
|
|
if (mr == null)
|
|
{
|
|
// must be MR or SMR
|
|
continue;
|
|
}
|
|
|
|
var sharedMesh = GetSharedMesh(mr);
|
|
if (sharedMesh == null) continue; // must have visible mesh
|
|
|
|
// Remove previous lightmap
|
|
#if UNITY_2018_1_OR_NEWER
|
|
if (mr.HasPropertyBlock())
|
|
{
|
|
// Reset shader props
|
|
mr.GetPropertyBlock(prop);
|
|
prop.SetFloat("bakeryLightmapMode", 0);
|
|
mr.SetPropertyBlock(prop);
|
|
}
|
|
#else
|
|
mr.GetPropertyBlock(prop);
|
|
if (!prop.isEmpty)
|
|
{
|
|
prop.SetFloat("bakeryLightmapMode", 0);
|
|
mr.SetPropertyBlock(prop);
|
|
}
|
|
#endif
|
|
if (((GameObjectUtility.GetStaticEditorFlags(obj) & StaticEditorFlags.LightmapStatic) == 0) && areaLight==null)
|
|
{
|
|
mr.lightmapIndex = 0xFFFF;
|
|
continue; // skip dynamic
|
|
}
|
|
|
|
var mrEnabled = mr.enabled || obj.GetComponent<BakeryAlwaysRender>() != null;
|
|
if (!mrEnabled && areaLight == null) continue;
|
|
|
|
var so = new SerializedObject(mr);//obj.GetComponent<Renderer>());
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = mr as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
|
|
if (ftRenderLightmap.fullSectorRender)
|
|
{
|
|
int status = IsInsideSector(obj.transform, sectorTform, mr.bounds, ftRenderLightmap.curSector);
|
|
if (status == 0)
|
|
{
|
|
if (outsideRenderers != null) outsideRenderers.Add(mr);
|
|
continue;
|
|
}
|
|
if (status == 2) scaleInLm = 0;
|
|
}
|
|
|
|
objToScaleInLm[obj] = scaleInLm;
|
|
|
|
BakeryLightmapGroup group = null;
|
|
if (scaleInLm > 0)
|
|
{
|
|
group = GetLMGroupFromObjectExplicit(obj, data);
|
|
if (group != null)
|
|
{
|
|
// Set LOD level for explicit group
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1;
|
|
|
|
var packer = group.atlasPacker == BakeryLightmapGroup.AtlasPacker.Auto ? atlasPacker : (ftGlobalStorage.AtlasPacker)group.atlasPacker;
|
|
|
|
if (!postPacking || packer != ftGlobalStorage.AtlasPacker.xatlas)
|
|
{
|
|
if (group.sceneLodLevel == -1)
|
|
{
|
|
group.sceneLodLevel = lodLevel;
|
|
}
|
|
else
|
|
{
|
|
if (lodLevel > 0)
|
|
{
|
|
if (!ExportSceneValidationMessage("Multiple LOD levels in " + group.name + ", this is only supported when xatlas is set as the atlas packer and post-packing is enabled.")) return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exportTerrainAsHeightmap && !group.isImplicit && obj.name == "__ExportTerrain")
|
|
{
|
|
if (!ExportSceneValidationMessage("Terrain Optimization is enabled and terrains are inside a lightmap group. This is not currently supported. Try disabling Terrain Optimization (" + group.name + ", " + group.isImplicit + ").")) return false;
|
|
}
|
|
|
|
// Set for explicit group
|
|
if (splitByScene) group.sceneName = obj.scene.name;
|
|
if (splitByTag) group.tag = GetLightmapTag(obj, data);
|
|
|
|
// New explicit Pack Atlas holder selection
|
|
if (!group.isImplicit && group.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
lmgroupHolder = obj; // by default pack each object
|
|
lmgroupHolder = TestPackAsSingleSquare(lmgroupHolder);
|
|
var prefabParent = PrefabUtility.GetPrefabParent(obj) as GameObject;
|
|
if (prefabParent != null)
|
|
{
|
|
var ptype = PrefabUtility.GetPrefabType(prefabParent);
|
|
if (ptype == PrefabType.ModelPrefab)
|
|
{
|
|
// but if object is a part of prefab/model
|
|
var sharedMesh2 = GetSharedMesh(obj);
|
|
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sharedMesh2)) as ModelImporter;
|
|
if (importer != null && !ModelUVsOverlap(importer, gstorage))
|
|
{
|
|
// or actually just non-overlapping model,
|
|
// then pack it as a whole
|
|
|
|
// find topmost asset parent
|
|
var t = prefabParent.transform;
|
|
while(t.parent != null) t = t.parent;
|
|
var assetTopMost = t.gameObject;
|
|
|
|
// find topmost scene instance parent
|
|
var g = obj;
|
|
while(PrefabUtility.GetPrefabParent(g) as GameObject != assetTopMost && g.transform.parent != null)
|
|
{
|
|
g = g.transform.parent.gameObject;
|
|
}
|
|
lmgroupHolder = g;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (data.autoVertexGroup == null) CreateAutoVertexGroup(data, obj);
|
|
group = data.autoVertexGroup;
|
|
|
|
tempStorage.implicitGroupMap[obj] = data.autoVertexGroup;
|
|
|
|
if (modifyLightmapStorage)
|
|
{
|
|
var st = storages[sceneToID[obj.scene]];
|
|
st.implicitGroupedObjects.Add(obj);
|
|
st.implicitGroups.Add(data.autoVertexGroup);
|
|
st.nonBakedRenderers.Add(mr);
|
|
}
|
|
}
|
|
|
|
bool vertexBake = (group != null && group.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex);
|
|
// must have UVs or be arealight or vertexbaked
|
|
var uv = sharedMesh.uv;
|
|
var uv2 = sharedMesh.uv2;
|
|
if (uv.Length == 0 && uv2.Length == 0 && areaLight==null && !vertexBake) continue;
|
|
|
|
var usedUVs = uv2.Length == 0 ? uv : uv2;
|
|
//bool validUVs = true;
|
|
for(int v=0; v<usedUVs.Length; v++)
|
|
{
|
|
if (usedUVs[v].x < -0.0001f || usedUVs[v].x > 1.0001f || usedUVs[v].y < -0.0001f || usedUVs[v].y > 1.0001f)
|
|
{
|
|
DebugLogWarning("Mesh " + sharedMesh.name + " on object " + obj.name + " possibly has incorrect UVs (UV2: " + (uv2.Length == 0 ? "no" : "yes")+", U: " + usedUVs[v].x + ", V: " + usedUVs[v].y + ")");
|
|
//validUVs = false;
|
|
break;
|
|
}
|
|
}
|
|
//if (!validUVs) continue;
|
|
|
|
if (vertexBake)
|
|
{
|
|
group.totalVertexCount = 0;
|
|
group.vertexCounter = 0;
|
|
}
|
|
|
|
objsToWrite.Add(obj);
|
|
objsToWriteNames.Add(obj.name);
|
|
objsToWriteLightmapped.Add((scaleInLm > 0 && areaLight == null) ? true : false);
|
|
objsToWriteGroup.Add(group);
|
|
objsToWriteHolder.Add(lmgroupHolder);
|
|
|
|
objsToWriteVerticesUV.Add(uv);
|
|
objsToWriteVerticesUV2.Add(uv2);
|
|
var inds = new int[sharedMesh.subMeshCount][];
|
|
for(int n=0; n<inds.Length; n++) inds[n] = sharedMesh.GetTriangles(n);
|
|
objsToWriteIndices.Add(inds);
|
|
|
|
if (group != null) group.passedFilter = ftRenderLightmap.passedFilterFlag;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void CalculateVertexCountForVertexGroups(ExportSceneData data)
|
|
{
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
|
|
// Calculate total vertex count for vertex-baked groups
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var lmgroup = objsToWriteGroup[i];
|
|
if (lmgroup == null || lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex) continue;
|
|
var sharedMesh = GetSharedMesh(objsToWrite[i]);
|
|
lmgroup.totalVertexCount += sharedMesh.vertexCount;
|
|
}
|
|
}
|
|
|
|
static void CreateAutoAtlasLMGroups(ExportSceneData data, bool renderTextures, bool atlasOnly)
|
|
{
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteLightmapped = data.objsToWriteLightmapped;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
var groupList = data.groupList;
|
|
var lmBounds = data.lmBounds;
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes;
|
|
|
|
// Create implicit temp LMGroups.
|
|
// If object is a part of prefab, and if UVs are not generated in Unity, group is only addded to the topmost object (aka holder).
|
|
|
|
// Implicit groups are added on every static object without ftLMGroupSelector.
|
|
// (Also init lmBounds and LMID as well)
|
|
// if autoAtlas == false: new group for every holder.
|
|
// if autoAtlas == true: single group for all holders (will be split later).
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
if (!objsToWriteLightmapped[i]) continue; // skip objects with scaleInLM == 0
|
|
if (objsToWriteGroup[i] != null) continue; // skip if already has lightmap assigned
|
|
var obj = objsToWrite[i];
|
|
|
|
var holder = obj; // holder is object itself (packed individually)
|
|
holder = TestPackAsSingleSquare(holder);
|
|
var prefabParent = PrefabUtility.GetPrefabParent(obj) as GameObject;
|
|
if (prefabParent != null) // object is part of prefab
|
|
{
|
|
// unity doesn't generate non-overlapping UVs for the whole model, only submeshes
|
|
// // if importer == null, it's an actual prefab, not model <-- not really; importer points to mesh's prefab, not real
|
|
// importer of a mesh is always model asset
|
|
// importers of components never exist
|
|
// at least check the prefab type
|
|
var ptype = PrefabUtility.GetPrefabType(prefabParent);
|
|
if (ptype == PrefabType.ModelPrefab)
|
|
{
|
|
var sharedMesh = GetSharedMesh(obj);
|
|
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sharedMesh)) as ModelImporter;
|
|
|
|
if (importer != null && !ModelUVsOverlap(importer, gstorage))
|
|
{
|
|
// find topmost asset parent
|
|
var t = prefabParent.transform;
|
|
while(t.parent != null) t = t.parent;
|
|
var assetTopMost = t.gameObject;
|
|
|
|
// find topmost scene instance parent
|
|
var g = obj;
|
|
var assetG = PrefabUtility.GetPrefabParent(g) as GameObject;
|
|
while(assetG != assetTopMost && g.transform.parent != null && assetG.transform.parent != null)
|
|
{
|
|
var g2 = g.transform.parent.gameObject;
|
|
var assetG2 = assetG.transform.parent.gameObject;
|
|
|
|
if (PrefabUtility.GetPrefabParent(g2) != assetG2) break; // avoid using parents which don't belong to this model
|
|
|
|
g = g2;
|
|
assetG = assetG2;
|
|
}
|
|
var sceneTopMost = g;
|
|
holder = sceneTopMost; // holder is topmost model object (non-overlapped UVs)
|
|
|
|
int lodLevel;
|
|
if (objToLodLevel.TryGetValue(obj, out lodLevel)) holder = obj; // separated if used in LOD
|
|
}
|
|
}
|
|
}
|
|
else if (obj.name == "__ExportTerrain")
|
|
{
|
|
holder = obj.transform.parent.gameObject; // holder is terrain parent
|
|
|
|
int lodLevel;
|
|
if (objToLodLevel.TryGetValue(obj, out lodLevel)) holder = obj; // separated if used in LOD
|
|
}
|
|
|
|
if (!storages[sceneToID[holder.scene]].implicitGroupedObjects.Contains(holder))
|
|
{
|
|
BakeryLightmapGroup newGroup;
|
|
if (autoAtlas && autoAtlasGroups.Count > 0)
|
|
{
|
|
newGroup = autoAtlasGroups[0];
|
|
}
|
|
else
|
|
{
|
|
newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
|
|
// Make sure first lightmap is always LM0, not LM1, if probes are used
|
|
int lmNum = storages[sceneToID[holder.scene]].implicitGroups.Count;
|
|
if (ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.L1 && ftRenderLightmap.hasAnyProbes && renderTextures && !atlasOnly) lmNum--;
|
|
|
|
newGroup.name = FilterNonASCII(holder.scene.name) + "_LM" + autoAtlasGroups.Count;//lmNum;
|
|
newGroup.isImplicit = true;
|
|
newGroup.resolution = 256;
|
|
newGroup.bitmask = 1;
|
|
newGroup.area = 0;
|
|
newGroup.mode = autoAtlas ? BakeryLightmapGroup.ftLMGroupMode.PackAtlas : BakeryLightmapGroup.ftLMGroupMode.OriginalUV;
|
|
|
|
newGroup.id = data.lmid;
|
|
groupList.Add(newGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
|
|
if (autoAtlas)
|
|
{
|
|
autoAtlasGroups.Add(newGroup);
|
|
var rootNode = new AtlasNode();
|
|
rootNode.rc = new Rect(0, 0, 1, 1);
|
|
autoAtlasGroupRootNodes.Add(rootNode);
|
|
}
|
|
}
|
|
storages[sceneToID[holder.scene]].implicitGroupedObjects.Add(holder);
|
|
|
|
storages[sceneToID[holder.scene]].implicitGroups.Add(newGroup);
|
|
//Debug.LogError("Add "+(storages[sceneToID[holder.scene]].implicitGroups.Count-1)+" "+newGroup.name);
|
|
|
|
tempStorage.implicitGroupMap[holder] = newGroup;
|
|
|
|
// Set for implicit group
|
|
if (splitByScene) newGroup.sceneName = holder.scene.name;
|
|
if (splitByTag) newGroup.tag = GetLightmapTag(holder, data);
|
|
}
|
|
|
|
if (!tempStorage.implicitGroupMap.ContainsKey(holder))
|
|
{
|
|
// happens with modifyLightmapStorage == false
|
|
var gholders = storages[sceneToID[holder.scene]].implicitGroupedObjects;
|
|
var grs = storages[sceneToID[holder.scene]].implicitGroups;
|
|
for(int g=0; g<gholders.Count; g++)
|
|
{
|
|
if (gholders[g] == holder)
|
|
{
|
|
tempStorage.implicitGroupMap[holder] = grs[g];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
objsToWriteGroup[i] = (BakeryLightmapGroup)tempStorage.implicitGroupMap[holder];
|
|
objsToWriteHolder[i] = holder;
|
|
}
|
|
}
|
|
|
|
static void TransformVertices(ExportSceneData data, bool tangentSHLights, int onlyID = -1)
|
|
{
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW;
|
|
var objsToWriteVerticesNormalW = data.objsToWriteVerticesNormalW;
|
|
var objsToWriteVerticesTangentW = data.objsToWriteVerticesTangentW;
|
|
|
|
int startIndex = 0;
|
|
int endIndex = objsToWrite.Count-1;
|
|
|
|
if (onlyID >= 0)
|
|
{
|
|
startIndex = onlyID;
|
|
endIndex = onlyID;
|
|
}
|
|
|
|
// Transform vertices to world space
|
|
for(int i=startIndex; i<=endIndex; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
bool isSkin;
|
|
var m = GetSharedMeshSkinned(obj, out isSkin);
|
|
var vertices = m.vertices;
|
|
var tform = obj.transform;
|
|
|
|
while(objsToWriteVerticesPosW.Count <= i)
|
|
{
|
|
objsToWriteVerticesPosW.Add(null);
|
|
objsToWriteVerticesNormalW.Add(null);
|
|
}
|
|
|
|
objsToWriteVerticesPosW[i] = new Vector3[vertices.Length];
|
|
if (isSkin)
|
|
{
|
|
var lossyScale = tform.lossyScale;
|
|
var inverseWorldScale = new Vector3(1.0f/lossyScale.x, 1.0f/lossyScale.y, 1.0f/lossyScale.z);
|
|
for(int t=0; t<vertices.Length; t++)
|
|
{
|
|
vertices[t].Scale(inverseWorldScale);
|
|
}
|
|
}
|
|
for(int t=0; t<vertices.Length; t++)
|
|
{
|
|
objsToWriteVerticesPosW[i][t] = tform.TransformPoint(vertices[t]);
|
|
}
|
|
var normals = m.normals;
|
|
objsToWriteVerticesNormalW[i] = new Vector3[vertices.Length];
|
|
var nbuff = objsToWriteVerticesNormalW[i];
|
|
var localScale = obj.transform.localScale;
|
|
bool flipX = localScale.x < 0;
|
|
bool flipY = localScale.y < 0;
|
|
bool flipZ = localScale.z < 0;
|
|
if (lmgroup != null && lmgroup.flipNormal)
|
|
{
|
|
flipX = !flipX;
|
|
flipY = !flipY;
|
|
flipZ = !flipZ;
|
|
}
|
|
for(int t=0; t<vertices.Length; t++)
|
|
{
|
|
if (normals.Length == 0)
|
|
{
|
|
nbuff[t] = Vector3.up;
|
|
}
|
|
else
|
|
{
|
|
nbuff[t] = normals[t];
|
|
if (flipX) nbuff[t].x *= -1;
|
|
if (flipY) nbuff[t].y *= -1;
|
|
if (flipZ) nbuff[t].z *= -1;
|
|
nbuff[t] = tform.TransformDirection(nbuff[t]);
|
|
}
|
|
}
|
|
if (NeedsTangents(lmgroup, tangentSHLights))
|
|
{
|
|
var tangents = m.tangents;
|
|
while(objsToWriteVerticesTangentW.Count <= i) objsToWriteVerticesTangentW.Add(null);
|
|
objsToWriteVerticesTangentW[i] = new Vector4[vertices.Length];
|
|
var tbuff = objsToWriteVerticesTangentW[i];
|
|
Vector3 tangent = Vector3.zero;
|
|
for(int t=0; t<vertices.Length; t++)
|
|
{
|
|
if (tangents.Length == 0)
|
|
{
|
|
tbuff[t] = Vector3.right;
|
|
}
|
|
else
|
|
{
|
|
tangent.Set(flipX ? -tangents[t].x : tangents[t].x,
|
|
flipY ? -tangents[t].y : tangents[t].y,
|
|
flipZ ? -tangents[t].z : tangents[t].z);
|
|
tangent = tform.TransformDirection(tangent);
|
|
tbuff[t] = new Vector4(tangent.x, tangent.y, tangent.z, tangents[t].w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CalculateUVPadding(ExportSceneData data, AdjustUVPaddingData adata)
|
|
{
|
|
var meshToPaddingMap = adata.meshToPaddingMap;
|
|
var meshToObjIDs = adata.meshToObjIDs;
|
|
|
|
float smallestMapScale = 1;
|
|
float colorScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.mainLightmapScale) * 6));
|
|
float maskScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.maskLightmapScale) * 6));
|
|
float dirScale = 1.0f / (1 << (int)((1.0f - ftBuildGraphics.dirLightmapScale) * 6));
|
|
smallestMapScale = Mathf.Min(colorScale, maskScale);
|
|
smallestMapScale = Mathf.Min(smallestMapScale, dirScale);
|
|
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
|
|
// Calculate every implicit mesh area and convert to proper padding value
|
|
var explicitGroupTotalArea = new Dictionary<int, float>();
|
|
var objsWithExplicitGroupPadding = new List<int>();
|
|
var objsWithExplicitGroupPaddingWidth = new List<float>();
|
|
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var lmgroup = objsToWriteGroup[i];
|
|
if (lmgroup == null) continue;
|
|
if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) continue; // no need to adjust for vertex-baked meshes
|
|
var prefabParent = PrefabUtility.GetPrefabParent(objsToWrite[i]) as GameObject;
|
|
if (prefabParent == null) continue;
|
|
var sharedMesh = GetSharedMesh(objsToWrite[i]);
|
|
var assetPath = AssetDatabase.GetAssetPath(sharedMesh);
|
|
var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter;
|
|
if (importer == null || !importer.generateSecondaryUV) continue;
|
|
// user doesn't care much about UVs - adjust
|
|
|
|
var m = sharedMesh;
|
|
var vpos = objsToWriteVerticesPosW[i];
|
|
float area = 0;
|
|
var inds = objsToWriteIndices[i];
|
|
for(int k=0;k<m.subMeshCount;k++) {
|
|
var indices = inds[k];// m.GetTriangles(k);
|
|
int indexA, indexB, indexC;
|
|
for(int j=0;j<indices.Length;j+=3)
|
|
{
|
|
indexA = indices[j];
|
|
indexB = indices[j + 1];
|
|
indexC = indices[j + 2];
|
|
|
|
var v1 = vpos[indexA];
|
|
var v2 = vpos[indexB];
|
|
var v3 = vpos[indexC];
|
|
area += Vector3.Cross(v2 - v1, v3 - v1).magnitude;
|
|
}
|
|
}
|
|
//var so = new SerializedObject(objsToWrite[i].GetComponent<Renderer>());
|
|
//var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
var scaleInLm = data.objToScaleInLm[objsToWrite[i]];
|
|
if (scaleInLm == 0) continue; // don't reunwrap objects with scale in lightmap == 0
|
|
|
|
area *= scaleInLm;
|
|
|
|
float width = Mathf.Sqrt(area);
|
|
float twidth = 1;
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
twidth = width * texelsPerUnit;
|
|
}
|
|
else
|
|
{
|
|
float currentArea;
|
|
if (!explicitGroupTotalArea.TryGetValue(lmgroup.id, out currentArea)) currentArea = 0;
|
|
explicitGroupTotalArea[lmgroup.id] = currentArea + area;
|
|
|
|
var holder = objsToWriteHolder[i];
|
|
BakeryLightmapGroupSelector comp = null;
|
|
if (holder != null) comp = holder.GetComponent<BakeryLightmapGroupSelector>();
|
|
if (comp != null && comp.instanceResolutionOverride)
|
|
{
|
|
// Explicit holder size
|
|
twidth = width * comp.instanceResolution;
|
|
}
|
|
else
|
|
{
|
|
// Texel size in atlas - can't calculate at this point
|
|
objsWithExplicitGroupPadding.Add(i);
|
|
objsWithExplicitGroupPaddingWidth.Add(width);
|
|
continue;
|
|
}
|
|
}
|
|
float requiredPadding = 4 * (1024.0f / (twidth * smallestMapScale));
|
|
int requiredPaddingClamped = (int)Mathf.Clamp(requiredPadding, 1, 256);
|
|
|
|
int existingPadding = 0;
|
|
meshToPaddingMap.TryGetValue(m, out existingPadding);
|
|
meshToPaddingMap[m] = Math.Max(requiredPaddingClamped, existingPadding); // select largest padding among instances
|
|
|
|
List<int> arr;
|
|
if (!meshToObjIDs.TryGetValue(m, out arr))
|
|
{
|
|
meshToObjIDs[m] = arr = new List<int>();
|
|
}
|
|
if (!arr.Contains(i)) arr.Add(i);
|
|
}
|
|
|
|
for(int j=0; j<objsWithExplicitGroupPadding.Count; j++)
|
|
{
|
|
int i = objsWithExplicitGroupPadding[j];
|
|
float width = objsWithExplicitGroupPaddingWidth[j];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
float totalArea = explicitGroupTotalArea[lmgroup.id];
|
|
float twidth = (width / Mathf.Sqrt(totalArea)) * lmgroup.resolution;
|
|
var m = GetSharedMesh(objsToWrite[i]);
|
|
|
|
// Following is copy-pasted from the loop above
|
|
float requiredPadding = 4 * (1024.0f / (twidth * smallestMapScale));
|
|
int requiredPaddingClamped = (int)Mathf.Clamp(requiredPadding, 1, 256);
|
|
|
|
int existingPadding = 0;
|
|
meshToPaddingMap.TryGetValue(m, out existingPadding);
|
|
meshToPaddingMap[m] = Math.Max(requiredPaddingClamped, existingPadding); // select largest padding among instances
|
|
|
|
List<int> arr;
|
|
if (!meshToObjIDs.TryGetValue(m, out arr))
|
|
{
|
|
meshToObjIDs[m] = arr = new List<int>();
|
|
}
|
|
if (!arr.Contains(i)) arr.Add(i);
|
|
}
|
|
}
|
|
|
|
static void ResetPaddingStorageData(ExportSceneData data)
|
|
{
|
|
var storages = data.storages;
|
|
|
|
// Reset scene padding backup
|
|
for(int s=0; s<storages.Length; s++)
|
|
{
|
|
var str = storages[s];
|
|
if (str == null) continue;
|
|
str.modifiedAssetPathList = new List<string>();
|
|
str.modifiedAssets = new List<ftGlobalStorage.AdjustedMesh>();
|
|
}
|
|
}
|
|
|
|
static void StoreNewUVPadding(ExportSceneData data, AdjustUVPaddingData adata)
|
|
{
|
|
var meshToPaddingMap = adata.meshToPaddingMap;
|
|
var meshToObjIDs = adata.meshToObjIDs;
|
|
var dirtyAssetList = adata.dirtyAssetList;
|
|
var dirtyObjList = adata.dirtyObjList;
|
|
var storages = data.storages;
|
|
|
|
foreach(var pair in meshToPaddingMap)
|
|
{
|
|
var m = pair.Key;
|
|
var requiredPaddingClamped = pair.Value;
|
|
var assetPath = AssetDatabase.GetAssetPath(m);
|
|
|
|
var ids = meshToObjIDs[m];
|
|
|
|
//for(int s=0; s<sceneCount; s++)
|
|
{
|
|
var objStorage = gstorage;// == null ? storages[0] : gstorage;// storages[s];
|
|
int mstoreIndex = objStorage.modifiedAssetPathList.IndexOf(assetPath);
|
|
int ind = -1;
|
|
var mname = m.name;
|
|
if (mstoreIndex >= 0) ind = objStorage.modifiedAssets[mstoreIndex].meshName.IndexOf(mname);
|
|
if (ind < 0)
|
|
{
|
|
if (mstoreIndex < 0)
|
|
{
|
|
// add new record to globalstorage
|
|
objStorage.modifiedAssetPathList.Add(assetPath);
|
|
var newStruct = new ftGlobalStorage.AdjustedMesh();
|
|
newStruct.meshName = new List<string>();
|
|
newStruct.padding = new List<int>();
|
|
objStorage.modifiedAssets.Add(newStruct);
|
|
mstoreIndex = objStorage.modifiedAssets.Count - 1;
|
|
}
|
|
|
|
var nameList = objStorage.modifiedAssets[mstoreIndex].meshName;
|
|
var paddingList = objStorage.modifiedAssets[mstoreIndex].padding;
|
|
var unwrapperList = objStorage.modifiedAssets[mstoreIndex].unwrapper;
|
|
if (unwrapperList == null)
|
|
{
|
|
var s = objStorage.modifiedAssets[mstoreIndex];
|
|
unwrapperList = s.unwrapper = new List<int>();
|
|
objStorage.modifiedAssets[mstoreIndex] = s;
|
|
}
|
|
while(nameList.Count > unwrapperList.Count) unwrapperList.Add(0); // fix legacy
|
|
|
|
nameList.Add(mname);
|
|
paddingList.Add(requiredPaddingClamped);
|
|
unwrapperList.Add((int)ftRenderLightmap.unwrapper);
|
|
|
|
if (!dirtyAssetList.Contains(assetPath)) dirtyAssetList.Add(assetPath);
|
|
for(int xx=0; xx<ids.Count; xx++) dirtyObjList.Add(ids[xx]);
|
|
#if UNITY_2017_1_OR_NEWER
|
|
objStorage.SyncModifiedAsset(mstoreIndex);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
var nameList = objStorage.modifiedAssets[mstoreIndex].meshName;
|
|
var paddingList = objStorage.modifiedAssets[mstoreIndex].padding;
|
|
var unwrapperList = objStorage.modifiedAssets[mstoreIndex].unwrapper;
|
|
if (unwrapperList == null)
|
|
{
|
|
var s = objStorage.modifiedAssets[mstoreIndex];
|
|
unwrapperList = s.unwrapper = new List<int>();
|
|
objStorage.modifiedAssets[mstoreIndex] = s;
|
|
}
|
|
while(nameList.Count > unwrapperList.Count) unwrapperList.Add(0); // fix legacy
|
|
|
|
// modify existing record
|
|
var oldValue = paddingList[ind];
|
|
var oldUnwrapperValue = (ftGlobalStorage.Unwrapper)unwrapperList[ind];
|
|
bool shouldModify = oldValue != requiredPaddingClamped;
|
|
if (uvPaddingMax)
|
|
{
|
|
shouldModify = oldValue < requiredPaddingClamped;
|
|
}
|
|
if (oldUnwrapperValue != ftRenderLightmap.unwrapper) shouldModify = true;
|
|
if (shouldModify)
|
|
{
|
|
if (!dirtyAssetList.Contains(assetPath)) dirtyAssetList.Add(assetPath);
|
|
for(int xx=0; xx<ids.Count; xx++) dirtyObjList.Add(ids[xx]);
|
|
paddingList[ind] = requiredPaddingClamped;
|
|
unwrapperList[ind] = (int)ftRenderLightmap.unwrapper;
|
|
#if UNITY_2017_1_OR_NEWER
|
|
objStorage.SyncModifiedAsset(mstoreIndex);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Backup padding storage to scene
|
|
for(int s=0; s<storages.Length; s++)
|
|
{
|
|
var str = storages[s];
|
|
if (str == null) continue;
|
|
var localIndex = str.modifiedAssetPathList.IndexOf(assetPath);
|
|
if (localIndex < 0)
|
|
{
|
|
str.modifiedAssetPathList.Add(assetPath);
|
|
str.modifiedAssets.Add(objStorage.modifiedAssets[mstoreIndex]);
|
|
}
|
|
else
|
|
{
|
|
str.modifiedAssets[localIndex] = objStorage.modifiedAssets[mstoreIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorUtility.SetDirty(gstorage);
|
|
}
|
|
|
|
static bool ValidatePaddingImmutability(AdjustUVPaddingData adata)
|
|
{
|
|
if (validateLightmapStorageImmutability)
|
|
{
|
|
if (adata.dirtyAssetList.Count > 0)
|
|
{
|
|
sceneNeedsToBeRebuilt = true;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ValidateScaleOffsetImmutability(ExportSceneData data)
|
|
{
|
|
if (validateLightmapStorageImmutability)
|
|
{
|
|
var holderRect = data.holderRect;
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
|
|
var emptyVec4 = new Vector4(1,1,0,0);
|
|
Rect rc = new Rect();
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
var holderObj = objsToWriteHolder[i];
|
|
if (holderObj != null)
|
|
{
|
|
if (!holderRect.TryGetValue(holderObj, out rc))
|
|
{
|
|
holderObj = null;
|
|
}
|
|
}
|
|
var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y);
|
|
|
|
var sceneID = sceneToID[obj.scene];
|
|
var st = storages[sceneID];
|
|
if (st == null)
|
|
{
|
|
Debug.LogError("ValidateScaleOffsetImmutability: no storage");
|
|
return false;
|
|
}
|
|
|
|
var storedScaleOffset = Vector4.zero;
|
|
if (obj.name == "__ExportTerrain")
|
|
{
|
|
#if USE_TERRAINS
|
|
var tindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject);
|
|
var terrain = terrainObjectToActual[tindex];
|
|
int index = st.bakedRenderersTerrain.IndexOf(terrain);
|
|
/*if (st.bakedIDsTerrain[index] != lmgroup.id)
|
|
{
|
|
Debug.LogError("ValidateScaleOffsetImmutability: terrain LMID does not match");
|
|
return false;
|
|
}*/
|
|
if (index < 0 || st.bakedScaleOffsetTerrain.Count <= index) continue;
|
|
storedScaleOffset = st.bakedScaleOffsetTerrain[index];
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int index = st.bakedRenderers.IndexOf(GetValidRenderer(obj));
|
|
/*if (st.bakedIDs[index] != lmgroup.id)
|
|
{
|
|
Debug.LogError("ValidateScaleOffsetImmutability: LMID does not match");
|
|
Debug.LogError(st.bakedIDs[index]+" "+lmgroup.id+" "+lmgroup.name);
|
|
return false;
|
|
}*/
|
|
if (index < 0 || st.bakedScaleOffset.Count <= index) continue;
|
|
storedScaleOffset = st.bakedScaleOffset[index];
|
|
}
|
|
// approx equality
|
|
if (!(scaleOffset == storedScaleOffset))
|
|
{
|
|
Debug.LogError("ValidateScaleOffsetImmutability: scale/offset does not match on " + obj.name);
|
|
Debug.Log("(" + scaleOffset.x + ", " + scaleOffset.y + "," + scaleOffset.z + ", " + scaleOffset.w + ") vs (" +
|
|
storedScaleOffset.x + ", " + storedScaleOffset.y + "," + storedScaleOffset.z + ", " + storedScaleOffset.w + ")"
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ClearUVPadding(ExportSceneData data, AdjustUVPaddingData adata)
|
|
{
|
|
var objsToWrite = data.objsToWrite;
|
|
var dirtyAssetList = adata.dirtyAssetList;
|
|
var dirtyObjList = adata.dirtyObjList;
|
|
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var sharedMesh = GetSharedMesh(objsToWrite[i]);
|
|
var assetPath = AssetDatabase.GetAssetPath(sharedMesh);
|
|
|
|
int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(assetPath);
|
|
if (mstoreIndex < 0) continue;
|
|
|
|
dirtyObjList.Add(i);
|
|
if (!dirtyAssetList.Contains(assetPath))
|
|
{
|
|
dirtyAssetList.Add(assetPath);
|
|
}
|
|
}
|
|
|
|
if (!ValidatePaddingImmutability(adata)) return false;
|
|
|
|
for(int i=0; i<dirtyAssetList.Count; i++)
|
|
{
|
|
var assetPath = dirtyAssetList[i];
|
|
int mstoreIndex = gstorage.modifiedAssetPathList.IndexOf(assetPath);
|
|
DebugLogInfo("Reimport " + assetPath);
|
|
ProgressBarShow("Exporting scene - clearing UV adjustment for " + assetPath + "...", 0, false);
|
|
|
|
gstorage.ClearAssetModifications(mstoreIndex);
|
|
}
|
|
|
|
EditorUtility.SetDirty(gstorage);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ReimportModifiedAssets(AdjustUVPaddingData adata)
|
|
{
|
|
var dirtyAssetList = adata.dirtyAssetList;
|
|
|
|
for(int i=0; i<dirtyAssetList.Count; i++)
|
|
{
|
|
var assetPath = dirtyAssetList[i];
|
|
DebugLogInfo("Reimport " + assetPath);
|
|
ProgressBarShow("Exporting scene - adjusting UV padding for " + assetPath + "...", 0, false);
|
|
//AssetDatabase.ImportAsset(assetPath);
|
|
(AssetImporter.GetAtPath(assetPath) as ModelImporter).SaveAndReimport();
|
|
|
|
if (CheckUnwrapError())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void TransformModifiedAssets(ExportSceneData data, AdjustUVPaddingData adata, bool tangentSHLights)
|
|
{
|
|
// Transform modified vertices to world space again
|
|
for(int d=0; d<adata.dirtyObjList.Count; d++)
|
|
{
|
|
int i = adata.dirtyObjList[d];
|
|
|
|
// Refresh attributes and indices after reimport
|
|
bool isSkin;
|
|
var m = GetSharedMeshSkinned(data.objsToWrite[i], out isSkin);
|
|
data.objsToWriteVerticesUV[i] = m.uv;
|
|
data.objsToWriteVerticesUV2[i] = m.uv2;
|
|
var inds = new int[m.subMeshCount][];
|
|
for(int n=0; n<inds.Length; n++) inds[n] = m.GetTriangles(n);
|
|
data.objsToWriteIndices[i] = inds;
|
|
|
|
TransformVertices(data, tangentSHLights, i); // because vertex count/order could be modified
|
|
}
|
|
}
|
|
|
|
static void CalculateHolderUVBounds(ExportSceneData data)
|
|
{
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW;
|
|
var objsToWriteVerticesUV = data.objsToWriteVerticesUV;
|
|
var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var holderObjUVBounds = data.holderObjUVBounds;
|
|
var holderObjArea = data.holderObjArea;
|
|
var groupToHolderObjects = data.groupToHolderObjects;
|
|
|
|
// Calculate implicit group / atlas packing data
|
|
// UV bounds and worldspace area
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
var calculateArea = lmgroup == null ? false : (lmgroup.isImplicit || lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas || lmgroup.autoResolution);
|
|
if (!calculateArea) continue;
|
|
|
|
var holderObj = objsToWriteHolder[i];
|
|
var m = GetSharedMesh(obj);
|
|
//var mr = obj.GetComponent<Renderer>();
|
|
|
|
var vpos = objsToWriteVerticesPosW[i];
|
|
var vuv = objsToWriteVerticesUV2[i];//m.uv2;
|
|
var inds = objsToWriteIndices[i];
|
|
//if (vuv.Length == 0 || obj.GetComponent<BakeryLightMesh>()!=null) vuv = objsToWriteVerticesUV[i];//m.uv; // area lights or objects without UV2 export UV1 instead
|
|
if (vuv.Length == 0 || obj.GetComponent<BakeryLightMesh>()!=null || temporaryAreaLightMeshList.Contains(obj)) vuv = objsToWriteVerticesUV[i];//m.uv; // area lights or objects without UV2 export UV1 instead
|
|
Vector2 uv1 = Vector2.zero;
|
|
Vector2 uv2 = Vector2.zero;
|
|
Vector2 uv3 = Vector2.zero;
|
|
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1;
|
|
|
|
for(int k=0;k<m.subMeshCount;k++) {
|
|
|
|
var indices = inds[k];//m.GetTriangles(k);
|
|
int indexA, indexB, indexC;
|
|
float area = 0;
|
|
//float areaUV = 0;
|
|
Vector4 uvBounds = new Vector4(1,1,0,0); // minx, miny, maxx, maxy
|
|
|
|
for(int j=0;j<indices.Length;j+=3)
|
|
{
|
|
indexA = indices[j];
|
|
indexB = indices[j + 1];
|
|
indexC = indices[j + 2];
|
|
|
|
var v1 = vpos[indexA];
|
|
var v2 = vpos[indexB];
|
|
var v3 = vpos[indexC];
|
|
area += Vector3.Cross(v2 - v1, v3 - v1).magnitude;
|
|
|
|
if (vuv.Length > 0)
|
|
{
|
|
uv1 = vuv[indexA];
|
|
uv2 = vuv[indexB];
|
|
uv3 = vuv[indexC];
|
|
}
|
|
|
|
/*var uv31 = new Vector3(uv1.x, uv1.y, 0);
|
|
var uv32 = new Vector3(uv2.x, uv2.y, 0);
|
|
var uv33 = new Vector3(uv3.x, uv3.y, 0);
|
|
areaUV += Vector3.Cross(uv32 - uv31, uv33 - uv31).magnitude;*/
|
|
|
|
if (uv1.x < uvBounds.x) uvBounds.x = uv1.x;
|
|
if (uv1.y < uvBounds.y) uvBounds.y = uv1.y;
|
|
if (uv1.x > uvBounds.z) uvBounds.z = uv1.x;
|
|
if (uv1.y > uvBounds.w) uvBounds.w = uv1.y;
|
|
|
|
if (uv2.x < uvBounds.x) uvBounds.x = uv2.x;
|
|
if (uv2.y < uvBounds.y) uvBounds.y = uv2.y;
|
|
if (uv2.x > uvBounds.z) uvBounds.z = uv2.x;
|
|
if (uv2.y > uvBounds.w) uvBounds.w = uv2.y;
|
|
|
|
if (uv3.x < uvBounds.x) uvBounds.x = uv3.x;
|
|
if (uv3.y < uvBounds.y) uvBounds.y = uv3.y;
|
|
if (uv3.x > uvBounds.z) uvBounds.z = uv3.x;
|
|
if (uv3.y > uvBounds.w) uvBounds.w = uv3.y;
|
|
}
|
|
|
|
// uv layouts always have empty spaces
|
|
//area /= areaUV;
|
|
|
|
var scaleInLm = data.objToScaleInLm[obj];
|
|
|
|
if (!pstorage.alternativeScaleInLightmap) area *= scaleInLm;
|
|
|
|
if ((lmgroup.isImplicit || lmgroup.autoResolution) && lodLevel == -1)
|
|
{
|
|
lmgroup.area += area; // accumulate LMGroup area
|
|
// only use base scene values, no LODs, to properly initialize autoatlas size
|
|
}
|
|
if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
// Accumulate per-holder area and UV bounds
|
|
float existingArea;
|
|
Vector4 existingBounds;
|
|
holderObjUVBounds.TryGetValue(holderObj, out existingBounds);
|
|
if (!holderObjArea.TryGetValue(holderObj, out existingArea))
|
|
{
|
|
existingArea = 0;
|
|
existingBounds = uvBounds;
|
|
List<GameObject> holderList;
|
|
if (!groupToHolderObjects.TryGetValue(lmgroup, out holderList))
|
|
{
|
|
groupToHolderObjects[lmgroup] = holderList = new List<GameObject>();
|
|
}
|
|
holderList.Add(holderObj);
|
|
}
|
|
holderObjArea[holderObj] = existingArea + area;
|
|
|
|
existingBounds.x = existingBounds.x < uvBounds.x ? existingBounds.x : uvBounds.x;
|
|
existingBounds.y = existingBounds.y < uvBounds.y ? existingBounds.y : uvBounds.y;
|
|
existingBounds.z = existingBounds.z > uvBounds.z ? existingBounds.z : uvBounds.z;
|
|
existingBounds.w = existingBounds.w > uvBounds.w ? existingBounds.w : uvBounds.w;
|
|
holderObjUVBounds[holderObj] = existingBounds;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ResolutionFromArea(float area)
|
|
{
|
|
int resolution = (int)(Mathf.Sqrt(area) * texelsPerUnit);
|
|
if (mustBePOT)
|
|
{
|
|
if (atlasCountPriority)
|
|
{
|
|
resolution = Mathf.NextPowerOfTwo(resolution);
|
|
}
|
|
else
|
|
{
|
|
resolution = Mathf.ClosestPowerOfTwo(resolution);
|
|
}
|
|
}
|
|
resolution = Math.Max(resolution, minAutoResolution);
|
|
resolution = Math.Min(resolution, maxAutoResolution);
|
|
|
|
return resolution;
|
|
}
|
|
|
|
static void CalculateAutoAtlasInitResolution(ExportSceneData data)
|
|
{
|
|
var groupList = data.groupList;
|
|
|
|
// Calculate implicit lightmap resolution
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
var lmgroup = groupList[i];
|
|
if (lmgroup.isImplicit || lmgroup.autoResolution)
|
|
{
|
|
lmgroup.resolution = ResolutionFromArea(lmgroup.area);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void NormalizeHolderArea(BakeryLightmapGroup lmgroup, List<GameObject> holderObjs, ExportSceneData data)
|
|
{
|
|
var holderObjArea = data.holderObjArea;
|
|
var holderObjUVBounds = data.holderObjUVBounds;
|
|
|
|
// Divide holders area to get from world space to -> UV space
|
|
float areaMult = 1.0f;
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
// ...by maximum lightmap area given texel size (autoAtlas)
|
|
//areaMult = 1.0f / lightmapMaxArea;
|
|
// don't modify
|
|
}
|
|
else
|
|
{
|
|
// ... by maximum holder area (normalize)
|
|
float lmgroupArea = 0;
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
// space outside of UV bounds shouldn't affect area
|
|
var uvbounds = holderObjUVBounds[holderObjs[i]];
|
|
var width = uvbounds.z - uvbounds.x;
|
|
var height = uvbounds.w - uvbounds.y;
|
|
float uvboundsArea = width * height;
|
|
|
|
lmgroupArea += holderObjArea[holderObjs[i]] * uvboundsArea;
|
|
}
|
|
areaMult = 1.0f / lmgroupArea;
|
|
}
|
|
|
|
// Perform the division and sum up total UV area
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
holderObjArea[holderObjs[i]] *= areaMult;
|
|
}
|
|
}
|
|
|
|
static void SumHolderAreaPerLODLevel(List<GameObject> holderObjs, ExportSceneData data, PackData pdata)
|
|
{
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var holderObjArea = data.holderObjArea;
|
|
var remainingAreaPerLodLevel = pdata.remainingAreaPerLodLevel;
|
|
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
int lodLevel = -1;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[i], out lodLevel)) lodLevel = -1;
|
|
|
|
float lodArea = 0;
|
|
if (!remainingAreaPerLodLevel.TryGetValue(lodLevel, out lodArea)) lodArea = 0;
|
|
|
|
remainingAreaPerLodLevel[lodLevel] = lodArea + holderObjArea[holderObjs[i]];
|
|
}
|
|
}
|
|
|
|
static int CompareGameObjectsForPacking(GameObject a, GameObject b)
|
|
{
|
|
if (splitByScene)
|
|
{
|
|
if (a.scene.name != b.scene.name) return a.scene.name.CompareTo(b.scene.name);
|
|
}
|
|
|
|
if (splitByTag)
|
|
{
|
|
int tagA = GetLightmapTag(a, _tempData);
|
|
int tagB = GetLightmapTag(b, _tempData);
|
|
if (tagA != tagB) return tagA.CompareTo(tagB);
|
|
}
|
|
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
bool ba = a.name != "__ExportTerrainParent";
|
|
bool bb = b.name != "__ExportTerrainParent";
|
|
if (ba != bb) return ba.CompareTo(bb);
|
|
}*/
|
|
|
|
int lodLevelA = -1;
|
|
int lodLevelB = -1;
|
|
if (!cmp_objToLodLevel.TryGetValue(a, out lodLevelA)) lodLevelA = -1;
|
|
if (!cmp_objToLodLevel.TryGetValue(b, out lodLevelB)) lodLevelB = -1;
|
|
|
|
if (lodLevelA != lodLevelB) return lodLevelA.CompareTo(lodLevelB);
|
|
|
|
float areaA = cmp_holderObjArea[a];
|
|
float areaB = cmp_holderObjArea[b];
|
|
|
|
// Workaround for "override resolution"
|
|
// Always pack such rectangles first
|
|
var comp = a.GetComponent<BakeryLightmapGroupSelector>();
|
|
if (comp != null && comp.instanceResolutionOverride) areaA = comp.instanceResolution * 10000;
|
|
|
|
comp = b.GetComponent<BakeryLightmapGroupSelector>();
|
|
if (comp != null && comp.instanceResolutionOverride) areaB = comp.instanceResolution * 10000;
|
|
|
|
return areaB.CompareTo(areaA);
|
|
}
|
|
|
|
static void ApplyAreaToUVBounds(float area, Vector4 uvbounds, out float width, out float height)
|
|
{
|
|
if (pstorage.alternativeScaleInLightmap) area *= 2;
|
|
width = height = Mathf.Sqrt(area);
|
|
if (pstorage.alternativeScaleInLightmap) width = height = Mathf.Max(1.0f, Mathf.Floor(height * 0.5f));
|
|
float uwidth = uvbounds.z - uvbounds.x;
|
|
float uheight = uvbounds.w - uvbounds.y;
|
|
if (uwidth == 0 && uheight == 0)
|
|
{
|
|
width = height = 0;
|
|
}
|
|
else
|
|
{
|
|
float uvratio = uheight / uwidth;
|
|
if (uvratio <= 1.0f)
|
|
{
|
|
width /= uvratio;
|
|
//height *= uvratio;
|
|
}
|
|
else
|
|
{
|
|
height *= uvratio;
|
|
//width /= uvratio;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool Pack(BakeryLightmapGroup lmgroup, List<GameObject> holderObjs, ExportSceneData data, PackData pdata)
|
|
{
|
|
var holderObjArea = data.holderObjArea;
|
|
var holderObjUVBounds = data.holderObjUVBounds;
|
|
var holderRect = data.holderRect;
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var groupList = data.groupList;
|
|
var lmBounds = data.lmBounds;
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes;
|
|
|
|
var remainingAreaPerLodLevel = pdata.remainingAreaPerLodLevel;
|
|
|
|
int atlasPaddingPixels = pstorage.texelPaddingForDefaultAtlasPacker;
|
|
|
|
//Debug.LogError("repack: "+repackScale);
|
|
pdata.repack = false;
|
|
|
|
AtlasNode rootNode;
|
|
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas && autoAtlasGroupRootNodes != null && autoAtlasGroupRootNodes.Count > 0)
|
|
{
|
|
rootNode = autoAtlasGroupRootNodes[0];
|
|
}
|
|
else
|
|
{
|
|
rootNode = new AtlasNode();
|
|
}
|
|
|
|
rootNode.rc = new Rect(0, 0, 1, 1);
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
var area = holderObjArea[holderObjs[i]];
|
|
var uvbounds = holderObjUVBounds[holderObjs[i]];
|
|
|
|
// Calculate width and height of each holder in atlas UV space
|
|
float width, height;
|
|
var comp = holderObjs[i].GetComponent<BakeryLightmapGroupSelector>();
|
|
if (comp != null && comp.instanceResolutionOverride)
|
|
{
|
|
// Explicit holder size
|
|
pdata.hasResOverrides = true;
|
|
width = height = comp.instanceResolution / (float)lmgroup.resolution;
|
|
}
|
|
else
|
|
{
|
|
// Automatic: width and height = sqrt(area) transformed by UV AABB aspect ratio
|
|
ApplyAreaToUVBounds(area, uvbounds, out width, out height);
|
|
|
|
if (pstorage.alternativeScaleInLightmap)
|
|
{
|
|
var mr = GetValidRenderer(holderObjs[i]);
|
|
if (mr != null)
|
|
{
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = mr as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
width *= scaleInLm;
|
|
height *= scaleInLm;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clamp to full lightmap size
|
|
float twidth = width;
|
|
float theight = height;
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
twidth = (width * texelsPerUnit) / lmgroup.resolution;
|
|
theight = (height * texelsPerUnit) / lmgroup.resolution;
|
|
|
|
//if (i==0) Debug.LogError(texelsPerUnit+" "+twidth);
|
|
}
|
|
//float unclampedTwidth = twidth;
|
|
//float unclampedTheight = twidth;
|
|
if (comp != null && comp.instanceResolutionOverride)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
twidth *= pdata.repackScale;
|
|
theight *= pdata.repackScale;
|
|
}
|
|
twidth = twidth > 1 ? 1 : twidth;
|
|
theight = theight > 1 ? 1 : theight;
|
|
twidth = Mathf.Max(twidth, 1.0f / lmgroup.resolution);
|
|
theight = Mathf.Max(theight, 1.0f / lmgroup.resolution);
|
|
var rect = new Rect(0, 0, twidth, theight);
|
|
|
|
if (float.IsNaN(twidth) || float.IsNaN(theight))
|
|
{
|
|
ExportSceneError("NaN UVs detected for " + holderObjs[i].name+" "+rect.width+" "+rect.height+" "+width+" "+height+" "+lmgroup.resolution+" "+area+" "+(uvbounds.z - uvbounds.x)+" "+(uvbounds.w - uvbounds.y));
|
|
return false;
|
|
}
|
|
|
|
// Try inserting this rect
|
|
// Break autoatlas if lod level changes
|
|
// Optionally break autoatlas if scene changes
|
|
AtlasNode node = null;
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[i], out lodLevel)) lodLevel = -1;
|
|
bool splitAtlas = false;
|
|
if (splitByScene)
|
|
{
|
|
if (holderObjs[i].scene.name != lmgroup.sceneName)
|
|
{
|
|
splitAtlas = true;
|
|
}
|
|
}
|
|
if (splitByTag)
|
|
{
|
|
if (GetLightmapTag(holderObjs[i], data) != lmgroup.tag)
|
|
{
|
|
splitAtlas = true;
|
|
}
|
|
}
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
bool ba = holderObjs[i].name == "__ExportTerrainParent";
|
|
if (ba) lmgroup.containsTerrains = true;
|
|
|
|
if (i > 0)
|
|
{
|
|
bool bb = holderObjs[i-1].name == "__ExportTerrainParent";
|
|
if (ba != bb)
|
|
{
|
|
splitAtlas = true;
|
|
}
|
|
}
|
|
}*/
|
|
if (!splitAtlas)
|
|
{
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
if (lodLevel == lmgroup.sceneLodLevel)
|
|
{
|
|
node = rootNode.Insert(holderObjs[i], rect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
node = rootNode.Insert(holderObjs[i], rect);
|
|
}
|
|
}
|
|
|
|
/*if (node!=null)
|
|
{
|
|
Debug.Log(holderObjs[i].name+" goes straight into "+lmgroup.name);
|
|
}*/
|
|
|
|
if (node == null)
|
|
{
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
// Can't fit - try other autoAtlas lightmaps
|
|
BakeryLightmapGroup newGroup = null;
|
|
var holder = holderObjs[i];
|
|
int goodGroup = -1;
|
|
for(int g=1; g<autoAtlasGroups.Count; g++)
|
|
{
|
|
if (splitByScene)
|
|
{
|
|
if (autoAtlasGroups[g].sceneName != holderObjs[i].scene.name) continue;
|
|
}
|
|
if (splitByTag)
|
|
{
|
|
if (autoAtlasGroups[g].tag != GetLightmapTag(holderObjs[i], data)) continue;
|
|
}
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
bool ba = holderObjs[i].name != "__ExportTerrainParent";
|
|
bool bb = !autoAtlasGroups[g].containsTerrains;
|
|
if (ba != bb) continue;
|
|
}*/
|
|
if (autoAtlasGroups[g].sceneLodLevel != lodLevel) continue;
|
|
twidth = (width * texelsPerUnit) / autoAtlasGroups[g].resolution;
|
|
theight = (height * texelsPerUnit) / autoAtlasGroups[g].resolution;
|
|
//unclampedTwidth = twidth;
|
|
//unclampedTheight = twidth;
|
|
twidth = twidth > 1 ? 1 : twidth;
|
|
theight = theight > 1 ? 1 : theight;
|
|
rect = new Rect(0, 0, twidth, theight);
|
|
|
|
node = autoAtlasGroupRootNodes[g].Insert(holder, rect);
|
|
if (node != null)
|
|
{
|
|
//Debug.Log(holder.name+" fits into "+autoAtlasGroups[g].name);
|
|
newGroup = autoAtlasGroups[g];
|
|
goodGroup = g;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Can't fit - create new lightmap (autoAtlas)
|
|
if (goodGroup < 0)
|
|
{
|
|
newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
newGroup.name = FilterNonASCII(holder.scene.name) + "_LMA" + autoAtlasGroups.Count;
|
|
newGroup.isImplicit = true;
|
|
newGroup.sceneLodLevel = lodLevel;
|
|
if (splitByScene) newGroup.sceneName = holderObjs[i].scene.name;
|
|
if (splitByTag) newGroup.tag = GetLightmapTag(holderObjs[i], data);
|
|
//Debug.Log(holder.name+" creates "+newGroup.name);
|
|
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
newGroup.containsTerrains = holderObjs[i].name == "__ExportTerrainParent";
|
|
}*/
|
|
|
|
newGroup.resolution = (int)(Mathf.Sqrt(remainingAreaPerLodLevel[lodLevel]) * texelsPerUnit);
|
|
if (mustBePOT)
|
|
{
|
|
if (atlasCountPriority)
|
|
{
|
|
newGroup.resolution = Mathf.NextPowerOfTwo(newGroup.resolution);
|
|
}
|
|
else
|
|
{
|
|
newGroup.resolution = Mathf.ClosestPowerOfTwo(newGroup.resolution);
|
|
}
|
|
}
|
|
newGroup.resolution = Math.Max(newGroup.resolution, minAutoResolution);
|
|
newGroup.resolution = Math.Min(newGroup.resolution, maxAutoResolution);
|
|
|
|
newGroup.bitmask = 1;
|
|
newGroup.area = 0;
|
|
newGroup.mode = BakeryLightmapGroup.ftLMGroupMode.PackAtlas;
|
|
|
|
newGroup.id = data.lmid;
|
|
groupList.Add(newGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
|
|
autoAtlasGroups.Add(newGroup);
|
|
var rootNode2 = new AtlasNode();
|
|
rootNode2.rc = new Rect(0, 0, 1, 1);
|
|
autoAtlasGroupRootNodes.Add(rootNode2);
|
|
|
|
twidth = (width * texelsPerUnit) / newGroup.resolution;
|
|
theight = (height * texelsPerUnit) / newGroup.resolution;
|
|
//unclampedTwidth = twidth;
|
|
//unclampedTheight = twidth;
|
|
twidth = twidth > 1 ? 1 : twidth;
|
|
theight = theight > 1 ? 1 : theight;
|
|
|
|
rect = new Rect(0, 0, twidth, theight);
|
|
|
|
node = rootNode2.Insert(holder, rect);
|
|
}
|
|
|
|
// Modify implicit group storage
|
|
MoveObjectToImplicitGroup(holder, newGroup, data);
|
|
/*
|
|
var scn = holder.scene;
|
|
tempStorage.implicitGroupMap[holder] = newGroup;
|
|
for(int k=0; k<storages[sceneToID[holder.scene]].implicitGroupedObjects.Count; k++)
|
|
{
|
|
if (storages[sceneToID[holder.scene]].implicitGroupedObjects[k] == holder)
|
|
{
|
|
storages[sceneToID[holder.scene]].implicitGroups[k] = newGroup;
|
|
//Debug.LogError("Implicit set: " + k+" "+newGroup.name+" "+holder.name);
|
|
}
|
|
}
|
|
*/
|
|
//lmgroup = newGroup;
|
|
}
|
|
else
|
|
{
|
|
if (!pdata.repackStage2)
|
|
{
|
|
// explicit packed atlas - can't fit - try shrinking the whole atlas
|
|
pdata.repackTries++;
|
|
if (pdata.repackTries < atlasMaxTries)
|
|
{
|
|
pdata.repack = true;
|
|
pdata.repackScale *= 0.75f;
|
|
//Debug.LogError("Can't fit, set " +repackScale);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// explicit packed atlas - did fit, now trying to scale up, doesn't work - found optimal fit
|
|
pdata.repack = true;
|
|
pdata.repackScale /= atlasScaleUpValue;//*= 0.75f;
|
|
//Debug.LogError("Final, set " +repackScale);
|
|
pdata.finalRepack = true;
|
|
pdata.repackTries = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (node == null)
|
|
{
|
|
// No way to fit
|
|
ExportSceneError("Can't fit " + holderObjs[i].name+" "+rect.width+" "+rect.height);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// Generate final rectangle to transform local UV -> atlas UV
|
|
float padding = ((float)atlasPaddingPixels) / lmgroup.resolution;
|
|
|
|
var paddedRc = new Rect(node.rc.x + padding,
|
|
node.rc.y + padding,
|
|
node.rc.width - padding * 2,
|
|
node.rc.height - padding * 2);
|
|
|
|
paddedRc.x -= uvbounds.x * (paddedRc.width / (uvbounds.z - uvbounds.x));
|
|
paddedRc.y -= uvbounds.y * (paddedRc.height / (uvbounds.w - uvbounds.y));
|
|
paddedRc.width /= uvbounds.z - uvbounds.x;
|
|
paddedRc.height /= uvbounds.w - uvbounds.y;
|
|
|
|
holderRect[holderObjs[i]] = paddedRc;
|
|
}
|
|
|
|
//float areaReduction = (twidth*theight) / (unclampedTwidth*unclampedTheight);
|
|
remainingAreaPerLodLevel[lodLevel] -= area;// * areaReduction;
|
|
}
|
|
|
|
if (!lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
if (pdata.finalRepack && pdata.repack)
|
|
{
|
|
pdata.continueRepack = true;
|
|
return true;
|
|
}
|
|
if (pdata.finalRepack)
|
|
{
|
|
pdata.continueRepack = false;
|
|
return true;
|
|
}
|
|
|
|
if (!pdata.repack && !pdata.repackStage2)
|
|
{
|
|
//if (repackTries > 0) break; // shrinked down just now - don't scale up
|
|
|
|
pdata.repackStage2 = true; // scale up now
|
|
pdata.repack = true;
|
|
pdata.repackScale *= atlasScaleUpValue;///= 0.75f;
|
|
pdata.repackTries = 0;
|
|
//Debug.LogError("Scale up, set " +repackScale);
|
|
}
|
|
else if (pdata.repackStage2)
|
|
{
|
|
pdata.repackTries++;
|
|
if (pdata.repackTries == atlasMaxTries)
|
|
{
|
|
pdata.continueRepack = false;
|
|
return true;
|
|
}
|
|
pdata.repack = true;
|
|
pdata.repackScale *= atlasScaleUpValue;///= 0.75f;
|
|
//Debug.LogError("Scale up cont, set " +repackScale);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void MoveObjectToImplicitGroup(GameObject holder, BakeryLightmapGroup newGroup, ExportSceneData data)
|
|
{
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
|
|
// Modify implicit group storage
|
|
var scn = holder.scene;
|
|
tempStorage.implicitGroupMap[holder] = newGroup;
|
|
for(int k=0; k<storages[sceneToID[holder.scene]].implicitGroupedObjects.Count; k++)
|
|
{
|
|
if (storages[sceneToID[holder.scene]].implicitGroupedObjects[k] == holder)
|
|
{
|
|
storages[sceneToID[holder.scene]].implicitGroups[k] = newGroup;
|
|
//Debug.LogError("Implicit set: " + k+" "+newGroup.name+" "+holder.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static List<int> GetAtlasBucketRanges(List<GameObject> holderObjs, ExportSceneData data, bool onlyUserSplits)
|
|
{
|
|
var objToLodLevel = data.objToLodLevel;
|
|
|
|
var ranges = new List<int>();
|
|
int start = 0;
|
|
int end = 0;
|
|
if (holderObjs.Count > 0)
|
|
{
|
|
var sceneName = holderObjs[0].scene.name;
|
|
int tag = -1;
|
|
if (splitByTag) tag = GetLightmapTag(holderObjs[0], data);
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[0], out lodLevel)) lodLevel = -1;
|
|
//bool isTerrain = holderObjs[0].name == "__ExportTerrainParent";
|
|
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
bool splitAtlas = false;
|
|
|
|
// Split by scene
|
|
if (splitByScene)
|
|
{
|
|
var objSceneName = holderObjs[i].scene.name;
|
|
if (objSceneName != sceneName)
|
|
{
|
|
splitAtlas = true;
|
|
sceneName = objSceneName;
|
|
}
|
|
}
|
|
|
|
if (splitByTag)
|
|
{
|
|
var objTag = GetLightmapTag(holderObjs[i], data);
|
|
if (objTag != tag)
|
|
{
|
|
splitAtlas = true;
|
|
tag = objTag;
|
|
}
|
|
}
|
|
|
|
if (!onlyUserSplits)
|
|
{
|
|
// Split by LOD
|
|
int objLodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[i], out objLodLevel)) objLodLevel = -1;
|
|
if (objLodLevel != lodLevel)
|
|
{
|
|
//bool objNonOr0 = objLodLevel <= 0;
|
|
//bool lodNonOr0 = lodLevel <= 0;
|
|
//bool validToCombine = objNonOr0 && lodNonOr0;
|
|
//if (!validToCombine) // didn't work; LOD0 never showed up in lmlods, thus never loaded as shadowcast for non-LOD
|
|
{
|
|
lodLevel = objLodLevel;
|
|
splitAtlas = true;
|
|
}
|
|
}
|
|
|
|
// Split by terrain
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
bool ba = holderObjs[i].name == "__ExportTerrainParent";
|
|
if (ba != isTerrain)
|
|
{
|
|
isTerrain = ba;
|
|
splitAtlas = true;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
if (splitAtlas)
|
|
{
|
|
end = i;
|
|
ranges.Add(start);
|
|
ranges.Add(end-1);
|
|
start = end;
|
|
}
|
|
}
|
|
}
|
|
end = holderObjs.Count-1;
|
|
ranges.Add(start);
|
|
ranges.Add(end);
|
|
return ranges;
|
|
}
|
|
|
|
static float SumObjectsArea(List<GameObject> holderObjs, int start, int end, ExportSceneData data)
|
|
{
|
|
var holderObjArea = data.holderObjArea;
|
|
|
|
float area = 0;
|
|
for(int i=start; i<=end; i++)
|
|
{
|
|
float a = holderObjArea[holderObjs[i]];
|
|
var mr = holderObjs[i].GetComponent<Renderer>();
|
|
if (mr != null)
|
|
{
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = mr as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
if (pstorage.alternativeScaleInLightmap)
|
|
{
|
|
a *= scaleInLm * scaleInLm;
|
|
}
|
|
else
|
|
{
|
|
a *= scaleInLm;
|
|
}
|
|
}
|
|
area += a;
|
|
}
|
|
return area;
|
|
}
|
|
|
|
static BakeryLightmapGroup AllocateAutoAtlas(int count, BakeryLightmapGroup lmgroup, ExportSceneData data, int[] atlasSizes = null)
|
|
{
|
|
var lmBounds = data.lmBounds;
|
|
var groupList = data.groupList;
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes;
|
|
|
|
BakeryLightmapGroup newGroup = null;
|
|
|
|
for(int i=0; i<count; i++)
|
|
{
|
|
// Create additional lightmaps
|
|
newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
newGroup.name = lmgroup.sceneName + "_LMA" + autoAtlasGroups.Count;
|
|
newGroup.isImplicit = true;
|
|
newGroup.sceneLodLevel = lmgroup.sceneLodLevel;
|
|
if (splitByScene) newGroup.sceneName = lmgroup.sceneName;
|
|
if (splitByTag) newGroup.tag = lmgroup.tag;
|
|
newGroup.containsTerrains = lmgroup.containsTerrains;
|
|
|
|
newGroup.resolution = atlasSizes != null ? atlasSizes[i] : lmgroup.resolution;
|
|
|
|
newGroup.bitmask = lmgroup.bitmask;
|
|
newGroup.area = 0;
|
|
newGroup.mode = lmgroup.mode;// BakeryLightmapGroup.ftLMGroupMode.PackAtlas;
|
|
|
|
newGroup.renderMode = lmgroup.renderMode;
|
|
newGroup.renderDirMode = lmgroup.renderDirMode;
|
|
newGroup.atlasPacker = lmgroup.atlasPacker;
|
|
newGroup.computeSSS = lmgroup.computeSSS;
|
|
newGroup.sssSamples = lmgroup.sssSamples;
|
|
newGroup.sssDensity = lmgroup.sssDensity;
|
|
newGroup.sssColor = lmgroup.sssColor * lmgroup.sssScale;
|
|
newGroup.fakeShadowBias = lmgroup.fakeShadowBias;
|
|
newGroup.transparentSelfShadow = lmgroup.transparentSelfShadow;
|
|
newGroup.flipNormal = lmgroup.flipNormal;
|
|
|
|
newGroup.id = data.lmid;
|
|
groupList.Add(newGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
|
|
autoAtlasGroups.Add(newGroup);
|
|
var rootNode2 = new AtlasNode();
|
|
rootNode2.rc = new Rect(0, 0, 1, 1);
|
|
autoAtlasGroupRootNodes.Add(rootNode2);
|
|
}
|
|
|
|
return newGroup;
|
|
}
|
|
|
|
static bool PackWithXatlas(BakeryLightmapGroup lmgroup, List<GameObject> holderObjs, ExportSceneData data, PackData pdata)
|
|
{
|
|
var holderObjArea = data.holderObjArea;
|
|
var holderObjUVBounds = data.holderObjUVBounds;
|
|
var holderRect = data.holderRect;
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWrite = data.objsToWrite;
|
|
|
|
bool warned = false;
|
|
|
|
// Split objects into "buckets" by scene, terrains, LODs, etc
|
|
// Objects are already pre-sorted, so we need only ranges
|
|
int bStart = 0;
|
|
int bEnd = holderObjs.Count-1;
|
|
int bucketCount = 2;
|
|
List<int> buckets = null;
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
buckets = GetAtlasBucketRanges(holderObjs, data, postPacking);
|
|
bucketCount = buckets.Count;
|
|
}
|
|
|
|
var holderAutoIndex = new int[holderObjs.Count];
|
|
|
|
for(int bucket=0; bucket<bucketCount; bucket+=2)
|
|
{
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
bStart = buckets[bucket];
|
|
bEnd = buckets[bucket+1];
|
|
}
|
|
int bSize = bEnd - bStart;
|
|
if (bucket > 0)
|
|
{
|
|
// Start new bucket
|
|
lmgroup = AllocateAutoAtlas(1, lmgroup, data);
|
|
}
|
|
int firstAutoAtlasIndex = autoAtlasGroups.Count - 1;
|
|
if (autoAtlasGroups.Count > 0 && autoAtlasGroups[0] == lmgroup)
|
|
{
|
|
// new bucket always uses the original LMGroup
|
|
// if the original LMGroup is implicit, it'll be moved to firstAutoAtlasIndex
|
|
// but in case of the actual autoAtlas, the original LMGroup should use atlas index 0
|
|
firstAutoAtlasIndex = 0;
|
|
}
|
|
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
float bucketArea = SumObjectsArea(holderObjs, bStart, bEnd, data);
|
|
lmgroup.resolution = ResolutionFromArea(bucketArea);
|
|
}
|
|
|
|
// Fill some LMGroup data
|
|
lmgroup.sceneName = holderObjs[bStart].scene.name;
|
|
if (splitByTag)
|
|
{
|
|
lmgroup.tag = GetLightmapTag(holderObjs[bStart], data);
|
|
}
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1;
|
|
lmgroup.sceneLodLevel = lodLevel;
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
lmgroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent";
|
|
}*/
|
|
|
|
var atlas = xatlas.xatlasCreateAtlas();
|
|
|
|
const int attempts = 4096;
|
|
int padding = pstorage.texelPaddingForXatlasAtlasPacker;
|
|
const bool allowRotate = false;
|
|
float packTexelsPerUnit = lmgroup.isImplicit ? 1.0f : 0.0f; // multiple atlaseses vs single atlas
|
|
int packResolution = lmgroup.resolution;
|
|
int maxChartSize = 0;//packResolution;
|
|
bool bruteForce = true; // high quality
|
|
|
|
int vertCount = 4;
|
|
int indexCount = 6;
|
|
Vector2[] uv = null;
|
|
int[] indices = null;
|
|
if (!holeFilling)
|
|
{
|
|
uv = new Vector2[4];
|
|
indices = new int[6];
|
|
indices[0] = 0;
|
|
indices[1] = 1;
|
|
indices[2] = 2;
|
|
indices[3] = 2;
|
|
indices[4] = 3;
|
|
indices[5] = 0;
|
|
}
|
|
var uvBuffer = new Vector2[4];
|
|
var xrefBuffer = new int[4];
|
|
var indexBuffer = new int[6];
|
|
|
|
for(int i=bStart; i<=bEnd; i++)
|
|
{
|
|
if (!warned)
|
|
{
|
|
var comp = holderObjs[i].GetComponent<BakeryLightmapGroupSelector>();
|
|
if (comp != null && comp.instanceResolutionOverride)
|
|
{
|
|
if (!ExportSceneValidationMessage("When using xatlas as atlas packer, 'Override resolution' option is not supported for LMGroups.\nOption is used on: " + holderObjs[i].name))
|
|
{
|
|
xatlas.xatlasClear(atlas);
|
|
return false;
|
|
}
|
|
warned = true;
|
|
}
|
|
}
|
|
|
|
var area = holderObjArea[holderObjs[i]];
|
|
var uvbounds = holderObjUVBounds[holderObjs[i]];
|
|
|
|
// Automatic: width and height = sqrt(area) transformed by UV AABB aspect ratio
|
|
float width, height;
|
|
ApplyAreaToUVBounds(area, uvbounds, out width, out height);
|
|
|
|
if (pstorage.alternativeScaleInLightmap)
|
|
{
|
|
var mr = holderObjs[i].GetComponent<Renderer>();
|
|
if (mr != null)
|
|
{
|
|
var so = new SerializedObject(mr);
|
|
var scaleInLm = so.FindProperty("m_ScaleInLightmap").floatValue;
|
|
#if UNITY_2019_2_OR_NEWER
|
|
var _r = mr as MeshRenderer;
|
|
if (pstorage.takeReceiveGIIntoAccount && _r != null && _r.receiveGI == ReceiveGI.LightProbes) scaleInLm = 0;
|
|
#endif
|
|
width *= scaleInLm;
|
|
height *= scaleInLm;
|
|
}
|
|
}
|
|
|
|
// Clamp to full lightmap size
|
|
float twidth = width;
|
|
float theight = height;
|
|
if (lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas)
|
|
{
|
|
twidth = (width * texelsPerUnit);// / lmgroup.resolution;
|
|
theight = (height * texelsPerUnit);// / lmgroup.resolution;
|
|
}
|
|
|
|
if (!holeFilling)
|
|
{
|
|
uv[0] = new Vector2(0,0);
|
|
uv[1] = new Vector2(twidth,0);
|
|
uv[2] = new Vector2(twidth,theight);
|
|
uv[3] = new Vector2(0,theight);
|
|
}
|
|
else
|
|
{
|
|
List<int> indexList = null;
|
|
List<Vector2> uvList = null;
|
|
vertCount = indexCount = 0;
|
|
int numMeshes = 0;
|
|
var ubounds = holderObjUVBounds[holderObjs[i]];
|
|
var holder = holderObjs[i];
|
|
for(int o=0; o<objsToWriteHolder.Count; o++)
|
|
{
|
|
if (objsToWriteHolder[o] != holder) continue;
|
|
|
|
if (numMeshes == 1)
|
|
{
|
|
indexList = new List<int>();
|
|
uvList = new List<Vector2>();
|
|
for(int j=0; j<indices.Length; j++)
|
|
{
|
|
indexList.Add(indices[j]);
|
|
}
|
|
for(int j=0; j<uv.Length; j++)
|
|
{
|
|
uvList.Add(uv[j]);
|
|
}
|
|
}
|
|
|
|
bool isSkin;
|
|
var mesh = GetSharedMeshSkinned(objsToWrite[o], out isSkin);
|
|
indices = mesh.triangles;
|
|
var uv1 = mesh.uv;
|
|
var uv2 = mesh.uv2;
|
|
if (uv2 == null || uv2.Length == 0)
|
|
{
|
|
uv = uv1;
|
|
}
|
|
else
|
|
{
|
|
uv = uv2;
|
|
}
|
|
for(int t=0; t<uv.Length; t++)
|
|
{
|
|
float u = (uv[t].x - ubounds.x) / (ubounds.z - ubounds.x);
|
|
float v = (uv[t].y - ubounds.y) / (ubounds.w - ubounds.y);
|
|
u *= twidth;
|
|
v *= theight;
|
|
uv[t] = new Vector2(u, v);
|
|
}
|
|
|
|
if (numMeshes > 0)
|
|
{
|
|
for(int j=0; j<indices.Length; j++)
|
|
{
|
|
indexList.Add(indices[j] + vertCount);
|
|
}
|
|
for(int j=0; j<uv.Length; j++)
|
|
{
|
|
uvList.Add(uv[j]);
|
|
}
|
|
}
|
|
|
|
vertCount += uv.Length;
|
|
indexCount += indices.Length;
|
|
numMeshes++;
|
|
}
|
|
if (numMeshes > 1)
|
|
{
|
|
uv = uvList.ToArray();
|
|
indices = indexList.ToArray();
|
|
}
|
|
}
|
|
|
|
var handleUV = GCHandle.Alloc(uv, GCHandleType.Pinned);
|
|
int err = 0;
|
|
|
|
try
|
|
{
|
|
var pointerUV = handleUV.AddrOfPinnedObject();
|
|
|
|
err = xatlas.xatlasAddUVMesh(atlas, vertCount, pointerUV, indexCount, indices, allowRotate);
|
|
}
|
|
finally
|
|
{
|
|
if (handleUV.IsAllocated) handleUV.Free();
|
|
}
|
|
|
|
if (err == 1)
|
|
{
|
|
Debug.LogError("xatlas::AddMesh: indices are out of range");
|
|
xatlas.xatlasClear(atlas);
|
|
return false;
|
|
}
|
|
else if (err == 2)
|
|
{
|
|
Debug.LogError("xatlas::AddMesh: index count is incorrect");
|
|
xatlas.xatlasClear(atlas);
|
|
return false;
|
|
}
|
|
else if (err != 0)
|
|
{
|
|
Debug.LogError("xatlas::AddMesh: unknown error");
|
|
xatlas.xatlasClear(atlas);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//xatlas.xatlasParametrize(atlas);
|
|
xatlas.xatlasPack(atlas, attempts, packTexelsPerUnit, packResolution, maxChartSize, padding, bruteForce, pstorage.alignToTextureBlocksWithXatlas);//, allowRotate);
|
|
|
|
int atlasCount = xatlas.xatlasGetAtlasCount(atlas);
|
|
var atlasSizes = new int[atlasCount];
|
|
|
|
xatlas.xatlasNormalize(atlas, atlasSizes, pstorage.alternativeScaleInLightmap);
|
|
|
|
// Create additional lightmaps
|
|
AllocateAutoAtlas(atlasCount-1, lmgroup, data, atlasSizes);
|
|
|
|
// Move objects into new atlases
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
for(int i=0; i<=bSize; i++)
|
|
{
|
|
int atlasIndex = xatlas.xatlasGetAtlasIndex(atlas, i, 0);
|
|
|
|
// Modify implicit group storage
|
|
var holder = holderObjs[bStart + i];
|
|
var newGroup = autoAtlasGroups[firstAutoAtlasIndex + atlasIndex];
|
|
MoveObjectToImplicitGroup(holderObjs[bStart + i], newGroup, data);
|
|
holderAutoIndex[bStart + i] = firstAutoAtlasIndex + atlasIndex;
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<=bSize; i++)
|
|
{
|
|
// Get data from xatlas
|
|
int newVertCount = xatlas.xatlasGetVertexCount(atlas, i);
|
|
uvBuffer = new Vector2[newVertCount];
|
|
xrefBuffer = new int[newVertCount];
|
|
|
|
int newIndexCount = xatlas.xatlasGetIndexCount(atlas, i);
|
|
indexBuffer = new int[newIndexCount];
|
|
|
|
if (holeFilling)
|
|
{
|
|
uvBuffer = new Vector2[newVertCount];
|
|
xrefBuffer = new int[newVertCount];
|
|
indexBuffer = new int[newIndexCount];
|
|
}
|
|
|
|
var handleT = GCHandle.Alloc(uvBuffer, GCHandleType.Pinned);
|
|
var handleX = GCHandle.Alloc(xrefBuffer, GCHandleType.Pinned);
|
|
var handleI = GCHandle.Alloc(indexBuffer, GCHandleType.Pinned);
|
|
try
|
|
{
|
|
var pointerT = handleT.AddrOfPinnedObject();
|
|
var pointerX = handleX.AddrOfPinnedObject();
|
|
var pointerI = handleI.AddrOfPinnedObject();
|
|
xatlas.xatlasGetData(atlas, i, pointerT, pointerX, pointerI);
|
|
}
|
|
finally
|
|
{
|
|
if (handleT.IsAllocated) handleT.Free();
|
|
if (handleX.IsAllocated) handleX.Free();
|
|
if (handleI.IsAllocated) handleI.Free();
|
|
}
|
|
|
|
float minU = float.MaxValue;
|
|
float minV = float.MaxValue;
|
|
float maxU = -float.MaxValue;
|
|
float maxV = -float.MaxValue;
|
|
for(int j=0; j<newVertCount; j++)
|
|
{
|
|
if (uvBuffer[j].x < minU) minU = uvBuffer[j].x;
|
|
if (uvBuffer[j].y < minV) minV = uvBuffer[j].y;
|
|
if (uvBuffer[j].x > maxU) maxU = uvBuffer[j].x;
|
|
if (uvBuffer[j].y > maxV) maxV = uvBuffer[j].y;
|
|
}
|
|
|
|
// Generate final rectangle to transform local UV -> atlas UV
|
|
float upadding = 0;
|
|
var uvbounds = holderObjUVBounds[holderObjs[bStart + i]];
|
|
var paddedRc = new Rect(minU + upadding,
|
|
minV + upadding,
|
|
(maxU-minU) - upadding * 2,
|
|
(maxV-minV) - upadding * 2);
|
|
|
|
paddedRc.x -= uvbounds.x * (paddedRc.width / (uvbounds.z - uvbounds.x));
|
|
paddedRc.y -= uvbounds.y * (paddedRc.height / (uvbounds.w - uvbounds.y));
|
|
paddedRc.width /= uvbounds.z - uvbounds.x;
|
|
paddedRc.height /= uvbounds.w - uvbounds.y;
|
|
|
|
holderRect[holderObjs[bStart + i]] = paddedRc;
|
|
}
|
|
|
|
xatlas.xatlasClear(atlas);
|
|
}
|
|
|
|
if (postPacking)
|
|
{
|
|
buckets = GetAtlasBucketRanges(holderObjs, data, false);
|
|
bucketCount = buckets.Count;
|
|
|
|
DebugLogInfo("Bucket count for " + lmgroup.name +": " + (bucketCount/2));
|
|
|
|
if (lmgroup.isImplicit)
|
|
{
|
|
// Post-packing for auto-atlased groups
|
|
var autoLMBuckets = new List<int>[autoAtlasGroups.Count];
|
|
for(int bucket=0; bucket<bucketCount; bucket+=2)
|
|
{
|
|
bStart = buckets[bucket];
|
|
bEnd = buckets[bucket+1];
|
|
for(int i=bStart; i<=bEnd; i++)
|
|
{
|
|
int autoLM = holderAutoIndex[i];
|
|
if (autoLMBuckets[autoLM] == null) autoLMBuckets[autoLM] = new List<int>();
|
|
if (!autoLMBuckets[autoLM].Contains(bucket)) autoLMBuckets[autoLM].Add(bucket);
|
|
}
|
|
}
|
|
int origGroupCount = autoAtlasGroups.Count;
|
|
for(int i=0; i<origGroupCount; i++)
|
|
{
|
|
if (autoLMBuckets[i] != null && autoLMBuckets[i].Count > 1)
|
|
{
|
|
// Split
|
|
for(int j=1; j<autoLMBuckets[i].Count; j++)
|
|
{
|
|
autoAtlasGroups[i].sceneName = holderObjs[bStart].scene.name;
|
|
|
|
var newGroup = AllocateAutoAtlas(1, autoAtlasGroups[i], data);
|
|
int bucket = autoLMBuckets[i][j];
|
|
bStart = buckets[bucket];
|
|
bEnd = buckets[bucket+1];
|
|
|
|
// Update LMGroup data
|
|
//newGroup.sceneName = holderObjs[bStart].scene.name;
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1;
|
|
newGroup.sceneLodLevel = lodLevel;
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
newGroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent";
|
|
}*/
|
|
newGroup.parentName = autoAtlasGroups[i].name;
|
|
autoAtlasGroups[i].parentName = "|";
|
|
//Debug.LogError(autoAtlasGroups[i].name+" (" +autoAtlasGroups[i].id+") -> "+newGroup.name + " (" + newGroup.id+", "+newGroup.parentID+")");
|
|
|
|
for(int k=bStart; k<=bEnd; k++)
|
|
{
|
|
int autoLM = holderAutoIndex[k];
|
|
if (autoLM == i)
|
|
{
|
|
MoveObjectToImplicitGroup(holderObjs[k], newGroup, data);
|
|
holderAutoIndex[k] = -1; // mark as moved
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<origGroupCount; i++)
|
|
{
|
|
if (autoLMBuckets[i] != null)
|
|
{
|
|
for(int j=0; j<holderObjs.Count; j++)
|
|
{
|
|
if (holderAutoIndex[j] != i) continue;
|
|
|
|
// Update LMGroup data
|
|
var newGroup = autoAtlasGroups[i];
|
|
newGroup.sceneName = holderObjs[j].scene.name;
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[j], out lodLevel)) lodLevel = -1;
|
|
newGroup.sceneLodLevel = lodLevel;
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
newGroup.containsTerrains = holderObjs[j].name == "__ExportTerrainParent";
|
|
}*/
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (bucketCount > 0)
|
|
{
|
|
// Post-packing for explicit groups
|
|
// Single LMGroup -> LMGroup*buckets
|
|
|
|
// Setup first bucket
|
|
bStart = buckets[0];
|
|
bEnd = buckets[1];
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1;
|
|
lmgroup.sceneLodLevel = lodLevel;
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
lmgroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent";
|
|
}*/
|
|
//Debug.LogError(lmgroup.name+": "+ lmgroup.sceneLodLevel+" because of " + holderObjs[bStart].name);
|
|
|
|
// Skip first bucket
|
|
for(int bucket=2; bucket<bucketCount; bucket+=2)
|
|
{
|
|
bStart = buckets[bucket];
|
|
bEnd = buckets[bucket+1];
|
|
|
|
var newGroup = AllocateAutoAtlas(1, lmgroup, data);
|
|
|
|
if (!objToLodLevel.TryGetValue(holderObjs[bStart], out lodLevel)) lodLevel = -1;
|
|
newGroup.sceneLodLevel = lodLevel;
|
|
/*if (ftRenderLightmap.giLodMode != ftRenderLightmap.GILODMode.ForceOff && exportTerrainAsHeightmap)
|
|
{
|
|
newGroup.containsTerrains = holderObjs[bStart].name == "__ExportTerrainParent";
|
|
}*/
|
|
newGroup.mode = lmgroup.mode;
|
|
newGroup.parentName = lmgroup.name;
|
|
lmgroup.parentName = "|";
|
|
|
|
//Debug.LogError(newGroup.name+": "+ newGroup.sceneLodLevel+" because of " + holderObjs[bStart].name);
|
|
|
|
for(int k=bStart; k<=bEnd; k++)
|
|
{
|
|
//MoveObjectToImplicitGroup(holderObjs[k], newGroup, data);
|
|
data.storages[data.sceneToID[holderObjs[k].scene]].implicitGroupedObjects.Add(holderObjs[k]);
|
|
data.storages[data.sceneToID[holderObjs[k].scene]].implicitGroups.Add(newGroup);
|
|
tempStorage.implicitGroupMap[holderObjs[k]] = newGroup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void NormalizeAtlas(BakeryLightmapGroup lmgroup, List<GameObject> holderObjs, ExportSceneData data, PackData pdata)
|
|
{
|
|
var holderRect = data.holderRect;
|
|
|
|
if (!lmgroup.isImplicit && lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.PackAtlas && !pdata.hasResOverrides)
|
|
{
|
|
float maxx = 0;
|
|
float maxy = 0;
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
var rect = holderRect[holderObjs[i]];
|
|
if ((rect.x + rect.width) > maxx) maxx = rect.x + rect.width;
|
|
if ((rect.y + rect.height) > maxy) maxy = rect.y + rect.height;
|
|
}
|
|
float maxDimension = maxx > maxy ? maxx : maxy;
|
|
float normalizeScale = 1.0f / maxDimension;
|
|
for(int i=0; i<holderObjs.Count; i++)
|
|
{
|
|
var rect = holderRect[holderObjs[i]];
|
|
holderRect[holderObjs[i]] = new Rect(rect.x * normalizeScale, rect.y * normalizeScale, rect.width * normalizeScale, rect.height * normalizeScale);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool PackAtlases(ExportSceneData data)
|
|
{
|
|
// IN, OUT lmgroup.containsTerrains, OUT holderObjs (sort)
|
|
var groupToHolderObjects = data.groupToHolderObjects;
|
|
|
|
// IN
|
|
var objToLodLevel = data.objToLodLevel; // LODs packed to separate atlases
|
|
cmp_objToLodLevel = objToLodLevel;
|
|
|
|
// IN/OUT
|
|
var holderObjArea = data.holderObjArea; // performs normalization
|
|
cmp_holderObjArea = holderObjArea;
|
|
|
|
// Pack atlases
|
|
// Try to scale all objects to occupy all atlas space
|
|
for(int pass=0; pass<2; pass++)
|
|
{
|
|
foreach(var pair in groupToHolderObjects)
|
|
{
|
|
// For every LMGroup with PackAtlas mode
|
|
var lmgroup = pair.Key;
|
|
if (pass == 0 && !lmgroup.isImplicit) continue; // implicit groups get packed first
|
|
if (pass == 1 && lmgroup.isImplicit) continue;
|
|
var holderObjs = pair.Value; // get all objects
|
|
|
|
var pdata = new PackData();
|
|
|
|
// Normalize by worldspace area and uv area
|
|
// Read/write holderObjArea
|
|
NormalizeHolderArea(lmgroup, holderObjs, data);
|
|
|
|
// Sort objects by area and scene LOD level
|
|
// + optionally by scene
|
|
// + split by terrain
|
|
_tempData = data;
|
|
holderObjs.Sort(CompareGameObjectsForPacking);
|
|
_tempData = null;
|
|
|
|
var packer = lmgroup.atlasPacker == BakeryLightmapGroup.AtlasPacker.Auto ? atlasPacker : (ftGlobalStorage.AtlasPacker)lmgroup.atlasPacker;
|
|
if (packer == ftGlobalStorage.AtlasPacker.xatlas)
|
|
{
|
|
if (!PackWithXatlas(lmgroup, holderObjs, data, pdata))
|
|
{
|
|
ExportSceneError("Failed packing atlas");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Calculate area sum for every scene LOD level in LMGroup
|
|
// Write remainingAreaPerLodLevel
|
|
SumHolderAreaPerLODLevel(holderObjs, data, pdata);
|
|
|
|
// Perform recursive packing
|
|
while(pdata.repack)
|
|
{
|
|
pdata.continueRepack = true;
|
|
if (!Pack(lmgroup, holderObjs, data, pdata)) return false;
|
|
if (!pdata.continueRepack) break;
|
|
}
|
|
// Normalize atlas by largest axis
|
|
NormalizeAtlas(lmgroup, holderObjs, data, pdata);
|
|
}
|
|
}
|
|
}
|
|
cmp_objToLodLevel = null;
|
|
cmp_holderObjArea = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void NormalizeAutoAtlases(ExportSceneData data)
|
|
{
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes;
|
|
var holderRect = data.holderRect;
|
|
|
|
// Normalize autoatlases
|
|
var stack = new Stack<AtlasNode>();
|
|
for(int g=0; g<autoAtlasGroups.Count; g++)
|
|
{
|
|
var lmgroup = autoAtlasGroups[g];
|
|
if (lmgroup.parentName != null && lmgroup.parentName.Length > 0) continue;
|
|
|
|
var rootNode = autoAtlasGroupRootNodes[g];
|
|
float maxx = 0;
|
|
float maxy = 0;
|
|
rootNode.GetMax(ref maxx, ref maxy);
|
|
float maxDimension = maxx > maxy ? maxx : maxy;
|
|
float normalizeScale = 1.0f / maxDimension;
|
|
stack.Clear();
|
|
stack.Push(rootNode);
|
|
while(stack.Count > 0)
|
|
{
|
|
var node = stack.Pop();
|
|
if (node.obj != null)
|
|
{
|
|
var rect = holderRect[node.obj];
|
|
holderRect[node.obj] = new Rect(rect.x * normalizeScale, rect.y * normalizeScale, rect.width * normalizeScale, rect.height * normalizeScale);
|
|
}
|
|
if (node.child0 != null) stack.Push(node.child0);
|
|
if (node.child1 != null) stack.Push(node.child1);
|
|
}
|
|
if (maxDimension < 0.5f)
|
|
{
|
|
lmgroup.resolution /= 2; // shrink the lightmap after normalization if it was too empty
|
|
lmgroup.resolution = Math.Max(lmgroup.resolution, minAutoResolution);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void JoinAutoAtlases(ExportSceneData data)
|
|
{
|
|
var autoAtlasGroups = data.autoAtlasGroups;
|
|
var autoAtlasGroupRootNodes = data.autoAtlasGroupRootNodes;
|
|
var groupList = data.groupList;
|
|
var lmBounds = data.lmBounds;
|
|
var holderRect = data.holderRect;
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
|
|
var stack = new Stack<AtlasNode>();
|
|
|
|
// Join autoatlases
|
|
var autoAtlasCategories = new List<string>();
|
|
bool joined = false;
|
|
for(int g=0; g<autoAtlasGroups.Count; g++)
|
|
{
|
|
if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 0) continue;
|
|
|
|
string cat = "/" + autoAtlasGroups[g].sceneLodLevel;
|
|
if (splitByScene) cat = autoAtlasGroups[g].sceneName + cat;
|
|
if (splitByTag) cat = autoAtlasGroups[g].tag + "/" + cat;
|
|
if (!autoAtlasCategories.Contains(cat)) autoAtlasCategories.Add(cat);
|
|
}
|
|
for(int alod=0; alod<autoAtlasCategories.Count; alod++)
|
|
{
|
|
var cat = autoAtlasCategories[alod];
|
|
var autoAtlasSizes = new List<int>();
|
|
var atlasStack = new Stack<BakeryLightmapGroup>();
|
|
for(int g=0; g<autoAtlasGroups.Count; g++)
|
|
{
|
|
if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 0) continue;
|
|
|
|
var thisCat = "/" + autoAtlasGroups[g].sceneLodLevel;
|
|
if (splitByScene) thisCat = autoAtlasGroups[g].sceneName + thisCat;
|
|
if (splitByTag) thisCat = autoAtlasGroups[g].tag + "/" + thisCat;
|
|
|
|
if (thisCat != cat) continue;
|
|
if (autoAtlasGroups[g].resolution == maxAutoResolution) continue;
|
|
if (!autoAtlasSizes.Contains(autoAtlasGroups[g].resolution)) autoAtlasSizes.Add(autoAtlasGroups[g].resolution);
|
|
}
|
|
autoAtlasSizes.Sort();
|
|
for(int s=0; s<autoAtlasSizes.Count; s++)
|
|
{
|
|
int asize = autoAtlasSizes[s];
|
|
atlasStack.Clear();
|
|
for(int g=0; g<autoAtlasGroups.Count; g++)
|
|
{
|
|
if (autoAtlasGroups[g].parentName != null && autoAtlasGroups[g].parentName.Length > 0) continue;
|
|
|
|
var thisCat = "/" + autoAtlasGroups[g].sceneLodLevel;
|
|
if (splitByScene) thisCat = autoAtlasGroups[g].sceneName + thisCat;
|
|
if (splitByTag) thisCat = autoAtlasGroups[g].tag + "/" + thisCat;
|
|
|
|
if (thisCat != cat) continue;
|
|
if (autoAtlasGroups[g].resolution != asize) continue;
|
|
atlasStack.Push(autoAtlasGroups[g]);
|
|
if (atlasStack.Count == 4)
|
|
{
|
|
var newGroup = ScriptableObject.CreateInstance<BakeryLightmapGroup>();
|
|
newGroup.name = autoAtlasGroups[g].name;
|
|
newGroup.isImplicit = true;
|
|
newGroup.sceneLodLevel = autoAtlasGroups[g].sceneLodLevel;
|
|
newGroup.sceneName = autoAtlasGroups[g].sceneName;
|
|
|
|
newGroup.resolution = asize * 2;
|
|
|
|
newGroup.bitmask = autoAtlasGroups[g].bitmask;
|
|
newGroup.mode = BakeryLightmapGroup.ftLMGroupMode.PackAtlas;
|
|
|
|
newGroup.id = data.lmid;
|
|
groupList.Add(newGroup);
|
|
lmBounds.Add(new Bounds(new Vector3(0,0,0), new Vector3(0,0,0)));
|
|
data.lmid++;
|
|
|
|
autoAtlasGroups.Add(newGroup);
|
|
var rootNode2 = new AtlasNode();
|
|
rootNode2.rc = new Rect(0, 0, 1, 1);
|
|
autoAtlasGroupRootNodes.Add(rootNode2);
|
|
|
|
// Top
|
|
rootNode2.child0 = new AtlasNode();
|
|
rootNode2.child0.rc = new Rect(0, 0, 1, 0.5f);
|
|
|
|
// Bottom
|
|
rootNode2.child1 = new AtlasNode();
|
|
rootNode2.child1.rc = new Rect(0, 0.5f, 1, 0.5f);
|
|
|
|
for(int gg=0; gg<4; gg++)
|
|
{
|
|
var subgroup = atlasStack.Pop();
|
|
var id = autoAtlasGroups.IndexOf(subgroup);
|
|
var subgroupRootNode = autoAtlasGroupRootNodes[id];
|
|
float ox, oy, sx, sy;
|
|
|
|
if (gg == 0)
|
|
{
|
|
// Left top
|
|
rootNode2.child0.child0 = subgroupRootNode;
|
|
//rootNode2.child0.child0.Transform(0, 0, 0.5f, 0.5f);
|
|
//offsetScale = rootNode2.child0.child0.rc;
|
|
ox = 0; oy = 0; sx = 0.5f; sy = 0.5f;
|
|
}
|
|
else if (gg == 1)
|
|
{
|
|
// Right top
|
|
rootNode2.child0.child1 = subgroupRootNode;
|
|
//rootNode2.child0.child1.Transform(0.5f, 0, 0.5f, 0.5f);
|
|
//offsetScale = rootNode2.child0.child1.rc;
|
|
ox = 0.5f; oy = 0; sx = 0.5f; sy = 0.5f;
|
|
}
|
|
else if (gg == 2)
|
|
{
|
|
// Left bottom
|
|
rootNode2.child1.child0 = subgroupRootNode;
|
|
//rootNode2.child1.child0.Transform(0, 0.5f, 0.5f, 0.5f);
|
|
//offsetScale = rootNode2.child1.child0.rc;
|
|
ox = 0; oy = 0.5f; sx = 0.5f; sy = 0.5f;
|
|
}
|
|
else
|
|
{
|
|
// Right bottom
|
|
rootNode2.child1.child1 = subgroupRootNode;
|
|
//rootNode2.child1.child1.Transform(0.5f, 0.5f, 0.5f, 0.5f);
|
|
//offsetScale = rootNode2.child1.child1.rc;
|
|
ox = 0.5f; oy = 0.5f; sx = 0.5f; sy = 0.5f;
|
|
}
|
|
|
|
autoAtlasGroups.RemoveAt(id);
|
|
autoAtlasGroupRootNodes.RemoveAt(id);
|
|
|
|
id = groupList.IndexOf(subgroup);
|
|
groupList.RemoveAt(id);
|
|
lmBounds.RemoveAt(id);
|
|
|
|
for(int x=id; x<groupList.Count; x++)
|
|
{
|
|
groupList[x].id--;
|
|
data.lmid--;
|
|
}
|
|
|
|
// Modify implicit group storage
|
|
joined = true;
|
|
stack.Clear();
|
|
stack.Push(subgroupRootNode);
|
|
while(stack.Count > 0)
|
|
{
|
|
var node = stack.Pop();
|
|
if (node.obj != null)
|
|
{
|
|
var rect = holderRect[node.obj];
|
|
holderRect[node.obj] = new Rect(rect.x * sx + ox,
|
|
rect.y * sy + oy,
|
|
rect.width * sx,
|
|
rect.height * sy);
|
|
|
|
MoveObjectToImplicitGroup(node.obj, newGroup, data);
|
|
|
|
/*
|
|
tempStorage.implicitGroupMap[node.obj] = newGroup;
|
|
for(int k=0; k<storages[sceneToID[node.obj.scene]].implicitGroupedObjects.Count; k++)
|
|
{
|
|
if (storages[sceneToID[node.obj.scene]].implicitGroupedObjects[k] == node.obj)
|
|
{
|
|
storages[sceneToID[node.obj.scene]].implicitGroups[k] = newGroup;
|
|
//Debug.LogError("Implicit set (join): " + k+" "+newGroup.name);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
if (node.child0 != null) stack.Push(node.child0);
|
|
if (node.child1 != null) stack.Push(node.child1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (joined)
|
|
{
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
objsToWriteGroup[i] = GetLMGroupFromObject(objsToWrite[i], data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool ExportSceneValidationMessage(string msg)
|
|
{
|
|
ProgressBarEnd(false);
|
|
if (ftRenderLightmap.verbose)
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Bakery", msg, "Continue anyway", "Cancel"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError(msg);
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...");
|
|
return true;
|
|
}
|
|
|
|
static void ExportSceneError(string phase)
|
|
{
|
|
DebugLogError("Error exporting scene (" + phase + ") - see console for details");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
}
|
|
|
|
class ExportSceneData
|
|
{
|
|
// Per-scene data
|
|
public ftLightmapsStorage[] storages;
|
|
public int firstNonNullStorage;
|
|
//public ftLightmapsStorage settingsStorage;
|
|
public Dictionary<Scene, int> sceneToID = new Dictionary<Scene, int>();
|
|
public Dictionary<Scene,bool> sceneHasStorage = new Dictionary<Scene,bool>();
|
|
|
|
// Object properties
|
|
public Dictionary<GameObject,int> objToLodLevel = new Dictionary<GameObject,int>(); // defines atlas LOD level
|
|
public Dictionary<GameObject,List<int>> objToLodLevelVisible = new Dictionary<GameObject,List<int>>(); // defines LOD levels where this object is visible
|
|
public Dictionary<GameObject, float> objToScaleInLm = new Dictionary<GameObject, float>();
|
|
public Dictionary<GameObject, int> objToBakeTag;
|
|
|
|
public List<GameObject> objsToWrite = new List<GameObject>();
|
|
public List<bool> objsToWriteLightmapped = new List<bool>();
|
|
public List<BakeryLightmapGroup> objsToWriteGroup = new List<BakeryLightmapGroup>();
|
|
public List<GameObject> objsToWriteHolder = new List<GameObject>();
|
|
public List<Vector4> objsToWriteScaleOffset = new List<Vector4>();
|
|
public List<Vector2[]> objsToWriteUVOverride = new List<Vector2[]>();
|
|
public List<string> objsToWriteNames = new List<string>();
|
|
public List<Vector3[]> objsToWriteVerticesPosW = new List<Vector3[]>();
|
|
public List<Vector3[]> objsToWriteVerticesNormalW = new List<Vector3[]>();
|
|
public List<Vector4[]> objsToWriteVerticesTangentW = new List<Vector4[]>();
|
|
public List<Vector2[]> objsToWriteVerticesUV = new List<Vector2[]>();
|
|
public List<Vector2[]> objsToWriteVerticesUV2 = new List<Vector2[]>();
|
|
public List<int[][]> objsToWriteIndices = new List<int[][]>();
|
|
public List<bool> objsToWriteHasMetaAlpha = new List<bool>();
|
|
|
|
public List<Renderer> outsideRenderers = new List<Renderer>(); // for sector+SRP only
|
|
|
|
// Auto-atlasing
|
|
public List<BakeryLightmapGroup> autoAtlasGroups = new List<BakeryLightmapGroup>();
|
|
public List<AtlasNode> autoAtlasGroupRootNodes = new List<AtlasNode>();
|
|
public BakeryLightmapGroup autoVertexGroup;
|
|
|
|
// Data to collect for atlas packing
|
|
public Dictionary<GameObject, float> holderObjArea = new Dictionary<GameObject, float>(); // LMGroup holder area, accumulated from all children
|
|
public Dictionary<GameObject, Vector4> holderObjUVBounds = new Dictionary<GameObject, Vector4>(); // LMGroup holder 2D UV AABB
|
|
public Dictionary<BakeryLightmapGroup, List<GameObject>> groupToHolderObjects = new Dictionary<BakeryLightmapGroup, List<GameObject>>(); // LMGroup -> holders map
|
|
public Dictionary<GameObject, Rect> holderRect = new Dictionary<GameObject, Rect>();
|
|
|
|
// Per-LMGroup data
|
|
public List<BakeryLightmapGroup> groupList = new List<BakeryLightmapGroup>();
|
|
public List<Bounds> lmBounds = new List<Bounds>(); // list of bounding boxes around LMGroups for testing lights
|
|
|
|
// Geometry data
|
|
public List<int>[] indicesOpaqueLOD = null;
|
|
public List<int>[] indicesTransparentLOD = null;
|
|
|
|
public int lmid = 0; // LMID counter
|
|
|
|
public ExportSceneData(int sceneCount)
|
|
{
|
|
storages = new ftLightmapsStorage[sceneCount];
|
|
}
|
|
}
|
|
|
|
class AdjustUVPaddingData
|
|
{
|
|
public List<int> dirtyObjList = new List<int>();
|
|
public List<string> dirtyAssetList = new List<string>();
|
|
public Dictionary<Mesh, List<int>> meshToObjIDs = new Dictionary<Mesh, List<int>>();
|
|
public Dictionary<Mesh, int> meshToPaddingMap = new Dictionary<Mesh, int>();
|
|
}
|
|
|
|
class PackData
|
|
{
|
|
public Dictionary<int,float> remainingAreaPerLodLevel = new Dictionary<int,float>();
|
|
public bool repack = true;
|
|
public bool repackStage2 = false;
|
|
public bool finalRepack = false;
|
|
public float repackScale = 1;
|
|
public int repackTries = 0;
|
|
public bool hasResOverrides = false;
|
|
public bool continueRepack = false;
|
|
}
|
|
|
|
static public IEnumerator ExportScene(EditorWindow window, bool renderTextures = true, bool atlasOnly = false, BakerySectorCapture sectorCaptureAsset = null)
|
|
{
|
|
lmgroupHolder = null; // important to properly flush previous scale-in-lightmaps
|
|
|
|
userCanceled = false;
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
yield return null;
|
|
|
|
var bakeryRuntimePath = ftLightmaps.GetRuntimePath();
|
|
gstorage = AssetDatabase.LoadAssetAtPath(bakeryRuntimePath + "ftGlobalStorage.asset", typeof(ftGlobalStorage)) as ftGlobalStorage;
|
|
pstorage = ftLightmaps.GetProjectSettings();
|
|
|
|
bool isDX11 = SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11;
|
|
bool nonDX11 = !isDX11;
|
|
|
|
bool _unwrapUVs = unwrapUVs;
|
|
bool _forceDisableUnwrapUVs = forceDisableUnwrapUVs;
|
|
if (ftRenderLightmap.fullSectorRender && !ftRenderLightmap.curSector.allowUVPaddingAdjustment)
|
|
{
|
|
_unwrapUVs = false;
|
|
_forceDisableUnwrapUVs = false;
|
|
}
|
|
|
|
var time = GetTime();
|
|
var ms = time;
|
|
var startMsU = ms;
|
|
double totalTime = GetTime();
|
|
double vbTimeRead = 0;
|
|
double vbTimeWrite = 0;
|
|
double vbTimeWriteFull = 0;
|
|
double vbTimeWriteT = 0;
|
|
double vbTimeWriteT2 = 0;
|
|
double vbTimeWriteT3 = 0;
|
|
double ibTime = 0;
|
|
var sceneCount = SceneManager.sceneCount;
|
|
var indicesOpaque = new List<int>();
|
|
var indicesTransparent = new List<int>();
|
|
|
|
var data = new ExportSceneData(sceneCount);
|
|
|
|
bool tangentSHLights = CheckForTangentSHLights();
|
|
|
|
// Per-LMGroup data
|
|
var lmAlbedoList = new List<IntPtr>(); // list of albedo texture for UV GBuffer rendering
|
|
var lmAlbedoListTex = new List<Texture>();
|
|
var lmAlphaList = new List<IntPtr>(); // list of alpha textures for alpha buffer generation
|
|
var lmAlphaListRAM = new List<TexInput>(); // non-DX11 array
|
|
var lmAlphaListTex = new List<Texture>();
|
|
var lmAlphaRefList = new List<float>(); // list of alpha texture refs
|
|
var lmAlphaChannelList = new List<int>(); // list of alpha channels
|
|
|
|
// lod-related
|
|
var lmVOffset = new List<int>();
|
|
var lmUVArrays = new List<List<float>>();
|
|
var lmIndexArrays = new List<List<int>>();
|
|
var lmLocalToGlobalIndices = new List<List<int>>();
|
|
|
|
vbtraceTexPosNormalArray = new List<float>();
|
|
vbtraceTexUVArray = new List<float>();
|
|
|
|
sceneLodsUsed = 0;
|
|
|
|
// Create temp path
|
|
CreateSceneFolder();
|
|
|
|
// Disable preview of any sectors
|
|
if (sectorCaptureAsset == null)
|
|
{
|
|
var allSectors = FindObjectsOfType(typeof(BakerySector)) as BakerySector[];
|
|
for(int i=0; i<allSectors.Length; i++)
|
|
{
|
|
if (allSectors[i].previewEnabled) BakerySectorInspector.DisablePreview(allSectors[i]);
|
|
}
|
|
}
|
|
|
|
// Init storages
|
|
try
|
|
{
|
|
InitSceneStorage(data);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
ExportSceneError("Global storage init");
|
|
Debug.LogError("Exception caught: " + e.ToString());
|
|
throw;
|
|
}
|
|
|
|
// Create LMGroup for light probes
|
|
if (ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.L1 && renderTextures && !atlasOnly && ftRenderLightmap.hasAnyProbes && !ftRenderLightmap.fullSectorRender)
|
|
{
|
|
var c = CreateLightProbeLMGroup(data);
|
|
while(c.MoveNext()) yield return null;
|
|
}
|
|
|
|
if (ftRenderLightmap.hasAnyVolumes)
|
|
{
|
|
var c2 = CreateVolumeLMGroup(data);
|
|
while(c2.MoveNext()) yield return null;
|
|
}
|
|
|
|
// wip
|
|
var lmBounds = data.lmBounds;
|
|
var storages = data.storages;
|
|
var sceneToID = data.sceneToID;
|
|
var groupList = data.groupList;
|
|
var objToLodLevel = data.objToLodLevel;
|
|
var objToLodLevelVisible = data.objToLodLevelVisible;
|
|
var objsToWrite = data.objsToWrite;
|
|
var objsToWriteGroup = data.objsToWriteGroup;
|
|
var objsToWriteHolder = data.objsToWriteHolder;
|
|
var objsToWriteIndices = data.objsToWriteIndices;
|
|
var objsToWriteNames = data.objsToWriteNames;
|
|
var objsToWriteUVOverride = data.objsToWriteUVOverride;
|
|
var objsToWriteScaleOffset = data.objsToWriteScaleOffset;
|
|
var objsToWriteVerticesUV = data.objsToWriteVerticesUV;
|
|
var objsToWriteVerticesUV2 = data.objsToWriteVerticesUV2;
|
|
var objsToWriteVerticesPosW = data.objsToWriteVerticesPosW;
|
|
var objsToWriteVerticesNormalW = data.objsToWriteVerticesNormalW;
|
|
var objsToWriteVerticesTangentW = data.objsToWriteVerticesTangentW;
|
|
var objsToWriteHasMetaAlpha = data.objsToWriteHasMetaAlpha;
|
|
var holderRect = data.holderRect;
|
|
|
|
terrainObjectList = new List<GameObject>();
|
|
#if USE_TERRAINS
|
|
terrainObjectToActual = new List<Terrain>();
|
|
#endif
|
|
terrainObjectToHeightMap = new List<Texture>();
|
|
terrainObjectToHeightMapRAM = new List<TexInput>();
|
|
terrainObjectToBounds = new List<float>();
|
|
terrainObjectToBoundsUV = new List<float>();
|
|
terrainObjectToFlags = new List<int>();
|
|
terrainObjectToLMID = new List<int>();
|
|
terrainObjectToHeightMips = new List<List<float[]>>();
|
|
temporaryGameObjects = new List<GameObject>();
|
|
temporaryAreaLightMeshList = new List<GameObject>();
|
|
temporaryAreaLightMeshList2 = new List<BakeryLightMesh>();
|
|
|
|
var objects = Resources.FindObjectsOfTypeAll(typeof(GameObject));
|
|
//var objects = UnityEngine.Object.FindObjectsOfTypeAll(typeof(GameObject));
|
|
|
|
try
|
|
{
|
|
ms = GetTime();
|
|
|
|
//if (!onlyUVdata)
|
|
//{
|
|
time = ms;
|
|
|
|
// Get manually created LMGroups
|
|
CollectExplicitLMGroups(data);
|
|
|
|
// Object conversion loop / also validate for multiple scene storages
|
|
for(int objNum = 0; objNum < objects.Length; objNum++)
|
|
{
|
|
GameObject obj = (GameObject)objects[objNum];
|
|
if (obj == null) continue;
|
|
|
|
if (!obj.activeInHierarchy) continue;
|
|
var path = AssetDatabase.GetAssetPath(obj);
|
|
if (path != "") continue; // must belong to scene
|
|
|
|
|
|
if (!CheckForMultipleSceneStorages(obj, data)) yield break;
|
|
if (ConvertUnityAreaLight(obj)) continue;
|
|
#if USE_TERRAINS
|
|
ConvertTerrain(obj);
|
|
#endif
|
|
}
|
|
|
|
// Regather objects if new were added
|
|
if (terrainObjectList.Count > 0 || temporaryGameObjects.Count > 0 || temporaryAreaLightMeshList.Count > 0)
|
|
{
|
|
//objects = UnityEngine.Object.FindObjectsOfTypeAll(typeof(GameObject));
|
|
objects = Resources.FindObjectsOfTypeAll(typeof(GameObject));
|
|
}
|
|
|
|
tempStorage.implicitGroupMap = new Dictionary<GameObject, UnityEngine.Object>(); // implicit holder -> LMGroup map. used by GetLMGroupFromObject
|
|
|
|
// Find LODGroups -> LODs -> scene-wide LOD distances
|
|
// Map objects to scene-wide LOD levels
|
|
MapObjectsToSceneLODs(data, objects);
|
|
|
|
ftModelPostProcessor.Init();
|
|
|
|
// Filter objects, convert to property arrays
|
|
if (!FilterObjects(data, objects)) yield break;
|
|
|
|
if (ftRenderLightmap.fullSectorRender)
|
|
{
|
|
var sector = ftRenderLightmap.curSector;
|
|
if (sector.captureMode == BakerySector.CaptureMode.CaptureInPlace || (sectorCaptureAsset != null && sectorCaptureAsset.write))
|
|
{
|
|
int objCount = data.objsToWrite.Count;
|
|
var cpoints = sector.cpoints;
|
|
|
|
// Render albedo/alpha/depth for each sector point
|
|
var fdata = new FarSphereRenderData[cpoints.Count];
|
|
for(int i=0; i<cpoints.Count; i++)
|
|
{
|
|
//if (!GenerateFarSphere(data, sector.cpoints[i].position, objCount)) yield break;
|
|
fdata[i] = GenerateFarSphereData(data, cpoints[i].position, objCount, i == 0);
|
|
if (fdata[i] == null)
|
|
{
|
|
DebugLogError("GenerateFarSphereData failed");
|
|
userCanceled = true;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
// Cull each other (closest texels win)
|
|
for(int i=0; i<cpoints.Count; i++)
|
|
{
|
|
for(int j=0; j<cpoints.Count; j++)
|
|
{
|
|
if (i == j) continue;
|
|
if (!ClipFarSphere(fdata[i], fdata[j]))
|
|
{
|
|
DebugLogError("ClipFarSphere failed");
|
|
userCanceled = true;
|
|
yield break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate meshes
|
|
if (!GenerateFarSpheres(data, fdata, cpoints))
|
|
{
|
|
DebugLogError("GenerateFarSpheres failed");
|
|
userCanceled = true;
|
|
yield break;
|
|
}
|
|
|
|
if (sectorCaptureAsset != null)
|
|
{
|
|
sectorCaptureAsset.meshes = new List<Mesh>();
|
|
sectorCaptureAsset.positions = new List<Vector3>();
|
|
sectorCaptureAsset.textures = new List<Texture2D>();
|
|
for(int i=0; i<cpoints.Count; i++)
|
|
{
|
|
var fd = fdata[i];
|
|
for(int j=0; j<fd.meshes.Length; j++)
|
|
{
|
|
sectorCaptureAsset.meshes.Add(fd.meshes[j]);
|
|
sectorCaptureAsset.positions.Add(fd.pos);
|
|
sectorCaptureAsset.textures.Add(fd.textures[j]);
|
|
}
|
|
}
|
|
sectorCaptureAsset.outsideRenderers = data.outsideRenderers;
|
|
yield break;
|
|
}
|
|
}
|
|
else if (sector.captureMode == BakerySector.CaptureMode.LoadCaptured)
|
|
{
|
|
if (sector.captureAsset == null)
|
|
{
|
|
DebugLogError("No capture asset is specified for sector " + sector.name);
|
|
userCanceled = true;
|
|
yield break;
|
|
}
|
|
else
|
|
{
|
|
if (!LoadSectorCapture(data, sector.captureAsset, sector.transform))
|
|
{
|
|
DebugLogError("Can't load sector capture for " + sector.name);
|
|
userCanceled = true;
|
|
yield break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CalculateVertexCountForVertexGroups(data);
|
|
CreateAutoAtlasLMGroups(data, renderTextures, atlasOnly);
|
|
|
|
TransformVertices(data, tangentSHLights);
|
|
|
|
if (_unwrapUVs)
|
|
{
|
|
var adata = new AdjustUVPaddingData();
|
|
|
|
CalculateUVPadding(data, adata);
|
|
ResetPaddingStorageData(data);
|
|
StoreNewUVPadding(data, adata);
|
|
|
|
if (!ValidatePaddingImmutability(adata)) yield break;
|
|
|
|
if (CheckUnwrapError()) yield break;
|
|
|
|
// Reimport assets with adjusted padding
|
|
if (modifyLightmapStorage)
|
|
{
|
|
if (!ReimportModifiedAssets(adata)) yield break;
|
|
|
|
TransformModifiedAssets(data, adata, tangentSHLights);
|
|
}
|
|
}
|
|
else if (_forceDisableUnwrapUVs)
|
|
{
|
|
var adata = new AdjustUVPaddingData();
|
|
|
|
ResetPaddingStorageData(data);
|
|
if (!ClearUVPadding(data, adata)) yield break;
|
|
|
|
if (CheckUnwrapError()) yield break;
|
|
|
|
TransformModifiedAssets(data, adata, tangentSHLights);
|
|
}
|
|
|
|
CalculateHolderUVBounds(data);
|
|
CalculateAutoAtlasInitResolution(data);
|
|
if (!PackAtlases(data)) yield break;
|
|
|
|
if (atlasPacker == ftGlobalStorage.AtlasPacker.Default)
|
|
{
|
|
if (!pstorage.alternativeScaleInLightmap)
|
|
{
|
|
NormalizeAutoAtlases(data);
|
|
JoinAutoAtlases(data);
|
|
}
|
|
}
|
|
|
|
if (!ValidateScaleOffsetImmutability(data))
|
|
{
|
|
sceneNeedsToBeRebuilt = true;
|
|
yield break;
|
|
}
|
|
|
|
InitSceneStorage2(data);
|
|
|
|
//TransformVertices(data); // shouldn't be necessary
|
|
|
|
// Update objToWriteGroups because of autoAtlas
|
|
if (autoAtlas)
|
|
{
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
objsToWriteGroup[i] = GetLMGroupFromObject(objsToWrite[i], data);
|
|
}
|
|
}
|
|
|
|
// Done collecting groups
|
|
|
|
if (groupList.Count == 0 && modifyLightmapStorage)
|
|
{
|
|
DebugLogError("You need to mark some objects static or add Bakery Lightmap Group Selector components on them.");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
|
|
if (objsToWrite.Count == 0)
|
|
{
|
|
DebugLogError("You need to mark some objects static or add Bakery Lightmap Group Selector components on them.");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
|
|
if (atlasOnly)
|
|
{
|
|
atlasOnlyObj = new List<Renderer>();
|
|
atlasOnlySize = new List<int>();
|
|
atlasOnlyID = new List<int>();
|
|
atlasOnlyGroup = new List<BakeryLightmapGroup>();
|
|
atlasOnlyScaleOffset = new List<Vector4>();
|
|
var emptyVec4 = new Vector4(1,1,0,0);
|
|
Rect rc = new Rect();
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var lmgroup = objsToWriteGroup[i];
|
|
if (lmgroup == null) continue;
|
|
if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) continue;
|
|
var obj = objsToWrite[i];
|
|
if (obj != null && obj.GetComponent<BakeryLightMesh>() != null) continue;
|
|
var holderObj = objsToWriteHolder[i];
|
|
if (holderObj != null)
|
|
{
|
|
if (!holderRect.TryGetValue(holderObj, out rc))
|
|
{
|
|
holderObj = null;
|
|
}
|
|
}
|
|
var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y);
|
|
atlasOnlyObj.Add(GetValidRenderer(objsToWrite[i]));
|
|
atlasOnlyScaleOffset.Add(scaleOffset);
|
|
atlasOnlySize.Add(lmgroup == null ? 0 : lmgroup.resolution);
|
|
int id = lmgroup == null ? 0 : lmgroup.id;
|
|
if (lmgroup != null)
|
|
{
|
|
var nm = lmgroup.parentName;
|
|
if (nm != null && nm.Length > 0 && nm != "|")
|
|
{
|
|
// dependant sub-lightmaps
|
|
for(int j=0; j<groupList.Count; j++)
|
|
{
|
|
if (groupList[j].name == nm)
|
|
{
|
|
id = groupList[j].id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
atlasOnlyID.Add(id);
|
|
atlasOnlyGroup.Add(lmgroup);
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
// Sort LMGroups so vertex groups are never first (because Unity assumes lightmap compression on LM0)
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
groupList[i].sortingID = i;
|
|
}
|
|
groupList.Sort(delegate(BakeryLightmapGroup a, BakeryLightmapGroup b)
|
|
{
|
|
int aa = (a.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) ? -1 : 1;
|
|
int bb = (b.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) ? -1 : 1;
|
|
return bb.CompareTo(aa);
|
|
});
|
|
var lmBounds2 = new List<Bounds>();
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
lmBounds2.Add(lmBounds[groupList[i].sortingID]); // apply same sorting to lmBounds
|
|
groupList[i].id = i;
|
|
}
|
|
lmBounds = lmBounds2;
|
|
|
|
if (splitByTag)
|
|
{
|
|
// Init settings set by tag overrides
|
|
var tagTable = gstorage.tagOverrides;
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
var group = groupList[i];
|
|
if (group.isImplicit && group.tag >= 0)
|
|
{
|
|
for(int j=0; j<tagTable.Count; j++)
|
|
{
|
|
var tagData = tagTable[j];
|
|
if (tagData.tag != group.tag) continue;
|
|
|
|
if (tagData.renderMode != (int)BakeryLightmapGroup.RenderMode.Auto) group.renderMode = (BakeryLightmapGroup.RenderMode)tagData.renderMode;
|
|
if (tagData.renderDirMode != (int)BakeryLightmapGroup.RenderDirMode.Auto) group.renderDirMode = (BakeryLightmapGroup.RenderDirMode)tagData.renderDirMode;
|
|
group.bitmask = tagData.bitmask;
|
|
group.transparentSelfShadow = tagData.transparentSelfShadow;
|
|
group.computeSSS = tagData.computeSSS;
|
|
if (group.computeSSS)
|
|
{
|
|
group.sssSamples = tagData.sssSamples;
|
|
group.sssDensity = tagData.sssDensity;
|
|
group.sssColor = tagData.sssColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for existing files
|
|
if (overwriteWarning)
|
|
{
|
|
var checkGroupList = groupList;
|
|
if (overwriteWarningSelectedOnly)
|
|
{
|
|
var selObjs = Selection.objects;
|
|
checkGroupList = new List<BakeryLightmapGroup>();
|
|
for(int o=0; o<selObjs.Length; o++)
|
|
{
|
|
if (selObjs[o] as GameObject == null) continue;
|
|
var selGroup = GetLMGroupFromObject(selObjs[o] as GameObject, data);
|
|
if (selGroup == null) continue;
|
|
if (!checkGroupList.Contains(selGroup))
|
|
{
|
|
checkGroupList.Add(selGroup);
|
|
}
|
|
}
|
|
}
|
|
var existingFilenames = "";
|
|
for(int i=0; i<checkGroupList.Count; i++)
|
|
{
|
|
var nm = checkGroupList[i].name;
|
|
var filename = nm + "_final" + overwriteExtensionCheck;
|
|
var outputPath = ftRenderLightmap.outputPathFull;
|
|
if (File.Exists("Assets/" + outputPath + "/" + filename))
|
|
{
|
|
existingFilenames += filename + "\n";
|
|
}
|
|
}
|
|
if (existingFilenames.Length > 0 && ftRenderLightmap.verbose)
|
|
{
|
|
ProgressBarEnd(false);
|
|
if (!EditorUtility.DisplayDialog("Lightmap overwrite", "These lightmaps will be overwritten:\n\n" + existingFilenames, "Overwrite", "Cancel"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
}
|
|
|
|
ftRenderLightmap.giLodModeEnabled = false;//ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.ForceOn;
|
|
ulong approxMem = 0;
|
|
|
|
if (groupList.Count > 100 && ftRenderLightmap.verbose)
|
|
{
|
|
ProgressBarEnd(false);
|
|
if (!EditorUtility.DisplayDialog("Lightmap count check", groupList.Count + " lightmaps are going to be rendered. Continue?", "Continue", "Cancel"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
|
|
if (memoryWarning || ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.Auto)
|
|
{
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
var lmgroup = groupList[i];
|
|
var res = lmgroup.resolution;
|
|
ulong lightingSize = (ulong)(res * res * 4 * 2); // RGBA16f
|
|
approxMem += lightingSize;
|
|
}
|
|
var tileSize = ftRenderLightmap.tileSize;
|
|
approxMem += (ulong)(tileSize * tileSize * 16 * 2); // maximum 2xRGBA32f (for fixPos12)
|
|
}
|
|
|
|
if (memoryWarning && ftRenderLightmap.verbose)
|
|
{
|
|
ProgressBarEnd(false);
|
|
if (!EditorUtility.DisplayDialog("Lightmap memory check", "Rendering may require more than " + (ulong)((approxMem/1024)/1024) + "MB of video memory. Continue?", "Continue", "Cancel"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
|
|
|
|
if (ftRenderLightmap.giLodMode == ftRenderLightmap.GILODMode.Auto)
|
|
{
|
|
|
|
approxMem /= 1024;
|
|
approxMem /= 1024;
|
|
approxMem += 1024; // scene geometry size estimation - completely random
|
|
|
|
if ((int)approxMem > SystemInfo.graphicsMemorySize)
|
|
{
|
|
DebugLogInfo("GI VRAM auto optimization ON: estimated usage " + (int)approxMem + " > " + SystemInfo.graphicsMemorySize);
|
|
ftRenderLightmap.giLodModeEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
DebugLogInfo("GI VRAM auto optimization OFF: estimated usage " + (int)approxMem + " < " + SystemInfo.graphicsMemorySize);
|
|
}
|
|
}
|
|
|
|
// Generate terrain geometry with detail enough for given size for UVGBuffer purposes
|
|
fhmaps = new BinaryWriter(File.Open(scenePath + "/heightmaps.bin", FileMode.Create));
|
|
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("heightmaps.bin");
|
|
#if USE_TERRAINS
|
|
if (exportTerrainAsHeightmap)
|
|
{
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
if (obj.name != "__ExportTerrain") continue;
|
|
|
|
var holderObj = objsToWriteHolder[i];
|
|
Rect rc = new Rect();
|
|
if (holderObj != null)
|
|
{
|
|
if (!holderRect.TryGetValue(holderObj, out rc))
|
|
{
|
|
holderObj = null;
|
|
}
|
|
}
|
|
if (holderObj == null) continue;
|
|
|
|
var lmgroup = objsToWriteGroup[i];
|
|
//float terrainPixelWidth = rc.width * lmgroup.resolution;
|
|
//float terrainPixelHeight = rc.height * lmgroup.resolution;
|
|
|
|
var index = terrainObjectList.IndexOf(obj.transform.parent.gameObject);
|
|
var terrain = terrainObjectToActual[index];
|
|
var tdata = terrain.terrainData;
|
|
//var heightmapResolution = tdata.heightmapResolution;
|
|
|
|
//int closestSize = (int)Mathf.Min(Mathf.NextPowerOfTwo((int)Mathf.Max(terrainPixelWidth, terrainPixelHeight)), heightmapResolution-1);
|
|
//if (closestSize < 2) continue;
|
|
//int mipLog2 = (int)(Mathf.Log(closestSize) / Mathf.Log(2.0f));
|
|
//int maxMipLog2 = (int)(Mathf.Log(heightmapResolution-1) / Mathf.Log(2.0f));
|
|
//int mip = maxMipLog2 - mipLog2;
|
|
|
|
float scaleX = tdata.size.x;// / (heightmapResolution-1);
|
|
//float scaleY = tdata.size.y;
|
|
float scaleZ = tdata.size.z;// / (heightmapResolution-1);
|
|
float offsetX = obj.transform.position.x;
|
|
float offsetY = obj.transform.position.y;
|
|
float offsetZ = obj.transform.position.z;
|
|
|
|
terrainObjectToLMID[index] = lmgroup.id;
|
|
terrainObjectToBoundsUV[index*4] = rc.x;
|
|
terrainObjectToBoundsUV[index*4+1] = rc.y;
|
|
terrainObjectToBoundsUV[index*4+2] = rc.width;
|
|
terrainObjectToBoundsUV[index*4+3] = rc.height;
|
|
|
|
if (uvgbHeightmap)
|
|
{
|
|
var indexArrays = objsToWriteIndices[i] = new int[1][];
|
|
var indexArray = indexArrays[0] = new int[6];//(closestSize-1)*(closestSize-1)*6];
|
|
//int indexOffset = 0;
|
|
//int vertOffset = 0;
|
|
var uvArray = objsToWriteVerticesUV[i] = objsToWriteVerticesUV2[i] = new Vector2[4];//closestSize*closestSize];
|
|
|
|
var posArray = objsToWriteVerticesPosW[i] = new Vector3[4];
|
|
var normalArray = objsToWriteVerticesNormalW[i] = new Vector3[4];
|
|
|
|
posArray[0] = new Vector3(offsetX, offsetY, offsetZ);
|
|
posArray[1] = new Vector3(offsetX + scaleX, offsetY, offsetZ);
|
|
posArray[2] = new Vector3(offsetX, offsetY, offsetZ + scaleZ);
|
|
posArray[3] = new Vector3(offsetX + scaleX, offsetY, offsetZ + scaleZ);
|
|
|
|
normalArray[0] = Vector3.up;
|
|
normalArray[1] = Vector3.up;
|
|
normalArray[2] = Vector3.up;
|
|
normalArray[3] = Vector3.up;
|
|
|
|
uvArray[0] = new Vector2(0,0);
|
|
uvArray[1] = new Vector2(1,0);
|
|
uvArray[2] = new Vector2(0,1);
|
|
uvArray[3] = new Vector2(1,1);
|
|
|
|
indexArray[0] = 0;
|
|
indexArray[1] = 2;
|
|
indexArray[2] = 3;
|
|
|
|
indexArray[3] = 0;
|
|
indexArray[4] = 3;
|
|
indexArray[5] = 1;
|
|
}
|
|
else
|
|
{
|
|
/*if (mip == 0)
|
|
{
|
|
// use existing heightmap
|
|
var heights = tdata.GetHeights(0, 0, heightmapResolution, heightmapResolution);
|
|
var posArray = objsToWriteVerticesPosW[i] = new Vector3[heightmapResolution * heightmapResolution];
|
|
objsToWriteVerticesNormalW[i] = terrainObjectToNormalMip0[index];
|
|
closestSize = heightmapResolution;
|
|
scaleX /= closestSize-1;
|
|
scaleZ /= closestSize-1;
|
|
for(int y=0; y<closestSize; y++)
|
|
{
|
|
for(int x=0; x<closestSize; x++)
|
|
{
|
|
float px = x * scaleX + offsetX;
|
|
float pz = y * scaleZ + offsetZ;
|
|
posArray[y * closestSize + x] = new Vector3(px, heights[y, x] * scaleY + offsetY, pz);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// use mip
|
|
var heights = terrainObjectToHeightMips[index][mip - 1];
|
|
var posArray = objsToWriteVerticesPosW[i] = new Vector3[closestSize * closestSize];
|
|
objsToWriteVerticesNormalW[i] = terrainObjectToNormalMips[index][mip - 1];
|
|
scaleX /= closestSize-1;
|
|
scaleZ /= closestSize-1;
|
|
for(int y=0; y<closestSize; y++)
|
|
{
|
|
for(int x=0; x<closestSize; x++)
|
|
{
|
|
float px = x * scaleX + offsetX;
|
|
float pz = y * scaleZ + offsetZ;
|
|
posArray[y * closestSize + x] = new Vector3(px, heights[y * closestSize + x] * scaleY + offsetY, pz);
|
|
}
|
|
}
|
|
}
|
|
var indexArrays = objsToWriteIndices[i] = new int[1][];
|
|
var indexArray = indexArrays[0] = new int[(closestSize-1)*(closestSize-1)*6];
|
|
int indexOffset = 0;
|
|
int vertOffset = 0;
|
|
var uvArray = objsToWriteVerticesUV[i] = objsToWriteVerticesUV2[i] = new Vector2[closestSize*closestSize];
|
|
for(int y=0; y<closestSize; y++)
|
|
{
|
|
for(int x=0; x<closestSize; x++)
|
|
{
|
|
uvArray[y * closestSize + x] = new Vector2(x / (float)(closestSize-1), y / (float)(closestSize-1));
|
|
|
|
if (x < closestSize-1 && y < closestSize-1)
|
|
{
|
|
indexArray[indexOffset] = vertOffset;
|
|
indexArray[indexOffset + 1] = vertOffset + closestSize;
|
|
indexArray[indexOffset + 2] = vertOffset + closestSize + 1;
|
|
|
|
indexArray[indexOffset + 3] = vertOffset;
|
|
indexArray[indexOffset + 4] = vertOffset + closestSize + 1;
|
|
indexArray[indexOffset + 5] = vertOffset + 1;
|
|
|
|
indexOffset += 6;
|
|
}
|
|
vertOffset++;
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
|
|
// Export heightmap metadata
|
|
if (terrainObjectToActual.Count > 0)
|
|
{
|
|
for(int i=0; i<terrainObjectToHeightMap.Count; i++)
|
|
{
|
|
fhmaps.Write(terrainObjectToLMID[i]);
|
|
for(int fl=0; fl<6; fl++) fhmaps.Write(terrainObjectToBounds[i*6+fl]);
|
|
for(int fl=0; fl<4; fl++) fhmaps.Write(terrainObjectToBoundsUV[i*4+fl]);
|
|
fhmaps.Write(terrainObjectToFlags[i]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// Write mark last written scene
|
|
File.WriteAllText(scenePath + "/lastscene.txt", ftRenderLightmap.GenerateLightingDataAssetName());
|
|
|
|
// Write lightmap definitions
|
|
var flms = new BinaryWriter(File.Open(scenePath + "/lms.bin", FileMode.Create));
|
|
var flmlod = new BinaryWriter(File.Open(scenePath + "/lmlod.bin", FileMode.Create));
|
|
var flmuvgb = new BinaryWriter(File.Open(scenePath + "/lmuvgb.bin", FileMode.Create));
|
|
|
|
if (ftRenderLightmap.clientMode)
|
|
{
|
|
ftClient.serverFileList.Add("lms.bin");
|
|
ftClient.serverFileList.Add("lmlod.bin");
|
|
ftClient.serverFileList.Add("lmuvgb.bin");
|
|
}
|
|
|
|
// Init global UVGBuffer flags
|
|
int uvgbGlobalFlags = 0;
|
|
int _UVGBFLAG_SMOOTHPOS = pstorage.generateSmoothPos ? UVGBFLAG_SMOOTHPOS : 0;
|
|
if (exportShaderColors)
|
|
{
|
|
if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.BakedNormalMaps)
|
|
{
|
|
uvgbGlobalFlags = UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS;
|
|
}
|
|
else if (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.RNM ||
|
|
(ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.SH && tangentSHLights) ||
|
|
(ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.MonoSH && tangentSHLights))
|
|
{
|
|
uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS | UVGBFLAG_TANGENT;
|
|
}
|
|
else
|
|
{
|
|
uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uvgbGlobalFlags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_ALBEDO | UVGBFLAG_EMISSIVE | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS;
|
|
}
|
|
#if USE_TERRAINS
|
|
if (terrainObjectToActual.Count > 0) uvgbGlobalFlags |= UVGBFLAG_TERRAIN;
|
|
#endif
|
|
SetUVGBFlags(uvgbGlobalFlags);
|
|
|
|
for(int i=0; i<groupList.Count; i++)
|
|
{
|
|
var lmgroup = groupList[i];
|
|
flms.Write(lmgroup.name);
|
|
|
|
flmlod.Write(lmgroup.sceneLodLevel);
|
|
|
|
int uvgbflags = 0;
|
|
|
|
if (lmgroup.containsTerrains && exportTerrainAsHeightmap)
|
|
uvgbflags = uvgbGlobalFlags | (UVGBFLAG_NORMAL | UVGBFLAG_TERRAIN);
|
|
|
|
if (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.BakedNormalMaps)
|
|
uvgbflags = UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS;
|
|
|
|
if (lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.RNM ||
|
|
(lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.SH && tangentSHLights) ||
|
|
(lmgroup.renderDirMode == BakeryLightmapGroup.RenderDirMode.MonoSH && tangentSHLights))
|
|
uvgbflags = UVGBFLAG_NORMAL | UVGBFLAG_FACENORMAL | UVGBFLAG_POS | _UVGBFLAG_SMOOTHPOS | UVGBFLAG_TANGENT;
|
|
|
|
if (lmgroup.probes) uvgbflags = UVGBFLAG_RESERVED;
|
|
|
|
flmuvgb.Write(uvgbflags);
|
|
|
|
if (ftRenderLightmap.clientMode)
|
|
{
|
|
if (uvgbflags == 0) uvgbflags = uvgbGlobalFlags;
|
|
|
|
ftClient.serverFileList.Add("uvpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
ftClient.serverFileList.Add("uvnormal_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
ftClient.serverFileList.Add("uvalbedo_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
if (lmgroup.mode != BakeryLightmapGroup.ftLMGroupMode.Vertex)
|
|
{
|
|
if ((uvgbflags & UVGBFLAG_SMOOTHPOS) != 0) ftClient.serverFileList.Add("uvsmoothpos_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
}
|
|
if ((uvgbflags & UVGBFLAG_FACENORMAL) != 0) ftClient.serverFileList.Add("uvfacenormal_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
if ((uvgbflags & UVGBFLAG_TANGENT) != 0) ftClient.serverFileList.Add("uvtangent_" + lmgroup.name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
}
|
|
|
|
if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex)
|
|
{
|
|
int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)lmgroup.totalVertexCount));
|
|
if (atlasTexSize > 8192) DebugLogWarning("Warning: vertex lightmap group " + lmgroup.name + " uses resolution of " + atlasTexSize);
|
|
atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize;
|
|
flms.Write(-atlasTexSize);
|
|
}
|
|
else
|
|
{
|
|
flms.Write(lmgroup.resolution);
|
|
}
|
|
//Debug.LogError(lmgroup.name+": " + lmgroup.resolution);
|
|
}
|
|
flms.Close();
|
|
flmlod.Close();
|
|
flmuvgb.Close();
|
|
|
|
voffset = ioffset = soffset = 0; // vertex/index/surface write
|
|
|
|
// Per-surface alpha texture IDs
|
|
var alphaIDs = new List<ushort>();
|
|
|
|
int albedoCounter = 0;
|
|
var albedoMap = new Dictionary<IntPtr, int>(); // albedo ptr -> ID map
|
|
|
|
int alphaCounter = 0;
|
|
var alphaMap = new Dictionary<IntPtr, List<int>>(); // alpha ptr -> ID map
|
|
|
|
var dummyTexList = new List<Texture>(); // list of single-color 1px textures
|
|
var dummyPixelArray = new Color[1];
|
|
|
|
if (ftRenderLightmap.checkOverlaps)
|
|
{
|
|
var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
|
var plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
|
var qmesh = quad.GetComponent<MeshFilter>().sharedMesh;
|
|
var pmesh = plane.GetComponent<MeshFilter>().sharedMesh;
|
|
DestroyImmediate(quad);
|
|
DestroyImmediate(plane);
|
|
bool canCheck = ftModelPostProcessor.InitOverlapCheck();
|
|
if (!canCheck)
|
|
{
|
|
DebugLogError("Can't load ftOverlapTest.shader");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
for(int g=0; g<groupList.Count; g++)
|
|
{
|
|
var lmgroup = groupList[g];
|
|
if (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) continue;
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
if (objsToWriteGroup[i] != lmgroup) continue;
|
|
var obj = objsToWrite[i];
|
|
|
|
var mesh = GetSharedMesh(obj);
|
|
if (mesh == qmesh || mesh == pmesh) continue;
|
|
|
|
var uv = objsToWriteVerticesUV[i];//mesh.uv;
|
|
var uv2 = objsToWriteVerticesUV2[i];//mesh.uv2;
|
|
var usedUVs = uv2.Length == 0 ? uv : uv2;
|
|
bool validUVs = true;
|
|
for(int v=0; v<usedUVs.Length; v++)
|
|
{
|
|
if (usedUVs[v].x < -0.0001f || usedUVs[v].x > 1.0001f || usedUVs[v].y < -0.0001f || usedUVs[v].y > 1.0001f)
|
|
{
|
|
validUVs = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!validUVs && ftRenderLightmap.verbose)
|
|
{
|
|
string objPath = obj.name;
|
|
var prt = obj.transform.parent;
|
|
while(prt != null)
|
|
{
|
|
objPath = prt.name + "\\" + objPath;
|
|
prt = prt.parent;
|
|
}
|
|
ftRenderLightmap.simpleProgressBarEnd();
|
|
if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " UVs are out of 0-1 bounds", "Continue", "Stop"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
|
|
int overlap = ftModelPostProcessor.DoOverlapCheck(obj, false);
|
|
if (overlap != 0 && ftRenderLightmap.verbose)
|
|
{
|
|
//storage.debugRT = ftModelPostProcessor.rt;
|
|
string objPath = obj.name;
|
|
var prt = obj.transform.parent;
|
|
while(prt != null)
|
|
{
|
|
objPath = prt.name + "\\" + objPath;
|
|
prt = prt.parent;
|
|
}
|
|
if (overlap < 0)
|
|
{
|
|
ftRenderLightmap.simpleProgressBarEnd();
|
|
if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " has no UV2", "Continue", "Stop"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
else
|
|
{
|
|
ftRenderLightmap.simpleProgressBarEnd();
|
|
if (!EditorUtility.DisplayDialog("Incorrect UVs", "Object " + objPath + " has overlapping UVs", "Continue", "Stop"))
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
ProgressBarInit("Exporting scene - preparing...", window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ftModelPostProcessor.EndOverlapCheck();
|
|
}
|
|
|
|
// Prepare progressbar
|
|
int progressNumObjects = 0;
|
|
foreach(GameObject obj in objects)
|
|
{
|
|
if (obj == null) continue;
|
|
if (!obj.activeInHierarchy) continue;
|
|
progressNumObjects++;
|
|
}
|
|
|
|
// Open files to write
|
|
fscene = new BinaryWriter(File.Open(scenePath + "/objects.bin", FileMode.Create));
|
|
fmesh = new BinaryWriter(File.Open(scenePath + "/mesh.bin", FileMode.Create));
|
|
flmid = new BinaryWriter(File.Open(scenePath + "/lmid.bin", FileMode.Create));
|
|
fseamfix = new BinaryWriter(File.Open(scenePath + "/seamfix.bin", FileMode.Create));
|
|
fsurf = new BinaryWriter(File.Open(scenePath + "/surf.bin", FileMode.Create));
|
|
fmatid = new BinaryWriter(File.Open(scenePath + "/matid.bin", FileMode.Create));
|
|
fmatide = new BinaryWriter(File.Open(scenePath + "/emissiveid.bin", FileMode.Create));
|
|
fmatideb = new BinaryWriter(File.Open(scenePath + "/emissivemul.bin", FileMode.Create));
|
|
fmatidh = new BinaryWriter(File.Open(scenePath + "/heightmapid.bin", FileMode.Create));
|
|
falphaid = new BinaryWriter(File.Open(scenePath + "/alphaid.bin", FileMode.Create));
|
|
|
|
fvbfull = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbfull.bin", FileMode.Create)) );
|
|
fvbtrace = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtrace.bin", FileMode.Create)) );
|
|
fvbtraceTex = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtraceTex.bin", FileMode.Create)) );
|
|
fvbtraceUV0 = new BufferedBinaryWriterFloat( new BinaryWriter(File.Open(scenePath + "/vbtraceUV0.bin", FileMode.Create)) );
|
|
|
|
fib = new BufferedBinaryWriterInt( new BinaryWriter(File.Open(scenePath + "/ib.bin", FileMode.Create)) );
|
|
|
|
fib32 = new BinaryWriter(File.Open(scenePath + "/ib32.bin", FileMode.Create));
|
|
fib32lod = new BinaryWriter[sceneLodsUsed];
|
|
for(int i=0; i<sceneLodsUsed; i++)
|
|
{
|
|
fib32lod[i] = new BinaryWriter(File.Open(scenePath + "/ib32_lod" + i + ".bin", FileMode.Create));
|
|
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("ib32_lod" + i + ".bin");
|
|
}
|
|
falphaidlod = new BinaryWriter[sceneLodsUsed];
|
|
for(int i=0; i<sceneLodsUsed; i++)
|
|
{
|
|
falphaidlod[i] = new BinaryWriter(File.Open(scenePath + "/alphaid_lod" + i + ".bin", FileMode.Create));
|
|
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("alphaid2_lod" + i + ".bin"); // alphaid2, not alphaid
|
|
}
|
|
|
|
if (ftRenderLightmap.clientMode)
|
|
{
|
|
ftClient.serverFileList.Add("objects.bin");
|
|
ftClient.serverFileList.Add("mesh.bin");
|
|
ftClient.serverFileList.Add("lmid.bin");
|
|
ftClient.serverFileList.Add("seamfix.bin");
|
|
ftClient.serverFileList.Add("surf.bin");
|
|
ftClient.serverFileList.Add("matid.bin");
|
|
ftClient.serverFileList.Add("emissiveid.bin");
|
|
ftClient.serverFileList.Add("emissivemul.bin");
|
|
ftClient.serverFileList.Add("heightmapid.bin");
|
|
ftClient.serverFileList.Add("alphaid2.bin"); // alphaid2, not alphaid
|
|
ftClient.serverFileList.Add("alphabuffer.bin");
|
|
ftClient.serverFileList.Add("vbfull.bin");
|
|
ftClient.serverFileList.Add("vbtrace.bin");
|
|
ftClient.serverFileList.Add("vbtraceTex.bin");
|
|
ftClient.serverFileList.Add("vbtraceUV0.bin");
|
|
ftClient.serverFileList.Add("ib.bin");
|
|
ftClient.serverFileList.Add("ib32.bin");
|
|
}
|
|
|
|
// Export some scene data
|
|
// - LMIDs
|
|
// - mesh definitions
|
|
// - surface definitions
|
|
// - albedo IDs
|
|
// - alpha IDs
|
|
// - update LMGroup bounds
|
|
// - export index buffer
|
|
// - generate tracing index buffer
|
|
|
|
areaLightCounter = -2;
|
|
//var defaultTexST = new Vector4(1,1,0,0);
|
|
//var objsToWriteTexST = new List<Vector4>();
|
|
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
var holderObj = objsToWriteHolder[i];
|
|
|
|
if (obj == null)
|
|
{
|
|
// wtf
|
|
DebugLogError("Object " + objsToWriteNames[i] + " was destroyed mid-export");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
|
|
var mr = GetValidRenderer(obj);
|
|
var m = GetSharedMesh(mr);
|
|
|
|
var inds = objsToWriteIndices[i];
|
|
|
|
// Write LMID, mesh and surface definition
|
|
int id = exportLMID(flmid, obj, lmgroup);
|
|
exportMesh(fmesh, m);
|
|
exportSurfs(fsurf, inds, inds.Length);// m);
|
|
|
|
int lodLevel;
|
|
if (!objToLodLevel.TryGetValue(obj, out lodLevel)) lodLevel = -1;
|
|
|
|
bool isTerrain = (exportTerrainAsHeightmap && obj.name == "__ExportTerrain");
|
|
bool hasMetaAlpha = false;
|
|
|
|
// Write albedo IDs, collect alpha IDs, update LMGroup bounds
|
|
if (id >= 0) {
|
|
for(int k=0; k<m.subMeshCount; k++) {
|
|
// Get mesh albedos
|
|
int texID = -1;
|
|
Material mat = null;
|
|
Texture tex = null;
|
|
//var texST = defaultTexST;
|
|
if (k < mr.sharedMaterials.Length) {
|
|
mat = mr.sharedMaterials[k];
|
|
if (mat != null)
|
|
{
|
|
if (mat.HasProperty("_MainTex"))
|
|
{
|
|
tex = mat.mainTexture;
|
|
//if (mat.HasProperty("_MainTex_ST"))
|
|
//{
|
|
// texST = mat.GetVector("_MainTex_ST");
|
|
//}
|
|
}
|
|
else if (mat.HasProperty("_BaseColorMap"))
|
|
{
|
|
// HDRP
|
|
tex = mat.GetTexture("_BaseColorMap");
|
|
}
|
|
else if (mat.HasProperty("_BaseMap"))
|
|
{
|
|
// URP
|
|
tex = mat.GetTexture("_BaseMap");
|
|
}
|
|
}
|
|
}
|
|
IntPtr texPtr = (IntPtr)0;
|
|
Texture texWrite = null;
|
|
if (tex != null)
|
|
{
|
|
texPtr = tex.GetNativeTexturePtr();
|
|
texWrite = tex;
|
|
if (texPtr == (IntPtr)0)
|
|
{
|
|
Debug.LogError("Texture " + tex.name + " cannot be used as albedo (GetNativeTexturePtr returned null)");
|
|
tex = null;
|
|
texWrite = null;
|
|
}
|
|
else if ((tex as RenderTexture) != null)
|
|
{
|
|
Debug.LogError("RenderTexture " + tex.name + " cannot be used as albedo (GetNativeTexturePtr returned null)");
|
|
tex = null;
|
|
texWrite = null;
|
|
}
|
|
}
|
|
if (tex == null)
|
|
{
|
|
// Create dummy 1px texture
|
|
var dummyTex = new Texture2D(1,1);
|
|
dummyPixelArray[0] = (mat == null || !mat.HasProperty("_Color")) ? Color.white : mat.color;
|
|
dummyTex.SetPixels(dummyPixelArray);
|
|
dummyTex.Apply();
|
|
texWrite = dummyTex;
|
|
dummyTexList.Add(dummyTex);
|
|
texPtr = dummyTex.GetNativeTexturePtr();
|
|
if (texPtr == (IntPtr)0)
|
|
{
|
|
Debug.LogError("Failed to call GetNativeTexturePtr() on newly created texture");
|
|
texWrite = null;
|
|
}
|
|
}
|
|
if (!albedoMap.TryGetValue(texPtr, out texID))
|
|
{
|
|
lmAlbedoList.Add(texPtr);
|
|
lmAlbedoListTex.Add(texWrite);
|
|
albedoMap[texPtr] = albedoCounter;
|
|
texID = albedoCounter;
|
|
albedoCounter++;
|
|
}
|
|
|
|
// Write albedo ID
|
|
fmatid.Write((ushort)texID);
|
|
|
|
// Get mesh alphas
|
|
ushort alphaID = 0xFFFF;
|
|
int alphaChannel = 3; // A
|
|
if (mat != null && mat.HasProperty("_TransparencyLM")) // will override _MainTex.a if present
|
|
{
|
|
var tex2 = mat.GetTexture("_TransparencyLM");
|
|
if (tex2 != null)
|
|
{
|
|
tex = tex2;
|
|
texPtr = tex.GetNativeTexturePtr();
|
|
alphaChannel = 0; // R
|
|
}
|
|
}
|
|
bool alphaMetaPass = (mat != null && mat.HasProperty("BAKERY_META_ALPHA_ENABLE"));
|
|
if (alphaMetaPass && ftRenderLightmap.lightProbeMode == ftRenderLightmap.LightProbeMode.Legacy)
|
|
{
|
|
alphaMetaPass = false;
|
|
DebugLogWarning("Skipping alpha meta pass in legacy light probe mode");
|
|
}
|
|
|
|
if (alphaMetaPass)
|
|
{
|
|
float alphaRef = 0.5f;
|
|
|
|
// Will use meta pass alpha
|
|
|
|
lmAlphaList.Add((System.IntPtr)0); // will be replaced after in-engine UVGBuffer part generation
|
|
if (nonDX11) lmAlphaListRAM.Add(new TexInput());
|
|
lmAlphaListTex.Add(null);
|
|
lmAlphaRefList.Add(alphaRef);
|
|
lmAlphaChannelList.Add(id); // channel is always 3; store LMID instead
|
|
|
|
texID = alphaCounter;
|
|
alphaCounter++;
|
|
alphaID = (ushort)texID;
|
|
|
|
hasMetaAlpha = true;
|
|
}
|
|
else if (tex != null)
|
|
{
|
|
var matTag = mat.GetTag("RenderType", true);
|
|
bool isCutout = matTag == "TransparentCutout";
|
|
if (isCutout || matTag == "Transparent" || matTag == "TreeLeaf") {
|
|
|
|
float alphaRef = 0.5f;
|
|
if (mat != null && mat.HasProperty("_Cutoff"))
|
|
{
|
|
alphaRef = mat.GetFloat("_Cutoff");
|
|
}
|
|
float opacity = 1.0f;
|
|
if (!isCutout && mat.HasProperty("_Color"))
|
|
{
|
|
opacity = mat.color.a;
|
|
}
|
|
// let constant alpha affect cutout theshold for alphablend materials
|
|
alphaRef = 1.0f - (1.0f - alphaRef) * opacity;
|
|
if (alphaRef > 1) alphaRef = 1;
|
|
|
|
// Using alpha texture directly
|
|
|
|
// allow same map instances with different threshold
|
|
List<int> texIDs;
|
|
if (!alphaMap.TryGetValue(texPtr, out texIDs))
|
|
{
|
|
alphaMap[texPtr] = texIDs = new List<int>();
|
|
|
|
lmAlphaList.Add(texPtr);
|
|
if (nonDX11) lmAlphaListRAM.Add(InputDataFromTex(tex));
|
|
lmAlphaListTex.Add(tex);
|
|
lmAlphaRefList.Add(alphaRef);
|
|
lmAlphaChannelList.Add(alphaChannel);
|
|
|
|
texIDs.Add(alphaCounter);
|
|
texID = alphaCounter;
|
|
alphaCounter++;
|
|
//Debug.Log("Alpha " + texID+": " + tex.name+" "+alphaRef);
|
|
alphaID = (ushort)texID;
|
|
}
|
|
else
|
|
{
|
|
int matchingInstance = -1;
|
|
for(int instance=0; instance<texIDs.Count; instance++)
|
|
{
|
|
texID = texIDs[instance];
|
|
if (Mathf.Abs(lmAlphaRefList[texID] - alphaRef) <= alphaInstanceThreshold)
|
|
{
|
|
if (lmAlphaChannelList[texID] == alphaChannel)
|
|
{
|
|
matchingInstance = instance;
|
|
alphaID = (ushort)texID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (matchingInstance < 0)
|
|
{
|
|
lmAlphaList.Add(texPtr);
|
|
if (nonDX11) lmAlphaListRAM.Add(InputDataFromTex(tex));
|
|
lmAlphaListTex.Add(tex);
|
|
lmAlphaRefList.Add(alphaRef);
|
|
lmAlphaChannelList.Add(alphaChannel);
|
|
|
|
texIDs.Add(alphaCounter);
|
|
texID = alphaCounter;
|
|
alphaCounter++;
|
|
//Debug.Log("Alpha " + texID+": " + tex.name+" "+alphaRef);
|
|
alphaID = (ushort)texID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
alphaIDs.Add(alphaID);
|
|
|
|
// Get mesh emissives
|
|
if (exportShaderColors)
|
|
{
|
|
for(int s=0; s<sceneCount; s++)
|
|
{
|
|
if (storages[s] == null) continue;
|
|
while(storages[s].hasEmissive.Count <= id) storages[s].hasEmissive.Add(true);
|
|
storages[s].hasEmissive[id] = true;
|
|
}
|
|
}
|
|
|
|
texID = -1;
|
|
tex = null;
|
|
if (mat!=null && mat.shaderKeywords.Contains("_EMISSION"))
|
|
{
|
|
if (mat.HasProperty("_EmissionMap")) tex = mat.GetTexture("_EmissionMap");
|
|
if (tex != null)
|
|
{
|
|
texPtr = tex.GetNativeTexturePtr();
|
|
if (texPtr == (IntPtr)0)
|
|
{
|
|
if ((tex as RenderTexture) != null)
|
|
{
|
|
Debug.LogError("RenderTexture " + tex.name + " cannot be used as emission (GetNativeTexturePtr returned null)");
|
|
tex = null;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Texture " + tex.name + " cannot be used as emission (GetNativeTexturePtr returned null)");
|
|
tex = null;
|
|
}
|
|
//DebugLogError("Null emission tex ptr");
|
|
}
|
|
}
|
|
if (tex == null && mat.HasProperty("_EmissionColor"))
|
|
{
|
|
// Create dummy 1px texture
|
|
var dummyTex = new Texture2D(1,1);
|
|
dummyPixelArray[0] = mat.GetColor("_EmissionColor");
|
|
dummyTex.SetPixels(dummyPixelArray);
|
|
dummyTex.Apply();
|
|
tex = dummyTex;
|
|
dummyTexList.Add(dummyTex);
|
|
texPtr = dummyTex.GetNativeTexturePtr();
|
|
if (texPtr == (IntPtr)0)
|
|
{
|
|
Debug.LogError("Failed to call GetNativeTexturePtr() on newly created texture");
|
|
texWrite = null;
|
|
//DebugLogError("Null dummy tex ptr");
|
|
}
|
|
}
|
|
if (!albedoMap.TryGetValue(texPtr, out texID))
|
|
{
|
|
lmAlbedoList.Add(texPtr);
|
|
lmAlbedoListTex.Add(tex);
|
|
albedoMap[texPtr] = albedoCounter;
|
|
texID = albedoCounter;
|
|
albedoCounter++;
|
|
}
|
|
for(int s=0; s<sceneCount; s++)
|
|
{
|
|
if (storages[s] == null) continue;
|
|
while(storages[s].hasEmissive.Count <= id) storages[s].hasEmissive.Add(false);
|
|
storages[s].hasEmissive[id] = true;
|
|
}
|
|
|
|
fmatide.Write((ushort)texID);
|
|
fmatideb.Write(mat.HasProperty("_EmissionColor") ? mat.GetColor("_EmissionColor").maxColorComponent : 1);
|
|
}
|
|
else
|
|
{
|
|
fmatide.Write((ushort)0xFFFF);
|
|
fmatideb.Write(0.0f);
|
|
}
|
|
|
|
if (isTerrain && uvgbHeightmap)
|
|
{
|
|
var hindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject);
|
|
//var htex = terrainObjectToHeightMap[hindex];
|
|
//texPtr = htex.GetNativeTexturePtr();
|
|
|
|
//heightmapList.Add(texPtr);
|
|
//heightmapListTex.Add(htex);
|
|
//heightmapListBounds.Add();
|
|
|
|
//texID = heightmapCounter;
|
|
//heightmapCounter++;
|
|
|
|
fmatidh.Write((ushort)hindex);//texID);
|
|
}
|
|
else
|
|
{
|
|
fmatidh.Write((ushort)0xFFFF);
|
|
}
|
|
}
|
|
|
|
// Update LMGroup bounds
|
|
if (modifyLightmapStorage)
|
|
{
|
|
if (lmBounds[id].size == Vector3.zero) {
|
|
lmBounds[id] = mr.bounds;
|
|
} else {
|
|
var b = lmBounds[id];
|
|
b.Encapsulate(mr.bounds);
|
|
lmBounds[id] = b;
|
|
}
|
|
|
|
#if USE_TERRAINS
|
|
if (isTerrain && exportTerrainAsHeightmap)
|
|
{
|
|
var b = lmBounds[id];
|
|
var hindex = terrainObjectList.IndexOf(obj.transform.parent.gameObject);
|
|
var terr = terrainObjectToActual[hindex];
|
|
var tb = terr.terrainData.bounds;
|
|
tb.center += terr.transform.position;
|
|
b.Encapsulate(tb);
|
|
lmBounds[id] = b;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
// Write empty albedo/alpha IDs for non-lightmapped
|
|
for(int k=0; k<m.subMeshCount; k++) {
|
|
fmatid.Write((ushort)0);
|
|
alphaIDs.Add(0xFFFF);
|
|
fmatide.Write((ushort)0xFFFF);
|
|
fmatideb.Write(0.0f);
|
|
fmatidh.Write((ushort)0xFFFF);
|
|
}
|
|
}
|
|
|
|
// Mark whole mesh that some submeshes use Meta Pass alpha
|
|
objsToWriteHasMetaAlpha.Add(hasMetaAlpha);
|
|
|
|
int currentVoffset = voffset;
|
|
voffset += objsToWriteVerticesPosW[i].Length;// m.vertexCount;
|
|
|
|
// Check if mesh is flipped
|
|
var ls = obj.transform.lossyScale;
|
|
bool isFlipped = Mathf.Sign(ls.x*ls.y*ls.z) < 0;
|
|
if (lmgroup != null && lmgroup.flipNormal) isFlipped = !isFlipped;
|
|
|
|
while(lmIndexArrays.Count <= id)
|
|
{
|
|
lmIndexArrays.Add(new List<int>());
|
|
lmLocalToGlobalIndices.Add(new List<int>());
|
|
lmVOffset.Add(0);
|
|
}
|
|
|
|
var mmr = GetValidRenderer(obj);
|
|
var castsShadows = mmr.shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off;
|
|
if (exportTerrainAsHeightmap && obj.name == "__ExportTerrain") castsShadows = false; // prevent exporting placeholder quads to ftrace
|
|
|
|
time = GetTime();
|
|
for(int k=0;k<m.subMeshCount;k++) {
|
|
// Export regular index buffer
|
|
//var indexCount = exportIB(fib, m, k, isFlipped, false, 0, null, 0);
|
|
var indexCount = exportIB(fib, inds[k], isFlipped, false, 0, null, 0);
|
|
|
|
bool submeshCastsShadows = castsShadows;
|
|
if (submeshCastsShadows)
|
|
{
|
|
var mats = mmr.sharedMaterials;
|
|
if (mats.Length > k)
|
|
{
|
|
if (mats[k] != null)
|
|
{
|
|
var matTag = mats[k].GetTag("RenderType", true);
|
|
if (matTag == "Transparent" || matTag == "TreeLeaf")
|
|
{
|
|
if (mats[k].HasProperty("_Color"))
|
|
{
|
|
if (mats[k].color.a < 0.5f)
|
|
{
|
|
if (!mats[k].HasProperty("BAKERY_META_ALPHA_ENABLE"))
|
|
{
|
|
submeshCastsShadows = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate tracing index buffer, write alpha IDs per triangle
|
|
if (submeshCastsShadows)
|
|
{
|
|
var alphaID = alphaIDs[(alphaIDs.Count - m.subMeshCount) + k];
|
|
|
|
if (lodLevel < 0)
|
|
{
|
|
// Export persistent IB
|
|
var indicesOpaqueArray = indicesOpaque;
|
|
var indicesTransparentArray = indicesTransparent;
|
|
var falphaidFile = falphaid;
|
|
exportIB32(indicesOpaqueArray, indicesTransparentArray, id>=0 ? lmIndexArrays[id] : null,
|
|
inds[k], isFlipped, currentVoffset, id>=0 ? lmVOffset[id] : 0, falphaidFile, alphaID);
|
|
}
|
|
else
|
|
{
|
|
// Export LOD IBs
|
|
var visList = objToLodLevelVisible[obj];
|
|
for(int vlod=0; vlod<visList.Count; vlod++)
|
|
{
|
|
int lod = visList[vlod];
|
|
var indicesOpaqueArray = data.indicesOpaqueLOD[lod];
|
|
var indicesTransparentArray = data.indicesTransparentLOD[lod];
|
|
var falphaidFile = falphaidlod[lod];
|
|
exportIB32(indicesOpaqueArray, indicesTransparentArray, id>=0 ? lmIndexArrays[id] : null,
|
|
inds[k], isFlipped, currentVoffset, id>=0 ? lmVOffset[id] : 0, falphaidFile, alphaID);
|
|
}
|
|
}
|
|
}
|
|
ioffset += indexCount;
|
|
}
|
|
ibTime += GetTime() - time;
|
|
|
|
if (id >= 0)
|
|
{
|
|
var vcount = objsToWriteVerticesPosW[i].Length;//m.vertexCount;
|
|
var remapArray = lmLocalToGlobalIndices[id];
|
|
var addition = lmVOffset[id];
|
|
for(int k=0; k<vcount; k++)
|
|
{
|
|
remapArray.Add(k + currentVoffset);
|
|
}
|
|
lmVOffset[id] += vcount;
|
|
}
|
|
}
|
|
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
DebugLogError("Error exporting scene - see console for details");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
Debug.LogError("Exception caught: " + e.ToString());
|
|
throw;
|
|
}
|
|
|
|
ProgressBarShow("Exporting scene - finishing objects...", 0.5f, false);
|
|
if (userCanceled)
|
|
{
|
|
CloseAllFiles();
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
yield return null;
|
|
|
|
try
|
|
{
|
|
// Write vertex buffers and update storage
|
|
Rect rc = new Rect();
|
|
var emptyVec4 = new Vector4(1,1,0,0);
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var m = GetSharedMesh(obj);
|
|
var lmgroup = objsToWriteGroup[i];
|
|
|
|
var id = lmgroup == null ? -1 : objsToWriteGroup[i].id;
|
|
|
|
BakeryLightMesh areaLight = obj.GetComponent<BakeryLightMesh>();
|
|
if (areaLight == null)
|
|
{
|
|
var areaIndex = temporaryAreaLightMeshList.IndexOf(obj);
|
|
if (areaIndex >= 0) areaLight = temporaryAreaLightMeshList2[areaIndex];
|
|
}
|
|
//var areaLight =
|
|
if (areaLight != null) id = areaLight.lmid;
|
|
|
|
var vertexBake = lmgroup != null ? (lmgroup.mode == BakeryLightmapGroup.ftLMGroupMode.Vertex) : false;
|
|
//var castsShadows = obj.GetComponent<Renderer>().shadowCastingMode != UnityEngine.Rendering.ShadowCastingMode.Off;
|
|
|
|
var holderObj = objsToWriteHolder[i];
|
|
if (holderObj != null)
|
|
{
|
|
if (!holderRect.TryGetValue(holderObj, out rc))
|
|
{
|
|
holderObj = null;
|
|
}
|
|
}
|
|
|
|
time = GetTime();
|
|
//var vertices = m.vertices;
|
|
//var normals = m.normals;
|
|
//var tangents = m.tangents;
|
|
var uv = objsToWriteVerticesUV[i];//m.uv;
|
|
var uv2 = objsToWriteVerticesUV2[i];//m.uv2;
|
|
if (uv2.Length == 0 && !vertexBake) uv2 = uv;//m.uv;
|
|
vbTimeRead += GetTime() - time;
|
|
|
|
var inds = objsToWriteIndices[i];
|
|
|
|
var time2 = GetTime();
|
|
time = time2;
|
|
|
|
// Transform UVs
|
|
var tformedPos = objsToWriteVerticesPosW[i];// new Vector3[vertices.Length];
|
|
var tformedNormals = objsToWriteVerticesNormalW[i];// new Vector3[normals.Length];
|
|
Vector4[] tformedTangents = null;
|
|
if (NeedsTangents(lmgroup, tangentSHLights))
|
|
{
|
|
tformedTangents = objsToWriteVerticesTangentW[i];
|
|
}
|
|
Vector2[] tformedUV2;
|
|
if (areaLight == null && !vertexBake)
|
|
{
|
|
tformedUV2 = holderObj == null ? uv2 : new Vector2[tformedPos.Length];
|
|
for(int t=0; t<tformedPos.Length; t++)
|
|
{
|
|
if (holderObj != null)
|
|
{
|
|
tformedUV2[t].x = uv2[t].x * rc.width + rc.x;
|
|
tformedUV2[t].y = uv2[t].y * rc.height + rc.y;
|
|
}
|
|
}
|
|
objsToWriteUVOverride.Add(null);
|
|
}
|
|
else if (vertexBake)
|
|
{
|
|
tformedUV2 = GenerateVertexBakeUVs(lmgroup.vertexCounter, tformedPos.Length, lmgroup.totalVertexCount);
|
|
lmgroup.vertexCounter += tformedPos.Length;
|
|
objsToWriteUVOverride.Add(tformedUV2);
|
|
}
|
|
else
|
|
{
|
|
tformedUV2 = uv;
|
|
objsToWriteUVOverride.Add(null);
|
|
}
|
|
|
|
if (id >= 0)
|
|
{
|
|
while(lmUVArrays.Count <= id)
|
|
{
|
|
lmUVArrays.Add(new List<float>());
|
|
}
|
|
var lmUVArray = lmUVArrays[id];
|
|
for(int k=0; k<tformedUV2.Length; k++)
|
|
{
|
|
lmUVArray.Add(tformedUV2[k].x);
|
|
lmUVArray.Add(tformedUV2[k].y);
|
|
}
|
|
}
|
|
|
|
if (objsToWriteHasMetaAlpha[i]) uv = tformedUV2; // objects using Meta Pass alpha use UV2 instead of UV1
|
|
|
|
exportVBFull(fvbfull, tformedPos, tformedNormals, tformedTangents, uv, tformedUV2);
|
|
vbTimeWriteFull += GetTime() - time;
|
|
time = GetTime();
|
|
//if (castsShadows)
|
|
//{
|
|
exportVBTrace(fvbtrace, m, tformedPos, tformedNormals);
|
|
vbTimeWriteT += GetTime() - time;
|
|
time = GetTime();
|
|
exportVBTraceTexAttribs(vbtraceTexPosNormalArray, vbtraceTexUVArray, tformedPos, tformedNormals, tformedUV2, id, vertexBake, obj);
|
|
vbTimeWriteT2 += GetTime() - time;
|
|
time = GetTime();
|
|
exportVBTraceUV0(fvbtraceUV0, uv, tformedPos.Length);
|
|
vbTimeWriteT3 += GetTime() - time;
|
|
time = GetTime();
|
|
//}
|
|
voffset += tformedPos.Length;
|
|
vbTimeWrite += GetTime() - time2;
|
|
|
|
|
|
// update storage
|
|
// also write seamfix.bin
|
|
var sceneID = sceneToID[obj.scene];
|
|
if (obj.name == "__ExportTerrain")
|
|
{
|
|
#if USE_TERRAINS
|
|
fseamfix.Write(false);
|
|
var index = terrainObjectList.IndexOf(obj.transform.parent.gameObject);
|
|
var terrain = terrainObjectToActual[index];
|
|
var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y);
|
|
if (!storages[sceneID].bakedRenderersTerrain.Contains(terrain))
|
|
{
|
|
if (modifyLightmapStorage)
|
|
{
|
|
storages[sceneID].bakedRenderersTerrain.Add(terrain);
|
|
storages[sceneID].bakedIDsTerrain.Add(CorrectLMGroupID(id, lmgroup, groupList));
|
|
storages[sceneID].bakedScaleOffsetTerrain.Add(scaleOffset);
|
|
}
|
|
}
|
|
objsToWriteScaleOffset.Add(scaleOffset);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
fseamfix.Write(true);
|
|
var scaleOffset = holderObj == null ? emptyVec4 : new Vector4(rc.width, rc.height, rc.x, rc.y);
|
|
if (modifyLightmapStorage)
|
|
{
|
|
bool vertexImplicit = false;
|
|
if (vertexBake)
|
|
{
|
|
if (lmgroup.isImplicit) vertexImplicit = true;
|
|
}
|
|
if (!vertexImplicit)
|
|
{
|
|
storages[sceneID].bakedRenderers.Add(GetValidRenderer(obj));
|
|
storages[sceneID].bakedIDs.Add(CorrectLMGroupID(id, lmgroup, groupList));
|
|
storages[sceneID].bakedScaleOffset.Add(scaleOffset);
|
|
storages[sceneID].bakedVertexOffset.Add(vertexBake ? (lmgroup.vertexCounter - tformedPos.Length) : -1);
|
|
storages[sceneID].bakedVertexColorMesh.Add(null);
|
|
}
|
|
}
|
|
objsToWriteScaleOffset.Add(scaleOffset);
|
|
}
|
|
}
|
|
|
|
// Write vbTraceTex
|
|
int numTraceVerts = vbtraceTexUVArray.Count/2;
|
|
for(int i=0; i<numTraceVerts; i++)
|
|
{
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6]);
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 1]);
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 2]);
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 3]);
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 4]);
|
|
fvbtraceTex.Write(vbtraceTexPosNormalArray[i * 6 + 5]);
|
|
|
|
fvbtraceTex.Write(vbtraceTexUVArray[i * 2]);
|
|
fvbtraceTex.Write(vbtraceTexUVArray[i * 2 + 1]);
|
|
}
|
|
|
|
// Write tracing index buffer
|
|
fib32.Write(indicesOpaque.Count); // firstAlphaTriangle
|
|
for(int i=0; i<indicesOpaque.Count; i++) fib32.Write(indicesOpaque[i]); // opaque triangles
|
|
for(int i=0; i<indicesTransparent.Count; i++) fib32.Write(indicesTransparent[i]); // alpha triangles
|
|
|
|
// Write scene LOD tracing index buffers
|
|
for(int lod=0; lod<sceneLodsUsed; lod++)
|
|
{
|
|
var indicesOpaqueArray = data.indicesOpaqueLOD[lod];
|
|
var indicesTransparentArray = data.indicesTransparentLOD[lod];
|
|
fib32lod[lod].Write(indicesOpaqueArray.Count);
|
|
for(int i=0; i<indicesOpaqueArray.Count; i++) fib32lod[lod].Write(indicesOpaqueArray[i]); // opaque triangles
|
|
for(int i=0; i<indicesTransparentArray.Count; i++) fib32lod[lod].Write(indicesTransparentArray[i]); // alpha triangles
|
|
}
|
|
|
|
DebugLogInfo("Wrote binaries in " + ((GetTime() - totalTime)/1000.0) + "s");
|
|
DebugLogInfo("VB read time " + (vbTimeRead/1000.0) + "s");
|
|
DebugLogInfo("VB write time " + (vbTimeWrite/1000.0) + "s");
|
|
DebugLogInfo("VB write time (full) " + (vbTimeWriteFull/1000.0) + "s");
|
|
DebugLogInfo("VB write time (trace) " + (vbTimeWriteT/1000.0) + "s");
|
|
DebugLogInfo("VB write time (trace tex) " + (vbTimeWriteT2/1000.0) + "s");
|
|
DebugLogInfo("VB write time (UV0) " + (vbTimeWriteT3/1000.0) + "s");
|
|
DebugLogInfo("IB time " + (ibTime/1000.0) + "s");
|
|
|
|
|
|
fscene.Write(objsToWrite.Count);
|
|
int meshID = 0;
|
|
foreach(var obj in objsToWrite) {
|
|
fscene.Write(meshID);
|
|
meshID++;
|
|
}
|
|
foreach(var obj in objsToWrite) {
|
|
fscene.Write(obj.name);
|
|
}
|
|
|
|
fscene.Close();
|
|
fmesh.Close();
|
|
flmid.Close();
|
|
fseamfix.Close();
|
|
fsurf.Close();
|
|
fmatid.Close();
|
|
fmatide.Close();
|
|
fmatideb.Close();
|
|
fmatidh.Close();
|
|
fvbfull.Close();
|
|
fvbtrace.Close();
|
|
fvbtraceTex.Close();
|
|
fvbtraceUV0.Close();
|
|
fib.Close();
|
|
fib32.Close();
|
|
falphaid.Close();
|
|
fhmaps.Close();
|
|
|
|
if (fib32lod != null)
|
|
{
|
|
for(int i=0; i<fib32lod.Length; i++) fib32lod[i].Close();
|
|
}
|
|
if (falphaidlod != null)
|
|
{
|
|
for(int i=0; i<falphaidlod.Length; i++) falphaidlod[i].Close();
|
|
}
|
|
|
|
if (modifyLightmapStorage)
|
|
{
|
|
for(int s=0; s<sceneCount; s++)
|
|
{
|
|
if (storages[s] == null) continue;
|
|
storages[s].bounds = lmBounds;
|
|
}
|
|
}
|
|
//}
|
|
|
|
startMsU = GetTime();
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
DebugLogError("Error exporting scene - see console for details");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
Debug.LogError("Exception caught: " + e.ToString());
|
|
throw;
|
|
}
|
|
|
|
if (exportShaderColors && renderTextures)
|
|
{
|
|
yield return null;
|
|
ProgressBarShow("Exporting scene - shaded surface colors...", 0.55f, false);
|
|
for(int g=0; g<groupList.Count; g++)
|
|
{
|
|
var str = storages[data.firstNonNullStorage];
|
|
if (str == null) Debug.LogError("storages[data.firstNonNullStorage] == null");
|
|
if (str.hasEmissive == null) Debug.LogError("storages[data.firstNonNullStorage].hasEmissive == null");
|
|
if (groupList[g] == null) Debug.LogError("group is null");
|
|
|
|
var hasEmissive = str.hasEmissive.Count > groupList[g].id && str.hasEmissive[groupList[g].id];
|
|
|
|
bool vertexBake = groupList[g].mode == BakeryLightmapGroup.ftLMGroupMode.Vertex;
|
|
|
|
int res = groupList[g].resolution;
|
|
if (vertexBake)
|
|
{
|
|
if (groupList[g].totalVertexCount == 0)
|
|
{
|
|
DebugLogError("Vertex lightmap group " + groupList[g].name + " has 0 static vertices. Make sure objects inside the group don't all have Scale In Lightmap == 0.");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
int atlasTexSize = (int)Mathf.Ceil(Mathf.Sqrt((float)groupList[g].totalVertexCount));
|
|
atlasTexSize = (int)Mathf.Ceil(atlasTexSize / (float)ftRenderLightmap.tileSize) * ftRenderLightmap.tileSize;
|
|
res = atlasTexSize;
|
|
}
|
|
|
|
var bakeWithNormalMaps = (groupList[g].renderDirMode == BakeryLightmapGroup.RenderDirMode.BakedNormalMaps) ?
|
|
true : (ftRenderLightmap.renderDirMode == ftRenderLightmap.RenderDirMode.BakedNormalMaps);
|
|
|
|
if (groupList[g].probes) bakeWithNormalMaps = false;
|
|
|
|
bool hasMetaAlpha = false;
|
|
|
|
ftUVGBufferGen.StartUVGBuffer(res, hasEmissive, bakeWithNormalMaps);
|
|
for(int i=0; i<objsToWrite.Count; i++)
|
|
{
|
|
var obj = objsToWrite[i];
|
|
var lmgroup = objsToWriteGroup[i];
|
|
if (lmgroup == null) continue;
|
|
if (lmgroup.id != groupList[g].id) continue;
|
|
if (obj.GetComponent<BakeryLightMesh>()) continue;
|
|
var bakedMesh = GetSharedMeshBaked(obj);
|
|
ftUVGBufferGen.RenderUVGBuffer(bakedMesh,
|
|
GetValidRenderer(obj),
|
|
objsToWriteScaleOffset[i],
|
|
obj.transform,
|
|
vertexBake,
|
|
objsToWriteUVOverride[i],
|
|
bakeWithNormalMaps && !exportTerrainAsHeightmap && obj.name == "__ExportTerrain",
|
|
objsToWriteHasMetaAlpha[i]);
|
|
|
|
if (objsToWriteHasMetaAlpha[i]) hasMetaAlpha = true;
|
|
}
|
|
ftUVGBufferGen.EndUVGBuffer();
|
|
|
|
var albedo = ftUVGBufferGen.texAlbedo;
|
|
var emissive = ftUVGBufferGen.texEmissive;
|
|
var normal = ftUVGBufferGen.texNormal;
|
|
var alpha = ftUVGBufferGen.texAlpha;
|
|
if (hasEmissive)
|
|
{
|
|
//albedo = ftUVGBufferGen.GetAlbedoWithoutEmissive(ftUVGBufferGen.texAlbedo, ftUVGBufferGen.texEmissive);
|
|
//if ((unityVersionMajor == 2017 && unityVersionMinor < 2) || unityVersionMajor < 2017)
|
|
//{
|
|
#if UNITY_2017_2_OR_NEWER
|
|
#else
|
|
// Unity before 2017.2: emissive packed to RGBM
|
|
// Unity after 2017.2: linear emissive
|
|
emissive = ftUVGBufferGen.DecodeFromRGBM(emissive);
|
|
#endif
|
|
//}
|
|
if (ftRenderLightmap.hackEmissiveBoost != 1.0f)
|
|
{
|
|
ftUVGBufferGen.Multiply(emissive, ftRenderLightmap.hackEmissiveBoost);
|
|
}
|
|
//if (!vertexBake) ftUVGBufferGen.Dilate(emissive);
|
|
}
|
|
if (!vertexBake) ftUVGBufferGen.Dilate(albedo);
|
|
|
|
if (isDX11)
|
|
{
|
|
SaveGBufferMap(albedo.GetNativeTexturePtr(),
|
|
scenePath + "/uvalbedo_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
GL.IssuePluginEvent(5);
|
|
yield return null;
|
|
}
|
|
else
|
|
{
|
|
var bytes = InputBytesFromTex(albedo);
|
|
SaveGBufferMapFromRAM(bytes, bytes.Length,
|
|
scenePath + "/uvalbedo_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
}
|
|
DestroyImmediate(albedo);
|
|
//if (g==2) storage.debugTex = emissive;
|
|
|
|
if (hasEmissive)
|
|
{
|
|
if (isDX11)
|
|
{
|
|
SaveGBufferMap(emissive.GetNativeTexturePtr(),
|
|
scenePath + "/uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
GL.IssuePluginEvent(5);
|
|
yield return null;
|
|
}
|
|
else
|
|
{
|
|
var bytes = InputBytesFromTex(emissive, TexInputType.HalfColor);
|
|
SaveGBufferMapFromRAM(bytes, bytes.Length,
|
|
scenePath + "/uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
}
|
|
|
|
if (ftRenderLightmap.clientMode) ftClient.serverFileList.Add("uvemissive_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"));
|
|
DestroyImmediate(emissive);
|
|
}
|
|
|
|
if (bakeWithNormalMaps)
|
|
{
|
|
if (isDX11)
|
|
{
|
|
SaveGBufferMap(normal.GetNativeTexturePtr(),
|
|
scenePath + "/uvnormal_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
GL.IssuePluginEvent(5);
|
|
yield return null;
|
|
}
|
|
else
|
|
{
|
|
var bytes = InputBytesFromTex(normal);
|
|
SaveGBufferMapFromRAM(bytes, bytes.Length,
|
|
scenePath + "/uvnormal_" + groupList[g].name + (ftRenderLightmap.compressedGBuffer ? ".lz4" : ".dds"),
|
|
ftRenderLightmap.compressedGBuffer);
|
|
}
|
|
DestroyImmediate(normal);
|
|
}
|
|
|
|
if (hasMetaAlpha)
|
|
{
|
|
for(int i=0; i<lmAlphaListTex.Count; i++)
|
|
{
|
|
if (lmAlphaListTex[i] == null) // must be meta alpha
|
|
{
|
|
int alphaLMID = lmAlphaChannelList[i];
|
|
if (alphaLMID != groupList[g].id) continue; // must link to the same LMID
|
|
|
|
// Patch with the new alpha texture
|
|
lmAlphaListTex[i] = alpha;
|
|
lmAlphaList[i] = alpha.GetNativeTexturePtr();
|
|
if (nonDX11) lmAlphaListRAM[i] = InputDataFromTex(alpha);
|
|
lmAlphaChannelList[i] = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ProgressBarShow(exportShaderColors ? "Exporting scene - alpha buffer..." : "Exporting scene - UV GBuffer and alpha buffer...", 0.55f, false);
|
|
if (userCanceled)
|
|
{
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
yield return null;
|
|
|
|
InitShaders();
|
|
LoadScene(scenePath);
|
|
|
|
// Force load textures to VRAM
|
|
var forceRt = new RenderTexture(1, 1, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
|
var forceTex = new Texture2D(1, 1, TextureFormat.ARGB32, false, false);
|
|
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
|
|
{
|
|
if (!exportShaderColors)
|
|
{
|
|
for(int i=0; i<lmAlbedoListTex.Count; i++)
|
|
{
|
|
Graphics.Blit(lmAlbedoListTex[i] as Texture2D, forceRt);
|
|
Graphics.SetRenderTarget(forceRt);
|
|
forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true);
|
|
forceTex.Apply();
|
|
lmAlbedoList[i] = lmAlbedoListTex[i].GetNativeTexturePtr();
|
|
}
|
|
}
|
|
for(int i=0; i<lmAlphaListTex.Count; i++)
|
|
{
|
|
Graphics.Blit(lmAlphaListTex[i] as Texture2D, forceRt);
|
|
Graphics.SetRenderTarget(forceRt);
|
|
forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true);
|
|
forceTex.Apply();
|
|
lmAlphaList[i] = lmAlphaListTex[i].GetNativeTexturePtr();
|
|
}
|
|
}
|
|
|
|
if (exportShaderColors)
|
|
{
|
|
#if USE_TERRAINS
|
|
if (terrainObjectToActual.Count > 0)
|
|
{
|
|
if (isDX11)
|
|
{
|
|
var terrainObjectToHeightMapPtr = new IntPtr[terrainObjectToHeightMap.Count];
|
|
for(int i=0; i<terrainObjectToHeightMap.Count; i++)
|
|
{
|
|
Graphics.Blit(terrainObjectToHeightMap[i] as Texture2D, forceRt);
|
|
Graphics.SetRenderTarget(forceRt);
|
|
forceTex.ReadPixels(new Rect(0,0,1, 1), 0, 0, true);
|
|
forceTex.Apply();
|
|
terrainObjectToHeightMapPtr[i] = terrainObjectToHeightMap[i].GetNativeTexturePtr();
|
|
}
|
|
SetAlbedos(terrainObjectToHeightMap.Count, terrainObjectToHeightMapPtr);
|
|
int cerr = CopyAlbedos();
|
|
if (cerr != 0)
|
|
{
|
|
DebugLogError("Failed to copy textures (" + cerr + ")");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int cerr = CopyHeightmapsFromRAM(terrainObjectToHeightMapRAM.Count, terrainObjectToHeightMapRAM.ToArray());
|
|
if (cerr != 0)
|
|
{
|
|
DebugLogError("Failed to copy textures from RAM (" + cerr + ")");
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SetAlbedos(0, null);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetAlbedos(lmAlbedoList.Count, lmAlbedoList.ToArray());
|
|
}
|
|
|
|
var lmAlphaListRAMHandle = new GCHandle();
|
|
if (isDX11)
|
|
{
|
|
// Pass texture objects directly
|
|
SetAlphas(lmAlphaList.Count, lmAlphaList.ToArray(), lmAlphaRefList.ToArray(), lmAlphaChannelList.ToArray(), sceneLodsUsed, flipAlpha);
|
|
}
|
|
else
|
|
{
|
|
// Pass via RAM
|
|
var lmAlphaListRAMArray = lmAlphaListRAM.ToArray();
|
|
lmAlphaListRAMHandle = GCHandle.Alloc(lmAlphaListRAMArray, GCHandleType.Pinned);
|
|
SetAlphasFromRAM(lmAlphaListRAM.Count, lmAlphaListRAMHandle.AddrOfPinnedObject(), lmAlphaRefList.ToArray(), lmAlphaChannelList.ToArray(), sceneLodsUsed, flipAlpha);
|
|
}
|
|
|
|
GL.IssuePluginEvent(6); // render alpha buffer
|
|
int uerr = 0;
|
|
while(uerr == 0)
|
|
{
|
|
uerr = GetABGErrorCode();
|
|
yield return null;
|
|
}
|
|
|
|
if (nonDX11) lmAlphaListRAMHandle.Free();
|
|
|
|
/*yield return new WaitForEndOfFrame();
|
|
int uerr = ftGenerateAlphaBuffer();*/
|
|
if (uerr != 0 && uerr != 99999)
|
|
{
|
|
DebugLogError("ftGenerateAlphaBuffer error: " + uerr);
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
|
|
DestroyImmediate(forceTex);
|
|
forceRt.Release();
|
|
|
|
if (!renderTextures)
|
|
{
|
|
ProgressBarEnd(true);//false);
|
|
yield break;
|
|
}
|
|
|
|
//ProgressBarShow("Exporting scene - UV GBuffer...", 0.8f);
|
|
//if (userCanceled) yield break;
|
|
//yield return null;
|
|
|
|
//GL.IssuePluginEvent(1); // render UV GBuffer
|
|
//yield return new WaitForEndOfFrame();
|
|
|
|
SetFixPos(false);//true); // do it manually
|
|
SetCompression(ftRenderLightmap.compressedGBuffer);
|
|
|
|
if (!exportShaderColors)
|
|
{
|
|
uerr = ftRenderUVGBuffer();
|
|
if (uerr != 0)
|
|
{
|
|
DebugLogError("ftRenderUVGBuffer error: " + uerr);
|
|
CloseAllFiles();
|
|
userCanceled = true;
|
|
ProgressBarEnd(true);
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
ms = GetTime();
|
|
DebugLogInfo("UVGB/fixPos/alpha time: " + ((ms - startMsU) / 1000.0) + " seconds");
|
|
|
|
ProgressBarEnd(true);
|
|
|
|
DebugLogInfo("Scene export finished");
|
|
}
|
|
|
|
int countChildrenFlat(Transform tform)
|
|
{
|
|
int count = 1;
|
|
foreach(Transform t in tform)
|
|
{
|
|
count += countChildrenFlat(t);
|
|
}
|
|
return count;
|
|
}
|
|
}
|
|
|
|
#endif
|