Skip to content

Commit

Permalink
Fixed PrefabLightmapData for BeatSaber after change to Unity 2019 (#24)
Browse files Browse the repository at this point in the history
* Fixed PrefabLightmapData for BeatSaber after change to Unity 2019

* Fixed lightmap reset on platform change

* Removed unncessary usings and added comments

* Adjust code style

Co-authored-by: affederaffe <[email protected]>
  • Loading branch information
Kylon99 and affederaffe authored Nov 16, 2022
1 parent b8412aa commit fcf4c21
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 50 deletions.
150 changes: 124 additions & 26 deletions Plugin/CustomFloorPlugin/Behaviours/PrefabLightmapData.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;

using CustomFloorPlugin.Interfaces;

using UnityEngine;

using Zenject;
Expand All @@ -10,48 +12,144 @@
// ReSharper disable once CheckNamespace
namespace CustomFloorPlugin
{
public class PrefabLightmapData : MonoBehaviour, INotifyPlatformEnabled
public struct RendererInfo
{
public Renderer Renderer;
public int LightmapIndex;
public Vector4 LightmapOffsetScale;
}

public struct LightInfo
{
public Light Light;
public int LightmapBaketype;
public int MixedLightingMode;
}

/// <summary>
/// Enables the light mapping data saved in this PrefabLightmapData
/// Adapted from https://github.com/Ayfel/PrefabLightmapping for use in Beat Saber
/// </summary>
public class PrefabLightmapData : MonoBehaviour, INotifyPlatformEnabled, INotifyPlatformDisabled
{
// ReSharper disable InconsistentNaming
public Renderer[]? m_Renderers;
public Vector4[]? m_LightmapOffsetScales;
public Texture2D[]? m_Lightmaps;
// ReSharper restore InconsistentNaming
// This version of Unity can't reconstruct structs or classes no matter how much you mark
// it up with SerializeField or Serializable. RendererInfo and LightInfo has to be expanded out.
// Is this because of Unity's use of a flat-JSON serializer?! WTF

public Renderer[]? renderInfoRenderer;
public int[]? renderInfoLightmapIndex;
public Vector4[]? renderInfoLightmapOffsetScale;

public Texture2D[]? lightmaps;
public Texture2D[]? lightmapsDir;
public Texture2D[]? shadowMasks;

public Light[]? lightInfoLight;
public int[]? lightInfoLightmapBakeType;
public int[]? lightInfoMixedLightingMode;

private LightmapData[]? _oldLightmaps;

public void PlatformEnabled(DiContainer container)
{
enabled = m_Renderers is not null && m_LightmapOffsetScales is not null && m_Lightmaps is not null && m_Renderers.Length > 0 &&
m_Renderers.Length != m_LightmapOffsetScales.Length && m_Renderers.Length != m_Lightmaps.Length &&
m_LightmapOffsetScales.Length != m_Lightmaps.Length &&
m_Renderers[m_Renderers.Length - 1].lightmapIndex >= LightmapSettings.lightmaps.Length;
Init();
}

public void PlatformDisabled()
{
if (_oldLightmaps is null)
return;

// Restore the old lightmaps
LightmapSettings.lightmaps = _oldLightmaps;
}

public void Update()
private void Init()
{
LightmapData[] lightmapData = LightmapSettings.lightmaps;
LightmapData[] combinedLightmaps = new LightmapData[m_Lightmaps!.Length + lightmapData.Length];
// Perform an exhaustive check to see if we have enough of the baked lightmap data to proceed
// This data is supplied when the editor script is run to bake the lightmaps data into the platform
if (renderInfoRenderer is null || renderInfoRenderer.Length == 0 ||
renderInfoLightmapIndex is null || renderInfoLightmapOffsetScale is null ||
lightInfoLight is null || lightInfoLightmapBakeType is null || lightInfoMixedLightingMode is null ||
lightmaps is null || lightmapsDir is null || shadowMasks is null)
return;

Array.Copy(lightmapData, combinedLightmaps, lightmapData.Length);
for (int i = 0; i < m_Lightmaps.Length; i++)
// Save old LightmapData for restoration
_oldLightmaps = LightmapSettings.lightmaps.Clone() as LightmapData[] ?? Array.Empty<LightmapData>();

// Create a new combined LightmapData for all recorded lightmaps with their index
LightmapData[] newLightmaps = lightmaps.Select((_, i) => new LightmapData
{
combinedLightmaps[lightmapData.Length + i] = new LightmapData
{
lightmapColor = m_Lightmaps[i]
};
}
lightmapColor = lightmaps[i],
lightmapDir = lightmapsDir[i],
shadowMask = shadowMasks[i]
}).ToArray();

ApplyRendererInfo(m_Renderers!, m_LightmapOffsetScales!, lightmapData.Length);
LightmapData[] combinedLightmaps = LightmapSettings.lightmaps.Concat(newLightmaps).ToArray();

// Determine if directional lighting is used
bool directional = lightmapsDir!.All(static t => t != null);
LightmapSettings.lightmapsMode = lightmapsDir.Length == lightmaps.Length && directional ? LightmapsMode.CombinedDirectional : LightmapsMode.NonDirectional;

// Apply lighting maps to the renderers
RendererInfo[] rendererInfo = renderInfoRenderer.Select((r, i) => new RendererInfo // Use the struct from the original code in case future versions will succeed
{
Renderer = r,
LightmapIndex = renderInfoLightmapIndex[i] + _oldLightmaps.Length,
LightmapOffsetScale = renderInfoLightmapOffsetScale[i]
}).ToArray();
ApplyRendererInfo(rendererInfo);

LightInfo[] lightInfo = lightInfoLight.Select((l, i) => new LightInfo // Use the struct from the original code in case future versions will succeed
{
Light = l,
LightmapBaketype = lightInfoLightmapBakeType[i],
MixedLightingMode = lightInfoMixedLightingMode[i]
}).ToArray();
ApplyLightInfo(lightInfo);

// Finally set the new light maps to the global light settings
LightmapSettings.lightmaps = combinedLightmaps;
}

private static void ApplyRendererInfo(IReadOnlyList<Renderer> renderers, IReadOnlyList<Vector4> lightmapOffsetScales, int lightmapIndexOffset)
/// <summary>
/// Applies the light map data to the renderer associated in the given structs
/// </summary>
private static void ApplyRendererInfo(IEnumerable<RendererInfo> rendererInfos)
{
foreach (RendererInfo rendererInfo in rendererInfos)
{
rendererInfo.Renderer.lightmapIndex = rendererInfo.LightmapIndex;
rendererInfo.Renderer.lightmapScaleOffset = rendererInfo.LightmapOffsetScale;

// Find common shaders
foreach (Material? sharedMaterial in rendererInfo.Renderer.sharedMaterials.Where(static sm => sm != null))
{
Shader? commonShader = Shader.Find(sharedMaterial.shader!.name);
if (commonShader is not null)
sharedMaterial.shader = commonShader;
}

}
}

/// <summary>
/// Create a new LightBakingOutput for the given light and apply the associated light map info in the given structs
/// </summary>
private static void ApplyLightInfo(IEnumerable<LightInfo> lightInfos)
{
for (int i = 0; i < renderers.Count; i++)
foreach (LightInfo lightInfo in lightInfos)
{
Renderer renderer = renderers[i];
renderer.lightmapIndex = i + lightmapIndexOffset;
renderer.lightmapScaleOffset = lightmapOffsetScales[i];
LightBakingOutput bakingOutput = new()
{
isBaked = true,
lightmapBakeType = (LightmapBakeType)lightInfo.LightmapBaketype,
mixedLightingMode = (MixedLightingMode)lightInfo.MixedLightingMode
};

lightInfo.Light.bakingOutput = bakingOutput;
}
}

}
}
Binary file modified Unity/CustomPlatforms/Assets/Scripts/CustomFloorPlugin.dll
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
using System.Collections.Generic;
using System.Linq;

using CustomFloorPlugin;

using UnityEditor;
using UnityEngine;


/// <summary>
/// Saves the light mapping data into all the PrefabLightmapData components
/// Adapted from https://github.com/Ayfel/PrefabLightmapping for use in Beat Saber
/// </summary>
public class PrefabLightmapDataEditor : MonoBehaviour
{
[MenuItem("Assets/Bake Prefab Lightmaps")]
Expand All @@ -17,41 +21,81 @@ public static void GenerateLightmapInfo()
return;
}

Lightmapping.Bake();
// Do not auto bake whenever this function is run since the user will manually bake
// Lightmapping.Bake();

PrefabLightmapData[] prefabs = FindObjectsOfType<PrefabLightmapData>();

foreach (PrefabLightmapData instance in prefabs)
{
GameObject gameObject = instance.gameObject;
List<Renderer> renderers = new List<Renderer>();
List<Vector4> lightmapOffsetScales = new List<Vector4>();
List<Texture2D> lightmaps = new List<Texture2D>();
var gameObject = instance.gameObject;
var rendererInfos = new List<PrefabLightmapData.RendererInfo>();
var lightmaps = new List<Texture2D>();
var lightmapsDir = new List<Texture2D>();
var shadowMasks = new List<Texture2D>();
var lightsInfos = new List<PrefabLightmapData.LightInfo>();


GenerateLightmapInfo(gameObject, rendererInfos, lightmaps, lightmapsDir, shadowMasks, lightsInfos);

GenerateLightmapInfo(gameObject, renderers, lightmapOffsetScales, lightmaps);
instance.renderInfoRenderer = rendererInfos.Select(r => r.renderer).ToArray();
instance.renderInfoLightmapIndex = rendererInfos.Select(r => r.lightmapIndex).ToArray();
instance.renderInfoLightmapOffsetScale = rendererInfos.Select(r => r.lightmapOffsetScale).ToArray();

// ReSharper disable InconsistentNaming
instance.m_Renderers = renderers.ToArray();
instance.m_LightmapOffsetScales = lightmapOffsetScales.ToArray();
instance.m_Lightmaps = lightmaps.ToArray();
// ReSharper restore InconsistentNaming
instance.lightmaps = lightmaps.ToArray();
instance.lightmapsDir = lightmapsDir.ToArray();
instance.shadowMasks = shadowMasks.ToArray();

instance.lightInfoLight = lightsInfos.Select(l => l.light).ToArray();
instance.lightInfoLightmapBakeType = lightsInfos.Select(l => l.lightmapBaketype).ToArray();
instance.lightInfoMixedLightingMode = lightsInfos.Select(l => l.mixedLightingMode).ToArray();

var targetPrefab = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
if (targetPrefab != null)
if (targetPrefab is not null)
PrefabUtility.ReplacePrefab(gameObject, targetPrefab);
}
}

static void GenerateLightmapInfo(GameObject root, List<Renderer> rendererList, List<Vector4> lightmapOffsetScaleList, List<Texture2D> lightmaps)
private static void GenerateLightmapInfo(GameObject root, List<PrefabLightmapData.RendererInfo> rendererInfos, List<Texture2D> lightmaps, List<Texture2D> lightmapsDir, List<Texture2D> shadowMasks, List<PrefabLightmapData.LightInfo> lightsInfo)
{
foreach (MeshRenderer renderer in root.GetComponentsInChildren<MeshRenderer>())
{
if (renderer.lightmapIndex != -1)
{
Texture2D lightmap = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapColor;
rendererList.Add(renderer);
lightmapOffsetScaleList.Add(renderer.lightmapScaleOffset);
lightmaps.Add(lightmap);
PrefabLightmapData.RendererInfo info = new PrefabLightmapData.RendererInfo();
info.renderer = renderer;
if (renderer.lightmapScaleOffset != Vector4.zero)
{
//1ibrium's pointed out this issue : https://docs.unity3d.com/ScriptReference/Renderer-lightmapIndex.html
if (renderer.lightmapIndex < 0 || renderer.lightmapIndex == 0xFFFE) continue;
info.lightmapOffsetScale = renderer.lightmapScaleOffset;

Texture2D lightmap = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapColor;
Texture2D lightmapDir = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapDir;
Texture2D shadowMask = LightmapSettings.lightmaps[renderer.lightmapIndex].shadowMask;

info.lightmapIndex = lightmaps.IndexOf(lightmap);
if (info.lightmapIndex == -1)
{
info.lightmapIndex = lightmaps.Count;
lightmaps.Add(lightmap);
lightmapsDir.Add(lightmapDir);
shadowMasks.Add(shadowMask);
}

rendererInfos.Add(info);
}

var lights = root.GetComponentsInChildren<Light>(true);

foreach (Light l in lights)
{
PrefabLightmapData.LightInfo lightInfo = new PrefabLightmapData.LightInfo();
lightInfo.light = l;
lightInfo.lightmapBakeType = (int)l.lightmapBakeType;
lightInfo.mixedLightingMode = (int)UnityEditor.LightmapEditorSettings.mixedBakeMode;
lightsInfo.Add(lightInfo);
}
}
}
}
Expand Down
18 changes: 12 additions & 6 deletions Unity/Script/CustomFloorPlugin/PrefabLightmapData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ namespace CustomFloorPlugin
{
public class PrefabLightmapData : MonoBehaviour
{
// ReSharper disable InconsistentNaming
public Renderer[]? m_Renderers;
public Vector4[]? m_LightmapOffsetScales;
public Texture2D[]? m_Lightmaps;
// ReSharper restore InconsistentNaming
public Renderer[]? renderInfoRenderer;
public int[]? renderInfoLightmapIndex;
public Vector4[]? renderInfoLightmapOffsetScale;

public Texture2D[]? lightmaps;
public Texture2D[]? lightmapsDir;
public Texture2D[]? shadowMasks;

public Light[]? lightInfoLight;
public int[]? lightInfoLightmapBakeType;
public int[]? lightInfoMixedLightingMode;
}
}
}

0 comments on commit fcf4c21

Please sign in to comment.