Skip to content

Instantly share code, notes, and snippets.

@richdrummer33
Created November 7, 2023 22:21
Show Gist options
  • Save richdrummer33/2951dbead589c28e83ec1335fd920f3d to your computer and use it in GitHub Desktop.
Save richdrummer33/2951dbead589c28e83ec1335fd920f3d to your computer and use it in GitHub Desktop.
Multi-height light probe generator for Unity
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Collections;
using UnityEngine.AI;
/// <summary>
/// Richard Beare modified original code from:
/// https://github.com/gampixi/auto-light-probes/blob/master/Assets/Editor/LightProbes/LightProbePlacement.cs
/// Uses navmesh to place light probes
/// Extended this to include min dist and height fields, and better evaluate floor heights
/// </summary>
public class LightProbePlacement : EditorWindow {
static float progress = 0.0f;
static string current = "Hello";
static bool working = false;
float mergeDistance = 1;
float maxDistance = 3f;
float minHeightOffset = 0.5f;
float maxHeight = 3f;
int heightSubdivisions = 2;
float yOffset = 10f;
GameObject probeObject;
bool disableMerging;
static bool isProcessing = false;
static string status = "Ready";
[MenuItem("Tools/Generate Light Probes")]
static void Init()
{
Debug.Log("Light Probes Enhanced");
EditorWindow window = GetWindow<LightProbePlacement>(true, "Light Probes Enhanced", true);
window.Show();
if (window != null)
{
Debug.Log("Window opened successfully.");
window.Show();
}
else
{
Debug.Log("Failed to open window.");
}
}
void PlaceProbes()
{
if (probeObject == null)
{
EditorUtility.DisplayDialog("Error", "Probe object not set", "OK");
return;
}
LightProbeGroup lightProbeGroup = probeObject.GetComponent<LightProbeGroup>();
if (lightProbeGroup == null)
{
EditorUtility.DisplayDialog("Error", "Probe object does not have a Light Probe Group attached to it", "OK");
return;
}
isProcessing = true;
status = "Triangulating NavMesh...";
progress = 0.1f;
EditorUtility.DisplayProgressBar("Generating Probes", status, progress);
UnityEngine.AI.NavMeshTriangulation navMesh = UnityEngine.AI.NavMesh.CalculateTriangulation();
List<Vector3> probePositions = new List<Vector3>();
Bounds bounds = new Bounds(probeObject.transform.position, Vector3.zero);
foreach (Vector3 vertex in navMesh.vertices)
{
bounds.Encapsulate(vertex);
}
for (float x = bounds.min.x; x < bounds.max.x; x += maxDistance)
{
for (float z = bounds.min.z; z < bounds.max.z; z += maxDistance)
{
Vector3 probePosition = new Vector3(x, bounds.center.y + yOffset, z);
// Set proper height
Vector3 samplePosition = probePosition + Vector3.up * 10f;
NavMeshHit hit;
if (NavMesh.SamplePosition(probePosition, out hit, yOffset * 2 , NavMesh.AllAreas))
{
samplePosition = hit.position;
float navMeshHeight = samplePosition.y;
probePosition.y = navMeshHeight;
}
// Multi-level
float stepHeight = maxHeight / heightSubdivisions;
for (int i = 0; i < heightSubdivisions; i++)
{
probePositions.Add
(
probePosition
+ i * stepHeight * Vector3.up
+ minHeightOffset * Vector3.up
);
}
}
}
lightProbeGroup.probePositions = probePositions.ToArray();
status = "Probes Generated";
EditorUtility.DisplayProgressBar("Generating Probes", status, 1f);
isProcessing = false;
EditorUtility.ClearProgressBar();
}
void OnGUI() {
if(GUILayout.Button("Generate probes"))
{
PlaceProbes ();
}
maxDistance = EditorGUILayout.FloatField("Max space between probes", maxDistance);
mergeDistance = EditorGUILayout.FloatField ("Vector merge distance",mergeDistance);
maxHeight = EditorGUILayout.FloatField("Max probe height", maxHeight);
heightSubdivisions = EditorGUILayout.IntField("Number of height subdivisions", heightSubdivisions);
disableMerging = EditorGUILayout.Toggle ("Disable merging", disableMerging);
probeObject = (GameObject)EditorGUILayout.ObjectField ("Probe GameObject" , probeObject, typeof(GameObject), true);
EditorGUILayout.LabelField ("This script will automatically generate light probe positions based on the current navmesh.");
EditorGUILayout.LabelField ("Please make sure that you have generated a navmesh before using the script.");
EditorGUILayout.LabelField ("If your navmesh is very large or complex, try using 'Disable Merging' to tremendously speed up the process. Keep in mind this may produce more probes than necessary, which may negatively impact performance.");
if(working) {
EditorUtility.DisplayProgressBar ("Generating probes", current, progress);
} else {
EditorUtility.ClearProgressBar ();
}
}
void OnInspectorUpdate() {
Repaint();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment