MD Package Technical & APi Documentation
MDM_FFD :: MD_ModifierBase
Namespace: MDPackage.Modifiers
(FreeFormDeformer) Deform mesh by the specific weight values that correspond to the specific weight nodes.
Read more here (Online documentation slide)
Public Methods
// Refresh selected FFD type & its grid (This will refresh & reset current FFD nodes to default positions)
public void FFD_RefreshFFDGrid()
// Clear FFD grid (if possible) and keep the edited mesh
public void FFD_ClearFFDGrid()
// Receive a list of currently generated nodes (if possible - might return null)
public List<Transform> FFD_GetNodes()
// Is the included node transform a part of this FFD?
public bool FFD_NodesContain(Transform node)
Public Fields and Properties
public FFDType ffdType = FFDType.ffd2x2x2; // ffd2x2x2, ffd3x3x3, ffd4x4x4, CustomSigned
public FFDShape ffdShape = FFDShape.Sphere; // Octahedron, Sphere, Cube, Custom
public GameObject ffdCustomShape;

public float ffdNodeSize = 0.25f;
[Range(0.0f, 1.0f)] public float ffdOffset = 0.2f;
public float ffdOffsetMultiplier = 1.0f;
[Range(2,12)] public int ffdCustomCount = 2;

[Range(0,1)] public float weight = 0.5f;
public float threshold = 0.15f;
public float density = 1.35f;
public float bias = 1.0f;
public float multiplier = 1.0f;

public float distanceLimitation = Mathf.Infinity;

public Transform NodeRoot { get; }
Examples
The following paragraphs contain FFD modifier used in a practical example - interaction with FFD grid at runtime. Drag/drop generated nodes at runtime.
using UnityEngine;
using MDPackage.Modifiers;

[RequireComponent(typeof(MeshFilter))]
public class SampleScript : MonoBehaviour
{
// Cache main camera in the inspector!
public Camera mainCam;

private MDM_FFD ffd;

// Cache selected node & depth
private Transform selectedNode;
private float depth;

private void Start()
 {
  // Add/Create a FFD modifier to this object right after start
  ffd = MD_ModifierBase.CreateModifier<MDM_FFD>(gameObject, MD_ModifierBase.MeshReferenceType.CreateNewReference);
  // Enable useNormalSmoothingAngle for 'smoother' looking normals
  ffd.useNormalSmoothingAngle = true;
  // Generate FFD grid 2x2x2 (default) right after start
  ffd.FFD_RefreshFFDGrid();
 }

private void Update()
 {
  // Main camera must be initialized
  if (!mainCam)
   return;

  // Additional extensions for mesh
  // If Space is pressed, subdivide mesh if possible
  if (Input.GetKeyDown(KeyCode.Space))
   ffd.MDModifier_SubdivideMesh();
  // If S is pressed, smooth mesh
  if (Input.GetKeyDown(KeyCode.S))
   ffd.MDModifier_SmoothMesh();
  // If R is pressed, restore mesh
  if (Input.GetKeyDown(KeyCode.R))
   ffd.MDModifier_RestoreMesh();

  if (Input.GetMouseButtonDown(0))
  {
   // Create raycast from camera to the cursor position
   Ray r = mainCam.ScreenPointToRay(Input.mousePosition);
   // Checking for potential hit - if the hit collider is a child of the node root = we've got the right node!
   bool hit = Physics.Raycast(r, out RaycastHit h) && h.transform && ffd.FFD_NodesContain(h.transform);
   if (hit)
   {
    selectedNode = h.transform;
    // Getting the actual depth between camera and selected node
    depth = mainCam.WorldToScreenPoint(selectedNode.position).z;
   }
  }

  // Return if node is not selected
  if (!selectedNode)
   return;

  if (Input.GetMouseButton(0))
  {
   // Making the selected node to follow the cursor location
   Vector3 mouse = Input.mousePosition;
   mouse.z = depth;
   Vector3 wp = mainCam.ScreenToWorldPoint(mouse);
   selectedNode.position = wp;
  }
  else if (Input.GetMouseButtonUp(0))
   selectedNode = null;
 }
}