DQ skinning for Unity
Dual Quaternion skinning implementation with bulging compensation
DualQuaternionSkinnerEditor.cs
1 using UnityEngine;
2 using UnityEditor;
3 
4 using System.Collections.Generic;
5 
6 #if UNITY_EDITOR
7 
11 [CustomEditor(typeof(DualQuaternionSkinner))]
12 public class DualQuaternionSkinnerEditor : Editor
13 {
14  SerializedProperty shaderComputeBoneDQ;
15  SerializedProperty shaderDQBlend;
16  SerializedProperty shaderApplyMorph;
17  SerializedProperty bulgeCompensation;
18 
20  SkinnedMeshRenderer smr
21  {
22  get
23  {
24  if (this._smr == null)
25  {
26  this._smr = ((DualQuaternionSkinner)this.target).gameObject.GetComponent<SkinnedMeshRenderer>();
27  }
28 
29  return this._smr;
30  }
31  }
32  SkinnedMeshRenderer _smr;
33 
34  bool showBlendShapes = false;
35 
36  enum BoneOrientation {X, Y, Z, negativeX, negativeY, negativeZ}
37  readonly Dictionary<BoneOrientation, Vector3> boneOrientationVectors = new Dictionary<BoneOrientation, Vector3>();
38 
39  private void OnEnable()
40  {
41  this.shaderComputeBoneDQ = this.serializedObject.FindProperty("shaderComputeBoneDQ");
42  this.shaderDQBlend = this.serializedObject.FindProperty("shaderDQBlend");
43  this.shaderApplyMorph = this.serializedObject.FindProperty("shaderApplyMorph");
44  this.bulgeCompensation = this.serializedObject.FindProperty("bulgeCompensation");
45 
46  this.boneOrientationVectors.Add(BoneOrientation.X, new Vector3(1,0,0));
47  this.boneOrientationVectors.Add(BoneOrientation.Y, new Vector3(0,1,0));
48  this.boneOrientationVectors.Add(BoneOrientation.Z, new Vector3(0,0,1));
49  this.boneOrientationVectors.Add(BoneOrientation.negativeX, new Vector3(-1,0,0));
50  this.boneOrientationVectors.Add(BoneOrientation.negativeY, new Vector3(0,-1,0));
51  this.boneOrientationVectors.Add(BoneOrientation.negativeZ, new Vector3(0,0,-1));
52  }
53 
54  public override void OnInspectorGUI()
55  {
56  this.serializedObject.Update();
57 
58  this.dqs = (DualQuaternionSkinner)this.target;
59 
60  EditorGUILayout.BeginHorizontal();
61  EditorGUILayout.LabelField("Mode: ", GUILayout.Width(80));
62  EditorGUILayout.LabelField(Application.isPlaying ? "Play" : "Editor", GUILayout.Width(80));
63  EditorGUILayout.EndHorizontal();
64 
65  EditorGUILayout.BeginHorizontal();
66  EditorGUILayout.LabelField("DQ skinning: ", GUILayout.Width(80));
67  EditorGUILayout.LabelField(this.dqs.started ? "ON" : "OFF", GUILayout.Width(80));
68  EditorGUILayout.EndHorizontal();
69 
70  EditorGUILayout.Space();
71  this.dqs.SetViewFrustrumCulling(EditorGUILayout.Toggle("View frustrum culling: ", this.dqs.GetViewFrustrumCulling()));
72  EditorGUILayout.Space();
73 
74  BoneOrientation currentOrientation = BoneOrientation.X;
75  foreach(BoneOrientation orientation in this.boneOrientationVectors.Keys)
76  {
77  if (this.dqs.boneOrientationVector == this.boneOrientationVectors[orientation])
78  {
79  currentOrientation = orientation;
80  break;
81  }
82  }
83  var newOrientation = (BoneOrientation)EditorGUILayout.EnumPopup("Bone orientation: ", currentOrientation);
84  if (this.dqs.boneOrientationVector != this.boneOrientationVectors[newOrientation])
85  {
86  this.dqs.boneOrientationVector = this.boneOrientationVectors[newOrientation];
88  }
89 
90  EditorGUILayout.PropertyField(this.bulgeCompensation);
91  EditorGUILayout.Space();
92 
93  this.showBlendShapes = EditorGUILayout.Foldout(this.showBlendShapes, "Blend shapes");
94 
95  if (this.showBlendShapes)
96  {
97  if (this.dqs.started == false)
98  {
99  EditorGUI.BeginChangeCheck();
100  Undo.RecordObject(this.dqs.gameObject.GetComponent<SkinnedMeshRenderer>(), "changed blendshape weights by DualQuaternionSkinner component");
101  }
102 
103  for (int i = 0; i < this.dqs.mesh.blendShapeCount; i++)
104  {
105  EditorGUILayout.BeginHorizontal();
106  EditorGUILayout.LabelField(" " + this.dqs.mesh.GetBlendShapeName(i), GUILayout.Width(EditorGUIUtility.labelWidth - 10));
107  float weight = EditorGUILayout.Slider(this.dqs.GetBlendShapeWeight(i), 0, 100);
108  EditorGUILayout.EndHorizontal();
109  this.dqs.SetBlendShapeWeight(i, weight);
110  }
111  }
112 
113  EditorGUILayout.Space();
114 
115  EditorGUILayout.PropertyField(this.shaderComputeBoneDQ);
116  EditorGUILayout.PropertyField(this.shaderDQBlend);
117  EditorGUILayout.PropertyField(this.shaderApplyMorph);
118 
119  EditorGUILayout.Space();
120  EditorGUILayout.LabelField("Problems: ");
121 
122  if (this.CheckProblems() == false)
123  {
124  EditorGUILayout.LabelField("not detected (this is good)");
125  }
126 
127  this.serializedObject.ApplyModifiedProperties();
128  }
129 
130  bool CheckProblems()
131  {
132  var wrapStyle = new GUIStyle() { wordWrap = true };
133 
134  if (this.smr.sharedMesh.isReadable == false)
135  {
136  EditorGUILayout.Space();
137  EditorGUILayout.LabelField("Skinned mesh must be read/write enabled (check import settings)", wrapStyle);
138  return true;
139  }
140 
141  if (this.smr.rootBone.parent != this.dqs.gameObject.transform.parent)
142  {
143  EditorGUILayout.Space();
144 
145  EditorGUILayout.BeginHorizontal();
146  EditorGUILayout.LabelField("Skinned object and root bone must be children of the same parent", wrapStyle);
147  if (GUILayout.Button("auto fix"))
148  {
149  Undo.SetTransformParent(
150  this.smr.rootBone,
151  this.dqs.gameObject.transform.parent,
152  "Changed root bone's parent by Dual Quaternion Skinner (auto fix)"
153  );
154  EditorApplication.RepaintHierarchyWindow();
155  }
156  EditorGUILayout.EndHorizontal();
157 
158  return true;
159  }
160 
161  foreach(Transform bone in this.smr.bones)
162  {
163  if (bone.localScale != Vector3.one)
164  {
165  EditorGUILayout.Space();
166 
167  EditorGUILayout.BeginHorizontal();
168  EditorGUILayout.LabelField(string.Format("Bone scaling not supported: {0}", bone.name), wrapStyle);
169  if (GUILayout.Button("auto fix"))
170  {
171  Undo.RecordObject(bone, "Set bone scale to (1,1,1) by Dual Quaternion Skinner (auto fix)");
172  bone.localScale = Vector3.one;
173  }
174  EditorGUILayout.EndHorizontal();
175 
176  return true;
177  }
178  }
179 
180  return false;
181  }
182 }
183 
184 #endif
DualQuaternionSkinner
Replaces Unity's default linear skinning with DQ skinning
Definition: DualQuaternionSkinner.cs:12
DualQuaternionSkinner.SetBlendShapeWeight
void SetBlendShapeWeight(int index, float weight)
Set weight for the blend shape with given index. Default range is 0-100. It is possible to apply ne...
Definition: DualQuaternionSkinner.cs:244
DualQuaternionSkinner.GetBlendShapeWeight
float GetBlendShapeWeight(int index)
Returns currently applied weight for the blend shape with given index. Default range is 0-100....
Definition: DualQuaternionSkinner.cs:269
DualQuaternionSkinner.started
bool started
Indicates whether DualQuaternionSkinner is currently active.
Definition: DualQuaternionSkinner.cs:75
DualQuaternionSkinner.mesh
Mesh mesh
UnityEngine.Mesh that is currently being rendered.
Definition: DualQuaternionSkinner.cs:291
DualQuaternionSkinner.UpdatePerVertexCompensationCoef
void UpdatePerVertexCompensationCoef()
If the value of boneOrientationVector was changed while DualQuaternionSkinner is active (started == t...
Definition: DualQuaternionSkinner.cs:306
DualQuaternionSkinner.boneOrientationVector
Vector3 boneOrientationVector
Bone orientation is required for bulge-compensation. Do not set directly, use custom editor instead.
Definition: DualQuaternionSkinner.cs:17
DualQuaternionSkinnerEditor
Provides custom inspector for DualQuaternionSkinner
Definition: DualQuaternionSkinnerEditor.cs:13
DualQuaternionSkinner.SetViewFrustrumCulling
void SetViewFrustrumCulling(bool viewFrustrumculling)
Enable or disable view frustrum culling. When moving the bones manually to test the script,...
Definition: DualQuaternionSkinner.cs:167