MD Package Technical & APi Documentation
MD_ModifierBase :: MD_MeshBase
Namespace: MDPackage.Modifiers
Base modifier class for all the modifier instances. Implement this base class to any script that should behave like a 'mesh-modifier'.
Please read the APi below carefully as the setup for classes that inherit from this base class is slightly different.
Public Methods
// Essential methods for scripts that inherit from this class

// Create new modifier of a specific type - you can also create a modifier simply calling the gameObject.AddComponent<Modifier>(). This static method allows you to define a referenceType
public static T CreateModifier<T>(GameObject entry, MeshReferenceType meshReferenceType)

// Inheritation

// Base modifier initialization - is required to invoke for every class that inherits from the ModifierBase
protected virtual void MDModifier_InitializeBase(MeshReferenceType meshReferenceType = MeshReferenceType.GetFromPreferences, bool forceInitialization = false, bool affectUpdateEveryFrameField = true)
// Base mesh data initialization & caching - this will initialize essential mesh data (vertices, triangles, uvs etc)
protected virtual void MDModifier_InitializeMeshData(bool initialMeshData = true, bool backupMeshData = true, bool workingMeshData = true)
// Modifier implementation for main processing - use this method for processing the specific modifier
public abstract void MDModifier_ProcessModifier();

// Cross-modifier mesh features

// Restore current mesh from the initial mesh data
public void MDModifier_RestoreMesh()
// Increase mesh vertex count
public void MDModifier_SubdivideMesh(int level = 2)
// Make the mesh edges rounded
public void MDModifier_SmoothMesh(float intensity = 0.5f)
// Bake current mesh - initial mesh data will be overrided by the current mesh data
public void MDModifier_BakeMesh(bool forceInitialMeshData = false, bool forceBackupMeshData = false, bool forceWorkingMeshData = false)
// Create a brand new mesh reference
public void MDModifier_CreateNewMeshReference(string meshName = "")

// Threading features

// Start current thread and initialize the thread event. Enter custom thread name
public virtual void MDModifierThreading_StartThread(string threadName = "DefaultMDThread")
// Stop current thread and destroy all the events related to this thread
public virtual void MDModifierThreading_StopThread()
Public Fields and Properties
public bool MbIsInitialized { get; }

// Is the current modifier multithreaded?
public bool MbMultithreadedModifier { get; }
public Thread ThreadInstance { get; }
public ManualResetEvent ThreadEvent { get; protected set; }

// Does the current modifier support base-mesh features such as 'Bake, Subdivide, Smooth' etc?
public bool MbUseModifierMeshFeatures = true;

// Not all mesh data will be initialized upon the modifier's creation - some modifiers don't need backup or initial mesh data. See the 'MbMeshData' struct for more
public MbMeshData MbInitialMeshData { get; }
public MbMeshData MbBackupMeshData { get; }
public MbMeshData MbWorkingMeshData { get; }

// Called once the modifier has been successfully initialized. There is actually one-frame delay before fully initialized
public Action OnModifierInitialized;

// Subscribe to delegate action when mesh is restored
public event Action OnMeshRestored;
// Subscribe to delegate action when mesh is baked
public event Action OnMeshBaked;
// Subscribe to delegate action when mesh is subdivided
public event Action OnMeshSubdivided;
// Subscribe to delegate action when mesh is smoothed
public event Action OnMeshSmoothed;
// Subscribe to delegate action when new mesh reference is created
public event Action OnNewMeshReferenceCreated;
Examples & Setup
All the modifiers that inherit from this base class must be initialized in a certain way.
You can either simply call gameObject.AddComponent() or call a static method MD_ModifierBase.CreateModifier(Advanced data...).
using UnityEngine;
using MDPackage.Modifiers;

public class SampleScript : MonoBehaviour
{
private MDM_Bend modifier;

private void Start()
 {
  // Create a Bend modifier for this object
  modifier = MD_ModifierBase.CreateModifier<MDM_Bend>(gameObject, MD_ModifierBase.MeshReferenceType.GetFromPreferences);
  // Or just:
  // modifier = gameObject.AddComponent<MDM_Bend>();
 }
}
The ModifierBase contains an interface called IMDThreadingSupport that allows you to add multithreading features.
See the following example script with custom modifier written from scratch.
using System.Threading;

using UnityEngine;

using MDPackage.Modifiers;

#if UNITY_EDITOR
using UnityEditor;
using MDPackage;
using MDPackage_Editor;
#endif

public class SampleScript : MD_ModifierBase, MD_ModifierBase.IMDThreadingSupport
{
// IMDThreadingSupport interface implementations (use this as a template - required)

public bool ThreadUseMultithreading { get => _threadUseMultithreading; set => _threadUseMultithreading = value; }
 [SerializeField] private bool _threadUseMultithreading;
public bool ThreadEditorThreadSupported { get => _threadEditorThreadSupported; private set => _threadEditorThreadSupported = value; }
 [SerializeField] private bool _threadEditorThreadSupported;
public bool ThreadIsDone { get; private set; }
public int ThreadSleep { get => _threadSleep; set => _threadSleep = value; }
 [SerializeField, Range(5, 60)] private int _threadSleep = 20;

public float vertexFloatingMultiplier = 1;

// Cached thread safe unity time
private float unityTime;

/// <summary>
/// Initialize this custom modifier - this is called either at runtime when you add the component to an object or in the editor when you manually drag/drop the component on an object
/// </summary>
protected override void MDModifier_InitializeBase(MeshReferenceType meshReferenceType = MeshReferenceType.GetFromPreferences, bool forceInitialization = false, bool affectUpdateEveryFrameField = true)
 {
  base.MDModifier_InitializeBase(meshReferenceType, forceInitialization, affectUpdateEveryFrameField);
  // We will use all three mesh data - initial, backup & working
  MDModifier_InitializeMeshData();
  // This custom modifier is thread-supported
  ThreadEditorThreadSupported = true;
  // Use advanced mesh features
  MbUseModifierMeshFeatures = true;
  // Use normal smoothing angle normals for smoother results
  useNormalSmoothingAngle = true;
 }

private void Awake()
 {
  // Start thread worker on start if multithreading is used
  if (ThreadUseMultithreading)
   MDModifierThreading_StartThread();
 }

protected override void Update()
 {
  if (!MbIsInitialized)
   return;

  base.Update();

  // Cache Unity time (thread safe)
  unityTime = Time.time;

  // Process by default if multithreading is not used
  if (!ThreadUseMultithreading)
  {
   ProcessModifierThreadSafeAPi();
   MDMeshBase_UpdateMesh();
   MDMeshBase_RecalculateMesh();
  }
 }

/// <summary>
/// Core logic for the modifier
/// </summary>
public override void MDModifier_ProcessModifier()
 {
  // Custom modifier must be initialized!
  if (!MbIsInitialized)
   return;

  if (ThreadUseMultithreading)
  {
   if (ThreadIsDone)
   {
    MDMeshBase_UpdateMesh();
    MDMeshBase_RecalculateMesh();
   }
   else ThreadEvent?.Set();
   return;
  }

  MDMeshBase_UpdateMesh();
  MDMeshBase_RecalculateMesh();
 }

/// <summary>
/// Custom thread-safe method for handling mesh vertices
/// </summary>
private void ProcessModifierThreadSafeAPi()
 {
  for (int i = 0; i < MbWorkingMeshData.vertices.Length; i++)
  {
   MbWorkingMeshData.vertices[i] =
    MbBackupMeshData.vertices[i] + Vector3.up *
    (Mathf.Sin(unityTime * MbBackupMeshData.vertices[i].x * 3.5f)
    * vertexFloatingMultiplier + Mathf.Abs(MbBackupMeshData.vertices[i].y));
  }
 }

/// <summary>
/// Thread worker from interface
/// </summary>
public void MDThreading_ProcessThreadWorker()
 {
  while (true)
  {
   ThreadIsDone = false;
   ThreadEvent?.WaitOne();
   // Must be Thread-Safe APi!
   ProcessModifierThreadSafeAPi();

   ThreadIsDone = true;
   Thread.Sleep(ThreadSleep);

   ThreadEvent?.Reset();
  }
 }
}

// The must-to-implement editor. Use this simple template for proper work
#if UNITY_EDITOR
[CanEditMultipleObjects]
[CustomEditor(typeof(SampleScript))]
public sealed class SampleScriptEditor : MD_ModifierBase_Editor
{
public override void OnEnable()
 {
  base.OnEnable();
  // All the base class must be found to make the inspector work
  mMeshBase = (MD_MeshBase)target;
  mModifierBase = (MD_ModifierBase)target;
 }

public override void OnInspectorGUI()
 {
  // Just draw all the public fields
  base.OnInspectorGUI();
  MDE_DrawProperty("vertexFloatingMultiplier", "vertexFloatingMultiplier");
 }
}
#endif