├── .gitignore ├── MMD4MFace.cs ├── MMD4MFaceBlink.cs ├── MMD4MFaceController.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | -------------------------------------------------------------------------------- /MMD4MFace.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * MMD4MFace 3 | * author : udasan 4 | */ 5 | using UnityEngine; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | 9 | public class MMD4MFace : MonoBehaviour 10 | { 11 | public string faceName; 12 | 13 | [System.Serializable] 14 | public class MorphParam { 15 | public string morphName; 16 | public float morphSpeed; 17 | public float morphWeight; 18 | //public bool overrideWeight; // true 19 | } 20 | public MorphParam[] morphParams; 21 | 22 | MMD4MFaceController faceController_; 23 | public MMD4MFaceController faceController { 24 | get { return faceController_ ? faceController_ : (faceController_ = this.GetComponent()); } 25 | } 26 | 27 | Dictionary morphParamDict_ = new Dictionary(); 28 | public MorphParam GetMorphParam (string morphName) { 29 | if (morphParamDict_.Count == 0 && morphParams.Length != 0) { 30 | foreach (var m in morphParams) { 31 | morphParamDict_.Add(m.morphName, m); 32 | } 33 | } 34 | 35 | MorphParam morphParam; 36 | morphParamDict_.TryGetValue(morphName, out morphParam); 37 | return morphParam; 38 | } 39 | 40 | void Update () 41 | { 42 | // to show checkbox on inspector 43 | } 44 | 45 | void OnDisable () 46 | { 47 | if (faceController.currentFace == this) { 48 | faceController.ResetFace(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MMD4MFaceBlink.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * MMD4MFaceBlink 3 | * author : udasan 4 | */ 5 | using UnityEngine; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | 9 | public class MMD4MFaceBlink : MonoBehaviour 10 | { 11 | [System.Serializable] 12 | public class BlinkParam { 13 | public string morphName; 14 | public const float minimumWeight = 0.0f; 15 | public const float maximumWeight = 1.0f; 16 | } 17 | public BlinkParam[] blinkParams; 18 | 19 | public float closingTime = 0.05f; 20 | public float closedTime = 0.1f; 21 | public float openingTime = 0.05f; 22 | 23 | public float minimumInterval = 0.5f; 24 | public float maximumInterval = 8.0f; 25 | 26 | public string[] preferredMorphs; 27 | 28 | float nextTime_; 29 | 30 | enum BlinkState { 31 | Opened, 32 | Closing, 33 | Closed, 34 | Opening 35 | } 36 | BlinkState blinkState_ = BlinkState.Opened; 37 | 38 | public bool isBlinking { 39 | get { return blinkState_ != BlinkState.Opened; } 40 | } 41 | 42 | MMD4MecanimModel model_; 43 | Dictionary preferredModelMorphDict_ = new Dictionary(); 44 | bool isPreferdMorphEnabledCache_; 45 | 46 | Dictionary morphHelperDict_ = new Dictionary(); 47 | public MMD4MecanimMorphHelper GetMorphHelper (string morphName) 48 | { 49 | MMD4MecanimMorphHelper morphHelper; 50 | morphHelperDict_.TryGetValue(morphName, out morphHelper); 51 | return morphHelper; 52 | } 53 | 54 | float GetNextInterval () 55 | { 56 | return Random.Range(minimumInterval, maximumInterval); 57 | } 58 | 59 | void Awake () 60 | { 61 | model_ = this.GetComponentInChildren(); 62 | if (!model_) { return; } 63 | 64 | var originalMorphHelperDict = new Dictionary(); 65 | foreach (var morphHelper in model_.GetComponents()) { 66 | originalMorphHelperDict.Add(morphHelper.morphName, morphHelper); 67 | } 68 | 69 | foreach (var blinkParam in blinkParams) { 70 | if (morphHelperDict_.ContainsKey(blinkParam.morphName)) { continue; } 71 | MMD4MecanimMorphHelper morphHelper; 72 | originalMorphHelperDict.TryGetValue(blinkParam.morphName, out morphHelper); 73 | if (!morphHelper) { 74 | morphHelper = model_.gameObject.AddComponent(); 75 | morphHelper.morphName = blinkParam.morphName; 76 | } 77 | morphHelperDict_.Add(blinkParam.morphName, morphHelper); 78 | } 79 | } 80 | 81 | void Start () 82 | { 83 | ResetBlinking(); 84 | nextTime_ = Time.time + GetNextInterval(); 85 | } 86 | 87 | void Update () 88 | { 89 | UpdatePreferredMorphs(); 90 | bool isPreferredMorphEnabled = IsPreferredMorphEnabled(); 91 | if (isPreferredMorphEnabled) { 92 | if (isPreferredMorphEnabled != isPreferdMorphEnabledCache_) { 93 | ResetBlinking(); 94 | } 95 | isPreferdMorphEnabledCache_ = isPreferredMorphEnabled; 96 | return; 97 | } else { 98 | if (isPreferredMorphEnabled != isPreferdMorphEnabledCache_) { 99 | nextTime_ = Time.time + GetNextInterval(); 100 | } 101 | isPreferdMorphEnabledCache_ = isPreferredMorphEnabled; 102 | } 103 | 104 | switch (blinkState_) { 105 | case BlinkState.Opened : OnOpened(); break; 106 | case BlinkState.Closing : OnClosing(); break; 107 | case BlinkState.Closed : OnClosed(); break; 108 | case BlinkState.Opening : OnOpening(); break; 109 | default : break; 110 | } 111 | } 112 | 113 | void OnEnable () 114 | { 115 | nextTime_ = Time.time + GetNextInterval(); 116 | } 117 | 118 | void OnDisable () 119 | { 120 | ResetBlinking(); 121 | } 122 | 123 | void UpdatePreferredMorphs () 124 | { 125 | foreach (string morphName in preferredMorphs) { 126 | if (preferredModelMorphDict_.ContainsKey(morphName)) { continue; } // already included 127 | 128 | MMD4MecanimModel.Morph modelMorph = model_.GetMorph(morphName); 129 | if (modelMorph == null) { continue; } // not found 130 | 131 | preferredModelMorphDict_.Add(morphName, modelMorph); 132 | } 133 | } 134 | 135 | bool IsPreferredMorphEnabled () 136 | { 137 | foreach (var modelMorph in preferredModelMorphDict_.Values) { 138 | if (modelMorph == null) { continue; } 139 | if (modelMorph.weight != 0.0f) { return true; } 140 | } 141 | return false; 142 | } 143 | 144 | void OnOpened () 145 | { 146 | if (Time.time < nextTime_) { return; } 147 | 148 | foreach (var blinkParam in blinkParams) { 149 | MMD4MecanimMorphHelper morphHelper = morphHelperDict_[blinkParam.morphName]; 150 | morphHelper.morphSpeed = closingTime; 151 | morphHelper.morphWeight = BlinkParam.maximumWeight; 152 | } 153 | 154 | nextTime_ = Time.time + closingTime; 155 | blinkState_ = BlinkState.Closing; 156 | } 157 | 158 | void OnClosing () 159 | { 160 | if (Time.time < nextTime_) { return; } 161 | 162 | foreach (var morphHelper in morphHelperDict_.Values) { 163 | if (morphHelper.isProcessing) { return; } 164 | } 165 | 166 | nextTime_ += closedTime; 167 | blinkState_ = BlinkState.Closed; 168 | } 169 | 170 | void OnClosed () 171 | { 172 | if (Time.time < nextTime_) { return; } 173 | 174 | foreach (var blinkParam in blinkParams) { 175 | MMD4MecanimMorphHelper morphHelper = morphHelperDict_[blinkParam.morphName]; 176 | morphHelper.morphSpeed = openingTime; 177 | morphHelper.morphWeight = BlinkParam.minimumWeight; 178 | } 179 | 180 | nextTime_ = Time.time + openingTime; 181 | blinkState_ = BlinkState.Opening; 182 | } 183 | 184 | void OnOpening () 185 | { 186 | if (Time.time < nextTime_) { return; } 187 | 188 | foreach (var morphHelper in morphHelperDict_.Values) { 189 | if (morphHelper.isProcessing) { return; } 190 | } 191 | 192 | nextTime_ = Time.time + GetNextInterval(); 193 | blinkState_ = BlinkState.Opened; 194 | } 195 | 196 | void ResetBlinking () 197 | { 198 | foreach (var morphHelper in morphHelperDict_.Values) { 199 | morphHelper.morphSpeed = 0.0f; 200 | morphHelper.morphWeight = BlinkParam.minimumWeight; 201 | } 202 | 203 | blinkState_ = BlinkState.Opened; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /MMD4MFaceController.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * MMD4MFaceController 3 | * author : udasan 4 | */ 5 | using UnityEngine; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | 9 | public class MMD4MFaceController : MonoBehaviour 10 | { 11 | public string defaultFaceName; 12 | 13 | public float defaultMorphSpeed = 0.1f; 14 | public bool defaultOverrideWeight = true; 15 | 16 | const float defaultMorphWeight = 0.0f; 17 | 18 | MMD4MFace currentFace_; 19 | public MMD4MFace currentFace { 20 | get { 21 | return currentFace_; 22 | } 23 | private set { 24 | currentFace_ = value; 25 | } 26 | } 27 | 28 | public string currentFaceName { 29 | get { 30 | return currentFace ? currentFace.faceName : ""; 31 | } 32 | private set { 33 | currentFace = this[value]; 34 | } 35 | } 36 | 37 | Dictionary faceDict_ = new Dictionary(); 38 | public MMD4MFace this[string faceName] { 39 | get { 40 | MMD4MFace face; 41 | faceDict_.TryGetValue(faceName, out face); 42 | return face; 43 | } 44 | } 45 | 46 | Dictionary morphHelperDict_ = new Dictionary(); 47 | public MMD4MecanimMorphHelper GetMorphHelper (string morphName) 48 | { 49 | MMD4MecanimMorphHelper morphHelper; 50 | morphHelperDict_.TryGetValue(morphName, out morphHelper); 51 | return morphHelper; 52 | } 53 | 54 | public bool IsFace (MMD4MFace face) 55 | { 56 | return (face && (face == currentFace)); 57 | } 58 | 59 | public bool IsFace (string faceName) 60 | { 61 | return IsFace(this[faceName]); 62 | } 63 | 64 | public bool HasFace (MMD4MFace face) 65 | { 66 | return face ? HasFace(face.faceName) : false; 67 | } 68 | 69 | public bool HasFace (string faceName) 70 | { 71 | return faceDict_.ContainsKey(faceName); 72 | } 73 | 74 | public bool isProcessing { 75 | get { 76 | foreach (var morphHelper in morphHelperDict_.Values) { 77 | if (morphHelper.isProcessing) { return true; } 78 | } 79 | return false; 80 | } 81 | } 82 | 83 | public bool isAnimating { 84 | get { 85 | foreach (var morphHelper in morphHelperDict_.Values) { 86 | if (morphHelper.isAnimating) { return true; } 87 | } 88 | return false; 89 | } 90 | } 91 | 92 | void Awake () 93 | { 94 | var model = this.GetComponentInChildren(); 95 | if (!model) { return; } 96 | 97 | var originalMorphHelperDict = new Dictionary(); 98 | foreach (var morphHelper in model.GetComponents()) { 99 | originalMorphHelperDict.Add(morphHelper.morphName, morphHelper); 100 | } 101 | 102 | foreach (var face in this.GetComponents()) { 103 | faceDict_.Add(face.faceName, face); 104 | foreach (var morphParam in face.morphParams) { 105 | if (morphHelperDict_.ContainsKey(morphParam.morphName)) { continue; } 106 | MMD4MecanimMorphHelper morphHelper; 107 | originalMorphHelperDict.TryGetValue(morphParam.morphName, out morphHelper); 108 | if (!morphHelper) { 109 | morphHelper = model.gameObject.AddComponent(); 110 | morphHelper.morphName = morphParam.morphName; 111 | } 112 | morphHelperDict_.Add(morphParam.morphName, morphHelper); 113 | } 114 | } 115 | } 116 | 117 | void Start () 118 | { 119 | SetFace(defaultFaceName); 120 | } 121 | 122 | void Update () 123 | { 124 | // to show checkbox on inspector 125 | } 126 | 127 | void OnDisable () 128 | { 129 | ResetFace(); 130 | } 131 | 132 | public void SetFace (MMD4MFace face) 133 | { 134 | if (!this.enabled) { return; } 135 | if (!face || !face.enabled) { ResetFace (); return; } 136 | 137 | foreach (var morphHelper in morphHelperDict_.Values) { 138 | var morphParam = face.GetMorphParam(morphHelper.morphName); 139 | if (morphParam != null) { 140 | morphHelper.morphSpeed = morphParam.morphSpeed; 141 | morphHelper.morphWeight = morphParam.morphWeight; 142 | } else { 143 | morphHelper.morphSpeed = defaultMorphSpeed; 144 | morphHelper.morphWeight = defaultMorphWeight; 145 | } 146 | morphHelper.overrideWeight = defaultOverrideWeight; 147 | } 148 | 149 | currentFace = face; 150 | } 151 | 152 | public void SetFace (string faceName) 153 | { 154 | SetFace (this[faceName]); 155 | } 156 | 157 | public void ResetFace () 158 | { 159 | foreach (var morphHelper in morphHelperDict_.Values) { 160 | morphHelper.morphSpeed = defaultMorphSpeed; 161 | morphHelper.morphWeight = defaultMorphWeight; 162 | morphHelper.overrideWeight = defaultOverrideWeight; 163 | } 164 | 165 | currentFace = null; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | unity-mmd4mface 2 | =============== 3 | 4 | face controller for MMD4Mecanim 5 | 6 | see this site for detail info : 7 | http://udasankoubou.blogspot.jp/2014/02/unitymmd4mface-mmd4mecanim.html 8 | 9 | MMD4Mecanim is made by Stereoarts (http://stereoarts.jp/). 10 | --------------------------------------------------------------------------------