407 lines
21 KiB
C#
407 lines
21 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEditor.IMGUI.Controls;
|
|
using UnityEditorInternal;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Obi
|
|
{
|
|
|
|
/**
|
|
* Custom inspector for ObiSolver components.
|
|
* Allows particle selection and constraint edition.
|
|
*
|
|
* Selection:
|
|
*
|
|
* - To select a particle, left-click on it.
|
|
* - You can select multiple particles by holding shift while clicking.
|
|
* - To deselect all particles, click anywhere on the object except a particle.
|
|
*
|
|
* Constraints:
|
|
*
|
|
* - To edit particle constraints, select the particles you wish to edit.
|
|
* - Constraints affecting any of the selected particles will appear in the inspector.
|
|
* - To add a new pin constraint to the selected particle(s), click on "Add Pin Constraint".
|
|
*
|
|
*/
|
|
[CustomEditor(typeof(ObiSolver)), CanEditMultipleObjects]
|
|
public class ObiSolverEditor : Editor
|
|
{
|
|
|
|
[MenuItem("GameObject/3D Object/Obi/Obi Solver", false, 100)]
|
|
static void CreateObiSolver(MenuCommand menuCommand)
|
|
{
|
|
GameObject go = ObiEditorUtils.CreateNewSolver();
|
|
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
|
|
Selection.activeGameObject = go;
|
|
}
|
|
|
|
ObiSolver solver;
|
|
|
|
SerializedProperty backend;
|
|
SerializedProperty substeps;
|
|
SerializedProperty maxStepsPerFrame;
|
|
SerializedProperty synchronization;
|
|
SerializedProperty simulateWhenInvisible;
|
|
SerializedProperty parameters;
|
|
SerializedProperty gravity;
|
|
SerializedProperty gravitySpace;
|
|
SerializedProperty ambientWind;
|
|
SerializedProperty windSpace;
|
|
SerializedProperty useLimits;
|
|
SerializedProperty boundaryLimits;
|
|
SerializedProperty killOffLimitsParticles;
|
|
SerializedProperty worldLinearInertiaScale;
|
|
SerializedProperty worldAngularInertiaScale;
|
|
|
|
SerializedProperty foamSubsteps;
|
|
SerializedProperty foamMinNeighbors;
|
|
SerializedProperty foamCollisions;
|
|
SerializedProperty foamRadiusScale;
|
|
SerializedProperty foamVolumeDensity;
|
|
SerializedProperty foamAmbientDensity;
|
|
SerializedProperty foamScatterColor;
|
|
SerializedProperty foamAmbientColor;
|
|
SerializedProperty maxFoamVelocityStretch;
|
|
SerializedProperty foamFade;
|
|
SerializedProperty foamAccelAgingRange;
|
|
SerializedProperty foamAccelAging;
|
|
|
|
SerializedProperty distanceConstraintParameters;
|
|
SerializedProperty bendingConstraintParameters;
|
|
SerializedProperty particleCollisionConstraintParameters;
|
|
SerializedProperty particleFrictionConstraintParameters;
|
|
SerializedProperty collisionConstraintParameters;
|
|
SerializedProperty frictionConstraintParameters;
|
|
SerializedProperty skinConstraintParameters;
|
|
SerializedProperty volumeConstraintParameters;
|
|
SerializedProperty shapeMatchingConstraintParameters;
|
|
SerializedProperty tetherConstraintParameters;
|
|
SerializedProperty pinConstraintParameters;
|
|
SerializedProperty pinholeConstraintParameters;
|
|
SerializedProperty stitchConstraintParameters;
|
|
SerializedProperty densityConstraintParameters;
|
|
SerializedProperty stretchShearConstraintParameters;
|
|
SerializedProperty bendTwistConstraintParameters;
|
|
SerializedProperty chainConstraintParameters;
|
|
|
|
SerializedProperty maxSurfaceChunks;
|
|
SerializedProperty maxQueryResults;
|
|
SerializedProperty maxFoamParticles;
|
|
SerializedProperty maxParticleNeighbors;
|
|
SerializedProperty maxParticleContacts;
|
|
|
|
BooleanPreference solverFoldout;
|
|
BooleanPreference simulationFoldout;
|
|
BooleanPreference advectionFoldout;
|
|
BooleanPreference collisionsFoldout;
|
|
BooleanPreference constraintsFoldout;
|
|
BooleanPreference memoryFoldout;
|
|
|
|
GUIContent constraintLabelContent;
|
|
|
|
BoxBoundsHandle limitsBoxHandle;
|
|
|
|
public void OnEnable()
|
|
{
|
|
solver = (ObiSolver)target;
|
|
constraintLabelContent = new GUIContent();
|
|
limitsBoxHandle = new BoxBoundsHandle();
|
|
|
|
solverFoldout = new BooleanPreference($"{target.GetType()}.solverFoldout", true);
|
|
simulationFoldout = new BooleanPreference($"{target.GetType()}.simulationFoldout", false);
|
|
advectionFoldout = new BooleanPreference($"{target.GetType()}.advectionFoldout", false);
|
|
collisionsFoldout = new BooleanPreference($"{target.GetType()}.collisionsFoldout", false);
|
|
constraintsFoldout = new BooleanPreference($"{target.GetType()}.constraintsFoldout", false);
|
|
memoryFoldout = new BooleanPreference($"{target.GetType()}.memoryFoldout", false);
|
|
|
|
backend = serializedObject.FindProperty("m_Backend");
|
|
substeps = serializedObject.FindProperty("substeps");
|
|
maxStepsPerFrame = serializedObject.FindProperty("maxStepsPerFrame");
|
|
synchronization = serializedObject.FindProperty("synchronization");
|
|
simulateWhenInvisible = serializedObject.FindProperty("simulateWhenInvisible");
|
|
parameters = serializedObject.FindProperty("parameters");
|
|
gravity = serializedObject.FindProperty("gravity");
|
|
gravitySpace = serializedObject.FindProperty("gravitySpace");
|
|
ambientWind = serializedObject.FindProperty("ambientWind");
|
|
windSpace = serializedObject.FindProperty("windSpace");
|
|
useLimits = serializedObject.FindProperty("useLimits");
|
|
boundaryLimits = serializedObject.FindProperty("boundaryLimits");
|
|
killOffLimitsParticles = serializedObject.FindProperty("killOffLimitsParticles");
|
|
worldLinearInertiaScale = serializedObject.FindProperty("worldLinearInertiaScale");
|
|
worldAngularInertiaScale = serializedObject.FindProperty("worldAngularInertiaScale");
|
|
|
|
foamSubsteps = serializedObject.FindProperty("foamSubsteps");
|
|
foamMinNeighbors = serializedObject.FindProperty("foamMinNeighbors");
|
|
foamCollisions = serializedObject.FindProperty("foamCollisions");
|
|
foamRadiusScale = serializedObject.FindProperty("foamRadiusScale");
|
|
foamVolumeDensity = serializedObject.FindProperty("foamVolumeDensity");
|
|
foamAmbientDensity = serializedObject.FindProperty("foamAmbientDensity");
|
|
foamScatterColor = serializedObject.FindProperty("foamScatterColor");
|
|
foamAmbientColor = serializedObject.FindProperty("foamAmbientColor");
|
|
maxFoamVelocityStretch = serializedObject.FindProperty("maxFoamVelocityStretch");
|
|
foamFade = serializedObject.FindProperty("foamFade");
|
|
foamAccelAgingRange = serializedObject.FindProperty("foamAccelAgingRange");
|
|
foamAccelAging = serializedObject.FindProperty("foamAccelAging");
|
|
|
|
distanceConstraintParameters = serializedObject.FindProperty("distanceConstraintParameters");
|
|
bendingConstraintParameters = serializedObject.FindProperty("bendingConstraintParameters");
|
|
particleCollisionConstraintParameters = serializedObject.FindProperty("particleCollisionConstraintParameters");
|
|
particleFrictionConstraintParameters = serializedObject.FindProperty("particleFrictionConstraintParameters");
|
|
collisionConstraintParameters = serializedObject.FindProperty("collisionConstraintParameters");
|
|
frictionConstraintParameters = serializedObject.FindProperty("frictionConstraintParameters");
|
|
skinConstraintParameters = serializedObject.FindProperty("skinConstraintParameters");
|
|
volumeConstraintParameters = serializedObject.FindProperty("volumeConstraintParameters");
|
|
shapeMatchingConstraintParameters = serializedObject.FindProperty("shapeMatchingConstraintParameters");
|
|
tetherConstraintParameters = serializedObject.FindProperty("tetherConstraintParameters");
|
|
pinConstraintParameters = serializedObject.FindProperty("pinConstraintParameters");
|
|
pinholeConstraintParameters = serializedObject.FindProperty("pinholeConstraintParameters");
|
|
stitchConstraintParameters = serializedObject.FindProperty("stitchConstraintParameters");
|
|
densityConstraintParameters = serializedObject.FindProperty("densityConstraintParameters");
|
|
stretchShearConstraintParameters = serializedObject.FindProperty("stretchShearConstraintParameters");
|
|
bendTwistConstraintParameters = serializedObject.FindProperty("bendTwistConstraintParameters");
|
|
chainConstraintParameters = serializedObject.FindProperty("chainConstraintParameters");
|
|
|
|
maxSurfaceChunks = serializedObject.FindProperty("m_MaxSurfaceChunks");
|
|
maxQueryResults = serializedObject.FindProperty("maxQueryResults");
|
|
maxFoamParticles = serializedObject.FindProperty("maxFoamParticles");
|
|
maxParticleNeighbors = serializedObject.FindProperty("maxParticleNeighbors");
|
|
maxParticleContacts = serializedObject.FindProperty("maxParticleContacts");
|
|
}
|
|
|
|
public void OnSceneGUI()
|
|
{
|
|
if (solver.useLimits)
|
|
{
|
|
using (new Handles.DrawingScope(Color.red, solver.transform.localToWorldMatrix))
|
|
{
|
|
limitsBoxHandle.center = solver.boundaryLimits.center;
|
|
limitsBoxHandle.size = solver.boundaryLimits.size;
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
limitsBoxHandle.DrawHandle();
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
solver.boundaryLimits = new Bounds(limitsBoxHandle.center, limitsBoxHandle.size);
|
|
EditorUtility.SetDirty(target);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.UpdateIfRequiredOrScript();
|
|
EditorGUILayout.HelpBox("Particles:" + solver.allocParticleCount + "\n" +
|
|
"Simplices:" + solver.simplexCounts.simplexCount + "\n" +
|
|
"Contacts:" + solver.contactCount + "\n" +
|
|
"Simplex contacts:" + solver.particleContactCount, MessageType.None);
|
|
|
|
solverFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(solverFoldout, "Solver settings");
|
|
if (solverFoldout)
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(backend);
|
|
|
|
#if !(OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
|
if (backend.enumValueIndex == (int)ObiSolver.BackendType.Burst)
|
|
EditorGUILayout.HelpBox("The Burst backend depends on the following packages: Mathematics, Collections, Jobs and Burst. Please install the required dependencies. The solver will try to fall back to the Compute backend instead.", MessageType.Warning);
|
|
#endif
|
|
if (!SystemInfo.supportsComputeShaders)
|
|
{
|
|
EditorGUILayout.HelpBox("This platform doesn't support compute shaders. Please switch to the Burst backend.", MessageType.Error);
|
|
}
|
|
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
foreach (var t in targets)
|
|
(t as ObiSolver).UpdateBackend();
|
|
}
|
|
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("mode"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("interpolation"));
|
|
EditorGUILayout.PropertyField(synchronization);
|
|
EditorGUILayout.PropertyField(substeps);
|
|
EditorGUILayout.PropertyField(maxStepsPerFrame);
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
simulationFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(simulationFoldout, "Simulation settings");
|
|
if (simulationFoldout)
|
|
{
|
|
EditorGUILayout.PropertyField(gravitySpace);
|
|
EditorGUILayout.PropertyField(gravity);
|
|
EditorGUILayout.PropertyField(windSpace);
|
|
EditorGUILayout.PropertyField(ambientWind);
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("sleepThreshold"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("maxVelocity"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("maxAngularVelocity"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("damping"));
|
|
EditorGUILayout.PropertyField(worldLinearInertiaScale);
|
|
EditorGUILayout.PropertyField(worldAngularInertiaScale);
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("maxAnisotropy"));
|
|
EditorGUILayout.PropertyField(simulateWhenInvisible);
|
|
EditorGUILayout.PropertyField(useLimits);
|
|
if (useLimits.boolValue)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(killOffLimitsParticles);
|
|
EditorGUILayout.PropertyField(boundaryLimits);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
advectionFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(advectionFoldout, "Foam settings");
|
|
if (advectionFoldout)
|
|
{
|
|
EditorGUILayout.PropertyField(foamSubsteps);
|
|
EditorGUILayout.PropertyField(foamMinNeighbors);
|
|
EditorGUILayout.PropertyField(foamCollisions, new GUIContent("Foam Collisions (Compute only)"));
|
|
EditorGUILayout.PropertyField(foamRadiusScale);
|
|
EditorGUILayout.PropertyField(foamVolumeDensity);
|
|
EditorGUILayout.PropertyField(foamAmbientDensity);
|
|
EditorGUILayout.PropertyField(foamScatterColor);
|
|
EditorGUILayout.PropertyField(foamAmbientColor);
|
|
EditorGUILayout.PropertyField(maxFoamVelocityStretch);
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("foamGravityScale"));
|
|
EditorGUILayout.PropertyField(foamFade);
|
|
EditorGUILayout.PropertyField(foamAccelAgingRange);
|
|
EditorGUILayout.PropertyField(foamAccelAging);
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
collisionsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(collisionsFoldout, "Collision settings");
|
|
if (collisionsFoldout)
|
|
{
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("colliderCCD"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("particleCCD"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("collisionMargin"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("maxDepenetration"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("shockPropagation"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("surfaceCollisionIterations"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("surfaceCollisionTolerance"));
|
|
EditorGUILayout.PropertyField(parameters.FindPropertyRelative("diffusionMask"));
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
constraintsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(constraintsFoldout, "Constraint settings");
|
|
if (constraintsFoldout)
|
|
{
|
|
constraintLabelContent.text = "Distance";
|
|
EditorGUILayout.PropertyField(distanceConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Bending";
|
|
EditorGUILayout.PropertyField(bendingConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Particle collision / Queries";
|
|
EditorGUILayout.PropertyField(particleCollisionConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Particle friction";
|
|
EditorGUILayout.PropertyField(particleFrictionConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Collision";
|
|
EditorGUILayout.PropertyField(collisionConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Friction";
|
|
EditorGUILayout.PropertyField(frictionConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Skin";
|
|
EditorGUILayout.PropertyField(skinConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Volume";
|
|
EditorGUILayout.PropertyField(volumeConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Shape matching";
|
|
EditorGUILayout.PropertyField(shapeMatchingConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Tether";
|
|
EditorGUILayout.PropertyField(tetherConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Pin";
|
|
EditorGUILayout.PropertyField(pinConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Pinhole";
|
|
EditorGUILayout.PropertyField(pinholeConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Stitch";
|
|
EditorGUILayout.PropertyField(stitchConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Density";
|
|
EditorGUILayout.PropertyField(densityConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Stretch & Shear";
|
|
EditorGUILayout.PropertyField(stretchShearConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Bend & Twist";
|
|
EditorGUILayout.PropertyField(bendTwistConstraintParameters, constraintLabelContent);
|
|
|
|
constraintLabelContent.text = "Chain";
|
|
EditorGUILayout.PropertyField(chainConstraintParameters, constraintLabelContent);
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
memoryFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(memoryFoldout, "Memory budget");
|
|
if (memoryFoldout)
|
|
{
|
|
EditorGUILayout.PropertyField(maxQueryResults);
|
|
EditorGUILayout.PropertyField(maxFoamParticles);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(maxSurfaceChunks);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
foreach (var t in targets)
|
|
(t as ObiSolver).dirtyRendering |= (int)Oni.RenderingSystemType.Fluid;
|
|
}
|
|
|
|
EditorGUILayout.PropertyField(maxParticleNeighbors);
|
|
EditorGUILayout.PropertyField(maxParticleContacts);
|
|
|
|
uint usedChunks = solver.usedSurfaceChunks;
|
|
float usagePercentage = usedChunks / (float)maxSurfaceChunks.intValue;
|
|
uint foamParticles = solver.initialized ? solver.implementation.activeFoamParticleCount : 0;
|
|
|
|
// memory consumption per chunk:
|
|
// (8 + 12 + 64*4 + 64*6*4 + 64*16) = 2836 bytes
|
|
EditorGUILayout.HelpBox("Active foam particles: " + foamParticles + "/" + maxFoamParticles.intValue + "\n"+
|
|
"Surface memory (Mb): " + string.Format("{0:N2}", maxSurfaceChunks.intValue * 0.002836f)+ "\n"+
|
|
"Used surface chunks: "+ usedChunks + "/"+ maxSurfaceChunks.intValue + ", hashtable usage "+ string.Format("{0:N1}", usagePercentage * 100) + "%", MessageType.None);
|
|
|
|
if (usagePercentage >= 0.5f)
|
|
{
|
|
EditorGUILayout.HelpBox("Hashtable usage should be below 50% for best performance. Increase max surface chunks if % is too high.", MessageType.Warning);
|
|
}
|
|
}
|
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
|
|
|
// Apply changes to the serializedProperty
|
|
if (GUI.changed)
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
solver.PushSolverParameters();
|
|
}
|
|
|
|
}
|
|
|
|
[DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.Selected)]
|
|
static void DrawGizmoForSolver(ObiSolver solver, GizmoType gizmoType)
|
|
{
|
|
if ((gizmoType & GizmoType.InSelectionHierarchy) != 0)
|
|
{
|
|
Gizmos.color = new Color(1, 1, 1, 0.5f);
|
|
var bounds = solver.bounds;
|
|
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|