Blog

[TUTORIAL] – Automatically Create Light Probes

Ok, so I ended up tweaking and bug fixing this script a bit more, but I feel it is ready to post. Please keep in mind this isn’t thoroughly tested so there might still be bugs in it.

How to use:

  1. First and foremost this script assumes that you have an empty gameobject in your scene named “_LightProbes”, and that it has a light probe group component attached to it. If you do not, then either make an empty game object called “_LightProbes” and attach a light probe component, or edit the script to point to your game object where your lightprobes are attached.
  2. Save this code in a file called “CreateLightProbes.cs”. Place this file in your projects Editor folder. If you do not have one, create a folder called “Editor”. A “Custom” menu tab will appear at the top of Unity once you place this script in the Editor folder.
  3. Select meshes that you would like light probes to appear on. In our setup we have flat ground planes, as this works the best. This will not work for caves or enclosed areas!
  4. Make sure that the meshes have a collider attached to them. Mesh colliders work the best!
  5. Set the spacing and secondary height values in the UI. The spacing is how far apart on the X and Z axis the probes will be. After it places a 2d grid of probes it will duplicate those and move them upwards to form a volume. The secondary spacing is how high up to move them.
  6. Select the objects you want to generate light probes for and click Create Light Probes on Selected Objects and you’re done! Rebuild the probe lighting from the Lightmapping tab to see the new probes.

Code:

// --------------------------------------------------
// Copyright (C) 2013 Ghost Mantis Games LLC
// This software is provided "as is", withouth warranty of any kind.
// This software is provided exclusively to you free of charge.
// Ghost Mantis Games LLC does not make and hereby disclaims any warranties
// of any kind. By using this software you agree to these terms.
// --------------------------------------------------

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class CreateLightProbes : EditorWindow {
	float Spacing = 2.0f;
	float SecondHeight = 4.0f;

	[MenuItem("Custom/Create Light Probes")]

	static void Init(){
		// Get existing open window or if none, make a new one:
		CreateLightProbes window = (CreateLightProbes)EditorWindow.GetWindow(typeof(CreateLightProbes), true, "Create Light Probes");
		window.position = new Rect((Screen.width / 2) - 125, Screen.height / 2 + 85, 300, 80);
		window.Show();
	}

	void OnGUI(){
		GUILayout.Label ("Creation:", EditorStyles.boldLabel);
		Spacing = EditorGUILayout.FloatField("Spacing:", Spacing);
		SecondHeight = EditorGUILayout.FloatField("Secondary Height:", SecondHeight);

		// Clamp values
		if(Spacing < 0.1f) Spacing = 0.1f;
		if(SecondHeight < 0.1f) SecondHeight = 0.1f;

		if(GUILayout.Button("Create Light Probes On Selected Objects")){
			Bounds();
		}
	}

	void Bounds(){
		// Get selection
		GameObject[] Select = Selection.gameObjects;
		if(Select.Length < 1) return;

		// Get total bounds for selected objects
		float minX = 0.0f;
		float minY = 0.0f;
		float minZ = 0.0f;
		float maxX = 0.0f;
		float maxY = 0.0f;
		float maxZ = 0.0f;
		for(int i=0; i<Select.Length; i++){
			// First check that mesh has a collider attached to it
			// if it doesn't then we can't raycast so we should ignore this object
			Collider col = Select[i].GetComponent();
			if(col == null) continue;

			// Get renderer component attached to object
			// If there is no renderer attached then we ignore this object
			Renderer renderer = Select[i].GetComponent();
			if(renderer == null) continue;

			// Get the renderer bounds
			Bounds bbox = renderer.bounds;

			// Update total bounds
			if(bbox.min.x < minX) minX = bbox.min.x;
			if(bbox.min.y < minY) minY = bbox.min.y;
			if(bbox.min.z < minZ) minZ = bbox.min.z;
			if(bbox.max.x > maxX) maxX = bbox.max.x;
			if(bbox.max.y > maxY) maxY = bbox.max.y;
			if(bbox.max.z > maxZ) maxZ = bbox.max.z;
		}

		// Now go through in a grid and attempt to place a light probe using raycasting
		float xCount = minX;
		float zCount = minZ;
		List VertPositions = new List();
		for(int z=0; z<maxZ; z++){
			for(int x=0; x<maxX; x++){
				// Raycast downwards through each selected object and
				// attempt to find one that we are over
				for(int j=0; j<Select.Length; j++){
					Collider col = Select[j].GetComponent();

					RaycastHit hit;
					Ray ray = new Ray();
					ray.origin = new Vector3(xCount, maxY, zCount);
					ray.direction = -Vector3.up;
					if(col.Raycast(ray, out hit, (maxY-minY)*2)){
						VertPositions.Add(hit.point + new Vector3(0, 0.07f, 0));
						VertPositions.Add(hit.point + new Vector3(0, SecondHeight, 0));
					}
				}

				xCount += Spacing;
			}

			// Reset X Counter
			xCount = minX;

			zCount += Spacing;
		}

		// Check if we have any hits
		if(VertPositions.Count < 1) return;

		// Get _LightProbes game object
		GameObject LightProbeGameObj = GameObject.Find("_LightProbes");
		if(LightProbeGameObj == null) return;

		// Get light probe group component
		LightProbeGroup LPGroup = LightProbeGameObj.GetComponent("LightProbeGroup") as LightProbeGroup;
		if(LPGroup == null) return;

		// Create lightprobe positions
		Vector3[] ProbePos = new Vector3[VertPositions.Count];
		for(int i=0; i<VertPositions.Count; i++){
			ProbePos[i] = VertPositions[i];
		}

		// Set new light probes
		LPGroup.probePositions = ProbePos;

		Debug.Log("Finished Probe Calculations with: " + ProbePos.Length + "Probes.");
	}
}

Related Posts

7 comments

  1. Excellent!

  2. zEternalVirus

    Hi when i insert your code into unity i receive this error.
    Please get back to me on this as i have copied the code perfectly.

    Assets/Editor/CreateLightProbes.cs(70,17): error CS0246: The type or namespace name `List’ could not be found. Are you missing a using directive or an assembly reference?

  3. Brian

    I get this error:

    “Assets/Editor/CreateLightProbes.cs(78,17): error CS0246: The type or namespace name `List’ could not be found. Are you missing a using directive or an assembly reference?”

    I am using Unity 4.3.4f1.

    I have System.Collections.Generic in the script, but I still receive this error…

  4. To the people having issues, it appears things have changed in the latest version of Unity. I will work on getting it converted and updated to the latest Unity release, and I will update the blog accordingly.

  5. for those who have error on compilate it

    // ————————————————–
    // Copyright (C) 2013 Ghost Mantis Games LLC
    // This software is provided “as is”, withouth warranty of any kind.
    // This software is provided exclusively to you free of charge.
    // Ghost Mantis Games LLC does not make and hereby disclaims any warranties
    // of any kind. By using this software you agree to these terms.
    //
    // Fixed by xalsVR 08/2014
    // ————————————————–

    using UnityEngine;
    using UnityEditor;
    using System.Collections.Generic;

    public class CreateLightProbes : EditorWindow {
    float Spacing = 2.0f;
    float SecondHeight = 4.0f;

    [MenuItem("Custom/Create Light Probes")]

    static void Init(){
    // Get existing open window or if none, make a new one:
    CreateLightProbes window = (CreateLightProbes)EditorWindow.GetWindow(typeof(CreateLightProbes), true, “Create Light Probes”);
    window.position = new Rect((Screen.width / 2) – 125, Screen.height / 2 + 85, 300, 80);
    window.Show();
    }

    void OnGUI(){
    GUILayout.Label (“Creation:”, EditorStyles.boldLabel);
    Spacing = EditorGUILayout.FloatField(“Spacing:”, Spacing);
    SecondHeight = EditorGUILayout.FloatField(“Secondary Height:”, SecondHeight);

    // Clamp values
    if(Spacing < 0.1f) Spacing = 0.1f;
    if(SecondHeight < 0.1f) SecondHeight = 0.1f;

    if(GUILayout.Button("Create Light Probes On Selected Objects")){
    Bounds();
    }
    }

    void Bounds(){
    // Get selection
    GameObject[] Select = Selection.gameObjects;
    if(Select.Length < 1) return;

    // Get total bounds for selected objects
    float minX = 0.0f;
    float minY = 0.0f;
    float minZ = 0.0f;
    float maxX = 0.0f;
    float maxY = 0.0f;
    float maxZ = 0.0f;
    for(int i=0; i<Select.Length; i++){
    // First check that mesh has a collider attached to it
    // if it doesn't then we can't raycast so we should ignore this object
    Collider col = Select[i].GetComponent();
    if(col == null) continue;

    // Get renderer component attached to object
    // If there is no renderer attached then we ignore this object
    Renderer renderer = Select[i].GetComponent();
    if(renderer == null) continue;

    // Get the renderer bounds
    Bounds bbox = renderer.bounds;

    // Update total bounds
    if(bbox.min.x < minX) minX = bbox.min.x;
    if(bbox.min.y < minY) minY = bbox.min.y;
    if(bbox.min.z maxX) maxX = bbox.max.x;
    if(bbox.max.y > maxY) maxY = bbox.max.y;
    if(bbox.max.z > maxZ) maxZ = bbox.max.z;
    }

    // Now go through in a grid and attempt to place a light probe using raycasting
    float xCount = minX;
    float zCount = minZ;

    List VertPositions = new List();

    for(int z=0; z<maxZ; z++){
    for(int x=0; x<maxX; x++){
    // Raycast downwards through each selected object and
    // attempt to find one that we are over
    for(int j=0; j<Select.Length; j++){
    Collider col = Select[j].GetComponent();

    RaycastHit hit;
    Ray ray = new Ray();
    ray.origin = new Vector3(xCount, maxY, zCount);
    ray.direction = -Vector3.up;
    if(col.Raycast(ray, out hit, (maxY-minY)*2)){
    VertPositions.Add(hit.point + new Vector3(0, 0.07f, 0));
    VertPositions.Add(hit.point + new Vector3(0, SecondHeight, 0));
    }
    }

    xCount += Spacing;
    }

    // Reset X Counter
    xCount = minX;

    zCount += Spacing;
    }

    // Check if we have any hits
    if(VertPositions.Count < 1) return;

    // Get _LightProbes game object
    GameObject LightProbeGameObj = GameObject.Find("_LightProbes");
    if(LightProbeGameObj == null) return;

    // Get light probe group component
    LightProbeGroup LPGroup = LightProbeGameObj.GetComponent("LightProbeGroup") as LightProbeGroup;
    if(LPGroup == null) return;

    // Create lightprobe positions
    Vector3[] ProbePos = new Vector3[VertPositions.Count];
    for(int i=0; i<VertPositions.Count; i++){
    ProbePos[i] = VertPositions[i];
    }

    // Set new light probes
    LPGroup.probePositions = ProbePos;

    Debug.Log("Finished Probe Calculations with: " + ProbePos.Length + "Probes.");
    }
    }

  6. I fixed the script :)

    // ————————————————–
    // Copyright (C) 2013 Ghost Mantis Games LLC
    // This software is provided “as is”, withouth warranty of any kind.
    // This software is provided exclusively to you free of charge.
    // Ghost Mantis Games LLC does not make and hereby disclaims any warranties
    // of any kind. By using this software you agree to these terms.
    //
    // Fixed by xalsVR 08/2014
    // ————————————————–

    using UnityEngine;
    using UnityEditor;
    using System.Collections.Generic;

    public class CreateLightProbes : EditorWindow {
    float Spacing = 2.0f;
    float SecondHeight = 4.0f;

    [MenuItem("Custom/Create Light Probes")]

    static void Init(){
    // Get existing open window or if none, make a new one:
    CreateLightProbes window = (CreateLightProbes)EditorWindow.GetWindow(typeof(CreateLightProbes), true, “Create Light Probes”);
    window.position = new Rect((Screen.width / 2) – 125, Screen.height / 2 + 85, 300, 80);
    window.Show();
    }

    void OnGUI(){
    GUILayout.Label (“Creation:”, EditorStyles.boldLabel);
    Spacing = EditorGUILayout.FloatField(“Spacing:”, Spacing);
    SecondHeight = EditorGUILayout.FloatField(“Secondary Height:”, SecondHeight);

    // Clamp values
    if(Spacing < 0.1f) Spacing = 0.1f;
    if(SecondHeight < 0.1f) SecondHeight = 0.1f;

    if(GUILayout.Button("Create Light Probes On Selected Objects")){
    Bounds();
    }
    }

    void Bounds(){
    // Get selection
    GameObject[] Select = Selection.gameObjects;
    if(Select.Length < 1) return;

    // Get total bounds for selected objects
    float minX = 0.0f;
    float minY = 0.0f;
    float minZ = 0.0f;
    float maxX = 0.0f;
    float maxY = 0.0f;
    float maxZ = 0.0f;
    for(int i=0; i<Select.Length; i++){
    // First check that mesh has a collider attached to it
    // if it doesn't then we can't raycast so we should ignore this object
    Collider col = Select[i].GetComponent();
    if(col == null) continue;

    // Get renderer component attached to object
    // If there is no renderer attached then we ignore this object
    Renderer renderer = Select[i].GetComponent();
    if(renderer == null) continue;

    // Get the renderer bounds
    Bounds bbox = renderer.bounds;

    // Update total bounds
    if(bbox.min.x < minX) minX = bbox.min.x;
    if(bbox.min.y < minY) minY = bbox.min.y;
    if(bbox.min.z maxX) maxX = bbox.max.x;
    if(bbox.max.y > maxY) maxY = bbox.max.y;
    if(bbox.max.z > maxZ) maxZ = bbox.max.z;
    }

    // Now go through in a grid and attempt to place a light probe using raycasting
    float xCount = minX;
    float zCount = minZ;

    List VertPositions = new List();

    for(int z=0; z<maxZ; z++){
    for(int x=0; x<maxX; x++){
    // Raycast downwards through each selected object and
    // attempt to find one that we are over
    for(int j=0; j<Select.Length; j++){
    Collider col = Select[j].GetComponent();

    RaycastHit hit;
    Ray ray = new Ray();
    ray.origin = new Vector3(xCount, maxY, zCount);
    ray.direction = -Vector3.up;
    if(col.Raycast(ray, out hit, (maxY-minY)*2)){
    VertPositions.Add(hit.point + new Vector3(0, 0.07f, 0));
    VertPositions.Add(hit.point + new Vector3(0, SecondHeight, 0));
    }
    }

    xCount += Spacing;
    }

    // Reset X Counter
    xCount = minX;

    zCount += Spacing;
    }

    // Check if we have any hits
    if(VertPositions.Count < 1) return;

    // Get _LightProbes game object
    GameObject LightProbeGameObj = GameObject.Find("_LightProbes");
    if(LightProbeGameObj == null) return;

    // Get light probe group component
    LightProbeGroup LPGroup = LightProbeGameObj.GetComponent("LightProbeGroup") as LightProbeGroup;
    if(LPGroup == null) return;

    // Create lightprobe positions
    Vector3[] ProbePos = new Vector3[VertPositions.Count];
    for(int i=0; i<VertPositions.Count; i++){
    ProbePos[i] = VertPositions[i];
    }

    // Set new light probes
    LPGroup.probePositions = ProbePos;

    Debug.Log("Finished Probe Calculations with: " + ProbePos.Length + "Probes.");
    }
    }

  7. Barbe63

    Yeah… not working.. Please try your scripts before posting it, I already see this “if(bbox.min.z maxX) maxX = bbox.max.x;” wich obviously makes no sense and i bet more are missing seeing how much monodevelop is angry at me when i paste this.

leave a comment


6 × = fifty four