Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: invert toggle states, disable dynamics and follow sync on adding to cabinet #147

Merged
merged 1 commit into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions Packages/com.chocopoi.vrc.dressingtools/Editor/DTEditorUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Chocopoi.DressingTools.Cabinet;
using Chocopoi.DressingTools.Lib.Cabinet;
using Chocopoi.DressingTools.Lib.Wearable;
using Chocopoi.DressingTools.Lib.Wearable.Modules.Providers;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
Expand Down Expand Up @@ -80,8 +81,14 @@ public static DTCabinet GetAvatarCabinet(GameObject avatar, bool createIfNotExis
return comp;
}

public static void AddCabinetWearable(DTCabinet cabinet, WearableConfig config, GameObject wearableGameObject)
public static bool AddCabinetWearable(DTCabinet cabinet, WearableConfig config, GameObject wearableGameObject)
{
// do not add if there's an existing component
if (wearableGameObject.GetComponent<DTCabinetWearable>() != null)
{
return false;
}

if (PrefabUtility.IsPartOfAnyPrefab(wearableGameObject) && PrefabUtility.GetPrefabInstanceStatus(wearableGameObject) == PrefabInstanceStatus.NotAPrefab)
{
// if not in scene, we instantiate it with a prefab connection
Expand All @@ -91,14 +98,31 @@ public static void AddCabinetWearable(DTCabinet cabinet, WearableConfig config,
// parent to avatar
wearableGameObject.transform.SetParent(cabinet.transform);

if (wearableGameObject.GetComponent<DTCabinetWearable>() == null)
// add cabinet wearable component
var cabinetWearable = wearableGameObject.AddComponent<DTCabinetWearable>();

cabinetWearable.wearableGameObject = wearableGameObject;
cabinetWearable.configJson = config.Serialize().ToString(Formatting.None);

// do provider hooks
var providers = ModuleProviderLocator.Instance.GetAllProviders();
foreach (var provider in providers)
{
// add cabinet wearable component
var cabinetWearable = wearableGameObject.AddComponent<DTCabinetWearable>();
var module = DTRuntimeUtils.FindWearableModule(config, provider.ModuleIdentifier);
if (module == null)
{
// config does not have such module
continue;
}

cabinetWearable.wearableGameObject = wearableGameObject;
cabinetWearable.configJson = config.Serialize().ToString(Formatting.None);
if (!provider.OnAddWearableToCabinet(cabinet, config, wearableGameObject, module))
{
Debug.LogWarning("[DressingTools] [AddCabinetWearable] Error processing provider OnAddWearableToCabinet hook: " + provider.ModuleIdentifier);
return false;
}
}

return true;
}

public static void RemoveCabinetWearable(DTCabinet cabinet, DTCabinetWearable wearable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,6 @@ public GenerateAnimationsHook(DTReport report, DTCabinet cabinet)
_cabinet = cabinet;
}

private AnimationGenerationModuleConfig FindAnimationGenerationModuleConfig(WearableConfig config)
{
foreach (var module in config.Modules)
{
if (module.config is AnimationGenerationModuleConfig agm)
{
return agm;
}
}
return null;
}

public bool OnPreprocessAvatar()
{
EditorUtility.DisplayProgressBar("DressingTools", "Generating animations...", 0);
Expand Down Expand Up @@ -151,7 +139,7 @@ public bool OnPreprocessAvatar()
var wearableDynamics = DTRuntimeUtils.ScanDynamics(wearables[i].wearableGameObject, false);

// find the animation generation module
var module = FindAnimationGenerationModuleConfig(config);
var module = DTRuntimeUtils.FindWearableModuleConfig<AnimationGenerationModuleConfig>(config);
if (module == null)
{
Debug.Log("[DressingTools] [BuildDTCabinetCallback] [GenerateAnimationHook] " + config.Info.name + " has no AnimationGenerationModule, skipping this wearable generation");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
* You should have received a copy of the GNU General Public License along with DressingTools. If not, see <https://www.gnu.org/licenses/>.
*/

using Chocopoi.DressingTools.Lib.Cabinet;
using Chocopoi.DressingTools.Lib.Wearable.Modules;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace Chocopoi.DressingTools.Lib.Wearable.Modules.Providers
{
Expand All @@ -34,5 +36,6 @@ public abstract class ModuleProviderBase
public virtual bool OnBeforeApplyCabinet(ApplyCabinetContext ctx, IModuleConfig moduleConfig) => true;
public virtual bool OnApplyWearable(ApplyCabinetContext cabCtx, ApplyWearableContext wearCtx, IModuleConfig moduleConfig) => true;
public virtual bool OnAfterApplyCabinet(ApplyCabinetContext ctx, IModuleConfig moduleConfig) => true;
public virtual bool OnAddWearableToCabinet(ICabinet cabinet, WearableConfig config, GameObject wearableGameObject, WearableModule module) => true;
}
}
25 changes: 25 additions & 0 deletions Packages/com.chocopoi.vrc.dressingtools/Runtime/DTRuntimeUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Chocopoi.DressingTools.Lib.Cabinet;
using Chocopoi.DressingTools.Lib.Proxy;
using Chocopoi.DressingTools.Lib.Wearable;
using Chocopoi.DressingTools.Lib.Wearable.Modules;
using Chocopoi.DressingTools.Proxy;
using Newtonsoft.Json;
using UnityEngine;
Expand Down Expand Up @@ -310,6 +311,30 @@ public static Component CopyComponent(Component originalComponent, GameObject de
return destComp;
}

public static T FindWearableModuleConfig<T>(WearableConfig config) where T : IModuleConfig
{
foreach (var module in config.Modules)
{
if (module.config is T moduleConfig)
{
return moduleConfig;
}
}
return default;
}

public static WearableModule FindWearableModule(WearableConfig config, string moduleName)
{
foreach (var module in config.Modules)
{
if (moduleName == module.moduleName)
{
return module;
}
}
return null;
}

public static bool IsOriginatedFromAnyWearable(Transform root, Transform transform)
{
var found = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,56 @@ static AnimationGenerationModuleProvider()
public override IModuleConfig DeserializeModuleConfig(JObject jObject) => jObject.ToObject<AnimationGenerationModuleConfig>();

public override IModuleConfig NewModuleConfig() => new AnimationGenerationModuleConfig();

public override bool OnAddWearableToCabinet(ICabinet cabinet, WearableConfig config, GameObject wearableGameObject, WearableModule module)
{
var agm = (AnimationGenerationModuleConfig)module.config;
var avatarGameObject = cabinet.AvatarGameObject;

// invert avatar toggles
foreach (var toggle in agm.avatarAnimationOnWear.toggles)
{
var avatarToggleObj = avatarGameObject.transform.Find(toggle.path);
if (avatarToggleObj == null)
{
Debug.LogWarning("[DressingTools] [AnimationGenerationModule] Avatar toggle GameObject not found at path: " + toggle.path);
continue;
}
avatarToggleObj.gameObject.SetActive(!toggle.state);
}

// invert wearable toggles
foreach (var toggle in agm.wearableAnimationOnWear.toggles)
{
var wearableToggleObj = wearableGameObject.transform.Find(toggle.path);
if (wearableGameObject == null)
{
Debug.LogWarning("[DressingTools] [AnimationGenerationModule] Wearable toggle GameObject not found at path: " + toggle.path);
continue;
}
wearableToggleObj.gameObject.SetActive(!toggle.state);
}

// set wearable dynamics inactive
var wearableDynamics = DTRuntimeUtils.ScanDynamics(wearableGameObject, false);
var visitedDynamicsTransforms = new List<Transform>();
foreach (var dynamics in wearableDynamics)
{
if (visitedDynamicsTransforms.Contains(dynamics.Transform))
{
// skip duplicates since it's meaningless
continue;
}

// we toggle GameObjects instead of components
dynamics.GameObject.SetActive(false);

// mark as visited
visitedDynamicsTransforms.Add(dynamics.Transform);
}

return true;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,62 @@ static BlendshapeSyncModuleProvider()
public override IModuleConfig DeserializeModuleConfig(JObject jObject) => jObject.ToObject<BlendshapeSyncModuleConfig>();

public override IModuleConfig NewModuleConfig() => new BlendshapeSyncModuleConfig();

public override bool OnAddWearableToCabinet(ICabinet cabinet, WearableConfig config, GameObject wearableGameObject, WearableModule module)
{
var avatarGameObject = cabinet.AvatarGameObject;
var bsm = (BlendshapeSyncModuleConfig)module.config;

// follow blendshape sync
foreach (var bs in bsm.blendshapeSyncs)
{
var avatarSmrObj = avatarGameObject.transform.Find(bs.avatarPath);
if (avatarSmrObj == null)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync avatar GameObject at path not found: " + bs.avatarPath);
continue;
}

var avatarSmr = avatarSmrObj.GetComponent<SkinnedMeshRenderer>();
if (avatarSmr == null || avatarSmr.sharedMesh == null)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync avatar GameObject at path does not have SkinnedMeshRenderer or Mesh attached: " + bs.avatarPath);
continue;
}

var avatarBlendshapeIndex = avatarSmr.sharedMesh.GetBlendShapeIndex(bs.avatarBlendshapeName);
if (avatarBlendshapeIndex == -1)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync avatar GameObject does not have blendshape: " + bs.avatarBlendshapeName);
continue;
}

var wearableSmrObj = wearableGameObject.transform.Find(bs.wearablePath);
if (wearableSmrObj == null)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync wearable GameObject at path not found: " + bs.wearablePath);
continue;
}

var wearableSmr = wearableSmrObj.GetComponent<SkinnedMeshRenderer>();
if (wearableSmr == null)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync wearable GameObject at path does not have SkinnedMeshRenderer or Mesh attached: " + bs.avatarPath);
continue;
}

var wearableBlendshapeIndex = wearableSmr.sharedMesh.GetBlendShapeIndex(bs.wearableBlendshapeName);
if (wearableBlendshapeIndex == -1)
{
Debug.LogWarning("[DressingTools] [BlendshapeSyncProvider] Blendshape sync wearable GameObject does not have blendshape: " + bs.wearableBlendshapeName);
continue;
}

// copy value from avatar to wearable
wearableSmr.SetBlendShapeWeight(wearableBlendshapeIndex, avatarSmr.GetBlendShapeWeight(avatarBlendshapeIndex));
}

return true;
}
}
}
Loading