ADD: VRCDeveloperTool 스크립트 업로드

This commit is contained in:
DESKTOP-S4BOTN2\user 2025-05-05 22:42:16 +09:00
parent 139756d662
commit df848b2c8b
87 changed files with 18784 additions and 0 deletions

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c0567de9323ae234dbc4465a542d0be3
folderAsset: yes
timeCreated: 1536028717
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9adcb8c102e5f7948b0f7e44f6c4bf0e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,361 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Text.RegularExpressions;
// ver 1.0
// © 2019 gatosyocora
namespace VRCDeveloperTool
{
public class AnimationPropertyConverter : EditorWindow
{
private SkinnedMeshRenderer avatarMesh;
private List<AnimationClip> preAnimationClips;
private List<AnimationProperty> animPropertyList;
private string[] blendShapeNameList;
private string saveFolder = "Assets/";
private bool convertBlendShapeName = false;
private bool isOpeningPreAnimationClips = true;
private bool isOpeningAnimationPropertyList = true;
private Vector2 propertyScrollPos = Vector2.zero;
private bool isConvertAll = false;
public class AnimationProperty
{
public string propertyType;
public string preName;
public string posName;
public bool isConvert;
public int selectedBlendShapeIndex;
public List<int> animIndexHavingThisProperty;
public AnimationProperty(string type, string name, int animIndex)
{
propertyType = type;
preName = name;
posName = preName;
isConvert = false;
selectedBlendShapeIndex = 0;
animIndexHavingThisProperty = new List<int>() { animIndex };
}
public void AddAnimIndexHavingThisProperty(int animIndex)
{
animIndexHavingThisProperty.Add(animIndex);
}
public bool RemoveAnimIndexHavingThisProperty(int animIndex)
{
return animIndexHavingThisProperty.Remove(animIndex);
}
public bool existAnimHavingThisProperty()
{
return animIndexHavingThisProperty.Count > 0;
}
}
[MenuItem("VRCDeveloperTool/AnimationPropertyConverter")]
private static void Create()
{
GetWindow<AnimationPropertyConverter>("AnimationProperty Converter");
}
private void OnEnable()
{
avatarMesh = null;
preAnimationClips = new List<AnimationClip>();
preAnimationClips.Add(null);
animPropertyList = new List<AnimationProperty>();
blendShapeNameList = null;
}
private void OnGUI()
{
convertBlendShapeName = EditorGUILayout.ToggleLeft("Convert BlendShapeName", convertBlendShapeName);
if (convertBlendShapeName)
{
using (var check = new EditorGUI.ChangeCheckScope())
{
avatarMesh = EditorGUILayout.ObjectField(
"Avatar's SkinnedMeshRenderer",
avatarMesh,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed && avatarMesh != null)
blendShapeNameList = GetBlendShapeNameList(avatarMesh);
}
}
isOpeningPreAnimationClips = EditorGUILayout.Foldout(isOpeningPreAnimationClips, "Pre AnimationClips");
if (isOpeningPreAnimationClips)
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("+"))
{
preAnimationClips.Add(null);
}
if (GUILayout.Button("-") && preAnimationClips.Count > 1)
{
var animIndex = preAnimationClips.Count - 1;
CheckAndRemoveAnimProperties(ref animPropertyList, animIndex);
preAnimationClips.RemoveAt(animIndex);
}
}
using (new EditorGUI.IndentLevelScope())
{
for (int animIndex = 0; animIndex < preAnimationClips.Count; animIndex++)
{
using (var check = new EditorGUI.ChangeCheckScope())
{
preAnimationClips[animIndex] = EditorGUILayout.ObjectField(
"AnimationClip " + (animIndex + 1),
preAnimationClips[animIndex],
typeof(AnimationClip),
true
) as AnimationClip;
if (check.changed)
{
CheckAndRemoveAnimProperties(ref animPropertyList, animIndex);
if (preAnimationClips[animIndex] != null)
UpdateAnimationPropertyNameList(preAnimationClips[animIndex], ref animPropertyList, animIndex);
}
}
}
}
}
EditorGUILayout.Space();
isOpeningAnimationPropertyList = EditorGUILayout.Foldout(isOpeningAnimationPropertyList, "AnimationPropertyList");
if (isOpeningAnimationPropertyList)
{
using (new EditorGUI.IndentLevelScope())
{
using (new EditorGUILayout.HorizontalScope())
{
using (var check = new EditorGUI.ChangeCheckScope())
{
isConvertAll = EditorGUILayout.Toggle(isConvertAll, GUILayout.Width(30f));
if (check.changed)
ChangeAllIsConvertParams(isConvertAll, ref animPropertyList);
}
EditorGUILayout.LabelField("prePropertyName", EditorStyles.boldLabel);
EditorGUILayout.LabelField("posPropertyName", EditorStyles.boldLabel);
}
using (var scrollPos = new GUILayout.ScrollViewScope(propertyScrollPos))
{
propertyScrollPos = scrollPos.scrollPosition;
foreach (var animProperty in animPropertyList)
{
if (!convertBlendShapeName || animProperty.propertyType == "blendShape")
{
using (new EditorGUILayout.HorizontalScope())
{
animProperty.isConvert = EditorGUILayout.Toggle(animProperty.isConvert, GUILayout.Width(30f));
if (convertBlendShapeName && avatarMesh != null)
{
EditorGUILayout.LabelField(animProperty.preName);
}
else
{
EditorGUILayout.LabelField(animProperty.propertyType + "." + animProperty.preName);
}
using (var check = new EditorGUI.ChangeCheckScope())
{
if (convertBlendShapeName && avatarMesh != null)
{
animProperty.selectedBlendShapeIndex = EditorGUILayout.Popup(animProperty.selectedBlendShapeIndex, blendShapeNameList);
}
else
{
animProperty.posName = EditorGUILayout.TextField(animProperty.posName);
}
if (check.changed)
{
if (convertBlendShapeName && avatarMesh != null)
animProperty.posName = blendShapeNameList[animProperty.selectedBlendShapeIndex];
animProperty.isConvert = true;
}
}
}
}
}
}
}
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("AnimClipSaveFolder", saveFolder);
if (GUILayout.Button("Select Folder", GUILayout.Width(100)))
{
saveFolder = EditorUtility.OpenFolderPanel("Select saved folder", saveFolder, "");
var match = Regex.Match(saveFolder, @"Assets/.*");
saveFolder = match.Value + "/";
if (saveFolder == "/") saveFolder = "Assets/";
}
}
if (GUILayout.Button("Convert Property & Save as New File"))
{
ConvertAndCreateAnimationClips(preAnimationClips, animPropertyList, saveFolder, convertBlendShapeName);
}
}
/// <summary>
/// アニメーションファイルのプロパティの名前を変更して新しいファイルとして書き出す
/// </summary>
/// <param name="baseAnimClips"></param>
/// <param name="animPropertyList"></param>
/// <param name="saveFolder"></param>
/// <param name="convertBlendShapeName"></param>
private void ConvertAndCreateAnimationClips(List<AnimationClip> baseAnimClips, List<AnimationProperty> animPropertyList, string saveFolder, bool convertBlendShapeName)
{
// 変換するプロパティisConvert == trueのものだけのリストをつくる
var shoundConvertedPropertyList = animPropertyList.Where(x => x.isConvert).ToList<AnimationProperty>();
foreach (var baseAnimClip in baseAnimClips)
{
if (baseAnimClip == null) continue;
var convertedAnimClip = Object.Instantiate<AnimationClip>(baseAnimClip);
var bindings = AnimationUtility.GetCurveBindings(convertedAnimClip);
for (int i = 0; i < bindings.Length; i++)
{
// binding.propertyName == blendShape.vrc.v_silみたいになっている
var propertyType = bindings[i].propertyName.Split('.')[0];
var blendShapeName = bindings[i].propertyName.Replace(propertyType + ".", "");
// blendShapeだけ変更するモードだったらそれ以外の場合は処理しない
if (convertBlendShapeName && propertyType != "blendShape") continue;
// 変換するプロパティのリストに含まれるプロパティだけ変換する
var targetAnimProperty = shoundConvertedPropertyList.Find(x => x.propertyType == propertyType && x.preName == blendShapeName);
if (targetAnimProperty != null)
{
var curve = AnimationUtility.GetEditorCurve(convertedAnimClip, bindings[i]);
AnimationUtility.SetEditorCurve(convertedAnimClip, bindings[i], null);
bindings[i].propertyName = targetAnimProperty.propertyType + "." + targetAnimProperty.posName;
AnimationUtility.SetEditorCurve(convertedAnimClip, bindings[i], curve);
}
}
AssetDatabase.CreateAsset(convertedAnimClip, AssetDatabase.GenerateUniqueAssetPath(saveFolder + baseAnimClip.name + ".anim"));
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
/// <summary>
/// アニメーションプロパティリストを更新する
/// </summary>
/// <param name="animClips"></param>
/// <param name="animPropertyList"></param>
private void UpdateAnimationPropertyNameList(AnimationClip animClips, ref List<AnimationProperty> animPropertyList, int animIndex)
{
var bindings = AnimationUtility.GetCurveBindings(animClips);
foreach (var binding in bindings)
{
// binding.propertyName == blendShape.vrc.v_silみたいになっている
var propertyType = binding.propertyName.Split('.')[0];
var blendShapeName = binding.propertyName.Replace(propertyType + ".", "");
// animPropertyListに含まれていないプロパティだけ追加する
// すでに含まれている場合AnimIndexHavingThisPropertyに追加する
var animProperty = animPropertyList.Find(x => x.propertyType == propertyType && x.preName == blendShapeName);
if (animProperty == null)
animPropertyList.Add(new AnimationProperty(propertyType, blendShapeName, animIndex));
else
animProperty.AddAnimIndexHavingThisProperty(animIndex);
}
}
/// <summary>
/// ブレンドシェイプの名前一覧を取得する
/// </summary>
/// <param name="meshRenderer"></param>
/// <returns></returns>
private string[] GetBlendShapeNameList(SkinnedMeshRenderer meshRenderer)
{
var mesh = meshRenderer.sharedMesh;
if (mesh == null) return null;
var blendShapeNameList = new string[mesh.blendShapeCount];
for (int i = 0; i < mesh.blendShapeCount; i++)
blendShapeNameList[i] = mesh.GetBlendShapeName(i);
return blendShapeNameList;
}
/// <summary>
/// すべてのプロパティリストのisConvertを変更する
/// </summary>
/// <param name="isConvertAll"></param>
/// <param name="animPropertyList"></param>
private void ChangeAllIsConvertParams(bool isConvertAll, ref List<AnimationProperty> animPropertyList)
{
foreach (var animProperty in animPropertyList)
{
animProperty.isConvert = isConvertAll;
}
}
/// <summary>
/// プロパティリストに含まれるすべてのプロパティのAnimIndexHavingThisPropertyからanimIndexを削除して
/// リストに不要なプロパティか調べ、削除する
/// </summary>
/// <param name="animPropertyList"></param>
/// <param name="animIndex"></param>
private void CheckAndRemoveAnimProperties(ref List<AnimationProperty> animPropertyList, int animIndex)
{
foreach (var animProperty in animPropertyList.ToArray())
{
animProperty.RemoveAnimIndexHavingThisProperty(animIndex);
if (!animProperty.existAnimHavingThisProperty())
animPropertyList.Remove(animProperty);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9826d703d0a307c478e8e2a69f9ec980
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8b7d1dbc790667b4d96b794d9102bb06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,262 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using System.Linq;
using System.IO;
using VRCDeveloperTool;
// ver 1.0.1
// Copyright (c) 2020 gatosyocora
namespace VRCDeveloperTool
{
public class AnimatorControllerDuplicator : EditorWindow
{
private RuntimeAnimatorController runtimeAnimatorController;
private static RuntimeAnimatorController tempController;
private List<ControllerAnimationClip> animationClips;
private string saveFolder;
private string endKeyword;
private bool isOverrideController = true;
private Vector2 scrollPos = Vector2.zero;
public class ControllerAnimationClip
{
public AnimationClip clip;
public List<int> controllerIndices;
public bool isDuplicate;
public ControllerAnimationClip(AnimationClip clip, int index, bool isDuplicate = true)
{
this.clip = clip;
controllerIndices = new List<int>();
AddIndex(index);
this.isDuplicate = isDuplicate;
}
public void AddIndex(int index)
{
controllerIndices.Add(index);
}
}
[MenuItem("CONTEXT/RuntimeAnimatorController/Duplicate Controller And Clips")]
private static void GetSelectController(MenuCommand menuCommand)
{
tempController = menuCommand.context as RuntimeAnimatorController;
Open();
}
[MenuItem("VRCDeveloperTool/AnimatorControllerDuplicator")]
public static void Open()
{
GetWindow<AnimatorControllerDuplicator>("AnimatorControllerDuplicator");
}
private void OnGUI()
{
if (tempController != null)
{
runtimeAnimatorController = tempController;
tempController = null;
LoadRuntimeControllerInfo(runtimeAnimatorController);
}
using (var check = new EditorGUI.ChangeCheckScope())
{
runtimeAnimatorController = EditorGUILayout.ObjectField(
"AnimatorController",
runtimeAnimatorController,
typeof(RuntimeAnimatorController),
true) as RuntimeAnimatorController;
EditorGUILayout.HelpBox("複製したいAnimatorOverrideControllerを設定してください", MessageType.Info);
if (!isOverrideController)
EditorGUILayout.HelpBox("まだAnimatorControllerは未対応です", MessageType.Error);
if (check.changed && runtimeAnimatorController != null)
{
LoadRuntimeControllerInfo(runtimeAnimatorController);
}
}
if (animationClips != null)
{
EditorGUILayout.LabelField("AnimaionClips", EditorStyles.boldLabel);
using (new EditorGUI.IndentLevelScope())
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("複製", EditorStyles.boldLabel, GUILayout.Width(50f));
EditorGUILayout.LabelField("AnimationClipの名前", EditorStyles.boldLabel);
}
using (var scroll = new EditorGUILayout.ScrollViewScope(scrollPos))
{
scrollPos = scroll.scrollPosition;
foreach (var animationClip in animationClips)
{
using (new EditorGUILayout.HorizontalScope())
{
animationClip.isDuplicate = EditorGUILayout.ToggleLeft(string.Empty, animationClip.isDuplicate, GUILayout.Width(50f));
EditorGUILayout.LabelField(animationClip.clip.name);
if (GUILayout.Button("Select"))
{
Selection.activeObject = animationClip.clip;
}
}
}
}
}
}
saveFolder = EditorGUILayout.TextField("保存先フォルダ", saveFolder);
endKeyword = EditorGUILayout.TextField("複製後Assetのキーワード", endKeyword);
EditorGUILayout.HelpBox("AnimatorControllerおよび選択したAnimationClipを複製します\n複製されると複製後のものがそれぞれ設定されます\n複製後Assetのキーワードに設定した文字がそれぞれの名前の末尾につきます", MessageType.Info);
using (new EditorGUI.DisabledGroupScope(runtimeAnimatorController == null || !isOverrideController))
{
if (GUILayout.Button("Duplicate AnimatorController & AnimationClips"))
{
DuplicateAnimatorControllerAndAnimationClips();
}
}
}
private void DuplicateAnimatorControllerAndAnimationClips()
{
var controllerPath = AssetDatabase.GetAssetPath(runtimeAnimatorController);
var newControllerPath = AssetDatabase.GenerateUniqueAssetPath(
saveFolder + "\\"
+ GatoEditorUtility.AddKeywordToEnd(Path.GetFileNameWithoutExtension(controllerPath), endKeyword)
+ Path.GetExtension(controllerPath));
var success = AssetDatabase.CopyAsset(controllerPath, newControllerPath);
if (!success)
{
Debug.LogError("AnimatorControllerの複製に失敗しました");
return;
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
runtimeAnimatorController = AssetDatabase.LoadAssetAtPath(newControllerPath, typeof(RuntimeAnimatorController)) as RuntimeAnimatorController;
foreach (var animationClip in animationClips)
{
if (!animationClip.isDuplicate) continue;
var animClipPath = AssetDatabase.GetAssetPath(animationClip.clip);
var newAnimClipPath = AssetDatabase.GenerateUniqueAssetPath(
saveFolder + "\\"
+ GatoEditorUtility.AddKeywordToEnd(Path.GetFileNameWithoutExtension(animClipPath), endKeyword)
+ Path.GetExtension(animClipPath));
var successAnimClip = AssetDatabase.CopyAsset(animClipPath, newAnimClipPath);
if (!successAnimClip)
{
Debug.LogErrorFormat("AnimationClip:{0}の複製に失敗しました", animationClip.clip.name);
return;
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
animationClip.clip = AssetDatabase.LoadAssetAtPath(newAnimClipPath, typeof(AnimationClip)) as AnimationClip;
foreach (var index in animationClip.controllerIndices)
{
runtimeAnimatorController.animationClips[index] = animationClip.clip;
}
}
if (isOverrideController)
{
var overrideController = runtimeAnimatorController as AnimatorOverrideController;
var baseAnimClips = overrideController.runtimeAnimatorController.animationClips;
foreach (var animationClip in animationClips)
{
foreach (var index in animationClip.controllerIndices)
{
var baseAnimClipName = baseAnimClips[index].name;
overrideController[baseAnimClipName] = animationClip.clip;
}
}
}
else
{
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private List<ControllerAnimationClip> DistinctControllerAnimationClips(List<ControllerAnimationClip> animClips)
{
var distinctedAnimClips = new List<ControllerAnimationClip>();
var animClipDictionary = new Dictionary<string, ControllerAnimationClip>();
for (int i = 0; i < animClips.Count; i++)
{
var animName = animClips[i].clip.name;
if (!animClipDictionary.ContainsKey(animName))
{
animClipDictionary.Add(animName, animClips[i]);
}
else
{
var animClipData = animClipDictionary[animName];
animClipData.AddIndex(animClips[i].controllerIndices.First());
}
}
distinctedAnimClips = animClipDictionary.Select(x => x.Value).ToList();
return distinctedAnimClips;
}
private void LoadRuntimeControllerInfo(RuntimeAnimatorController runtimeAnimatorController)
{
AnimatorController controller = runtimeAnimatorController as AnimatorController;
AnimatorOverrideController overrideController = runtimeAnimatorController as AnimatorOverrideController;
if (controller != null)
{
isOverrideController = false;
// AnimatorControllerからAnimationClipの取得
}
else if (overrideController != null)
{
isOverrideController = true;
// AnimatorOverrideControllerからAnimationClipの取得
// OverrideされたAnimationClipのみ取得
var baseAnimationController = overrideController.runtimeAnimatorController as AnimatorController;
animationClips = overrideController.animationClips
.Select((x, index) => new { Value = x, Index = index })
.Where(x => baseAnimationController.animationClips[x.Index].name != x.Value.name)
.Select(x => new ControllerAnimationClip(x.Value, x.Index))
.ToList();
animationClips = DistinctControllerAnimationClips(animationClips);
}
saveFolder = Path.GetDirectoryName(AssetDatabase.GetAssetPath(runtimeAnimatorController));
endKeyword = string.Empty;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b31bd1421b78b1e46b35ab56873af04e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0ecf1e763fda23f48a947d07940662a5
folderAsset: yes
timeCreated: 1537791342
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,268 @@
using UnityEngine;
using UnityEditor;
#if VRC_SDK_VRCSDK2
using VRCSDK2;
#endif
using System.Collections.Generic;
// ver 1.2.2
// © 2018 gatosyocora
namespace VRCDeveloperTool
{
public class ComponentAdder : EditorWindow
{
GameObject targetObject = null;
private enum AddType
{
Current_Children_Only,
All_Childrens,
};
AddType addType;
private bool isRigidbody = false;
private bool useGravityFlag = true;
private bool isKinematicFlag = false;
private bool freezePosFlag = false;
private bool freezeRotFlag = false;
private bool isObjectSync = false;
private bool isPickup = false;
private bool isBoxCollider = false;
private bool isTriggerFlag = false;
[MenuItem("VRCDeveloperTool/Component Adder")]
private static void Create()
{
GetWindow<ComponentAdder>("Component Adder");
}
private void OnGUI()
{
targetObject = EditorGUILayout.ObjectField(
"ParentObject",
targetObject,
typeof(GameObject),
true
) as GameObject;
addType = (AddType)EditorGUILayout.EnumPopup("Add Type", addType);
guiRigidbody();
guiObjectSync();
guiPickup();
guiBoxCollider();
guiAction();
}
// 指定オブジェクトの直接的な子オブジェクトをすべて取得する
private List<GameObject> getCurrentChildrens(GameObject parentObj)
{
List<GameObject> objs = new List<GameObject>(parentObj.transform.childCount);
foreach (Transform child in parentObj.transform)
{
objs.Add(child.gameObject);
}
return objs;
}
// 指定オブジェクトの子オブジェクト以降をすべて取得する
private List<GameObject> getAllChildrens(GameObject parentObj)
{
List<GameObject> objs = new List<GameObject>();
var childTransform = parentObj.GetComponentsInChildren<Transform>();
foreach (Transform child in childTransform)
{
objs.Add(child.gameObject);
}
return objs;
}
// 特定のオブジェクトにコンポーネントを追加する
private void AddComponentObject(GameObject obj)
{
if (isRigidbody)
{
var rigid = obj.GetComponent<Rigidbody>();
if (rigid == null)
rigid = obj.AddComponent<Rigidbody>();
rigid.isKinematic = isKinematicFlag;
rigid.useGravity = useGravityFlag;
rigid.constraints = 0;
if (freezePosFlag) rigid.constraints |= RigidbodyConstraints.FreezePosition;
if (freezeRotFlag) rigid.constraints |= RigidbodyConstraints.FreezeRotation;
}
if (isObjectSync)
{
#if VRC_SDK_VRCSDK2
if (obj.GetComponent<VRC_ObjectSync>() == null)
{
var com = obj.AddComponent<VRC_ObjectSync>();
}
#endif
}
if (isPickup)
{
#if VRC_SDK_VRCSDK2
if (obj.GetComponent<VRC_Pickup>() == null)
{
var com = obj.AddComponent<VRC_Pickup>();
}
#endif
}
if (isBoxCollider)
{
if (obj.GetComponent<Collider>() == null || obj.GetComponent<BoxCollider>() != null)
{
var com = obj.GetComponent<BoxCollider>();
if (com == null)
com = obj.AddComponent<BoxCollider>();
com.isTrigger = isTriggerFlag;
}
}
}
// 特定のオブジェクトのコンポーネントを削除する
private void DeleteComponentObject(GameObject obj)
{
if (isPickup)
{
#if VRC_SDK_VRCSDK2
var com = obj.GetComponent<VRC_Pickup>();
if (com != null) DestroyImmediate(com);
#endif
}
if (isRigidbody)
{
var com = obj.GetComponent<Rigidbody>();
if (com != null) DestroyImmediate(com);
}
if (isObjectSync)
{
#if VRC_SDK_VRCSDK2
var com = obj.GetComponent<VRC_ObjectSync>();
if (com != null) DestroyImmediate(com);
#endif
}
if (isBoxCollider)
{
var com = obj.GetComponent<BoxCollider>();
if (com != null) DestroyImmediate(com);
}
}
private void guiRigidbody()
{
isRigidbody = EditorGUILayout.BeginToggleGroup("Rigidbody", isRigidbody);
if (isRigidbody)
{
useGravityFlag = EditorGUILayout.Toggle("useGravity", useGravityFlag);
isKinematicFlag = EditorGUILayout.Toggle("isKinematic", isKinematicFlag);
freezePosFlag = EditorGUILayout.Toggle("Freeze Positions", freezePosFlag);
freezeRotFlag = EditorGUILayout.Toggle("Freeze Rotations", freezeRotFlag);
}
EditorGUILayout.EndToggleGroup();
}
private void guiObjectSync()
{
isObjectSync = EditorGUILayout.BeginToggleGroup("VRC_ObjectSync", isObjectSync);
//syncPhysicsFlag = EditorGUILayout.Toggle("Synchronize Physics", syncPhysicsFlag);
//collisionTransferFlag = EditorGUILayout.Toggle("Collision Transfer", collisionTransferFlag);
#if VRC_SDK_VRCSDK2
#else
if (isObjectSync)
{
EditorGUILayout.HelpBox("VRCSDK2をインポートしてください", MessageType.Error);
}
#endif
EditorGUILayout.EndToggleGroup();
}
private void guiPickup()
{
isPickup = EditorGUILayout.BeginToggleGroup("VRC_Pickup", isPickup);
#if VRC_SDK_VRCSDK2
#else
if (isPickup)
{
EditorGUILayout.HelpBox("VRCSDK2をインポートしてください", MessageType.Error);
}
#endif
EditorGUILayout.EndToggleGroup();
}
private void guiBoxCollider()
{
isBoxCollider = EditorGUILayout.BeginToggleGroup("BoxCollider", isBoxCollider);
if (isBoxCollider)
{
isTriggerFlag = EditorGUILayout.Toggle("isTrigger", isTriggerFlag);
}
EditorGUILayout.EndToggleGroup();
}
private void guiAction()
{
EditorGUI.BeginDisabledGroup(targetObject == null);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Add/Change Components"))
{
List<GameObject> objs;
if (addType == AddType.Current_Children_Only)
{
objs = getCurrentChildrens(targetObject);
}
else
{
objs = getAllChildrens(targetObject);
}
foreach (GameObject obj in objs)
{
AddComponentObject(obj);
}
}
if (GUILayout.Button("Delete Components"))
{
List<GameObject> objs;
if (addType == AddType.Current_Children_Only)
{
objs = getCurrentChildrens(targetObject);
}
else
{
objs = getAllChildrens(targetObject);
}
foreach (GameObject obj in objs)
{
DeleteComponentObject(obj);
}
}
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e5f0ce7345f2dcb4b9c8d9a0f1a9de10
timeCreated: 1537079080
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 38b7d06e6faf6454ba07c362e80890d1
folderAsset: yes
timeCreated: 1537791366
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d4451bc762558274ea722d45840a70a0
folderAsset: yes
timeCreated: 1537791379
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3d6aa19a0d34987449b69a30397c0171
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bf1036ae4731baf41b35d98e03686f41
timeCreated: 1537791411
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ea99b4a464dafa541b7d326a128bb5af
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 188c432963af8ea4fab44ea681a8df5d
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d13ab5ba79c432547ab955ad1343f36d
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fc63fb35bd0032944a05af79bb27fa14
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 445b91c568bfc6e438d551e42d995dca
timeCreated: 1537791431
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,128 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEditor;
// ver 1.2
// © 2018-9-24 gatosyocora
namespace VRCDeveloperTool
{
public class HandPoseAdder : Editor
{
private static string ORIGIN_ANIM_PATH = "Assets/VRCDeveloperTool/Editor/HandPoseAdder/Animations/"; // コピー元となるAnimationファイルが置いてあるディレクトリのパス
private static readonly string[] HANDNAMES ={"LeftHand.Index", "LeftHand.Little", "LeftHand.Middle", "LeftHand.Ring", "LeftHand.Thumb",
"RightHand.Index", "RightHand.Little", "RightHand.Middle", "RightHand.Ring", "RightHand.Thumb"};
private static readonly string[] HANDPOSTYPES = { "1 Stretched", "2 Stretched", "3 Stretched", "Spread" };
// None
[MenuItem("CONTEXT/Motion/Clear Hand pose", false, 0)]
private static void ClearHandAnimationKeys(MenuCommand command)
{
ClearHandPoseAnimationKeys(command);
}
// FINGER POINT
[MenuItem("CONTEXT/Motion/Add Hand pose 'FINGER POINT'", false, 1)]
private static void AddFPAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "FingerPoint.anim");
}
// FIST
[MenuItem("CONTEXT/Motion/Add Hand pose 'FIST'", false, 2)]
private static void AddFISTAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "Fist.anim");
}
// HAND GUN
[MenuItem("CONTEXT/Motion/Add Hand pose 'HAND GUN'", false, 3)]
private static void AddHGAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "HandGun.anim");
}
// HAND OPEN
[MenuItem("CONTEXT/Motion/Add Hand pose 'HAND OPEN'", false, 4)]
private static void AddHOAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "HandOpen.anim");
}
// ROCKN ROLL
[MenuItem("CONTEXT/Motion/Add Hand pose 'ROCK N ROLL'", false, 5)]
private static void AddRRAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "RocknRoll.anim");
}
// THUMBS UP
[MenuItem("CONTEXT/Motion/Add Hand pose 'THUMBS UP'", false, 6)]
private static void AddTUAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "ThumbsUp.anim");
}
// VICTORY
[MenuItem("CONTEXT/Motion/Add Hand pose 'VICTORY'", false, 7)]
private static void AddVICTORYAnimationKeys(MenuCommand command)
{
AddHandPoseAnimationKeys(command, ORIGIN_ANIM_PATH + "Victory.anim");
}
// 特定のAnimationファイルのAnimationキー全てをコピーする
public static void AddHandPoseAnimationKeys(MenuCommand command, string originPath)
{
AnimationClip targetClip = command.context as AnimationClip;
AnimationClip originClip = (AnimationClip)AssetDatabase.LoadAssetAtPath(originPath, typeof(AnimationClip)); // originPathよりAnimationClipの読み込み
CopyAnimationKeys(originClip, targetClip);
}
// originClipに設定されたAnimationKeyをすべてtargetclipにコピーする
public static void CopyAnimationKeys(AnimationClip originClip, AnimationClip targetClip)
{
foreach (var binding in AnimationUtility.GetCurveBindings(originClip).ToArray())
{
// AnimationClipよりAnimationCurveを取得
AnimationCurve curve = AnimationUtility.GetEditorCurve(originClip, binding);
// AnimationClipにキーリダクションを行ったAnimationCurveを設定
AnimationUtility.SetEditorCurve(targetClip, binding, curve);
}
}
/// <summary>
/// 手の形に関するAnimationキーを全て削除する
/// </summary>
/// <param name="command"></param>
public static void ClearHandPoseAnimationKeys(MenuCommand command)
{
var targetClip = command.context as AnimationClip;
foreach (var handname in HANDNAMES)
{
foreach (var handpostype in HANDPOSTYPES)
{
var binding = new EditorCurveBinding();
binding.path = "";
binding.type = typeof(Animator);
binding.propertyName = handname + "." + handpostype;
// キーを削除する
AnimationUtility.SetEditorCurve(targetClip, binding, null);
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1f784a4681f082c418e5751c74cc50ec
timeCreated: 1537770890
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,121 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
// ver 1.1
// © 2018-9-25 gatosyocora
namespace VRCDeveloperTool
{
public class HandPoseSetting : EditorWindow
{
private static string EditorPath = "Assets/VRCDeveloperTool/Editor/HandPoseAdder/";
private static string handPoseName = "";
private static AnimationClip handPoseAnimClip = null;
[MenuItem("VRCDeveloperTool/HandPose Adder Setting")]
private static void Create()
{
loadSettingData();
GetWindow<HandPoseSetting>("HandPose Adder Setting");
}
private void OnGUI()
{
EditorGUILayout.PrefixLabel("Custom Hand Pose");
handPoseName = EditorGUILayout.TextField("Hand Pose Name", handPoseName);
handPoseAnimClip = EditorGUILayout.ObjectField(
"Hand Pose AnimationClip",
handPoseAnimClip,
typeof(AnimationClip),
true
) as AnimationClip;
EditorGUI.BeginDisabledGroup(handPoseName == "" || handPoseAnimClip == null);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Apply Setting"))
{
createSettingData(handPoseName, handPoseAnimClip);
}
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
EditorGUI.BeginDisabledGroup(!File.Exists(EditorPath + "SettingData.asset") || !File.Exists(EditorPath + "CustomHandPoseAdder.cs"));
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Load Setting"))
{
loadSettingData();
}
if (GUILayout.Button("Clear Setting"))
{
handPoseName = "";
handPoseAnimClip = null;
ClearSettingData();
}
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
// 設定データを保存
private static void createSettingData(string name, AnimationClip animClip)
{
HandPoseSettingData.CreateSettingData(name, animClip);
CreateCustomHandPoseAdderScript();
}
// 設定データを読み込み
private static void loadSettingData()
{
HandPoseSettingData settingData = HandPoseSettingData.LoadSettingData();
if (settingData == null) return;
handPoseName = settingData.handPoseName;
handPoseAnimClip = settingData.handPoseAnimClip;
}
// 追加した手の形のAnimationファイルをコピーするためのスクリプトを作成
public static void CreateCustomHandPoseAdderScript()
{
string scriptAssetPath = EditorPath + "CustomHandPoseAdder.cs";
StreamWriter sw = new StreamWriter(scriptAssetPath, false);
sw.WriteLine("using UnityEngine;");
sw.WriteLine("using UnityEditor;");
sw.WriteLine("using VRCDeveloperTool;");
sw.WriteLine("");
sw.WriteLine("// Generated by HandPoseSetting.cs ver 1.1");
sw.WriteLine("// © 2018-9-25 gatosyocora");
sw.WriteLine("");
sw.WriteLine("public class CustomHandPoseAdder : EditorWindow {");
sw.WriteLine(" [MenuItem(\"CONTEXT/Motion/Add Hand pose '" + handPoseName + "'\", false, 8)]");
sw.WriteLine(" private static void AddFPAnimationKeys(MenuCommand command) {");
sw.WriteLine(" string animClipPath = \"" + AssetDatabase.GetAssetPath(handPoseAnimClip) + "\";");
sw.WriteLine(" HandPoseAdder.AddHandPoseAnimationKeys(command, animClipPath);");
sw.WriteLine(" }");
sw.WriteLine("}");
sw.Flush();
sw.Close();
AssetDatabase.ImportAsset(scriptAssetPath);
}
// 設定データを管理するファイルとCustomスクリプトを削除
private static void ClearSettingData()
{
if (File.Exists(EditorPath + "SettingData.asset")) AssetDatabase.DeleteAsset(EditorPath + "SettingData.asset");
if (File.Exists(EditorPath + "CustomHandPoseAdder.cs")) AssetDatabase.DeleteAsset(EditorPath + "CustomHandPoseAdder.cs");
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 51c6f466a893c0f45b1aa5bc320b341a
timeCreated: 1537829968
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,50 @@
using UnityEngine;
using UnityEditor;
// ver 1.1
// © 2018-9-25 gatosyocora
namespace VRCDeveloperTool
{
public class HandPoseSettingData : ScriptableObject
{
private static string EditorPath = "Assets/VRCDeveloperTool/Editor/HandPoseAdder/";
/*
int handPoseNum = 0;
struct handPoseData
{
public string handPoseName;
public AnimationClip handPoseAnimClip;
}
List<handPoseData> handPoses;
*/
public string handPoseName; // 手の形の名前
public AnimationClip handPoseAnimClip; // 手の形のAnimationキーを持つAnimationClip
// 設定データを保存するファイルを作成
public static void CreateSettingData(string name, AnimationClip animClip)
{
var settingData = CreateInstance<HandPoseSettingData>();
settingData.handPoseName = name;
settingData.handPoseAnimClip = animClip;
AssetDatabase.CreateAsset(settingData, EditorPath + "SettingData.asset");
AssetDatabase.Refresh();
}
// 設定データを保存したファイルからデータを読み込み
public static HandPoseSettingData LoadSettingData()
{
var settingData = AssetDatabase.LoadAssetAtPath<HandPoseSettingData>(EditorPath + "SettingData.asset");
return settingData;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4cc22f6d24d159048837aed82bd0c02b
timeCreated: 1537828928
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cbaf9806198075042b23b2457593a9cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
// ver 1.2
// (c) 2019 gatosyocora
namespace VRCDeveloperTool
{
public static class HumanoidPoseResetter
{
private static HumanBodyBones[] boneList
= {
HumanBodyBones.Hips,
HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm,
HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm,
HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg,
HumanBodyBones.RightUpperLeg,HumanBodyBones.RightLowerLeg,
/*HumanBodyBones.LeftThumbDistal, */HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbProximal,
HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexProximal,
HumanBodyBones.LeftMiddleDistal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleProximal,
HumanBodyBones.LeftRingDistal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingProximal,
HumanBodyBones.LeftLittleDistal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleProximal,
/*HumanBodyBones.RightThumbDistal, */HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbProximal,
HumanBodyBones.RightIndexDistal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexProximal,
HumanBodyBones.RightMiddleDistal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleProximal,
HumanBodyBones.RightRingDistal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingProximal,
HumanBodyBones.RightLittleDistal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleProximal
};
public static void ResetPose(GameObject obj)
{
/* 対象オブジェクトのポーズを取得 */
Animator animator = obj.GetComponent<Animator>();
if (animator == null) return;
var sourcePath = AssetDatabase.GetAssetPath(animator.avatar);
var sourceObj = AssetDatabase.LoadAssetAtPath(sourcePath, typeof(GameObject)) as GameObject;
var boneTrans = new Transform[boneList.Length];
for (int i = 0; i < boneList.Length; i++)
{
boneTrans[i] = animator.GetBoneTransform(boneList[i]);
}
/* 対象オブジェクトの親Prefabのポーズを取得 */
Animator sourceAnim = sourceObj.GetComponent<Animator>();
if (sourceAnim == null) return;
var boneTrans_p = new Transform[boneList.Length];
for (int i = 0; i < boneList.Length; i++)
boneTrans_p[i] = sourceAnim.GetBoneTransform(boneList[i]);
for (int j = 0; j < boneList.Length; j++)
{
var trans = boneTrans[j];
var prefabTrans = boneTrans_p[j];
if (trans == null)
{
Debug.Log("[Transform not found]:" + j + ":" + boneList[j]);
continue;
}
Undo.RecordObject(trans, "Change Transform " + trans.name);
trans.localPosition = prefabTrans.localPosition;
trans.localRotation = prefabTrans.localRotation;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1bdf532e6fe46b148a65d0eae915bc28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,76 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
// ver 1.2
// © 2019 gatosyocora
namespace VRCDeveloperTool
{
public class HumanoidPoseResetterEditor : EditorWindow
{
private GameObject targetObject = null;
private static HumanBodyBones[] boneList
= {
HumanBodyBones.Hips,
HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm,
HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm,
HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg,
HumanBodyBones.RightUpperLeg,HumanBodyBones.RightLowerLeg,
/*HumanBodyBones.LeftThumbDistal, */HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbProximal,
HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexProximal,
HumanBodyBones.LeftMiddleDistal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleProximal,
HumanBodyBones.LeftRingDistal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingProximal,
HumanBodyBones.LeftLittleDistal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleProximal,
/*HumanBodyBones.RightThumbDistal, */HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbProximal,
HumanBodyBones.RightIndexDistal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexProximal,
HumanBodyBones.RightMiddleDistal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleProximal,
HumanBodyBones.RightRingDistal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingProximal,
HumanBodyBones.RightLittleDistal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleProximal
};
[MenuItem("VRCDeveloperTool/HumanoidPose Resetter")]
private static void Create()
{
GetWindow<HumanoidPoseResetterEditor>("HumanoidPose Resetter");
}
[MenuItem("GameObject/VRCDeveloperTool/Reset Pose", false, 20)]
public static void ResetPoseFromHierarchy(MenuCommand command)
{
var obj = command.context as GameObject;
HumanoidPoseResetter.ResetPose(obj);
}
private void OnGUI()
{
targetObject = EditorGUILayout.ObjectField(
"TargetObject",
targetObject,
typeof(GameObject),
true
) as GameObject;
GuiAction();
}
private void GuiAction()
{
EditorGUI.BeginDisabledGroup(targetObject == null);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Reset Pose"))
{
HumanoidPoseResetter.ResetPose(targetObject);
}
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c08fbe5f95513a94bb86a4eccef27711
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 03b40ddbf4e82064b8013d1a4c0712a0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,128 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
// ver 1.02
// © 2019-2-1 gatosyocora
namespace VRCDeveloperTool
{
public class MeshBoundsSetter : EditorWindow
{
private GameObject targetObject = null;
private Vector3 boundsScale = new Vector3(1, 2, 1);
private List<GameObject> exclusions = new List<GameObject>();
[MenuItem("VRCDeveloperTool/MeshBounds Setter")]
private static void Create()
{
GetWindow<MeshBoundsSetter>("MeshBounds Setter");
}
private void OnGUI()
{
targetObject = EditorGUILayout.ObjectField(
"TargetObject",
targetObject,
typeof(GameObject),
true
) as GameObject;
boundsScale = EditorGUILayout.Vector3Field("Bounds Scale", boundsScale);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Exclusions");
if (GUILayout.Button("+"))
{
exclusions.Add(null);
}
if (GUILayout.Button("-"))
{
if (exclusions.Count > 0)
exclusions.RemoveAt(exclusions.Count - 1);
}
EditorGUILayout.EndHorizontal();
for (int i = 0; i < exclusions.Count; i++)
{
exclusions[i] = EditorGUILayout.ObjectField(
"Object " + (i + 1),
exclusions[i],
typeof(GameObject),
true
) as GameObject;
}
guiAction();
}
private void guiAction()
{
EditorGUI.BeginDisabledGroup(targetObject == null);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Set Bounds"))
{
BoundsSetter(targetObject);
}
EditorGUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
}
private void BoundsSetter(GameObject parentObj)
{
var objs = GetAllChildrens(parentObj);
foreach (var obj in objs)
{
// 除外リストに含まれていれば処理しない
if (exclusions.Contains(obj)) continue;
var mesh = obj.GetComponent<MeshRenderer>();
var skinnedMesh = obj.GetComponent<SkinnedMeshRenderer>();
if (mesh == null && skinnedMesh == null) continue;
// Mesh Rendererの場合
if (mesh != null)
{
Undo.RecordObject(mesh, "Change Transform " + mesh.name);
}
// SkinnedMeshRendererの場合
else
{
Undo.RecordObject(skinnedMesh, "Change Transform " + skinnedMesh.name);
var objScale = skinnedMesh.gameObject.transform.localScale;
var meshBoundsScale = new Vector3(boundsScale.x / objScale.x, boundsScale.y / objScale.y, boundsScale.z / objScale.z);
skinnedMesh.localBounds = new Bounds(Vector3.zero, meshBoundsScale);
}
}
}
// 指定オブジェクトの子オブジェクト以降をすべて取得する
private List<GameObject> GetAllChildrens(GameObject parentObj)
{
List<GameObject> objs = new List<GameObject>();
var childTransform = parentObj.GetComponentsInChildren<Transform>();
foreach (Transform child in childTransform)
{
objs.Add(child.gameObject);
}
return objs;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71aa7b7ed4050f44c84d3ba57de2304a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d4e191026c25604d8a8f1d8554eca58
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,244 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
// ver 1.0
// © 2019-2-16 gatosyocora
namespace VRCDeveloperTool
{
public class ProbeAnchorSetter : EditorWindow
{
private GameObject targetObject = null;
public enum TARGETPOS
{
HEAD,
CHEST,
//ARMATURE,
ROOTOBJECT,
}
private TARGETPOS targetPos = TARGETPOS.HEAD;
private const string TARGETOBJNAME = "Anchor Target";
private bool isGettingSkinnedMeshRenderer = true;
private bool isGettingMeshRenderer = true;
private bool isOpeningRendererList = false;
private List<SkinnedMeshRenderer> skinnedMeshList;
private List<MeshRenderer> meshList;
private bool[] isSettingToSkinnedMesh = null;
private bool[] isSettingToMesh = null;
private Vector2 leftScrollPos = Vector2.zero;
[MenuItem("VRCDeveloperTool/ProbeAnchor Setter")]
private static void Create()
{
GetWindow<ProbeAnchorSetter>("ProbeAnchor Setter");
}
private void OnGUI()
{
EditorGUI.BeginChangeCheck();
targetObject = EditorGUILayout.ObjectField(
"TargetObject",
targetObject,
typeof(GameObject),
true
) as GameObject;
if (EditorGUI.EndChangeCheck())
{
if (targetObject != null)
{
skinnedMeshList = GetSkinnedMeshList(targetObject);
meshList = GetMeshList(targetObject);
isSettingToSkinnedMesh = new bool[skinnedMeshList.Count];
for (int i = 0; i < skinnedMeshList.Count; i++) isSettingToSkinnedMesh[i] = true;
isSettingToMesh = new bool[meshList.Count];
for (int i = 0; i < meshList.Count; i++) isSettingToMesh[i] = true;
}
}
// 設定するRendererの選択
isGettingSkinnedMeshRenderer = EditorGUILayout.Toggle("Set To SkinnedMeshRenderer", isGettingSkinnedMeshRenderer);
isGettingMeshRenderer = EditorGUILayout.Toggle("Set To MeshRenderer", isGettingMeshRenderer);
// ライティングの計算の基準とする位置を選択
targetPos = (TARGETPOS)EditorGUILayout.EnumPopup("TargetPosition", targetPos);
// Rendererの一覧を表示
if (targetObject != null)
{
isOpeningRendererList = EditorGUILayout.Foldout(isOpeningRendererList, "Renderer List");
if (isOpeningRendererList)
{
leftScrollPos = EditorGUILayout.BeginScrollView(leftScrollPos, GUI.skin.box);
{
EditorGUI.indentLevel++;
int index = 0;
if (isGettingSkinnedMeshRenderer)
{
foreach (var skinnedMesh in skinnedMeshList)
{
EditorGUILayout.BeginHorizontal();
isSettingToSkinnedMesh[index] = EditorGUILayout.Toggle(skinnedMesh.gameObject.name, isSettingToSkinnedMesh[index]);
if (GUILayout.Button("Select"))
Selection.activeGameObject = skinnedMesh.gameObject;
EditorGUILayout.EndHorizontal();
index++;
}
}
index = 0;
if (isGettingMeshRenderer)
{
foreach (var mesh in meshList)
{
EditorGUILayout.BeginHorizontal();
isSettingToMesh[index] = EditorGUILayout.Toggle(mesh.gameObject.name, isSettingToMesh[index]);
if (GUILayout.Button("Select"))
Selection.activeGameObject = mesh.gameObject;
EditorGUILayout.EndHorizontal();
index++;
}
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndScrollView();
}
}
EditorGUI.BeginDisabledGroup(targetObject == null);
if (GUILayout.Button("Set ProbeAnchor"))
{
SetProbeAnchor(targetObject);
}
EditorGUI.EndDisabledGroup();
}
/// <summary>
/// 特定のオブジェクト以下のRendererのProbeAnchorに設定する
/// </summary>
/// <param name="obj"></param>
private void SetProbeAnchor(GameObject obj)
{
var animator = obj.GetComponent<Animator>();
if (animator == null) return;
// AnchorTargetを設定する基準の場所を取得
Transform targetPosTrans = null;
if (targetPos == TARGETPOS.HEAD)
{
targetPosTrans = animator.GetBoneTransform(HumanBodyBones.Head);
}
else if (targetPos == TARGETPOS.CHEST)
{
targetPosTrans = animator.GetBoneTransform(HumanBodyBones.Chest);
}
/*else if (targetPos == TARGETPOS.ARMATURE)
{
var hipsTrans = animator.GetBoneTransform(HumanBodyBones.Hips);
targetPosTrans = hipsTrans.parent;
}*/
else if (targetPos == TARGETPOS.ROOTOBJECT)
{
targetPosTrans = obj.transform;
}
if (targetPosTrans == null) return;
// AnchorTargetに設定用のオブジェクトを作成
GameObject anchorTargetObj = GameObject.Find(obj.name + "/" + TARGETOBJNAME);
if (anchorTargetObj == null)
{
anchorTargetObj = new GameObject(TARGETOBJNAME);
anchorTargetObj.transform.parent = obj.transform;
}
anchorTargetObj.transform.position = targetPosTrans.position;
// SkiinedMeshRendererに設定
if (isGettingSkinnedMeshRenderer)
{
int index = 0;
var skinnedMeshes = skinnedMeshList;
foreach (var skinnedMesh in skinnedMeshes)
{
if (isSettingToSkinnedMesh[index++])
skinnedMesh.probeAnchor = anchorTargetObj.transform;
else
skinnedMesh.probeAnchor = null;
}
}
// MeshRendererに設定
if (isGettingMeshRenderer)
{
int index = 0;
var meshes = meshList;
foreach (var mesh in meshes)
{
if (isSettingToMesh[index++])
mesh.probeAnchor = anchorTargetObj.transform;
else
mesh.probeAnchor = null;
}
}
}
/// <summary>
/// 指定オブジェクト以下のSkinnedMeshRendererのリストを取得する
/// </summary>
/// <param name="parentObj">親オブジェクト</param>
/// <returns>SkinnedMeshRendererのリスト</returns>
private List<SkinnedMeshRenderer> GetSkinnedMeshList(GameObject parentObj)
{
var skinnedMeshList = new List<SkinnedMeshRenderer>();
var skinnedMeshes = parentObj.GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (var skinnedMesh in skinnedMeshes)
{
skinnedMeshList.Add(skinnedMesh);
}
return skinnedMeshList;
}
/// <summary>
/// 指定オブジェクト以下のMeshRendererのリストを取得する
/// </summary>
/// <param name="parentObj">親オブジェクト</param>
/// <returns>MeshRendererのリスト</returns>
private List<MeshRenderer> GetMeshList(GameObject parentObj)
{
var meshList = new List<MeshRenderer>();
var meshes = parentObj.GetComponentsInChildren<MeshRenderer>(true);
foreach (var mesh in meshes)
{
meshList.Add(mesh);
}
return meshList;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d8a4080ea13a5749994d099d4a6149a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 394a8c1a4729a6742a4182a7922497fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,165 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.IO;
namespace VRCDeveloperTool
{
public class ShapeKeyDeleter : EditorWindow
{
private SkinnedMeshRenderer renderer;
private List<string> shapeKeyNames;
private bool[] selectedShapeKeys;
private bool isOpenedBlendShape = true;
private Vector2 shapeKeyScrollPos = Vector2.zero;
private int lastSelectedIndex = -1; // 마지막 선택된 인덱스 저장
[MenuItem("VRCDeveloperTool/Mesh/ShapeKey Deleter")]
private static void Open()
{
GetWindow<ShapeKeyDeleter>("ShapeKey Deleter");
}
private void OnEnable()
{
renderer = null;
shapeKeyNames = null;
selectedShapeKeys = null;
isOpenedBlendShape = true;
shapeKeyScrollPos = Vector2.zero;
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
renderer = EditorGUILayout.ObjectField(
"SkinnedMeshRenderer",
renderer,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed)
{
if (renderer != null)
{
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
selectedShapeKeys = new bool[shapeKeyNames.Count()];
}
}
}
if (shapeKeyNames != null)
{
isOpenedBlendShape = EditorGUILayout.Foldout(isOpenedBlendShape, "Shape Keys");
if (isOpenedBlendShape)
{
using (new EditorGUI.IndentLevelScope())
using (var scroll = new EditorGUILayout.ScrollViewScope(shapeKeyScrollPos, GUI.skin.box))
{
shapeKeyScrollPos = scroll.scrollPosition;
Event e = Event.current;
for (int i = 0; i < shapeKeyNames.Count(); i++)
{
bool originalValue = selectedShapeKeys[i];
bool newValue = EditorGUILayout.ToggleLeft(shapeKeyNames[i], selectedShapeKeys[i]);
// Shift 클릭 처리
if (e.shift && lastSelectedIndex != -1 && newValue != originalValue)
{
int startIndex = Mathf.Min(lastSelectedIndex, i);
int endIndex = Mathf.Max(lastSelectedIndex, i);
for (int j = startIndex; j <= endIndex; j++)
{
selectedShapeKeys[j] = true;
}
}
// 마지막 선택된 인덱스 갱신
if (newValue != originalValue)
{
lastSelectedIndex = i;
}
selectedShapeKeys[i] = newValue;
}
}
}
}
using (new EditorGUI.DisabledScope(renderer == null || (selectedShapeKeys != null && selectedShapeKeys.All(x => !x))))
{
if (GUILayout.Button("Delete ShapeKeys"))
{
var selectedBlendShapeIndexs = selectedShapeKeys
.Select((isSelect, index) => new { Index = index, Value = isSelect })
.Where(x => x.Value)
.Select(x => x.Index)
.ToArray();
DeleteShapeKey(renderer, selectedBlendShapeIndexs);
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
selectedShapeKeys = new bool[shapeKeyNames.Count()];
}
}
}
private bool DeleteShapeKey(SkinnedMeshRenderer renderer, int[] selectedShapeKeyIndexs)
{
var mesh = renderer.sharedMesh;
if (mesh == null) return false;
var mesh_custom = Instantiate(mesh);
mesh_custom.ClearBlendShapes();
int frameIndex = 0;
string shapeKeyName;
float weight;
Vector3[] deltaVertices, deltaNormals, deltaTangents;
for (int blendShapeIndex = 0; blendShapeIndex < mesh.blendShapeCount; blendShapeIndex++)
{
deltaVertices = new Vector3[mesh.vertexCount];
deltaNormals = new Vector3[mesh.vertexCount];
deltaTangents = new Vector3[mesh.vertexCount];
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents);
weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
if (!selectedShapeKeyIndexs.Contains(blendShapeIndex))
{
shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex);
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
}
}
Undo.RecordObject(renderer, "Renderer " + renderer.name);
renderer.sharedMesh = mesh_custom;
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_shapekeydeleted.asset";
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
AssetDatabase.SaveAssets();
return true;
}
private List<string> GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer)
{
List<string> shapeKeyNames = new List<string>();
var mesh = renderer.sharedMesh;
if (mesh != null)
for (int i = 0; i < mesh.blendShapeCount; i++)
shapeKeyNames.Add(mesh.GetBlendShapeName(i));
return shapeKeyNames;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a2159ac33f59084e8747b8b2a84b43b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 64cbfc8dbdbd8b44e9828fd5431afb69
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,186 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
// ver 1.0
// Copyright (c) 2019 gatosyocora
namespace VRCDeveloperTool
{
public class ShapeKeyMixer : EditorWindow
{
private SkinnedMeshRenderer renderer;
private List<string> shapeKeyNames;
private bool[] selectedShapeKeys;
private bool isOpenedBlendShape = true;
private string combinedShapeKeyName = "";
private bool deleteOriginShapeKey = true;
private Vector2 shapeKeyScrollPos = Vector2.zero;
[MenuItem("VRCDeveloperTool/Mesh/ShapeKey Mixer")]
private static void Open()
{
GetWindow<ShapeKeyMixer>("ShapeKey Mixer");
}
private void OnEnable()
{
renderer = null;
shapeKeyNames = null;
selectedShapeKeys = null;
isOpenedBlendShape = true;
combinedShapeKeyName = "";
shapeKeyScrollPos = Vector2.zero;
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
renderer = EditorGUILayout.ObjectField(
"SkinnedMeshRenderer",
renderer,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed)
{
if (renderer != null)
{
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
selectedShapeKeys = new bool[shapeKeyNames.Count()];
}
}
}
if (shapeKeyNames != null)
{
isOpenedBlendShape = EditorGUILayout.Foldout(isOpenedBlendShape, "Shape Keys");
if (isOpenedBlendShape)
{
using (new EditorGUI.IndentLevelScope())
using (var scroll = new EditorGUILayout.ScrollViewScope(shapeKeyScrollPos, GUI.skin.box))
{
shapeKeyScrollPos = scroll.scrollPosition;
for (int i = 0; i < shapeKeyNames.Count(); i++)
{
selectedShapeKeys[i] = EditorGUILayout.ToggleLeft(shapeKeyNames[i], selectedShapeKeys[i]);
}
}
}
deleteOriginShapeKey = EditorGUILayout.Toggle("Delete Origin ShapeKey", deleteOriginShapeKey);
combinedShapeKeyName = EditorGUILayout.TextField("Mixed ShapeKey Name", combinedShapeKeyName);
}
using (new EditorGUI.DisabledScope(renderer == null || combinedShapeKeyName == "" || (selectedShapeKeys != null && selectedShapeKeys.Sum(x => x ? 1 : 0) <= 1)))
{
if (GUILayout.Button("Mix ShapeKeys"))
{
// 2つ以上が選択されている
if (selectedShapeKeys.Sum(x => x ? 1 : 0) > 1)
{
// 選択されている要素のインデックスの配列
var selectedBlendShapeIndexs = selectedShapeKeys
.Select((isSelect, index) => new { Index = index, Value = isSelect })
.Where(x => x.Value)
.Select(x => x.Index)
.ToArray();
MixShapeKey(renderer, selectedBlendShapeIndexs, combinedShapeKeyName, deleteOriginShapeKey);
}
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
selectedShapeKeys = new bool[shapeKeyNames.Count()];
}
}
}
private bool MixShapeKey(SkinnedMeshRenderer renderer, int[] selectedShapeKeyIndexs, string combinedBlendShapeName, bool deleteOriginShapeKey)
{
var mesh = renderer.sharedMesh;
if (mesh == null) return false;
var mesh_custom = Instantiate(mesh);
mesh_custom.ClearBlendShapes();
int frameIndex = 0;
string shapeKeyName;
float weight;
Vector3[] deltaVertices, deltaNormals, deltaTangents;
var combinedDeltaVertices = new Vector3[mesh.vertexCount];
var combinedDeltaNormals = new Vector3[mesh.vertexCount];
var combinedDeltaTangents = new Vector3[mesh.vertexCount];
float combinedWeight = 0;
for (int blendShapeIndex = 0; blendShapeIndex < mesh.blendShapeCount; blendShapeIndex++)
{
deltaVertices = new Vector3[mesh.vertexCount];
deltaNormals = new Vector3[mesh.vertexCount];
deltaTangents = new Vector3[mesh.vertexCount];
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents);
weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
if (selectedShapeKeyIndexs.Contains(blendShapeIndex))
{
for (int i = 0; i < mesh.vertexCount; i++)
{
combinedDeltaVertices[i] += deltaVertices[i];
combinedDeltaNormals[i] += deltaNormals[i];
combinedDeltaTangents[i] += deltaTangents[i];
combinedWeight = Mathf.Max(combinedWeight, weight);
}
if (!deleteOriginShapeKey)
{
shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex);
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
}
}
else
{
shapeKeyName = mesh.GetBlendShapeName(blendShapeIndex);
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
}
}
if (selectedShapeKeyIndexs.Length > 0)
mesh_custom.AddBlendShapeFrame(combinedShapeKeyName, combinedWeight, combinedDeltaVertices, combinedDeltaNormals, combinedDeltaTangents);
Undo.RecordObject(renderer, "Renderer " + renderer.name);
renderer.sharedMesh = mesh_custom;
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_custom.asset";
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
AssetDatabase.SaveAssets();
return true;
}
/// <summary>
/// SkinnedMeshRendererのもつメッシュのシェイプキーの名前のリストを取得する
/// </summary>
/// <param name="renderer"></param>
/// <returns></returns>
private List<string> GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer)
{
List<string> shapeKeyNames = new List<string>();
var mesh = renderer.sharedMesh;
if (mesh != null)
for (int i = 0; i < mesh.blendShapeCount; i++)
shapeKeyNames.Add(mesh.GetBlendShapeName(i));
return shapeKeyNames;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9134ffb5949591d44911390f91211dcc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 359dd1b9f7cde6c43978974cd33a2a52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 608b764bb9c5e1e428df13219c16514b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1ac7f172661b0a74299f8e583d5c7695
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 931bf2795a569b2478bc0b01f922683b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,269 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Linq;
using System;
namespace VRCDeveloperTool
{
public class ShapeKeyNameChanger : EditorWindow
{
private List<string> shapeKeyNames;
private SkinnedMeshRenderer renderer;
private string[] posNames;
private bool useDuplication = false;
private Vector2 scrollPos = Vector2.zero;
enum SelectType
{
Input,
Select
}
private SelectType selectTab = SelectType.Input;
public static GUIContent[] tabToggles
{
get
{
return System.Enum.GetNames(typeof(SelectType)).Select(x => new GUIContent(x)).ToArray();
}
}
[Serializable]
public class JsonData
{
public string[] ShapeKeyNames;
}
private string[] selectableNames;
private int[] selectedIndices;
private void OnEnable()
{
shapeKeyNames = null;
renderer = null;
posNames = null;
selectableNames = LoadJsonDataFromResourceFolder("ShapeKeyNameChanger/shapekeynames");
}
[MenuItem("VRCDeveloperTool/Mesh/ShapeKeyName Changer")]
private static void Create()
{
GetWindow<ShapeKeyNameChanger>("ShapeKeyName Changer");
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
renderer = EditorGUILayout.ObjectField(
"Renderer",
renderer,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed)
{
if (renderer == null)
{
shapeKeyNames = null;
posNames = null;
selectedIndices = null;
}
else
{
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
posNames = shapeKeyNames.ToArray();
selectedIndices = new int[shapeKeyNames.Count];
for (int i = 0; i < selectedIndices.Length; i++)
{
selectedIndices[i] = -1;
}
}
}
}
selectTab = (SelectType)GUILayout.Toolbar((int)selectTab, tabToggles, "LargeButton", GUI.ToolbarButtonSize.Fixed);
if (shapeKeyNames != null)
{
using (var pos = new GUILayout.ScrollViewScope(scrollPos))
{
scrollPos = pos.scrollPosition;
if (selectTab == SelectType.Input)
{
for (int i = 0; i < shapeKeyNames.Count; i++)
{
using (new EditorGUILayout.HorizontalScope())
{
using (var toggle = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.Toggle(shapeKeyNames[i] != posNames[i], GUILayout.Width(30));
if (toggle.changed && shapeKeyNames[i] != posNames[i])
{
posNames[i] = shapeKeyNames[i];
selectedIndices[i] = -1;
}
}
posNames[i] = EditorGUILayout.TextField(shapeKeyNames[i], posNames[i]);
}
}
}
else
{
for (int i = 0; i < shapeKeyNames.Count; i++)
{
using (new EditorGUILayout.HorizontalScope())
{
using (var toggle = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.Toggle(shapeKeyNames[i] != posNames[i], GUILayout.Width(30));
if (toggle.changed && shapeKeyNames[i] != posNames[i])
{
posNames[i] = shapeKeyNames[i];
selectedIndices[i] = -1;
}
}
using (var check = new EditorGUI.ChangeCheckScope())
{
selectedIndices[i] = EditorGUILayout.Popup(shapeKeyNames[i], selectedIndices[i], selectableNames);
if (check.changed && selectedIndices[i] != -1)
posNames[i] = selectableNames[selectedIndices[i]];
}
}
}
}
}
}
using (new EditorGUI.DisabledScope(renderer == null))
{
useDuplication = EditorGUILayout.Toggle("Duplication ShapeKeys", useDuplication);
// 대문자 -> 소문자 변환 버튼
if (GUILayout.Button("Convert First Letter to Lowercase"))
{
ConvertFirstLetterToLowercase();
}
// 소문자 -> 대문자 변환 버튼
if (GUILayout.Button("Convert First Letter to Uppercase"))
{
ConvertFirstLetterToUppercase();
}
if (GUILayout.Button("Change ShapeKeyName"))
{
CreateNewShapeKeyNameMesh(renderer, posNames, useDuplication, shapeKeyNames);
shapeKeyNames = GetBlendShapeListFromRenderer(renderer);
posNames = shapeKeyNames.ToArray();
selectedIndices = new int[shapeKeyNames.Count];
for (int i = 0; i < selectedIndices.Length; i++)
{
selectedIndices[i] = -1;
}
}
}
}
// 첫 글자를 소문자로 변환하는 함수
private void ConvertFirstLetterToLowercase()
{
for (int i = 0; i < posNames.Length; i++)
{
if (!char.IsLower(posNames[i][0]))
{
posNames[i] = char.ToLower(posNames[i][0]) + posNames[i].Substring(1);
}
}
}
// 첫 글자를 대문자로 변환하는 함수
private void ConvertFirstLetterToUppercase()
{
for (int i = 0; i < posNames.Length; i++)
{
if (!char.IsUpper(posNames[i][0]))
{
posNames[i] = char.ToUpper(posNames[i][0]) + posNames[i].Substring(1);
}
}
}
private bool CreateNewShapeKeyNameMesh(SkinnedMeshRenderer renderer, string[] posShapeKeyNames, bool useDuplication, List<string> preShapeKeyNames)
{
var mesh = renderer.sharedMesh;
if (mesh == null) return false;
if (posShapeKeyNames.Length != mesh.blendShapeCount) return false;
var mesh_custom = UnityEngine.Object.Instantiate<Mesh>(mesh);
mesh_custom.ClearBlendShapes();
var frameIndex = 0;
var shapeKeyName = string.Empty;
Vector3[] deltaVertices, deltaNormals, deltaTangents;
for (int blendShapeIndex = 0; blendShapeIndex < mesh.blendShapeCount; blendShapeIndex++)
{
deltaVertices = new Vector3[mesh.vertexCount];
deltaNormals = new Vector3[mesh.vertexCount];
deltaTangents = new Vector3[mesh.vertexCount];
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents);
var weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
shapeKeyName = posNames[blendShapeIndex];
if (useDuplication && !preShapeKeyNames[blendShapeIndex].Equals(shapeKeyName))
{
mesh_custom.AddBlendShapeFrame(preShapeKeyNames[blendShapeIndex], weight, deltaVertices, deltaNormals, deltaTangents);
}
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
}
Undo.RecordObject(renderer, "Renderer " + renderer.name);
renderer.sharedMesh = mesh_custom;
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_custom.asset";
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
AssetDatabase.SaveAssets();
return true;
}
private List<string> GetBlendShapeListFromRenderer(SkinnedMeshRenderer renderer)
{
List<string> shapeKeyNames = new List<string>();
var mesh = renderer.sharedMesh;
if (mesh != null)
for (int i = 0; i < mesh.blendShapeCount; i++)
shapeKeyNames.Add(mesh.GetBlendShapeName(i));
return shapeKeyNames;
}
private string[] LoadJsonDataFromResourceFolder(string path)
{
var textAsset = Resources.Load<TextAsset>(path);
var jsonData = JsonUtility.FromJson<JsonData>(textAsset.ToString());
return jsonData.ShapeKeyNames;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e5e446425695a2428c640bf776865be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91e09be55fd184f469aadf4c0caf5f89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,237 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using UnityEditorInternal;
using System.IO;
// ver 1.00
// (C) 2019 gatosyocora
namespace VRCDeveloperTool
{
public class ShapeKeyReorder : EditorWindow
{
private ReorderableList blendShapeReorderableList;
private SkinnedMeshRenderer renderer;
private Vector2 scrollPos = Vector2.zero;
public class BlendShape
{
public int index;
public string name;
public BlendShape(int index, string name)
{
this.index = index;
this.name = name;
}
}
[MenuItem("VRCDeveloperTool/Mesh/ShapeKey Reorder")]
private static void Open()
{
GetWindow<ShapeKeyReorder>("ShapeKey Reorder");
}
private void OnEnable()
{
renderer = null;
blendShapeReorderableList = null;
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
renderer = EditorGUILayout.ObjectField(
"Renderer",
renderer,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed)
{
if (renderer != null)
{
var blendShapePairList = new List<BlendShape>();
var mesh = renderer.sharedMesh;
var blendShapeCount = mesh.blendShapeCount;
for (int i = 0; i < blendShapeCount; i++)
{
blendShapePairList.Add(new BlendShape(i, mesh.GetBlendShapeName(i)));
}
blendShapeReorderableList = InitializeReorderableList<BlendShape>(blendShapePairList);
}
else
{
blendShapeReorderableList = null;
}
}
}
if (blendShapeReorderableList != null)
{
using (var scroll = new EditorGUILayout.ScrollViewScope(scrollPos))
{
scrollPos = scroll.scrollPosition;
blendShapeReorderableList.DoLayoutList();
}
}
using (new EditorGUI.DisabledGroupScope(renderer == null))
{
EditorGUILayout.LabelField("Auto Sort");
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("UnSort"))
{
blendShapeReorderableList.list
= blendShapeReorderableList.list
.Cast<BlendShape>()
.OrderBy(x => x.index)
.ToList();
}
if (GUILayout.Button("VRChat Default"))
{
blendShapeReorderableList.list
= SortByVRChatDefault(
blendShapeReorderableList.list as List<BlendShape>
);
}
if (GUILayout.Button("A-Z"))
{
blendShapeReorderableList.list
= blendShapeReorderableList.list
.Cast<BlendShape>()
.OrderBy(x => x.name)
.ToList();
}
if (GUILayout.Button("Z-A"))
{
blendShapeReorderableList.list
= blendShapeReorderableList.list
.Cast<BlendShape>()
.OrderByDescending(x => x.name)
.ToList();
}
}
EditorGUILayout.Space();
if (GUILayout.Button("Change ShapeKey order"))
{
CreateNewShapeKeyNameMesh(renderer, blendShapeReorderableList.list as List<BlendShape>);
}
}
}
/// <summary>
/// シェイプキーの順番を変えたメッシュを作成し, SkinnedMeshRendererに設定する
/// </summary>
/// <param name="renderer">シェイプキーの順番を変更したいメッシュを持つSkinnedMeshRenderer</param>
/// <param name="reorderdBlendShapeList">変更後のシェイプキーの名称のリスト</param>
/// <returns></returns>
private bool CreateNewShapeKeyNameMesh(SkinnedMeshRenderer renderer, List<BlendShape> reorderdBlendShapeList)
{
var mesh = renderer.sharedMesh;
if (mesh == null) return false;
if (reorderdBlendShapeList.Count != mesh.blendShapeCount) return false;
var mesh_custom = Object.Instantiate<Mesh>(mesh);
mesh_custom.ClearBlendShapes();
var blendShapeIndex = 0;
var frameIndex = 0;
var shapeKeyName = "";
Vector3[] deltaVertices, deltaNormals, deltaTangents;
for (int i = 0; i < reorderdBlendShapeList.Count; i++)
{
deltaVertices = new Vector3[mesh.vertexCount];
deltaNormals = new Vector3[mesh.vertexCount];
deltaTangents = new Vector3[mesh.vertexCount];
blendShapeIndex = reorderdBlendShapeList[i].index;
mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameIndex, deltaVertices, deltaNormals, deltaTangents);
var weight = mesh.GetBlendShapeFrameWeight(blendShapeIndex, frameIndex);
shapeKeyName = reorderdBlendShapeList[i].name;
mesh_custom.AddBlendShapeFrame(shapeKeyName, weight, deltaVertices, deltaNormals, deltaTangents);
}
Undo.RecordObject(renderer, "Renderer " + renderer.name);
renderer.sharedMesh = mesh_custom;
var path = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)) + "/" + mesh.name + "_reorderd.asset";
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(path));
AssetDatabase.SaveAssets();
return true;
}
/// <summary>
/// シェイプキーの順番をVRChat標準のものに変更する
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
private List<BlendShape> SortByVRChatDefault(List<BlendShape> list)
{
var vrcBlendShapes
= new string[]{
"vrc.blink_left",
"vrc.blink_right",
"vrc.lowerlid_left",
"vrc.lowerlid_right",
};
var newList = new List<BlendShape>();
for (int i = 0; i < vrcBlendShapes.Length; i++)
{
var index = list.Select(x => x.name)
.ToList()
.IndexOf(vrcBlendShapes[i]);
if (index == -1) continue;
var blendShape = list[index];
list.RemoveAt(index);
newList.Add(blendShape);
}
newList.AddRange(list);
return newList;
}
/// <summary>
/// ReorderableListを作成する
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
private ReorderableList InitializeReorderableList<T>(List<T> list)
{
var reorderbleList = new ReorderableList(list, typeof(T));
reorderbleList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "BlendShape");
reorderbleList.drawElementCallback = (rect, index, isActive, isFoused) =>
{
var item = reorderbleList.list[index] as BlendShape;
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.LabelField(rect, item.name);
};
reorderbleList.displayAdd = false;
reorderbleList.displayRemove = false;
return reorderbleList;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ed3c5d0ba2f70545b6b5ae60e0ab634
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f87b301891c2eab4782b10287033b78b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,299 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
// ver 1.0.1
// Copyright (c) 2019 gatosyocora
namespace VRCDeveloperTool
{
public class SubMeshDeleter : EditorWindow
{
private SkinnedMeshRenderer renderer;
private List<SubMeshInfo> subMeshList;
private int triangleCount = 0;
private string saveFolder = "Assets/";
private bool isOpenedSubMesh = true;
private Vector2 subMeshScrollPos = Vector2.zero;
[MenuItem("VRCDeveloperTool/Mesh/SubMesh Deleter")]
private static void Open()
{
GetWindow<SubMeshDeleter>("SubMeshDeleter");
}
private void OnEnable()
{
renderer = null;
subMeshList = null;
triangleCount = 0;
}
private void OnGUI()
{
using (var check = new EditorGUI.ChangeCheckScope())
{
renderer = EditorGUILayout.ObjectField(
"SkinnedMeshRenderer",
renderer,
typeof(SkinnedMeshRenderer),
true
) as SkinnedMeshRenderer;
if (check.changed)
{
if (renderer != null)
{
var mesh = renderer.sharedMesh;
if (mesh != null)
{
subMeshList = GetSubMeshList(mesh);
triangleCount = GetMeshTriangleCount(mesh);
saveFolder = GetMeshPath(mesh);
}
}
else
{
subMeshList = null;
}
}
}
if (subMeshList != null)
{
isOpenedSubMesh = EditorGUILayout.Foldout(isOpenedSubMesh, "SubMesh");
if (isOpenedSubMesh)
{
using (var scroll = new EditorGUILayout.ScrollViewScope(subMeshScrollPos))
{
subMeshScrollPos = scroll.scrollPosition;
for (int i = 0; i < subMeshList.Count(); i++)
{
subMeshList[i].selected = EditorGUILayout.ToggleLeft("subMesh " + (i + 1) + "(" + renderer.sharedMaterials[i].name + "):" + subMeshList[i].triangleCount, subMeshList[i].selected);
}
}
}
}
EditorGUILayout.LabelField("Triangle Count", triangleCount+"");
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("Mesh SaveFolder", saveFolder);
if (GUILayout.Button("Select Folder", GUILayout.Width(100)))
{
saveFolder = EditorUtility.OpenFolderPanel("Select saved folder", saveFolder, "");
var match = Regex.Match(saveFolder, @"Assets/.*");
saveFolder = match.Value + "/";
if (saveFolder == "/") saveFolder = "Assets/";
}
}
using (new EditorGUI.DisabledGroupScope(subMeshList == null || subMeshList.Count() <= 1))
{
if (GUILayout.Button("Delete SubMesh"))
{
DeleteSelectedSubMesh(renderer, subMeshList);
var mesh = renderer.sharedMesh;
if (mesh != null)
{
subMeshList = GetSubMeshList(mesh);
triangleCount = GetMeshTriangleCount(mesh);
}
}
}
}
/// <summary>
/// 選択中のサブメッシュを削除する
/// </summary>
/// <param name="renderer"></param>
/// <param name="subMeshList"></param>
/// <returns></returns>
private bool DeleteSelectedSubMesh(SkinnedMeshRenderer renderer, List<SubMeshInfo> subMeshList)
{
// 削除する頂点インデックスのリスト(読み取り専用, 降順)
var deleteVerticesIndicesUniqueDescending
= subMeshList
.Where(x => x.selected)
.SelectMany(x => x.verticesIndices)
.Distinct()
.OrderByDescending(x => x)
.ToList()
.AsReadOnly();
// 削除するサブメッシュのインデックスのリスト
var deleteSubMeshIndexList
= subMeshList
.Select((value, index) => new { Value = value, Index = index })
.Where(x => x.Value.selected)
.Select(x => x.Index)
.ToList()
.AsReadOnly();
var mesh = renderer.sharedMesh;
var mesh_custom = Instantiate(mesh);
mesh_custom.Clear();
// 頂点を削除
var vertices = mesh.vertices.ToList();
var boneWeights = mesh.boneWeights.ToList();
var uvs = mesh.uv.ToList();
var normals = mesh.normals.ToList();
var tangents = mesh.tangents.ToList();
var uv2s = mesh.uv2.ToList();
var uv3s = mesh.uv3.ToList();
var uv4s = mesh.uv4.ToList();
foreach (var deleteVertexIndex in deleteVerticesIndicesUniqueDescending)
{
vertices.RemoveAt(deleteVertexIndex);
boneWeights.RemoveAt(deleteVertexIndex);
normals.RemoveAt(deleteVertexIndex);
tangents.RemoveAt(deleteVertexIndex);
if (deleteVertexIndex < uvs.Count())
uvs.RemoveAt(deleteVertexIndex);
if (deleteVertexIndex < uv2s.Count())
uv2s.RemoveAt(deleteVertexIndex);
if (deleteVertexIndex < uv3s.Count())
uv3s.RemoveAt(deleteVertexIndex);
if (deleteVertexIndex < uv4s.Count())
uv4s.RemoveAt(deleteVertexIndex);
}
mesh_custom.SetVertices(vertices);
mesh_custom.boneWeights = boneWeights.ToArray();
mesh_custom.normals = normals.ToArray();
mesh_custom.tangents = tangents.ToArray();
mesh_custom.SetUVs(0, uvs);
mesh_custom.SetUVs(1, uv2s);
mesh_custom.SetUVs(2, uv3s);
mesh_custom.SetUVs(3, uv4s);
// サブメッシュごとにポリゴンを処理
mesh_custom.subMeshCount = mesh.subMeshCount - deleteSubMeshIndexList.Count();
var subMeshNumber = 0;
for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++)
{
if (deleteSubMeshIndexList.Contains(subMeshIndex)) continue;
var subMeshTriangles = mesh.GetTriangles(subMeshIndex);
// インデックスがずれるので各頂点への対応付けが必要
// インデックスが大きいものから順に処理していく
// O(n*m)
foreach (var deleteVerticesIndex in deleteVerticesIndicesUniqueDescending) // n
for (int i = 0; i < subMeshTriangles.Count(); i++) // m
if (subMeshTriangles[i] > deleteVerticesIndex)
subMeshTriangles[i]--;
mesh_custom.SetTriangles(subMeshTriangles, subMeshNumber++);
}
// BlendShapeを設定する
string blendShapeName;
float frameWeight;
var deltaVertices = new Vector3[mesh.vertexCount];
var deltaNormals = new Vector3[mesh.vertexCount];
var deltaTangents = new Vector3[mesh.vertexCount];
List<Vector3> deltaVerticesList, deltaNormalsList, deltaTangentsList;
for (int blendshapeIndex = 0; blendshapeIndex < mesh.blendShapeCount; blendshapeIndex++)
{
blendShapeName = mesh.GetBlendShapeName(blendshapeIndex);
frameWeight = mesh.GetBlendShapeFrameWeight(blendshapeIndex, 0);
mesh.GetBlendShapeFrameVertices(blendshapeIndex, 0, deltaVertices, deltaNormals, deltaTangents);
deltaVerticesList = deltaVertices.ToList();
deltaNormalsList = deltaNormals.ToList();
deltaTangentsList = deltaTangents.ToList();
foreach (var deleteVertexIndex in deleteVerticesIndicesUniqueDescending)
{
deltaVerticesList.RemoveAt(deleteVertexIndex);
deltaNormalsList.RemoveAt(deleteVertexIndex);
deltaTangentsList.RemoveAt(deleteVertexIndex);
}
mesh_custom.AddBlendShapeFrame(blendShapeName, frameWeight,
deltaVerticesList.ToArray(),
deltaNormalsList.ToArray(),
deltaTangentsList.ToArray());
}
AssetDatabase.CreateAsset(mesh_custom, AssetDatabase.GenerateUniqueAssetPath(saveFolder + mesh.name + "_deleteSubmesh.asset"));
AssetDatabase.SaveAssets();
Undo.RecordObject(renderer, "Change mesh " + mesh_custom.name);
renderer.sharedMesh = mesh_custom;
// 削除したサブメッシュのマテリアルを参照から外す
var materials = renderer.sharedMaterials.ToList();
for (var index = materials.Count() - 1; index >= 0; index--)
if (deleteSubMeshIndexList.Contains(index))
materials.RemoveAt(index);
renderer.sharedMaterials = materials.ToArray();
return true;
}
/// <summary>
/// サブメッシュのリストを取得する
/// </summary>
/// <param name="mesh"></param>
/// <returns></returns>
private List<SubMeshInfo> GetSubMeshList(Mesh mesh)
{
List<SubMeshInfo> subMeshList = new List<SubMeshInfo>();
for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++)
{
var meshInfo = new SubMeshInfo(mesh, subMeshIndex);
subMeshList.Add(meshInfo);
}
return subMeshList;
}
/// <summary>
/// Meshのポリゴン数を取得する
/// </summary>
/// <param name="mesh"></param>
/// <returns></returns>
private int GetMeshTriangleCount(Mesh mesh)
{
return mesh.triangles.Length / 3;
}
/// <summary>
/// mesh保存先のパスを取得する
/// </summary>
/// <param name="Mesh"></param>
/// <returns></returns>
private string GetMeshPath(Mesh mesh)
{
return Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh))+"/";
}
public class SubMeshInfo
{
public int subMeshIndex;
public int[] verticesIndices;
public int vertexCount;
public int triangleCount;
public bool selected = false;
public SubMeshInfo(Mesh mesh, int subMeshIndex)
{
this.subMeshIndex = subMeshIndex;
this.verticesIndices = mesh.GetIndices(subMeshIndex);
vertexCount = verticesIndices.Length;
triangleCount = mesh.GetTriangles(subMeshIndex).Length / 3;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39615a0c8ecb8ba48b303c82d3524389
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 27ecb87b1ac306e4e840689971bda2a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,199 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
// MIT License
/*
* Copyright 2020 gatosyocora
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace VRCDeveloperTool
{
public class GatoEditorUtility
{
private const char BSLASH = '\\';
/// <summary>
/// インデントなしのHelpbox
/// </summary>
/// <param name="message"></param>
/// <param name="messageType"></param>
public static void NonIndentHelpBox(string message, MessageType messageType)
{
var currentIndentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUILayout.HelpBox(message, messageType);
EditorGUI.indentLevel = currentIndentLevel;
}
/// <summary>
/// インデントなしのButton
/// </summary>
/// <param name="text"></param>
/// <param name="action"></param>
public static void NonIndentButton(string text, Action action)
{
var currentIndentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
if (GUILayout.Button(text))
{
action.Invoke();
}
EditorGUI.indentLevel = currentIndentLevel;
}
/// <summary>
/// パス内で存在しないフォルダを作成する
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static bool CreateNoExistFolders(string path)
{
string directoryPath;
if (string.IsNullOrEmpty(Path.GetExtension(path)))
{
directoryPath = path;
}
else
{
directoryPath = Path.GetDirectoryName(path);
}
if (!Directory.Exists(directoryPath))
{
var directories = directoryPath.Split(BSLASH);
directoryPath = "Assets";
for (int i = 1; i < directories.Length; i++)
{
if (!Directory.Exists(directoryPath + BSLASH + directories[i]))
{
AssetDatabase.CreateFolder(directoryPath, directories[i]);
}
directoryPath += BSLASH + directories[i];
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return true;
}
return false;
}
/// <summary>
/// 任意のアセットを複製する
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="newAssetName"></param>
/// <param name="saveFolderPath"></param>
/// <returns></returns>
public static T DuplicateAsset<T>(T source, string newAssetPath) where T : UnityEngine.Object
{
var sourcePath = AssetDatabase.GetAssetPath(source);
return DuplicateAsset<T>(sourcePath, newAssetPath);
}
public static T DuplicateAsset<T>(string sourcePath, string newAssetPath) where T : UnityEngine.Object
{
var newFolderPath = Path.GetDirectoryName(newAssetPath);
CreateNoExistFolders(newFolderPath);
var newPath = AssetDatabase.GenerateUniqueAssetPath(newAssetPath);
AssetDatabase.CopyAsset(sourcePath, newPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
var newAsset = AssetDatabase.LoadAssetAtPath(newPath, typeof(T)) as T;
return newAsset;
}
/// <summary>
/// 最後にキーワードを追加する(重複なし)
/// </summary>
/// <param name="target"></param>
/// <param name="keyword"></param>
/// <returns></returns>
public static string AddKeywordToEnd(string target, string keyword)
{
if (string.IsNullOrEmpty(keyword)) return target;
var normalString = Regex.Replace(target, keyword + ".*", string.Empty);
return normalString + keyword;
}
/// <summary>
/// 特定のオブジェクトから特定のオブジェクトまでのパスを取得する
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string GetHierarchyPathFromObj1ToObj2(GameObject obj1, GameObject obj2)
{
string path = obj2.name;
var parent = obj2.transform.parent;
while (parent != null)
{
if (parent.gameObject.name == obj1.name) return path;
path = parent.name + "/" + path;
parent = parent.parent;
}
return path;
}
/// <summary>
/// 特定のオブジェクトまでのパスを取得する
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string GetHierarchyPath(GameObject obj)
{
string path = obj.name;
Transform parent = obj.transform.parent;
while (parent != null)
{
if (parent.parent == null) return path;
path = parent.name + "/" + path;
parent = parent.parent;
}
return path;
}
/// <summary>
/// フォルダ名からフォルダパスを取得する
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static string GetFolderPathFromName(string folderName)
{
var guid = AssetDatabase.FindAssets(folderName + " t:Folder").FirstOrDefault();
return AssetDatabase.GUIDToAssetPath(guid);
}
/// <summary>
/// 複製された2つのオブジェクト間で片方の特定のTransformに対応したTransformを取得する
/// </summary>
/// <param name="source"></param>
/// <param name="duplicated"></param>
/// <param name="target"></param>
/// <returns></returns>
public static Transform GetCorrespondTransformBetweenDuplicatedObjects(GameObject source, GameObject duplicated, Transform target)
{
if (source.transform == target) return duplicated.transform;
var path = GetHierarchyPathFromObj1ToObj2(source, target.gameObject);
return duplicated.transform.Find(path);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea831e1b44c1f2e40a8b07d7fcad1be8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1d61406e20153444092677a33f354983
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
// ver 1.0
// Copyright (c) 2020 gatosyocora
namespace VRCDeveloperTool
{
public class VRCAssetCreator : Editor
{
#if VRC_SDK_VRCSDK2
[MenuItem("Assets/Create/VRChat/CustomOverrideController", priority = 0)]
public static void CreateVRCCustomOverrideController()
{
var outputFolderPath = AssetDatabase.GetAssetPath(Selection.activeInstanceID);
var originalGuid = AssetDatabase.FindAssets("CustomOverrideEmpty").First();
var originalPath = AssetDatabase.GUIDToAssetPath(originalGuid);
var outputPath = outputFolderPath + "/" + Path.GetFileName(originalPath);
DuplicateAsset(originalPath, outputPath);
}
#endif
private static void DuplicateAsset(string originalPath, string outputPath)
{
outputPath = AssetDatabase.GenerateUniqueAssetPath(outputPath);
AssetDatabase.CopyAsset(originalPath, outputPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99d09038281ca144e87244fd8ff48e42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ebf40d759e151f4a86f46b6de8580cf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8e14459547668344b92d23570c122e5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a93d8c7d5fa23224db2d4765580f70d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 595f725aaeb14c74ebbc662992c556b6
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,376 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Threading.Tasks;
using System;
using UnityEditor.Animations;
using UnityEditor.SceneManagement;
#if VRC_SDK_VRCSDK2
using VRCSDK2;
#endif
// ver 1.0
// Copyright (c) 2020 gatosyocora
namespace VRCDeveloperTool
{
public class VRCAvatarTester : EditorWindow
{
#if VRC_SDK_VRCSDK2
private VRC_AvatarDescriptor avatar;
#endif
[SerializeField]
private Animator animator;
[SerializeField]
private AnimatorOverrideController controller;
[SerializeField]
private RuntimeAnimatorController defaultController;
public enum PlayingType
{
NONE, OVERRIDE, EMOTE
};
private PlayingType playingType = PlayingType.NONE;
public enum PlayingHand
{
NONE, RIGHT, LEFT, BOTH
};
private PlayingHand playingHand = PlayingHand.NONE;
private static readonly string[] OVERRIDES = new string[]
{
"FIST", "HANDOPEN", "FINGERPOINT", "VICTORY", "ROCKNROLL", "HANDGUN", "THUMBSUP"
};
private static readonly string[] EMOTES = new string[]
{
"EMOTE1", "EMOTE2", "EMOTE3", "EMOTE4", "EMOTE5", "EMOTE6", "EMOTE7", "EMOTE8"
};
[SerializeField]
private GameObject poseConstraintObj;
private PoseConstraint poseConstraint;
[MenuItem("VRCDeveloperTool/VRCAvatarTester")]
public static void Open()
{
GetWindow<VRCAvatarTester>(nameof(VRCAvatarTester));
}
private void Update()
{
#if VRC_SDK_VRCSDK2
// 再生中
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
// 毎回取得しないとActiveへの変更がなぜか適用されない
poseConstraint = poseConstraintObj.GetComponent<PoseConstraint>();
animator.runtimeAnimatorController = controller;
if (playingType == PlayingType.OVERRIDE)
{
poseConstraint.Active = true;
animator.SetInteger($"Emote", 0);
switch (playingHand)
{
case PlayingHand.NONE:
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
break;
case PlayingHand.RIGHT:
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 1);
break;
case PlayingHand.LEFT:
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 1);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
break;
case PlayingHand.BOTH:
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 1);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 1);
break;
default:
break;
}
}
else if (playingType == PlayingType.EMOTE)
{
poseConstraint.Active = false;
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
}
else
{
poseConstraint.Active = true;
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
}
}
// 未再生
else
{
if (animator != null && controller != null)
{
animator.runtimeAnimatorController = defaultController;
animator.SetLayerWeight(animator.GetLayerIndex("HandLeft"), 0);
animator.SetLayerWeight(animator.GetLayerIndex("HandRight"), 0);
}
}
#endif
}
private void OnGUI()
{
#if VRC_SDK_VRCSDK2
using (var check = new EditorGUI.ChangeCheckScope())
{
avatar = EditorGUILayout.ObjectField("Avatar", avatar, typeof(VRC_AvatarDescriptor), true) as VRC_AvatarDescriptor;
if (check.changed)
{
if (avatar != null)
{
animator = avatar.gameObject.GetComponent<Animator>();
controller = avatar.CustomStandingAnims;
}
else
{
animator = null;
controller = null;
}
}
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Camera", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Scene View"))
{
EditorApplication.ExecuteMenuItem("Window/General/Scene");
}
if (GUILayout.Button("Game View"))
{
EditorApplication.ExecuteMenuItem("Window/General/Game");
}
}
/*
using (new EditorGUI.DisabledGroupScope(animator is null))
{
if (GUILayout.Button("Focus on Avatar"))
{
var sceneViewCamera = SceneView.lastActiveSceneView.camera;
sceneViewCamera.transform.position = animator.transform.position;
}
}
*/
GUILayout.Space(15);
EditorGUILayout.LabelField("Testing", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
using (new EditorGUI.DisabledGroupScope(EditorApplication.isPlayingOrWillChangePlaymode || avatar == null))
{
if (GUILayout.Button("Play"))
{
defaultController = animator.runtimeAnimatorController;
animator.runtimeAnimatorController = controller;
poseConstraintObj = CreatePoseConstrainterToRootIfNeeded();
poseConstraint = poseConstraintObj.GetComponent<PoseConstraint>();
poseConstraint.UpdateBoneInfo(animator);
EditorApplication.isPlaying = true;
}
}
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
{
if (GUILayout.Button("Stop"))
{
EditorApplication.isPlaying = false;
animator.runtimeAnimatorController = defaultController;
}
}
}
if (avatar == null && !EditorApplication.isPlaying)
{
EditorGUILayout.HelpBox("Avatarを設定してください", MessageType.Error);
}
if (avatar != null && animator != null && controller != null)
{
EditorGUILayout.HelpBox("Playを選択するとテストが実行できます", MessageType.Info);
}
EditorGUILayout.Space();
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
{
if (GUILayout.Button("Reset All"))
{
playingType = PlayingType.NONE;
playingHand = PlayingHand.NONE;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("AnimationOverrides", EditorStyles.boldLabel);
using (new EditorGUI.IndentLevelScope())
{
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("NONE");
if (GUILayout.Button("Left"))
{
if (playingType == PlayingType.OVERRIDE &&
playingHand == PlayingHand.BOTH)
{
playingHand = PlayingHand.RIGHT;
}
else
{
playingType = PlayingType.NONE;
}
PlayOverride("Left", 0, animator);
}
if (GUILayout.Button("Right"))
{
if (playingType == PlayingType.OVERRIDE &&
playingHand == PlayingHand.BOTH)
{
playingHand = PlayingHand.LEFT;
}
else
{
playingType = PlayingType.NONE;
}
PlayOverride("Right", 0, animator);
}
}
for (int overrideNumber = 0; overrideNumber < OVERRIDES.Length; overrideNumber++)
{
AnimationClip overrideAnimation = null;
string overrideName = string.Empty;
if (controller != null)
{
overrideAnimation = controller[OVERRIDES[overrideNumber]];
if (overrideAnimation.name != OVERRIDES[overrideNumber])
{
overrideName = $"({overrideAnimation.name})";
}
}
// AnimationClipとOVERRIDES[overrideNumber]の名前が同じであれば未設定
using (new EditorGUI.DisabledGroupScope(controller == null || overrideAnimation.name == OVERRIDES[overrideNumber]))
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(OVERRIDES[overrideNumber], overrideName);
if (GUILayout.Button("Left"))
{
playingType = PlayingType.OVERRIDE;
if (playingHand == PlayingHand.RIGHT ||
playingHand == PlayingHand.BOTH)
{
playingHand = PlayingHand.BOTH;
}
else
{
playingHand = PlayingHand.LEFT;
}
PlayOverride("Left", overrideNumber, animator);
}
if (GUILayout.Button("Right"))
{
playingType = PlayingType.OVERRIDE;
if (playingHand == PlayingHand.LEFT ||
playingHand == PlayingHand.BOTH)
{
playingHand = PlayingHand.BOTH;
}
else
{
playingHand = PlayingHand.RIGHT;
}
PlayOverride("Right", overrideNumber, animator);
}
}
}
}
}
EditorGUILayout.Space();
using (new EditorGUI.DisabledGroupScope(!EditorApplication.isPlayingOrWillChangePlaymode))
{
EditorGUILayout.LabelField("Emotes", EditorStyles.boldLabel);
using (new EditorGUI.IndentLevelScope())
{
for (int emoteNumber = 0; emoteNumber < EMOTES.Length; emoteNumber++)
{
AnimationClip emoteAnimation = null;
string buttonText = EMOTES[emoteNumber];
if (controller != null)
{
emoteAnimation = controller[EMOTES[emoteNumber]];
buttonText = emoteAnimation.name;
}
// 取得できるAnimationClipの名前が"EMOTE*"だったら設定されていない
using (new EditorGUI.DisabledGroupScope(emoteAnimation == null || emoteAnimation.name == EMOTES[emoteNumber]))
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(EMOTES[emoteNumber]);
if (GUILayout.Button(buttonText) && emoteAnimation != null)
{
if (animator.GetInteger($"Emote") != 0) return;
playingType = PlayingType.EMOTE;
playingHand = PlayingHand.NONE;
PlayEmote(emoteNumber + 1, animator, emoteAnimation);
}
}
}
}
}
#else
EditorGUILayout.HelpBox("使用するにはVRCSDK2がプロジェクトにインポートされている必要があります", MessageType.Error);
#endif
}
private void PlayOverride(string hand, int overrideNumber, Animator animator)
{
animator.SetInteger($"HandGesture{hand}", overrideNumber);
}
private async void PlayEmote(int emoteNumber, Animator animator, AnimationClip animationClip)
{
int waitMilliSecond = (int)(animationClip.length * 1000);
if (animationClip == null) waitMilliSecond = 1000;
animator.SetInteger($"Emote", emoteNumber);
// EmoteAnimationの実行が終わるまで待つ必要がある
await Task.Delay(waitMilliSecond);
animator.SetInteger($"Emote", 0);
}
private GameObject CreatePoseConstrainterToRootIfNeeded()
{
var obj = GameObject.Find("PoseConstrainter");
if (obj is null)
{
var prefab = Resources.Load("Tester/PoseConstrainter");
obj = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
}
return obj;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad6a8a6e01f535447b7c5b353726db9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/External/VRCDeveloperTool/LICENSE.txt (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8ab5348371ff39442bb728561d7afefc
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/External/VRCDeveloperTool/README_VRCDeveloperTool.txt (Stored with Git LFS) vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f58d8d4097bd56349ba1953e6c80e2d9
timeCreated: 1537082424
licenseType: Free
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: efb64c83056940b49ab22f22512cc222
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// ver 1.00
// © 2019-1-31 gatosyocora
public class ScaleLimitVisualizer : MonoBehaviour {
public Vector3 scaleLimit = new Vector3(4, 5, 3);
public bool isWireFrame = true;
void OnDrawGizmos()
{
var pos = transform.position + new Vector3(0, (float)(scaleLimit.y / 2.0), 0);
if (isWireFrame)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(pos, scaleLimit);
}
else
{
Gizmos.color = new Color(1, 1, 0, 0.8f);
Gizmos.DrawCube(pos, scaleLimit);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5be28f395556f6b47937df98fb2db262
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af0898226daf7164999ceea1e2c8092d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 100100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 76f28943da1e75c4f89da9708fdca076
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,88 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace VRCDeveloperTool
{
public class PoseConstraint : MonoBehaviour
{
[Serializable]
public class BoneInfo
{
public Transform Transform { get; set; }
public Vector3 Position { get; set; }
public Quaternion Rotation { get; set; }
}
private List<BoneInfo> boneList;
public Animator animator;
public bool Active = true;
public void Start()
{
boneList = GetBoneInfo(animator);
Active = true;
}
private void LateUpdate()
{
if (Active && boneList != null) SetBoneInfo(boneList);
}
public void UpdateBoneInfo(Animator animator)
{
this.animator = animator;
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
boneList = GetBoneInfo(animator);
}
private List<BoneInfo> GetBoneInfo(Animator animator)
{
return Enum.GetNames(typeof(HumanBodyBones))
.Select(boneName =>
{
if (Enum.TryParse(boneName, out HumanBodyBones bone))
{
Transform trans;
try
{
trans = animator.GetBoneTransform(bone);
}
catch (IndexOutOfRangeException)
{
return null;
}
if (trans is null) return null;
return new BoneInfo
{
Transform = trans,
Position = trans.position,
Rotation = trans.rotation
};
}
else
{
return null;
}
})
.Where(x => x != null)
.ToList();
}
private void SetBoneInfo(List<BoneInfo> boneList)
{
foreach (var bone in boneList)
{
bone.Transform.position = bone.Position;
bone.Transform.rotation = bone.Rotation;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ced8b4af24d7fee4e8b95e1c85e26eeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: