Skip to content

Commit

Permalink
[ME] Projector support (#244)
Browse files Browse the repository at this point in the history
Projector support for ME. All projector properties are treated as material properties in the UI, and thus grouped together with them.
  • Loading branch information
RikkiBalboa authored Apr 25, 2024
1 parent 9a92e3b commit 4e00a27
Show file tree
Hide file tree
Showing 13 changed files with 1,115 additions and 33 deletions.
33 changes: 32 additions & 1 deletion src/MaterialEditor.Base/CopyContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public class CopyContainer
/// List of shader edits
/// </summary>
public List<MaterialShader> MaterialShaderList = new List<MaterialShader>();
/// <summary>
/// List of projector edits
/// </summary>
public List<ProjectorProperty> ProjectorPropertyList = new List<ProjectorProperty>();

/// <summary>
/// Whether there are any copied edits
Expand All @@ -36,7 +40,7 @@ public bool IsEmpty
{
get
{
if (MaterialFloatPropertyList.Count == 0 && MaterialKeywordPropertyList.Count == 0 && MaterialColorPropertyList.Count == 0 && MaterialTexturePropertyList.Count == 0 && MaterialShaderList.Count == 0)
if (MaterialFloatPropertyList.Count == 0 && MaterialKeywordPropertyList.Count == 0 && MaterialColorPropertyList.Count == 0 && MaterialTexturePropertyList.Count == 0 && MaterialShaderList.Count == 0 && ProjectorPropertyList.Count == 0)
return true;
return false;
}
Expand All @@ -52,6 +56,7 @@ public void ClearAll()
MaterialColorPropertyList = new List<MaterialColorProperty>();
MaterialTexturePropertyList = new List<MaterialTextureProperty>();
MaterialShaderList = new List<MaterialShader>();
ProjectorPropertyList = new List<ProjectorProperty>();
}

/// <summary>
Expand Down Expand Up @@ -217,5 +222,31 @@ public MaterialShader(int? renderQueue)
/// <returns></returns>
public bool NullCheck() => ShaderName.IsNullOrEmpty() && RenderQueue == null;
}

/// <summary>
/// Data storage class for projector properties
/// </summary>
public class ProjectorProperty
{
/// <summary>
/// Name of the property
/// </summary>
public MaterialAPI.ProjectorProperties Property;
/// <summary>
/// Value
/// </summary>
public float Value;

/// <summary>
/// Data storage class for projector properties
/// </summary>
/// <param name="property">Name of the property</param>
/// <param name="value">Value</param>
public ProjectorProperty(MaterialAPI.ProjectorProperties property, float value)
{
Property = property;
Value = value;
}
}
}
}
1 change: 1 addition & 0 deletions src/MaterialEditor.Base/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static void Randomize<T>(this IList<T> list)
public static string NameFormatted(this GameObject go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
public static string NameFormatted(this Material go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
public static string NameFormatted(this Renderer go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
public static string NameFormatted(this Projector go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
public static string NameFormatted(this Shader go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
public static string NameFormatted(this Mesh go) => go == null ? "" : go.name.Replace("(Instance)", "").Replace(" Instance", "").Trim();
/// <summary>
Expand Down
274 changes: 273 additions & 1 deletion src/MaterialEditor.Base/MaterialAPI.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace MaterialEditorAPI
Expand All @@ -10,6 +9,12 @@ namespace MaterialEditorAPI
/// </summary>
public static class MaterialAPI
{
/// <summary>
/// Projector materials share an instance by default, unlike renderers which get a unique one by default.
/// This list keeps track of every projector and it's new unique material instance
/// </summary>
internal static readonly Dictionary<Projector, Material> ProjectorMaterialInstances = new Dictionary<Projector, Material>();

/// <summary>
/// Postfix added to the name of a material when copied
/// </summary>
Expand All @@ -26,6 +31,30 @@ public static IEnumerable<Renderer> GetRendererList(GameObject gameObject)
return gameObject.GetComponentsInChildren<Renderer>(true);
}

/// <summary>
/// Get a list of all the projectors of a GameObject
/// </summary>
public static IEnumerable<Projector> GetProjectorList(GameObject gameObject, bool inChildren = false)
{
if (gameObject == null)
return new List<Projector>();

IEnumerable<Projector> projectors;
if(inChildren)
projectors = gameObject.GetComponentsInChildren<Projector>(true);
else
projectors = gameObject.GetComponents<Projector>();

//Assign a unique copy of the material if the projector didn't have one already
foreach (var projector in projectors)
if (!ProjectorMaterialInstances.ContainsKey(projector))
{
projector.material = new Material(projector.material);
ProjectorMaterialInstances[projector] = projector.material;
}
return projectors;
}

/// <summary>
/// Get a list of materials for the renderer
/// </summary>
Expand Down Expand Up @@ -58,6 +87,9 @@ private static List<Material> GetObjectMaterials(GameObject gameObject, string m
if (material.NameFormatted() == materialName)
materials.Add(material);

foreach (var projector in GetProjectorList(gameObject))
materials.Add(projector.material);

return materials;
}

Expand Down Expand Up @@ -396,6 +428,204 @@ public static bool SetRendererRecalculateNormals(GameObject gameObject, string r
return didSet;
}

/// <summary>
/// Set the value of the specified projector property
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="propertyName">Property of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorProperty(GameObject gameObject, string projectorName, ProjectorProperties propertyName, float value)
{
if (propertyName == ProjectorProperties.Enabled)
return SetProjectorEnabled(gameObject, projectorName, System.Convert.ToBoolean(value));
else if (propertyName == ProjectorProperties.FarClipPlane)
return SetProjectorFarClipPlane(gameObject, projectorName, value);
else if (propertyName == ProjectorProperties.NearClipPlane)
return SetProjectorNearClipPlane(gameObject, projectorName, value);
else if (propertyName == ProjectorProperties.FieldOfView)
return SetProjectorFieldOfView(gameObject, projectorName, value);
else if (propertyName == ProjectorProperties.AspectRatio)
return SetProjectorAspectRatio(gameObject, projectorName, value);
else if (propertyName == ProjectorProperties.Orthographic)
return SetProjectorOrthographic(gameObject, projectorName, System.Convert.ToBoolean(value));
else if (propertyName == ProjectorProperties.OrthographicSize)
return SetProjectorOrthographicSize(gameObject, projectorName, value);
else if (propertyName == ProjectorProperties.IgnoreCharaLayer)
return SetProjectorIgnoreLayers(gameObject, projectorName, System.Convert.ToBoolean(value), 10);
else if (propertyName == ProjectorProperties.IgnoreMapLayer)
return SetProjectorIgnoreLayers(gameObject, projectorName, System.Convert.ToBoolean(value), 11);
return false;
}

/// <summary>
/// Set the enabled property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the renderer</param>
/// <param name="projectorName">Name of the renderer being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorEnabled(GameObject gameObject, string projectorName, bool value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.enabled = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the farclipPlane property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorFarClipPlane(GameObject gameObject, string projectorName, float value)
{
foreach(var projector in GetProjectorList(gameObject))
{
if(projector.NameFormatted() == projectorName)
{
projector.farClipPlane = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the nearClipPlane property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorNearClipPlane(GameObject gameObject, string projectorName, float value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.nearClipPlane = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the fieldOfView property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorFieldOfView(GameObject gameObject, string projectorName, float value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.fieldOfView = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the aspectRatio property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorAspectRatio(GameObject gameObject, string projectorName, float value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.aspectRatio = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the orthographic property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorOrthographic(GameObject gameObject, string projectorName, bool value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.orthographic = value;
return true;
}
}
return false;
}

/// <summary>
/// Set the orthoGraphicSize property of a projector
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="value">Value to be set</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorOrthographicSize(GameObject gameObject, string projectorName, float value)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
projector.orthographicSize = value;
return true;
}
}
return false;
}

/// <summary>
/// Add or remove the given layer to/from the ignored layers property.
/// Ensures it's not removed/added that was already the case
/// </summary>
/// <param name="gameObject">GameObject to search for the projector</param>
/// <param name="projectorName">Name of the projector being modified</param>
/// <param name="ignore">If the given layer should be ignored or not</param>
/// <param name="layer">The index of the layer to be added/removed</param>
/// <returns>True if the value was set, false if it could not be set</returns>
public static bool SetProjectorIgnoreLayers(GameObject gameObject, string projectorName, bool ignore, int layer)
{
foreach (var projector in GetProjectorList(gameObject))
{
if (projector.NameFormatted() == projectorName)
{
bool inMask = projector.ignoreLayers == (projector.ignoreLayers | (1 << layer));
//Remove layer from mask
if (inMask && !ignore)
projector.ignoreLayers &= ~(1 << layer);
//Add layer to mask
else if(!inMask && ignore)
projector.ignoreLayers |= (1 << layer);
}
}
return false;
}

/// <summary>
/// Set the texture property of a material
/// </summary>
Expand Down Expand Up @@ -625,5 +855,47 @@ public enum RendererProperties
/// </summary>
UpdateWhenOffscreen
}
/// <summary>
/// Properties of a projector that can be set
/// </summary>
public enum ProjectorProperties
{
/// <summary>
/// Whether the projector is enabled
/// </summary>
Enabled,
/// <summary>
/// Near clip plane to start projecting
/// </summary>
NearClipPlane,
/// <summary>
/// Far clip plane to stop projecting
/// </summary>
FarClipPlane,
/// <summary>
/// Field of View of projection
/// </summary>
FieldOfView,
/// <summary>
/// Aspect ratio of the projection
/// </summary>
AspectRatio,
/// <summary>
/// Whether the projector should project in orthographic mode
/// </summary>
Orthographic,
/// <summary>
/// The size of the orthographic projection
/// </summary>
OrthographicSize,
/// <summary>
/// If the projector should ignore objects on the chara layer
/// </summary>
IgnoreCharaLayer,
/// <summary>
/// If the projector should ignore objects on the map layer
/// </summary>
IgnoreMapLayer
}
}
}
Loading

0 comments on commit 4e00a27

Please sign in to comment.