Exemple #1
0
    private static void SliderFloat(LyumaAv3Menu menu, VRCExpressionsMenu.Control.Parameter subParam, string intent, float left, float right)
    {
        if (subParam == null || subParam.name == "")
        {
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.Slider(intent, 0, left, right);
            EditorGUI.EndDisabledGroup();
            return;
        }

        menu.UserFloat(subParam.name, EditorGUILayout.Slider(intent + " (" + subParam.name + ")", menu.FindFloat(subParam.name), left, right));
    }
Exemple #2
0
 public ParameterDefinition AddParameter(VRCExpressionsMenu.Control.Parameter parameter)
 {
     return(Children.AddChild(new ParameterDefinition(this, parameter.name)));
 }
        /// <summary>
        /// Creates a copy of the avatar descriptor's topmost menu asset or creates one if it doesn't exist, adds the provided menu as a submenu,
        /// assigns the new topmost menu asset, and stores it in the specified directory.
        /// </summary>
        /// <param name="descriptor">The avatar descriptor to add the submenu to.</param>
        /// <param name="menuToAdd">The menu to add, which will become a submenu of the topmost menu.</param>
        /// <param name="controlName">The name of the submenu control for the menu to add.</param>
        /// <param name="directory">The unique directory to store the new topmost menu asset, ex. "Assets/MyCoolScript/GeneratedAssets/725638/".</param>
        /// <param name="controlParameter">Optionally, the parameter to trigger when the submenu is opened.</param>
        /// <param name="icon"> Optionally, the icon to display on this submenu. </param>
        /// <param name="overwrite">Optionally, choose to not overwrite an asset of the same name in directory. See class for more info.</param>
        public static void AddSubMenu(VRCAvatarDescriptor descriptor, VRCExpressionsMenu menuToAdd, string controlName, string directory, VRCExpressionsMenu.Control.Parameter controlParameter = null, Texture2D icon = null, bool overwrite = true)
        {
            if (descriptor == null)
            {
                Debug.LogError("Couldn't add the menu, the avatar descriptor is null!");
                return;
            }
            else if ((menuToAdd == null) || (controlName == null) || (controlName == ""))
            {
                Debug.LogError("Couldn't add the menu, it or the name of its control is null!");
                return;
            }
            else if ((directory == null) || (directory == ""))
            {
                Debug.Log("Directory was not specified, storing new topmost menu in " + defaultDirectory);
                directory = defaultDirectory;
            }

            descriptor.customExpressions = true;
            VRCExpressionsMenu topMenu = ScriptableObject.CreateInstance <VRCExpressionsMenu>();

            if (descriptor.expressionsMenu == null)
            {
                AssetDatabase.CreateAsset(topMenu, directory + "Menu Topmost.asset");
            }
            else
            {
                if (descriptor.expressionsMenu.controls.Count == 8)
                {
                    Debug.LogWarning("Couldn't add menu. Please have an available slot in your avatar's topmost Expression Menu.");
                    return;
                }

                string path = (directory + descriptor.expressionsMenu.name + ".asset");
                path = (overwrite) ? path : AssetDatabase.GenerateUniqueAssetPath(path);
                if (AssetDatabase.GetAssetPath(descriptor.expressionsMenu) != path) // if we have not made a copy yet
                {                                                                   // CopyAsset with two identical strings yields exception
                    AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(descriptor.expressionsMenu), path);
                }
                topMenu = AssetDatabase.LoadAssetAtPath(path, typeof(VRCExpressionsMenu)) as VRCExpressionsMenu;
            }

            List <VRCExpressionsMenu.Control> controlList = topMenu.controls;

            for (int i = 0; i < controlList.Count; i++)
            {
                if (controlList[i].name.Equals(controlName) && controlList[i].type.Equals(VRCExpressionsMenu.Control.ControlType.SubMenu))
                { // if a control for a submenu exists with the same name, replace the submenu
                    controlList[i].subMenu   = menuToAdd;
                    controlList[i].parameter = controlParameter;
                    controlList[i].icon      = icon;
                    EditorUtility.SetDirty(topMenu);
                    AssetDatabase.SaveAssets();
                    AssetDatabase.Refresh();
                    descriptor.expressionsMenu = topMenu;
                    return;
                }
            }

            VRCExpressionsMenu.Control control = new VRCExpressionsMenu.Control
            {
                name = controlName, type = VRCExpressionsMenu.Control.ControlType.SubMenu, subMenu = menuToAdd, parameter = controlParameter, icon = icon
            };
            controlList.Add(control);
            topMenu.controls = controlList;
            EditorUtility.SetDirty(topMenu);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            descriptor.expressionsMenu = topMenu;
        }
Exemple #4
0
        public void Generate()
        {
            // Unique directory setup, named after avatar
            Directory.CreateDirectory("Assets/VRLabs/GeneratedAssets/Marker/");
            AssetDatabase.Refresh();
            // Folder name cannot contain these chars
            string cleanedName = string.Join("", descriptor.name.Split('/', '?', '<', '>', '\\', ':', '*', '|', '\"'));
            string guid        = AssetDatabase.CreateFolder("Assets/VRLabs/GeneratedAssets/Marker", cleanedName);
            string directory   = AssetDatabase.GUIDToAssetPath(guid) + "/";

            // Install layers, parameters, and menu before prefab setup
            // FX layer
            if (useIndexFinger)
            {
                AssetDatabase.CopyAsset("Assets/VRLabs/Marker/Resources/M_FX (Finger).controller", directory + "FXtemp.controller");
            }
            else
            {
                AssetDatabase.CopyAsset("Assets/VRLabs/Marker/Resources/M_FX.controller", directory + "FXtemp.controller");
            }
            AnimatorController FX = AssetDatabase.LoadAssetAtPath(directory + "FXtemp.controller", typeof(AnimatorController)) as AnimatorController;

            // remove controller layers before merging to avatar, corresponding to setup
            if (leftHanded)
            {
                RemoveLayer(FX, "M_Marker R");
            }
            else
            {
                RemoveLayer(FX, "M_Marker L");
            }

            if (!brushSize)
            {
                RemoveLayer(FX, "M_Size");
                RemoveParameter(FX, "M_Size");
            }

            if (!eraserSize)
            {
                RemoveLayer(FX, "M_EraserSize");
                RemoveParameter(FX, "M_EraserSize");
            }

            if (!localSpace)
            {
                RemoveLayer(FX, "M_Space");
                RemoveParameter(FX, "M_Space");
                RemoveLayer(FX, "M_Cull");
            }
            else
            {
                RemoveLayer(FX, "M_SpaceSimple");
                RemoveParameter(FX, "M_SpaceSimple");
                RemoveLayer(FX, "M_CullSimple");
            }

            if (writeDefaults)
            {
                AV3ManagerFunctions.SetWriteDefaults(FX);
            }
            if (gestureToDraw != 3)             // uses fingerpoint by default
            {
                ChangeGestureCondition(FX, 0, gestureToDraw);
            }

            // Set parameter driver on 'Clear' state to reset local space
            AnimatorState            state  = FX.layers[0].stateMachine.states.FirstOrDefault(s => s.state.name.Equals("Clear")).state;
            VRCAvatarParameterDriver driver = (VRCAvatarParameterDriver)state.behaviours[0];
            string driverParamName          = localSpace ? "M_Space" : "M_SpaceSimple";

            VRC.SDKBase.VRC_AvatarParameterDriver.Parameter param = new VRC.SDKBase.VRC_AvatarParameterDriver.Parameter()
            {
                name  = driverParamName,
                type  = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set,
                value = 0f
            };
            driver.parameters.Add(param);

            EditorUtility.SetDirty(FX);
            AV3ManagerFunctions.MergeToLayer(descriptor, FX, AV3ManagerFunctions.PlayableLayer.FX, directory);
            AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(FX));             // delete temporary FX layer

            // Gesture layer
            AssetDatabase.CopyAsset("Assets/VRLabs/Marker/Resources/M_Gesture.controller", directory + "gestureTemp.controller");             // to modify
            AnimatorController gesture = AssetDatabase.LoadAssetAtPath(directory + "gestureTemp.controller", typeof(AnimatorController)) as AnimatorController;

            if (descriptor.baseAnimationLayers[2].isDefault == true || descriptor.baseAnimationLayers[2].animatorController == null)
            {
                AssetDatabase.CopyAsset(path_defaultGesture, directory + "Gesture.controller");
                AnimatorController gestureOriginal = AssetDatabase.LoadAssetAtPath(directory + "Gesture.controller", typeof(AnimatorController)) as AnimatorController;

                descriptor.customExpressions = true;
                descriptor.baseAnimationLayers[2].isDefault          = false;
                descriptor.baseAnimationLayers[2].animatorController = gestureOriginal;

                if (writeDefaults)
                {
                    AV3ManagerFunctions.SetWriteDefaults(gestureOriginal);
                    EditorUtility.SetDirty(gestureOriginal);
                }
            }

            gesture.RemoveLayer((leftHanded) ? 1 : 0);
            if (useIndexFinger)
            {               // use different hand animations
                for (int i = 0; i < 3; i++)
                {
                    if (gesture.layers[0].stateMachine.states[i].state.motion.name == "M_Gesture")
                    {
                        gesture.layers[0].stateMachine.states[i].state.motion = AssetDatabase.LoadAssetAtPath("Assets/VRLabs/Marker/Resources/Animations/Gesture/M_Gesture (Finger).anim", typeof(AnimationClip)) as AnimationClip;
                    }
                    else if (gesture.layers[0].stateMachine.states[i].state.motion.name == "M_Gesture Draw")
                    {
                        gesture.layers[0].stateMachine.states[i].state.motion = AssetDatabase.LoadAssetAtPath("Assets/VRLabs/Marker/Resources/Animations/Gesture/M_Gesture Draw (Finger).anim", typeof(AnimationClip)) as AnimationClip;
                    }
                }
            }
            if (gestureToDraw != 3)
            {
                ChangeGestureCondition(gesture, 0, gestureToDraw);
            }
            if (writeDefaults)
            {
                AV3ManagerFunctions.SetWriteDefaults(gesture, true);
            }

            EditorUtility.SetDirty(gesture);
            AV3ManagerFunctions.MergeToLayer(descriptor, gesture, AV3ManagerFunctions.PlayableLayer.Gesture, directory);
            AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(gesture));             // delete temporary gesture layer

            // layer weight control from merged layer may need index set correctly
            AnimatorController avatarGesture = (AnimatorController)descriptor.baseAnimationLayers[2].animatorController;

            for (int i = 0; i < avatarGesture.layers.Length; i++)
            {               // the controls' layer is normally 3 (AllParts, LeftHand, RightHand, >>>M_Gesture<<<)
                if (avatarGesture.layers[i].name.Contains("M_Gesture") && (i != 3))
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (avatarGesture.layers[i].stateMachine.states[j].state.behaviours.Length != 0)
                        {
                            VRCAnimatorLayerControl ctrl = (VRCAnimatorLayerControl)avatarGesture.layers[i].stateMachine.states[j].state.behaviours[0];
                            ctrl.layer = i;
                        }
                    }
                }
            }

            EditorUtility.SetDirty(avatarGesture);

            // Parameters
            VRCExpressionParameters.Parameter
                p_marker = new VRCExpressionParameters.Parameter
            {
                name = "M_Marker", valueType = VRCExpressionParameters.ValueType.Bool, saved = false
            },
                p_eraser = new VRCExpressionParameters.Parameter
            {
                name = "M_Eraser", valueType = VRCExpressionParameters.ValueType.Bool, saved = false
            },
                p_clear = new VRCExpressionParameters.Parameter
            {
                name = "M_Clear", valueType = VRCExpressionParameters.ValueType.Bool, saved = false
            },
                p_color = new VRCExpressionParameters.Parameter
            {
                name = "M_Color", valueType = VRCExpressionParameters.ValueType.Float, saved = true
            };
            AV3ManagerFunctions.AddParameter(descriptor, p_marker, directory);
            AV3ManagerFunctions.AddParameter(descriptor, p_eraser, directory);
            AV3ManagerFunctions.AddParameter(descriptor, p_clear, directory);
            AV3ManagerFunctions.AddParameter(descriptor, p_color, directory);

            if (localSpace)
            {
                VRCExpressionParameters.Parameter p_space = new VRCExpressionParameters.Parameter
                {
                    name = "M_Space", valueType = VRCExpressionParameters.ValueType.Int, saved = false
                };
                AV3ManagerFunctions.AddParameter(descriptor, p_space, directory);
            }
            else
            {
                VRCExpressionParameters.Parameter p_spaceSimple = new VRCExpressionParameters.Parameter
                {
                    name = "M_SpaceSimple", valueType = VRCExpressionParameters.ValueType.Bool, saved = false
                };
                AV3ManagerFunctions.AddParameter(descriptor, p_spaceSimple, directory);
            }

            if (brushSize)
            {
                VRCExpressionParameters.Parameter p_size = new VRCExpressionParameters.Parameter
                {
                    name = "M_Size", valueType = VRCExpressionParameters.ValueType.Float, saved = false
                };
                AV3ManagerFunctions.AddParameter(descriptor, p_size, directory);
            }
            if (eraserSize)
            {
                VRCExpressionParameters.Parameter p_eraserSize = new VRCExpressionParameters.Parameter
                {
                    name = "M_EraserSize", valueType = VRCExpressionParameters.ValueType.Float, saved = false
                };
                AV3ManagerFunctions.AddParameter(descriptor, p_eraserSize, directory);
            }

            VRCExpressionParameters.Parameter p_menu = new VRCExpressionParameters.Parameter
            {
                name = "M_Menu", valueType = VRCExpressionParameters.ValueType.Bool, saved = false
            };
            AV3ManagerFunctions.AddParameter(descriptor, p_menu, directory);

            // handle menu instancing
            AssetDatabase.CopyAsset("Assets/VRLabs/Marker/Resources/M_Menu.asset", directory + "Marker Menu.asset");
            VRCExpressionsMenu markerMenu = AssetDatabase.LoadAssetAtPath(directory + "Marker Menu.asset", typeof(VRCExpressionsMenu)) as VRCExpressionsMenu;

            if (!localSpace)             // change from submenu to 1 toggle
            {
                VRCExpressionsMenu.Control.Parameter pm_spaceSimple = new VRCExpressionsMenu.Control.Parameter
                {
                    name = "M_SpaceSimple"
                };
                markerMenu.controls[6].type      = VRCExpressionsMenu.Control.ControlType.Toggle;
                markerMenu.controls[6].parameter = pm_spaceSimple;
                markerMenu.controls[6].subMenu   = null;               // or else the submenu is still there internally, SDK complains
            }
            else
            {
                AssetDatabase.CopyAsset("Assets/VRLabs/Marker/Resources/M_Menu Space.asset", directory + "Marker Space Submenu.asset");
                VRCExpressionsMenu subMenu = AssetDatabase.LoadAssetAtPath(directory + "Marker Space Submenu.asset", typeof(VRCExpressionsMenu)) as VRCExpressionsMenu;

                if (localSpaceFullBody == 0)                 // remove left and right foot controls
                {
                    subMenu.controls.RemoveAt(7);
                    subMenu.controls.RemoveAt(6);
                }
                markerMenu.controls[6].subMenu = subMenu;
                EditorUtility.SetDirty(subMenu);
            }

            if (!brushSize)
            {
                RemoveMenuControl(markerMenu, "Brush Size");
            }

            if (!eraserSize)
            {
                RemoveMenuControl(markerMenu, "Eraser Size");
            }

            EditorUtility.SetDirty(markerMenu);

            VRCExpressionsMenu.Control.Parameter pm_menu = new VRCExpressionsMenu.Control.Parameter
            {
                name = "M_Menu"
            };
            Texture2D markerIcon = AssetDatabase.LoadAssetAtPath("Assets/VRLabs/Marker/Resources/Icons/M_Icon_Menu.png", typeof(Texture2D)) as Texture2D;

            AV3ManagerFunctions.AddSubMenu(descriptor, markerMenu, "Marker", directory, pm_menu, markerIcon);

            // setup in scene
            GameObject marker = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath("Assets/VRLabs/Marker/Resources/Marker.prefab", typeof(GameObject))) as GameObject;

            if (PrefabUtility.IsPartOfPrefabInstance(marker))
            {
                PrefabUtility.UnpackPrefabInstance(marker, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
            }
            marker.transform.SetParent(avatar.transform, false);

            Transform system       = marker.transform.Find("System");
            Transform targets      = marker.transform.Find("Targets");
            Transform markerTarget = targets.Find("MarkerTarget");
            Transform markerModel  = targets.Find("Model");
            Transform eraser       = system.Find("Eraser");
            Transform local        = marker.transform.Find("World").Find("Local");

            // constrain cull object to avatar
            Transform cull = marker.transform.Find("Cull");

            cull.GetComponent <ParentConstraint>().SetSource(0, new ConstraintSource {
                sourceTransform = descriptor.transform, weight = 1f
            });

            if (useIndexFinger)
            {
                DestroyImmediate(markerTarget.GetChild(0).gameObject);                 // destroy Flip
                Transform indexDistal = leftHanded ? avatar.GetBoneTransform(HumanBodyBones.LeftIndexDistal) : avatar.GetBoneTransform(HumanBodyBones.RightIndexDistal);

                // prefer the end bone of the index finger if it exists
                if (indexDistal.Find(indexDistal.gameObject.name + "_end") != null)
                {
                    markerTarget.SetParent(indexDistal.Find(indexDistal.gameObject.name + "_end"), true);
                }
                else
                {
                    markerTarget.SetParent(indexDistal, false);
                }
            }
            else                                         // using model: scale Model to target freely, and until script is destroyed, scale System to target uniformly with X-axis
            {
                markerModel.SetParent(marker.transform); // move it out of Targets hierarchy
                if (leftHanded)
                {
                    markerTarget.SetParent(avatar.GetBoneTransform(HumanBodyBones.LeftHand), true);
                }
                else
                {
                    markerTarget.SetParent(avatar.GetBoneTransform(HumanBodyBones.RightHand), true);
                }
                ((Marker)target).markerModel = markerModel;                 // to turn its mesh renderer off when script is finished
            }
            markerTarget.localPosition = Vector3.zero;
            markerTarget.localRotation = Quaternion.Euler(0f, 0f, 0f);

            HumanBodyBones[] bones = { HumanBodyBones.Hips,     HumanBodyBones.Chest, HumanBodyBones.Head, HumanBodyBones.LeftHand, HumanBodyBones.RightHand,
                                       HumanBodyBones.LeftFoot, HumanBodyBones.RightFoot };
            ParentConstraint localConstraint = local.GetComponent <ParentConstraint>();

            localConstraint.SetSource(0, new ConstraintSource {
                sourceTransform = avatar.transform, weight = 1f
            });
            if (localSpace)
            {
                for (int i = 0; i < 5; i++)
                {
                    localConstraint.SetSource(i + 1, new ConstraintSource {
                        sourceTransform = avatar.GetBoneTransform(bones[i]), weight = 0f
                    });
                }
                if (localSpaceFullBody == 1)
                {
                    for (int i = 5; i < 7; i++)
                    {
                        localConstraint.SetSource(i + 1, new ConstraintSource {
                            sourceTransform = avatar.GetBoneTransform(bones[i]), weight = 0f
                        });
                    }
                }
            }

            DestroyImmediate(targets.gameObject);             // remove the "Targets" container object when finished

            // set anything not adjustable to a medium-ish amount
            if (!eraserSize)
            {
                eraser.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            }
            if (!brushSize)
            {
                ParticleSystem.MinMaxCurve size = new ParticleSystem.MinMaxCurve(0.024f);
                Transform draw    = system.transform.Find("Draw");
                Transform preview = draw.GetChild(0);
                ParticleSystem.MainModule main = draw.GetComponent <ParticleSystem>().main;
                main.startSize = size;
                main           = preview.GetComponent <ParticleSystem>().main;
                main.startSize = size;
            }

            // scale MarkerTarget, which controls prefab size, according to a (normalized) worldspace distance between avatar hips and head
            Transform hips = avatar.GetBoneTransform(HumanBodyBones.Hips);
            Transform head = avatar.GetBoneTransform(HumanBodyBones.Head);
            Vector3   dist = (head.position - hips.position);

            float normalizedDist = (Math.Max(Math.Max(dist.x, dist.y), dist.z) / KSIVL_UNIT);
            float newScale       = markerTarget.localScale.x * normalizedDist;

            markerTarget.localScale = new Vector3(newScale, newScale, newScale);

            ((Marker)target).system       = system;
            ((Marker)target).markerTarget = markerTarget;
            ((Marker)target).finished     = true;
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            Debug.Log("Successfully generated Marker!");
        }