public override void OnInspectorGUI()
        {
            serializedObject.Update();

            var script               = serializedObject.FindProperty("m_Script");
            var groupName            = serializedObject.FindProperty("GroupName");
            var syncWithPlayerUpdate = serializedObject.FindProperty("SyncWithPlayerUpdate");
            var updateRate           = serializedObject.FindProperty("UpdateRate");
            var positionThreshold    = serializedObject.FindProperty("PositionThreshold");
            var rotationThreshold    = serializedObject.FindProperty("RotationThreshold");
            var snapshotOnEnable     = serializedObject.FindProperty("SnapshotOnEnable");
            var updateTicksOnEnable  = serializedObject.FindProperty("UpdateTicksOnEnable");
            var releaseOnDisable     = serializedObject.FindProperty("ReleaseIdOnDisable");
            var releaseOnDestroy     = serializedObject.FindProperty("ReleaseIdOnDestroy");
            var useCustomID          = serializedObject.FindProperty("UseCustomId");
            var customId             = serializedObject.FindProperty("CustomId");
            var commonMeshName       = serializedObject.FindProperty("CommonMesh");
            var meshname             = serializedObject.FindProperty("MeshName");
            var useCustomMesh        = serializedObject.FindProperty("UseCustomMesh");
            var trackGaze            = serializedObject.FindProperty("TrackGaze");
            var requiresManualEnable = serializedObject.FindProperty("RequiresManualEnable");

            foreach (var t in serializedObject.targetObjects)
            {
                var dynamic = t as DynamicObject;
                if (dynamic.iId != dynamic.GetInstanceID() || string.IsNullOrEmpty(dynamic.CustomId)) //only check if something has changed on a dynamic
                {
                    if (dynamic.UseCustomId)
                    {
                        dynamic.iId = dynamic.GetInstanceID(); //this will often mark the scene dirty without any apparent or meaningful changes
                        CheckCustomId(ref dynamic.CustomId);
                        //TODO cache while scene active, but don't bother marking scene dirty if only iId is dirty
                    }
                }
            }


#if UNITY_5_6_OR_NEWER
            //video
            //var flipVideo = serializedObject.FindProperty("FlipVideo");
            //var externalVideoSource = serializedObject.FindProperty("ExternalVideoSource");
            var videoPlayer = serializedObject.FindProperty("VideoPlayer");
#endif

            //display script on component
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.PropertyField(script, true, new GUILayoutOption[0]);
            EditorGUI.EndDisabledGroup();

            GUILayout.BeginHorizontal();

            UnityEditor.EditorGUILayout.PropertyField(useCustomMesh);

            bool anycustomnames = false;
            foreach (var t in targets)
            {
                var dyn = t as DynamicObject;
                if (dyn.UseCustomMesh)
                {
                    anycustomnames = true;
                    if (string.IsNullOrEmpty(dyn.MeshName))
                    {
                        dyn.MeshName = dyn.gameObject.name.ToLower().Replace(" ", "_");
                        UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                    }
                    if (targets.Length == 1)
                    {
                        dyn.MeshName = UnityEditor.EditorGUILayout.TextField("", dyn.MeshName);
                    }
                }
            }
            if (!anycustomnames)
            {
                UnityEditor.EditorGUILayout.PropertyField(commonMeshName, new GUIContent(""));
            }

            GUILayout.EndHorizontal();

            EditorGUILayout.PropertyField(trackGaze, new GUIContent("Track Gaze on Dynamic Object"));
            DisplayGazeTrackHelpbox(trackGaze.boolValue);

            GUILayout.Space(10);

            foldout = EditorGUILayout.Foldout(foldout, "Advanced");
            if (foldout)
            {
                if (useCustomMesh.boolValue)
                {
                    //Mesh
                    GUILayout.Label("Mesh", EditorStyles.boldLabel);


                    //EditorGUI.BeginDisabledGroup(useCustomMesh.boolValue);
                    //UnityEditor.EditorGUILayout.PropertyField(commonMeshName);
                    //EditorGUI.EndDisabledGroup();

                    if (string.IsNullOrEmpty(meshname.stringValue))
                    {
                        //meshname.stringValue = serializedObject.targetObject.name.ToLower().Replace(" ", "_");
                        UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                    }

                    GUILayout.BeginHorizontal();
                    //UnityEditor.EditorGUILayout.PropertyField(useCustomMesh);

                    //EditorGUI.BeginDisabledGroup(!useCustomMesh.boolValue);
                    UnityEditor.EditorGUILayout.PropertyField(meshname, new GUIContent(""));
                    if (GUILayout.Button("Export", "ButtonLeft", GUILayout.MaxWidth(100)))
                    {
                        CognitiveVR_SceneExportWindow.ExportSelectedObjectsPrefab();
                        foreach (var t in serializedObject.targetObjects)
                        {
                            var dyn = t as DynamicObject;
                            if (!dyn.UseCustomId)
                            {
                                EditorCore.SaveDynamicThumbnailAutomatic(dyn.gameObject);
                            }
                        }
                    }

                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Upload", "ButtonRight", GUILayout.MaxWidth(100)))
                    {
                        CognitiveVR_SceneExportWindow.UploadSelectedDynamicObjects(true);
                    }
                    EditorGUI.EndDisabledGroup();

                    GUILayout.EndHorizontal();

                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Thumbnail from SceneView", GUILayout.MaxWidth(180)))
                    {
                        foreach (var v in serializedObject.targetObjects)
                        {
                            EditorCore.SaveDynamicThumbnailSceneView((v as DynamicObject).gameObject);
                        }
                    }
                    EditorGUI.EndDisabledGroup();
                    GUILayout.EndHorizontal();
                }

                //Setup
                GUILayout.Label("Setup", EditorStyles.boldLabel);

                UnityEditor.EditorGUILayout.PropertyField(snapshotOnEnable, new GUIContent("Snapshot On Enable", "Save the transform when this object is first enabled"));

                //EditorGUI.BeginDisabledGroup(!snapshotOnEnable.boolValue);
                UnityEditor.EditorGUILayout.PropertyField(updateTicksOnEnable, new GUIContent("Update Ticks on Enable", "Begin coroutine that saves the transform of this object when it moves"));
                //EditorGUI.EndDisabledGroup();

                EditorGUILayout.PropertyField(trackGaze, new GUIContent("Track Gaze on Dynamic Object"));

                DisplayGazeTrackHelpbox(trackGaze.boolValue);

                EditorGUILayout.PropertyField(requiresManualEnable, new GUIContent("Requires Manual Enable", "If true, ManualEnable must be called before OnEnable will function. Used to set initial variables on an object"));


                //Object ID
                GUILayout.Label("Ids", EditorStyles.boldLabel);

                GUILayout.BeginHorizontal();

                EditorGUILayout.PropertyField(useCustomID, new GUIContent("Use Custom Id", "This is used to identify specific objects to aggregate the position across multiple play sessions"));

                EditorGUI.BeginDisabledGroup(!useCustomID.boolValue);
                EditorGUILayout.PropertyField(customId, new GUIContent(""));
                EditorGUI.EndDisabledGroup();

                GUILayout.EndHorizontal();

                EditorGUILayout.PropertyField(groupName, new GUIContent("Group Name", "This is used to identify types of objects and combine aggregated data"));

                EditorGUILayout.PropertyField(releaseOnDisable, new GUIContent("Release Id OnDisable", "Allow other objects to use this Id when this object is no longer active"));
                EditorGUILayout.PropertyField(releaseOnDestroy, new GUIContent("Release Id OnDestroy", "Allow other objects to use this Id when this object is no longer active"));

                //Snapshot Threshold
                GUILayout.Label("Snapshot Threshold", EditorStyles.boldLabel);


                EditorGUILayout.PropertyField(syncWithPlayerUpdate, new GUIContent("Sync with Player Update", "This is the Snapshot interval in the Tracker Options Window"));

                EditorGUI.BeginDisabledGroup(syncWithPlayerUpdate.boolValue);
                if (syncWithPlayerUpdate.boolValue)
                {
                    EditorGUILayout.FloatField(new GUIContent("Update Rate", "Synced with Player Update.\nThe interval between checking for modified position and rotation"), EditorCore.GetPreferences().SnapshotInterval);
                }
                else
                {
                    EditorGUILayout.PropertyField(updateRate, new GUIContent("Update Rate", "The interval between checking for modified position and rotation"));
                }
                updateRate.floatValue = Mathf.Max(0.1f, updateRate.floatValue);
                EditorGUI.EndDisabledGroup();


                EditorGUILayout.PropertyField(positionThreshold, new GUIContent("Position Threshold", "Meters the object must move to write a new snapshot. Checked each 'Tick'"));
                positionThreshold.floatValue = Mathf.Max(0, positionThreshold.floatValue);

                EditorGUILayout.PropertyField(rotationThreshold, new GUIContent("Rotation Threshold", "Degrees the object must rotate to write a new snapshot. Checked each 'Tick'"));
                rotationThreshold.floatValue = Mathf.Max(0, rotationThreshold.floatValue);

#if !UNITY_5_6_OR_NEWER
                GUILayout.Label("Video Settings", EditorStyles.boldLabel);
                EditorGUILayout.HelpBox("Video Player requires Unity 5.6 or newer!", MessageType.Warning);
#else
                GUILayout.Label("Video Settings", EditorStyles.boldLabel);

                EditorGUILayout.PropertyField(videoPlayer, new GUIContent("Video Player"));

                //EditorGUILayout.PropertyField(externalVideoSource, new GUIContent("External Video Source", "The URL source of the video"));
                //EditorGUILayout.PropertyField(flipVideo, new GUIContent("Flip Video Horizontally"));
#endif
            } //advanced foldout


            if (GUI.changed)
            {
                foreach (var t in targets)
                {
                    var dyn = t as DynamicObject;
                    if (dyn.UseCustomMesh)
                    {
                        dyn.MeshName = dyn.MeshName.Replace(" ", "_");
                    }
                }

                //remove spaces from meshname
                //meshname.stringValue = meshname.stringValue.Replace(" ", "_");

                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.Update();
        }
        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            var script            = serializedObject.FindProperty("m_Script");
            var updateRate        = serializedObject.FindProperty("UpdateRate");
            var positionThreshold = serializedObject.FindProperty("PositionThreshold");
            var rotationThreshold = serializedObject.FindProperty("RotationThreshold");
            var scaleThreshold    = serializedObject.FindProperty("ScaleThreshold");
            var useCustomId       = serializedObject.FindProperty("UseCustomId");
            var customId          = serializedObject.FindProperty("CustomId");
            var commonMeshName    = serializedObject.FindProperty("CommonMesh");
            var meshname          = serializedObject.FindProperty("MeshName");
            var useCustomMesh     = serializedObject.FindProperty("UseCustomMesh");
            var isController      = serializedObject.FindProperty("IsController");
            var syncWithGaze      = serializedObject.FindProperty("SyncWithPlayerGazeTick");
            var idPool            = serializedObject.FindProperty("IdPool");

            foreach (var t in serializedObject.targetObjects) //makes sure a custom id is valid
            {
                var dynamic = t as DynamicObject;
                if (dynamic.editorInstanceId != dynamic.GetInstanceID() || string.IsNullOrEmpty(dynamic.CustomId)) //only check if something has changed on a dynamic, or if the id is empty
                {
                    if (dynamic.UseCustomId && idType == 0)
                    {
                        if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(dynamic.gameObject)))//scene asset
                        {
                            dynamic.editorInstanceId = dynamic.GetInstanceID();
                            CheckCustomId();
                        }
                        else //project asset
                        {
                            dynamic.editorInstanceId = dynamic.GetInstanceID();
                            if (string.IsNullOrEmpty(dynamic.CustomId))
                            {
                                string s = System.Guid.NewGuid().ToString();
                                dynamic.CustomId = "editor_" + s;
                            }
                        }
                    }
                }
            }

            //display script on component
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.PropertyField(script, true, new GUILayoutOption[0]);
            EditorGUI.EndDisabledGroup();

            //use custom mesh and mesh text field
            GUILayout.BeginHorizontal();
            UnityEditor.EditorGUILayout.PropertyField(useCustomMesh);
            bool anycustomnames = false;

            foreach (var t in targets)
            {
                var dyn = t as DynamicObject;
                if (dyn.UseCustomMesh)
                {
                    anycustomnames = true;
                    if (string.IsNullOrEmpty(dyn.MeshName))
                    {
                        dyn.MeshName = dyn.gameObject.name.ToLower().Replace(" ", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_").Replace("?", "_").Replace("*", "_").Replace("\"", "_").Replace("/", "_").Replace("\\", "_").Replace(":", "_");
                        if (!Application.isPlaying)
                        {
                            UnityEditor.EditorUtility.SetDirty(dyn);
                            UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                        }
                    }
                    if (targets.Length == 1)
                    {
                        dyn.MeshName = UnityEditor.EditorGUILayout.TextField("", dyn.MeshName);
                    }
                }
            }
            if (!anycustomnames)
            {
                UnityEditor.EditorGUILayout.PropertyField(commonMeshName, new GUIContent(""));
            }
            GUILayout.EndHorizontal();

            if (idType == -1)
            {
                var dyn = target as DynamicObject;
                if (dyn.UseCustomId)
                {
                    idType = 0;
                }
                else if (dyn.IdPool != null)
                {
                    idType = 2;
                }
                else
                {
                    idType = 1;
                }
            }
            GUILayout.BeginHorizontal();
            idType = EditorGUILayout.Popup(new GUIContent("Id Source"), idType, idTypeNames);

            if (idType == 0) //custom id
            {
                EditorGUILayout.PropertyField(customId, new GUIContent(""));
                useCustomId.boolValue       = true;
                idPool.objectReferenceValue = null;
            }
            else if (idType == 1) //generate id
            {
                EditorGUI.BeginDisabledGroup(true);
                EditorGUILayout.LabelField(new GUIContent("Id will be generated at runtime", "This object will not be included in aggregation metrics on the dashboard"));
                EditorGUI.EndDisabledGroup();
                customId.stringValue        = string.Empty;
                useCustomId.boolValue       = false;
                idPool.objectReferenceValue = null;
            }
            else if (idType == 2) //id pool
            {
                EditorGUILayout.ObjectField(idPool, new GUIContent("", "Provides a consistent list of Ids to be used at runtime. Allows aggregated data from objects spawned at runtime"));
                customId.stringValue  = string.Empty;
                useCustomId.boolValue = false;
            }
            GUILayout.EndHorizontal();

            if (idType == 2) //id pool
            {
                var dyn = target as DynamicObject;
                if (dyn.IdPool == null)
                {
                    if (GUILayout.Button("New Dynamic Object Id Pool"))
                    {
                        var pool = ScriptableObject.CreateInstance <DynamicObjectIdPool>();
                        //write some values
                        pool.Ids = new string[1] {
                            System.Guid.NewGuid().ToString()
                        };
                        pool.MeshName   = dyn.MeshName;
                        pool.PrefabName = dyn.gameObject.name;
                        //save to root assets folder
                        AssetDatabase.CreateAsset(pool, "Assets/" + pool.MeshName + " Id Pool.asset");
                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                        //get reference to file
                        idPool.objectReferenceValue = pool;
                    }
                }
            }

            foldout = EditorGUILayout.Foldout(foldout, "Advanced");
            if (foldout)
            {
                if (useCustomMesh.boolValue)
                {
                    //Mesh
                    GUILayout.Label("Export and Upload", EditorStyles.boldLabel);
                    GUILayout.BeginHorizontal();
                    if (GUILayout.Button("Export Mesh", "ButtonLeft", GUILayout.Height(30)))
                    {
                        ExportUtility.ExportAllSelectedDynamicObjects();
                    }

                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Thumbnail from\nSceneView", "ButtonMid", GUILayout.Height(30)))
                    {
                        foreach (var v in serializedObject.targetObjects)
                        {
                            EditorCore.SaveDynamicThumbnailSceneView((v as DynamicObject).gameObject);
                        }
                    }
                    EditorGUI.EndDisabledGroup();

                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Upload Mesh", "ButtonRight", GUILayout.Height(30)))
                    {
                        ExportUtility.UploadSelectedDynamicObjectMeshes(true);
                    }
                    EditorGUI.EndDisabledGroup();

                    //texture export settings
                    GUILayout.EndHorizontal();
                    GUIContent[] textureQualityNames = new GUIContent[] { new GUIContent("Full"), new GUIContent("Half"), new GUIContent("Quarter") /*, new GUIContent("Eighth"), new GUIContent("Sixteenth"), new GUIContent("Thirty Second"), new GUIContent("Sixty Fourth") */ };
                    int[]        textureQualities    = new int[] { 1, 2, 4 /*, 8, 16, 32, 64*/ };
                    CognitiveVR_Preferences.Instance.TextureResize = EditorGUILayout.IntPopup(new GUIContent("Texture Export Quality", "Reduce textures when uploading to scene explorer"), CognitiveVR_Preferences.Instance.TextureResize, textureQualityNames, textureQualities);
                    GUILayout.Space(5);

                    //ID upload
                    var dyn = target as DynamicObject;
                    if (dyn.UseCustomId)
                    {
                        EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                        if (GUILayout.Button("Upload Custom ID for aggregation"))
                        {
                            Debug.Log("upload custom id to scene");
                            //ExportUtility.UploadSelectedDynamicObjectMeshes(true);
                            EditorCore.RefreshSceneVersion(delegate()
                            {
                                ManageDynamicObjects.AggregationManifest manifest = new ManageDynamicObjects.AggregationManifest();
                                manifest.objects.Add(new ManageDynamicObjects.AggregationManifest.AggregationManifestEntry(dyn.gameObject.name, dyn.MeshName, dyn.CustomId));
                                ManageDynamicObjects.UploadManifest(manifest, null);
                            });
                        }
                        EditorGUI.EndDisabledGroup();
                    }
                    else if (dyn.IdPool != null)
                    {
                        EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                        if (GUILayout.Button("Upload ID Pool for aggregation"))
                        {
                            Debug.Log("upload id pool to scene");
                            //ExportUtility.UploadSelectedDynamicObjectMeshes(true);
                            EditorCore.RefreshSceneVersion(delegate()
                            {
                                ManageDynamicObjects.AggregationManifest manifest = new ManageDynamicObjects.AggregationManifest();
                                for (int i = 0; i < dyn.IdPool.Ids.Length; i++)
                                {
                                    manifest.objects.Add(new ManageDynamicObjects.AggregationManifest.AggregationManifestEntry(dyn.gameObject.name, dyn.MeshName, dyn.IdPool.Ids[i]));
                                }
                                ManageDynamicObjects.UploadManifest(manifest, null);
                            });
                        }
                        EditorGUI.EndDisabledGroup();
                    }
                }

                //Snapshot Threshold

                GUILayout.Label("Data Snapshot", EditorStyles.boldLabel);

                //controller stuff
                GUILayout.BeginHorizontal();

                UnityEditor.EditorGUILayout.PropertyField(isController, new GUIContent("Is Controller", "If true, this will record user's inputs and display the inputs in a popup on SceneExplorer"));

                if (targets.Length == 1)
                {
                    var dyn = targets[0] as DynamicObject;

                    if (dyn.IsController)
                    {
                        dyn.ControllerType = (DynamicObject.ControllerDisplayType)EditorGUILayout.EnumPopup(dyn.ControllerType);
                    }

                    if (dyn.IsController)
                    {
                        EditorGUILayout.LabelField("Is Right", GUILayout.Width(60));
                        dyn.IsRight = EditorGUILayout.Toggle(dyn.IsRight, GUILayout.Width(20));
                    }
                }

                GUILayout.EndHorizontal();

                EditorGUILayout.PropertyField(syncWithGaze, new GUIContent("Sync with Gaze", "Records the transform of the dynamic object on the same frame as gaze. This may smooth movement of this object in SceneExplorer relative to the player's position"));
                EditorGUI.BeginDisabledGroup(syncWithGaze.boolValue);
                EditorGUILayout.PropertyField(updateRate, new GUIContent("Update Rate", "This is the Snapshot interval in the Tracker Options Window"), GUILayout.MinWidth(50));
                updateRate.floatValue = Mathf.Max(0.1f, updateRate.floatValue);
                EditorGUI.EndDisabledGroup();

                EditorGUILayout.PropertyField(positionThreshold, new GUIContent("Position Threshold", "Meters the object must move to write a new snapshot. Checked each 'Tick'"));
                positionThreshold.floatValue = Mathf.Max(0, positionThreshold.floatValue);

                EditorGUILayout.PropertyField(rotationThreshold, new GUIContent("Rotation Threshold", "Degrees the object must rotate to write a new snapshot. Checked each 'Tick'"));
                rotationThreshold.floatValue = Mathf.Max(0, rotationThreshold.floatValue);

                EditorGUILayout.PropertyField(scaleThreshold, new GUIContent("Scale Threshold", "Scale multiplier that must be exceeded to write a new snapshot. Checked each 'Tick'"));
                scaleThreshold.floatValue = Mathf.Max(0, scaleThreshold.floatValue);

                EditorGUI.EndDisabledGroup();
            } //advanced foldout


            if (GUI.changed)
            {
                foreach (var t in targets)
                {
                    var dyn = t as DynamicObject;
                    if (dyn.UseCustomMesh)
                    {
                        //replace all invalid characters <>|?*"/\: with _
                        dyn.MeshName = dyn.MeshName.Replace(" ", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_").Replace("?", "_").Replace("*", "_").Replace("\"", "_").Replace("/", "_").Replace("\\", "_").Replace(":", "_");
                    }
                }

                //IMPROVEMENT should check that some meaningful property changed, not just foldout
                if (!Application.isPlaying)
                {
                    foreach (var t in targets)
                    {
                        EditorUtility.SetDirty(t);
                    }
                    UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                }
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.Update();
        }
Esempio n. 3
0
        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            var script            = serializedObject.FindProperty("m_Script");
            var updateRate        = serializedObject.FindProperty("UpdateRate");
            var positionThreshold = serializedObject.FindProperty("PositionThreshold");
            var rotationThreshold = serializedObject.FindProperty("RotationThreshold");
            var scaleThreshold    = serializedObject.FindProperty("ScaleThreshold");
            var useCustomId       = serializedObject.FindProperty("UseCustomId");
            var customId          = serializedObject.FindProperty("CustomId");
            var commonMeshName    = serializedObject.FindProperty("CommonMesh");
            var meshname          = serializedObject.FindProperty("MeshName");
            var useCustomMesh     = serializedObject.FindProperty("UseCustomMesh");
            var isController      = serializedObject.FindProperty("IsController");
            var syncWithGaze      = serializedObject.FindProperty("SyncWithPlayerGazeTick");

            foreach (var t in serializedObject.targetObjects) //makes sure a custom id is valid
            {
                var dynamic = t as DynamicObject;
                if (dynamic.editorInstanceId != dynamic.GetInstanceID() || string.IsNullOrEmpty(dynamic.CustomId)) //only check if something has changed on a dynamic, or if the id is empty
                {
                    if (dynamic.UseCustomId)
                    {
                        if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(dynamic.gameObject)))//scene asset
                        {
                            dynamic.editorInstanceId = dynamic.GetInstanceID();
                            CheckCustomId(ref dynamic.CustomId);
                        }
                        else //project asset
                        {
                            dynamic.editorInstanceId = dynamic.GetInstanceID();
                            if (string.IsNullOrEmpty(dynamic.CustomId))
                            {
                                string s = System.Guid.NewGuid().ToString();
                                dynamic.CustomId = "editor_" + s;
                            }
                        }
                    }
                }
            }

            //video
            //var flipVideo = serializedObject.FindProperty("FlipVideo");
            //var externalVideoSource = serializedObject.FindProperty("ExternalVideoSource");
            //var videoPlayer = serializedObject.FindProperty("VideoPlayer");

            //display script on component
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.PropertyField(script, true, new GUILayoutOption[0]);
            EditorGUI.EndDisabledGroup();

            //use custom mesh and mesh text field
            GUILayout.BeginHorizontal();
            UnityEditor.EditorGUILayout.PropertyField(useCustomMesh);
            bool anycustomnames = false;

            foreach (var t in targets)
            {
                var dyn = t as DynamicObject;
                if (dyn.UseCustomMesh)
                {
                    anycustomnames = true;
                    if (string.IsNullOrEmpty(dyn.MeshName))
                    {
                        dyn.MeshName = dyn.gameObject.name.ToLower().Replace(" ", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_").Replace("?", "_").Replace("*", "_").Replace("\"", "_").Replace("/", "_").Replace("\\", "_").Replace(":", "_");
                        if (!Application.isPlaying)
                        {
                            UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                        }
                    }
                    if (targets.Length == 1)
                    {
                        dyn.MeshName = UnityEditor.EditorGUILayout.TextField("", dyn.MeshName);
                    }
                }
            }
            if (!anycustomnames)
            {
                UnityEditor.EditorGUILayout.PropertyField(commonMeshName, new GUIContent(""));
            }
            GUILayout.EndHorizontal();


            //use custom id and custom id text field
            GUILayout.BeginHorizontal();
            bool previousUseCustomId = useCustomId.boolValue;

            EditorGUILayout.PropertyField(useCustomId, new GUIContent("Custom Id"));

            if (Selection.activeGameObject)
            {
                string path = AssetDatabase.GetAssetPath(Selection.activeObject);
                if (!string.IsNullOrEmpty(path) && useCustomId.boolValue)
                {
                    //display small warning icon when prefab has custom id set
                    EditorGUILayout.LabelField(new GUIContent(EditorCore.Alert, "Project assets should not have CustomId set, unless there will be only 1 instance spawned during runtime"), GUILayout.Width(20));
                }
            }

            if (previousUseCustomId != useCustomId.boolValue)
            {
                if (previousUseCustomId != useCustomId.boolValue) //use custom id changed
                {
                    if (useCustomId.boolValue == false)           //changed to false
                    {
                        customId.stringValue = string.Empty;
                    }
                    else
                    {
                        foreach (var t in targets)
                        {
                            var dyn = t as DynamicObject;
                            CheckCustomId(ref dyn.CustomId);
                        }
                    }
                }
            }

            if (!useCustomId.boolValue) //display custom id field
            {
                EditorGUI.BeginDisabledGroup(true);
                EditorGUILayout.LabelField(new GUIContent("Id will be generated at runtime", "This object will not be included in aggregation metrics on the dashboard"));
                EditorGUI.EndDisabledGroup();
            }
            else
            {
                if (targets.Length > 1)
                {
                    EditorGUI.BeginDisabledGroup(true);
                    EditorGUILayout.LabelField("multiple values");
                    EditorGUI.EndDisabledGroup();
                }
                else
                {
                    EditorGUILayout.PropertyField(customId, new GUIContent(""));
                }
            }
            GUILayout.EndHorizontal();



            foldout = EditorGUILayout.Foldout(foldout, "Advanced");
            if (foldout)
            {
                if (useCustomMesh.boolValue)
                {
                    //Mesh
                    GUILayout.Label("Mesh", EditorStyles.boldLabel);


                    //EditorGUI.BeginDisabledGroup(useCustomMesh.boolValue);
                    //UnityEditor.EditorGUILayout.PropertyField(commonMeshName);
                    //EditorGUI.EndDisabledGroup();

                    if (string.IsNullOrEmpty(meshname.stringValue))
                    {
                        //meshname.stringValue = serializedObject.targetObject.name.ToLower().Replace(" ", "_");
                        if (!Application.isPlaying)
                        {
                            UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                        }
                    }

                    GUILayout.BeginHorizontal();
                    //UnityEditor.EditorGUILayout.PropertyField(useCustomMesh);

                    //EditorGUI.BeginDisabledGroup(!useCustomMesh.boolValue);
                    //UnityEditor.EditorGUILayout.PropertyField(meshname, new GUIContent(""));
                    if (GUILayout.Button("Export Mesh", "ButtonLeft", GUILayout.Height(30)))
                    {
                        CognitiveVR_SceneExportWindow.ExportSelectedObjectsPrefab();
                    }

                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Thumbnail from\nSceneView", "ButtonMid", GUILayout.Height(30)))
                    {
                        foreach (var v in serializedObject.targetObjects)
                        {
                            EditorCore.SaveDynamicThumbnailSceneView((v as DynamicObject).gameObject);
                        }
                    }
                    EditorGUI.EndDisabledGroup();

                    EditorGUI.BeginDisabledGroup(!EditorCore.HasDynamicExportFiles(meshname.stringValue));
                    if (GUILayout.Button("Upload Mesh", "ButtonRight", GUILayout.Height(30)))
                    {
                        CognitiveVR_SceneExportWindow.UploadSelectedDynamicObjects(true);
                    }
                    EditorGUI.EndDisabledGroup();

                    GUILayout.EndHorizontal();

                    //GUILayout.BeginHorizontal();


                    GUIContent[] textureQualityNames = new GUIContent[] { new GUIContent("Full"), new GUIContent("Half"), new GUIContent("Quarter"), new GUIContent("Eighth"), new GUIContent("Sixteenth"), new GUIContent("Thirty Second"), new GUIContent("Sixty Fourth") };
                    int[]        textureQualities    = new int[] { 1, 2, 4, 8, 16, 32, 64 };
                    CognitiveVR_Preferences.Instance.TextureResize = EditorGUILayout.IntPopup(new GUIContent("Texture Export Quality", "Reduce textures when uploading to scene explorer"), CognitiveVR_Preferences.Instance.TextureResize, textureQualityNames, textureQualities);


                    //GUILayout.EndHorizontal();
                    GUILayout.Space(5);
                }

                //Snapshot Threshold

                GUILayout.Label("Snapshot", EditorStyles.boldLabel);

                //controller stuff
                GUILayout.BeginHorizontal();

                UnityEditor.EditorGUILayout.PropertyField(isController, new GUIContent("Is Controller", "If true, this will record user's inputs and display the inputs in a popup on SceneExplorer"));

                if (targets.Length == 1)
                {
                    var dyn = targets[0] as DynamicObject;

                    if (dyn.IsController)
                    {
                        string[] controllernames = new string[3] {
                            "vivecontroller", "oculustouchleft", "oculustouchright"
                        };
                        int selected = 0;
                        if (dyn.ControllerType == "vivecontroller")
                        {
                            selected = 0;
                        }
                        if (dyn.ControllerType == "oculustouchleft")
                        {
                            selected = 1;
                        }
                        if (dyn.ControllerType == "oculustouchright")
                        {
                            selected = 2;
                        }

                        selected           = EditorGUILayout.Popup(selected, controllernames);
                        dyn.ControllerType = controllernames[selected];
                    }

                    if (dyn.IsController)
                    {
                        EditorGUILayout.LabelField("Is Right", GUILayout.Width(60));
                        dyn.IsRight = EditorGUILayout.Toggle(dyn.IsRight, GUILayout.Width(20));
                    }
                }

                GUILayout.EndHorizontal();


                EditorGUILayout.PropertyField(syncWithGaze, new GUIContent("Sync with Gaze", "Records the transform of the dynamic object on the same frame as gaze. This may smooth movement of this object in SceneExplorer relative to the player's position"));
                EditorGUI.BeginDisabledGroup(syncWithGaze.boolValue);
                EditorGUILayout.PropertyField(updateRate, new GUIContent("Update Rate", "This is the Snapshot interval in the Tracker Options Window"), GUILayout.MinWidth(50));
                updateRate.floatValue = Mathf.Max(0.1f, updateRate.floatValue);
                EditorGUI.EndDisabledGroup();

                EditorGUILayout.PropertyField(positionThreshold, new GUIContent("Position Threshold", "Meters the object must move to write a new snapshot. Checked each 'Tick'"));
                positionThreshold.floatValue = Mathf.Max(0, positionThreshold.floatValue);

                EditorGUILayout.PropertyField(rotationThreshold, new GUIContent("Rotation Threshold", "Degrees the object must rotate to write a new snapshot. Checked each 'Tick'"));
                rotationThreshold.floatValue = Mathf.Max(0, rotationThreshold.floatValue);

                EditorGUILayout.PropertyField(scaleThreshold, new GUIContent("Scale Threshold", "Scale multiplier that must be exceeded to write a new snapshot. Checked each 'Tick'"));
                scaleThreshold.floatValue = Mathf.Max(0, scaleThreshold.floatValue);

                EditorGUI.EndDisabledGroup();
            } //advanced foldout


            if (GUI.changed)
            {
                foreach (var t in targets)
                {
                    var dyn = t as DynamicObject;
                    if (dyn.UseCustomMesh)
                    {
                        //TODO replace all invalid characters <>|?*"/\: with _
                        dyn.MeshName = dyn.MeshName.Replace(" ", "_").Replace("<", "_").Replace(">", "_").Replace("|", "_").Replace("?", "_").Replace("*", "_").Replace("\"", "_").Replace("/", "_").Replace("\\", "_").Replace(":", "_");
                    }
                }

                //remove spaces from meshname
                //meshname.stringValue = meshname.stringValue.Replace(" ", "_");

                if (!Application.isPlaying)
                {
                    UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
                }
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.Update();
        }