// This script demonstrates how to create a new action that can be accessed from the ProBuilder toolbar.
// A new menu item is registered under "Geometry" actions called "Gen. Shadows".
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using UnityEngine.Rendering;
// When creating your own actions use your own namespace.
namespace ProBuilder.ExampleActions
{
[ProBuilderMenuAction]
sealed class CreateShadowObject : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Object; } }
public override Texture2D icon { get { return null; } }
public override TooltipContent tooltip { get { return k_Tooltip; } }
static readonly GUIContent k_VolumeSize = new GUIContent("Volume Size", "How far the shadow volume extends from " +
"the base mesh. To visualize, imagine the width of walls.\n\nYou can also select the child ShadowVolume " +
"object and turn the Shadow Casting Mode to \"One\" or \"Two\" sided to see the resulting mesh.");
// What to show in the hover tooltip window. TooltipContent is similar to GUIContent, with the exception
// that it also includes an optional params[] char list in the constructor to define shortcut keys
// (ex, CMD_CONTROL, K).
static readonly TooltipContent k_Tooltip = new TooltipContent(
"Gen Shadow Obj",
"Creates a new ProBuilder mesh child with inverted normals that only exists to cast shadows. Use to " +
"create lit interior scenes with shadows from directional lights.\n\nNote that this exists largely as " +
"a workaround for real-time shadow light leaks. Baked shadows do not require this workaround.",
""
);
static bool showPreview
{
get { return EditorPrefs.GetBool("pb_shadowVolumePreview", true); }
set { EditorPrefs.SetBool("pb_shadowVolumePreview", value); }
}
// Determines if the action should be enabled or shown as disabled in the menu.
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
///
/// Determines if the action should be loaded in the menu (ex, face actions shouldn't be shown when in vertex editing mode).
///
///
public override bool hidden
{
get { return false; }
}
protected override void OnSettingsEnable()
{
if( showPreview )
PerformAction();
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Create Shadow Volume Options", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUI.BeginChangeCheck();
float volumeSize = EditorPrefs.GetFloat("pb_CreateShadowObject_volumeSize", .07f);
volumeSize = EditorGUILayout.Slider(k_VolumeSize, volumeSize, 0.001f, 1f);
if( EditorGUI.EndChangeCheck() )
EditorPrefs.SetFloat("pb_CreateShadowObject_volumeSize", volumeSize);
#if !UNITY_4_6 && !UNITY_4_7
EditorGUI.BeginChangeCheck();
ShadowCastingMode shadowMode = (ShadowCastingMode) EditorPrefs.GetInt("pb_CreateShadowObject_shadowMode", (int) ShadowCastingMode.ShadowsOnly);
shadowMode = (ShadowCastingMode) EditorGUILayout.EnumPopup("Shadow Casting Mode", shadowMode);
if(EditorGUI.EndChangeCheck())
EditorPrefs.SetInt("pb_CreateShadowObject_shadowMode", (int) shadowMode);
#endif
EditorGUI.BeginChangeCheck();
ExtrudeMethod extrudeMethod = (ExtrudeMethod) EditorPrefs.GetInt("pb_CreateShadowObject_extrudeMethod", (int) ExtrudeMethod.FaceNormal);
extrudeMethod = (ExtrudeMethod) EditorGUILayout.EnumPopup("Extrude Method", extrudeMethod);
if(EditorGUI.EndChangeCheck())
EditorPrefs.SetInt("pb_CreateShadowObject_extrudeMethod", (int) extrudeMethod);
if(EditorGUI.EndChangeCheck())
PerformAction();
GUILayout.FlexibleSpace();
if(GUILayout.Button("Create Shadow Volume"))
{
PerformAction();
SceneView.RepaintAll();
// MenuOption.CloseAll();
}
}
///
/// Perform the action.
///
/// Return a pb_ActionResult indicating the success/failure of action.
protected override ActionResult PerformActionImplementation()
{
ShadowCastingMode shadowMode = (ShadowCastingMode) EditorPrefs.GetInt("pb_CreateShadowObject_shadowMode", (int) ShadowCastingMode.ShadowsOnly);
float extrudeDistance = EditorPrefs.GetFloat("pb_CreateShadowObject_volumeSize", .08f);
ExtrudeMethod extrudeMethod = (ExtrudeMethod) EditorPrefs.GetInt("pb_CreateShadowObject_extrudeMethod", (int) ExtrudeMethod.FaceNormal);
foreach(ProBuilderMesh mesh in MeshSelection.top)
{
ProBuilderMesh shadow = GetShadowObject(mesh);
if(shadow == null)
continue;
foreach (Face f in shadow.faces)
{
f.SetIndexes(f.indexes.Reverse().ToArray());
f.manualUV = true;
}
shadow.Extrude(shadow.faces, extrudeMethod, extrudeDistance);
shadow.ToMesh();
shadow.Refresh();
shadow.Optimize();
#if !UNITY_4_6 && !UNITY_4_7
MeshRenderer mr = shadow.gameObject.GetComponent();
mr.shadowCastingMode = shadowMode;
if(shadowMode == ShadowCastingMode.ShadowsOnly)
mr.receiveShadows = false;
#endif
Collider collider = shadow.GetComponent();
while(collider != null)
{
Object.DestroyImmediate(collider);
collider = shadow.GetComponent();
}
}
// Refresh the Editor wireframe and working caches.
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Create Shadow Object");
}
private ProBuilderMesh GetShadowObject(ProBuilderMesh mesh)
{
if(mesh == null || mesh.name.Contains("-ShadowVolume"))
return null;
for(int i = 0; i < mesh.transform.childCount; i++)
{
Transform t = mesh.transform.GetChild(i);
if(t.name.Equals(string.Format("{0}-ShadowVolume", mesh.name)))
{
ProBuilderMesh shadow = t.GetComponent();
if(shadow != null)
{
Undo.RecordObject(shadow, "Update Shadow Object");
Face[] faces = new Face[mesh.faceCount];
for(int nn = 0; nn < mesh.faceCount; nn++)
faces[nn] = new Face(mesh.faces[nn]);
shadow.RebuildWithPositionsAndFaces(mesh.positions, faces);
return shadow;
}
}
}
ProBuilderMesh newShadowMesh = ProBuilderMesh.Create();
newShadowMesh.ToMesh();
newShadowMesh.CopyFrom(mesh);
newShadowMesh.name = string.Format("{0}-ShadowVolume", mesh.name);
newShadowMesh.transform.SetParent(mesh.transform, false);
Undo.RegisterCreatedObjectUndo(newShadowMesh.gameObject, "Create Shadow Object");
return newShadowMesh;
}
}
}