예제 #1
0
        void OnEnable()
        {
            MeshTreeBase meshTree = target as MeshTreeBase;

            m_isBuilding = meshTree.IsBuilding();
            if (!m_isBuilding)
            {
                m_memoryUsage = meshTree.GetMemoryUsage();
            }
            m_showBuiltData = true;
        }
예제 #2
0
        public static void BuildMeshTree(MeshTreeBase meshTreeBase)
        {
            MeshTree   meshTree = meshTreeBase as MeshTree;
            GameObject prefab   = null;

            if (meshTree != null && meshTree.srcMesh is GameObject)
            {
                PrefabType prefabType = PrefabUtility.GetPrefabType(meshTree.srcMesh);
                if (prefabType == PrefabType.Prefab || prefabType == PrefabType.ModelPrefab)
                {
                    prefab           = (GameObject)meshTree.srcMesh;
                    meshTree.srcMesh = PrefabUtility.InstantiatePrefab(prefab);
                }
            }
            meshTreeBase.AsyncBuild();
            if (prefab != null)
            {
                DestroyImmediate(meshTree.srcMesh);
                meshTree.SetSrcMeshWithoutClear(prefab);
            }
            EditorUtility.SetDirty(meshTreeBase);
        }
예제 #3
0
        bool OnGUISetEnvironment()
        {
            MeshShadowReceiver receiver = null;

            if (0 < m_projectorManager.receivers.Count)
            {
                receiver = m_projectorManager.receivers[0] as MeshShadowReceiver;
            }

            GameObject   rootObject = null;
            MeshTreeBase meshTree   = null;

            if (receiver != null)
            {
                if (receiver.meshTransform != null)
                {
                    rootObject = receiver.meshTransform.gameObject;
                }
                meshTree = receiver.meshTree;
                GameObject newRootObject = EditorGUILayout.ObjectField("Root Object", rootObject, typeof(GameObject), true) as GameObject;
                if (newRootObject != rootObject)
                {
                    Undo.RecordObject(receiver, "Easy Setup: Set Root Object");
                    receiver.meshTransform = (newRootObject == null) ? null : newRootObject.transform;
                    EditorUtility.SetDirty(receiver);
                    rootObject          = newRootObject;
                    m_environmentMeshes = null;
                }
            }
            EditorGUILayout.PropertyField(m_serializedObject.FindProperty("m_environmentLayers"));
            m_serializedObject.ApplyModifiedProperties();
            bool findObject = false;

            if (receiver != null && receiver.meshTransform != null)
            {
                // check Environment Layers
                for (int i = 0; i < environmentMeshes.Length; ++i)
                {
                    if ((m_projectorManager.environmentLayers & (1 << environmentMeshes[i].gameObject.layer)) != 0)
                    {
                        findObject = true;
                        break;
                    }
                }
                if (!findObject)
                {
                    GUILayout.TextArea("<color=red>No child objects of Root Object found in Environment Layers.</color>", m_errorStyle);
                }
            }
            GUILayout.TextArea("The objects in the Environment Layers will be ignored by Projectors. They will not receive the shadows casted by the Projectors. Instead, shadow receiver will receive the shadows on behalf of them.");
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Edit Layers"))
            {
                Selection.activeObject = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0];
            }
            if (receiver != null)
            {
                if (rootObject == null)
                {
                    GUI.enabled = false;
                }
                if (GUILayout.Button("Collect Layers from Root Object"))
                {
                    LayerMask    layers = 0;
                    MeshFilter[] meshes = rootObject.GetComponentsInChildren <MeshFilter>();
                    {
                        var __array1       = meshes;
                        var __arrayLength1 = __array1.Length;
                        for (int __i1 = 0; __i1 < __arrayLength1; ++__i1)
                        {
                            var mesh = (MeshFilter)__array1[__i1];
                            {
                                layers |= (1 << mesh.gameObject.layer);
                            }
                        }
                    }
                    if (layers != m_projectorManager.environmentLayers)
                    {
                        Undo.RecordObject(m_projectorManager, "Easy Setup: Collect Layers from Root Object");
                        m_projectorManager.environmentLayers = layers;
                        EditorUtility.SetDirty(m_projectorManager);
                        m_serializedObject.Update();
                    }
                }
                GUI.enabled = true;
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Separator();

            if (receiver == null)
            {
                m_isAbleToGoNext = m_projectorManager.environmentLayers != 0;
                return(true);
            }
            MeshTreeBase newMeshTree = EditorGUILayout.ObjectField("Mesh Tree", meshTree, typeof(MeshTreeBase), false) as MeshTreeBase;

            if (newMeshTree != meshTree)
            {
                Undo.RecordObject(receiver, "Easy Setup: Set Mesh Tree");
                receiver.meshTree = newMeshTree;
                meshTree          = newMeshTree;
                EditorUtility.SetDirty(receiver);
            }
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (rootObject == null || !findObject)
            {
                GUI.enabled = false;
            }
            if (GUILayout.Button("Search Assets folder"))
            {
                newMeshTree = MeshShadowReceiverEditor.FindMeshTreeFromMeshTransform(rootObject.transform);
                if (newMeshTree == null)
                {
                    EditorUtility.DisplayDialog("No mesh tree found for the Root Object.", "", "OK");
                }
                else
                {
                    Undo.RecordObject(receiver, "Easy Setup: Set Mesh Tree");
                    receiver.meshTree = newMeshTree;
                    meshTree          = newMeshTree;
                    EditorUtility.SetDirty(receiver);
                }
            }
            if (GUILayout.Button("Create a New Mesh Tree"))
            {
                CreateMeshTreeWizard.CreateWizard(rootObject.transform, m_projectorManager.environmentLayers, receiver);
            }
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();
            m_errorString = null;
            LayerMask missingLayers       = 0;
            LayerMask existingLayers      = 0;
            bool      multiObjectMeshTree = false;

            if (meshTree != null && rootObject != null)
            {
                if (meshTree is TerrainMeshTree)
                {
                    Terrain terrain = rootObject.GetComponent <Terrain>();
                    if (terrain == null)
                    {
                        m_errorString = "<color=red>The mesh tree is a TerrainMeshTree but Root Object is not a terrain object.</color>";
                    }
                    else if (terrain.terrainData != ((TerrainMeshTree)meshTree).terrainData)
                    {
                        m_errorString = "<color=red>The mesh tree was not the one which was created from the Root Object</color>";
                    }
                    else if ((m_projectorManager.environmentLayers & (1 << rootObject.layer)) == 0)
                    {
                        m_errorString  = "<color=red>Environment Layers doesn't contain the layer of the Root Object.</color>";
                        missingLayers |= (1 << rootObject.layer);
                    }
                }
                else
                {
                    Object srcMesh = ((MeshTree)meshTree).srcMesh;
                    if (srcMesh is Mesh)
                    {
                        MeshFilter mesh = rootObject.GetComponent <MeshFilter>();
                        if (mesh == null || mesh.sharedMesh != srcMesh)
                        {
                            m_errorString = "<color=red>The mesh tree was created from a single mesh but the Root Object doesn't have the mesh.</color>";
                        }
                        else if ((m_projectorManager.environmentLayers & (1 << rootObject.layer)) == 0)
                        {
                            m_errorString  = "<color=red>Environment Layers doesn't contain the layer of the Root Object.</color>";
                            missingLayers |= (1 << rootObject.layer);
                        }
                    }
                    else
                    {
                        multiObjectMeshTree = true;
                        if (srcMesh != null)
                        {
                            if (PrefabUtility.GetPrefabParent(rootObject) != srcMesh && rootObject != srcMesh)
                            {
                                PrefabType type = PrefabUtility.GetPrefabType(srcMesh);
                                if (type == PrefabType.Prefab || type == PrefabType.ModelPrefab)
                                {
                                    m_errorString = "<color=red>The mesh tree was created from a prefab object but the Root Object was not instantiated from the prefab.</color>";
                                }
                                else
                                {
                                    m_errorString = "<color=red>The mesh tree was not created from the Root Object.</color>";
                                }
                            }
                        }
                        if (m_errorString == null)
                        {
                            MeshFilter[] meshes         = rootObject.GetComponentsInChildren <MeshFilter>();
                            LayerMask    meshTreeLayers = ((MeshTree)meshTree).layerMask;
                            {
                                var __array2       = meshes;
                                var __arrayLength2 = __array2.Length;
                                for (int __i2 = 0; __i2 < __arrayLength2; ++__i2)
                                {
                                    var mesh = (MeshFilter)__array2[__i2];
                                    {
                                        if ((meshTreeLayers & (1 << mesh.gameObject.layer)) != 0)
                                        {
                                            if ((m_projectorManager.environmentLayers & (1 << mesh.gameObject.layer)) == 0)
                                            {
                                                missingLayers |= (1 << mesh.gameObject.layer);
                                            }
                                            else
                                            {
                                                existingLayers |= (1 << mesh.gameObject.layer);
                                            }
                                        }
                                    }
                                }
                            }
                            if (missingLayers != 0)
                            {
                                m_errorString = "<color=red>Some of the mesh tree layers are missing in Environment Layers.</color>";
                            }
                        }
                    }
                }
            }
            if (m_errorString != null)
            {
                GUILayout.TextArea(m_errorString, m_errorStyle);
                if (missingLayers != 0)
                {
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    if (GUILayout.Button("Add mesh tree layers to Environment Layers"))
                    {
                        Undo.RecordObject(m_projectorManager, "Easy Setup: Add mesh tree layers to Environment Layers");
                        m_projectorManager.environmentLayers |= missingLayers;
                        EditorUtility.SetDirty(m_projectorManager);
                        m_serializedObject.Update();
                    }
                    EditorGUILayout.EndHorizontal();
                    if (multiObjectMeshTree && existingLayers != 0)
                    {
                        EditorGUILayout.BeginHorizontal();
                        GUILayout.FlexibleSpace();
                        if (GUILayout.Button("Rebuild the mesh tree with Environment Layers"))
                        {
                            ((MeshTree)meshTree).layerMask &= ~missingLayers;
                            if (((MeshTree)meshTree).srcMesh == null)
                            {
                                ((MeshTree)meshTree).srcMesh = rootObject;
                            }
                            MeshTreeEditor.BuildMeshTree(meshTree);
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                }
            }
            m_isAbleToGoNext = rootObject != null && meshTree != null && m_errorString == null && m_projectorManager.environmentLayers != 0;
            return(true);
        }
예제 #4
0
        public override void OnInspectorGUI()
        {
            MeshTreeBase meshTreeBase = target as MeshTreeBase;

            if (meshTreeBase.IsBuilding())
            {
                GUI.enabled = false;
            }
            DrawDefaultInspector();
            if (meshTreeBase is MeshTree)
            {
                MeshTree meshTree   = meshTreeBase as MeshTree;
                Object   meshObject = EditorGUILayout.ObjectField(meshTree.srcMesh is Mesh ? "Mesh" : "Root Object", meshTree.srcMesh, typeof(Object), true);
                if (meshObject != meshTree.srcMesh && meshObject is GameObject && Event.current.command)
                {
                    MeshFilter meshFilter = ((GameObject)meshObject).GetComponent <MeshFilter>();
                    if (meshFilter != null && meshFilter.sharedMesh != null)
                    {
                        meshObject = meshFilter.sharedMesh;
                    }
                }
                if (meshObject != meshTree.srcMesh)
                {
                    Undo.RegisterCompleteObjectUndo(meshTree, "Inspector");
                    meshTree.srcMesh = meshObject;
                    EditorUtility.SetDirty(meshTree);
                }
                if (meshObject is GameObject)
                {
                    PrefabType prefabType = PrefabUtility.GetPrefabType(meshObject);
                    if (prefabType != PrefabType.Prefab && prefabType != PrefabType.ModelPrefab)
                    {
                        if (m_errorStyle == null)
                        {
                            m_errorStyle          = new GUIStyle();
                            m_errorStyle.richText = true;
                            m_errorStyle.wordWrap = true;
                        }
                        GUILayout.TextArea("<color=red>A reference to a scene object will not be serialized in Asset data. You will need to set the root object again when you rebuild the tree. Use a prefab instead.</color>", m_errorStyle);
                    }
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("m_layerMask"));
                    SerializedProperty excludeRenderTypes = serializedObject.FindProperty("m_excludeRenderTypes");
                    excludeRenderTypes.isExpanded = EditorGUILayout.Foldout(excludeRenderTypes.isExpanded, "Exclude Render Types");
                    if (excludeRenderTypes.isExpanded)
                    {
                        for (int i = 0; i < excludeRenderTypes.arraySize + 1; ++i)
                        {
                            string renderType;
                            if (m_editingRenderType == i)
                            {
                                renderType = m_editingString;
                            }
                            else if (i < excludeRenderTypes.arraySize)
                            {
                                renderType = excludeRenderTypes.GetArrayElementAtIndex(i).stringValue;
                            }
                            else
                            {
                                renderType = "";
                            }
                            string controlName = "RenderType" + i.ToString();
                            GUI.SetNextControlName(controlName);
                            string newRenderType  = EditorGUILayout.TextField(renderType);
                            string focusedControl = GUI.GetNameOfFocusedControl();
                            if (m_editingRenderType == i)
                            {
                                m_editingString = newRenderType;
                                if (focusedControl != controlName || (/*Event.current.isKey &&*/ (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter)))
                                {
                                    ApplyEditingRenderType();
                                }
                                else if (/*Event.current.isKey &&*/ Event.current.keyCode == KeyCode.Escape)
                                {
                                    CancelEditingRenderType();
                                }
                            }
                            else if (renderType != newRenderType)
                            {
                                ApplyEditingRenderType();
                                m_editingRenderType = i;
                                m_editingString     = newRenderType;
                            }
                        }
                    }
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("m_scaledOffset"));
                    EditorGUILayout.PropertyField(serializedObject.FindProperty("m_fixedOffset"));
                    serializedObject.ApplyModifiedProperties();
                }
            }
            if (meshTreeBase is TerrainMeshTree)
            {
                TerrainMeshTree terrainMeshTree = meshTreeBase as TerrainMeshTree;
                Object          terrainObj      = EditorGUILayout.ObjectField("Terrain Data", terrainMeshTree.terrainData, typeof(Object), true);
                if (terrainObj != terrainMeshTree.terrainData)
                {
                    TerrainData terrainData = terrainObj as TerrainData;
                    if (terrainData == null && terrainObj is GameObject)
                    {
                        Terrain terrain = ((GameObject)terrainObj).GetComponent <Terrain>();
                        if (terrain != null)
                        {
                            terrainData = terrain.terrainData;
                        }
                    }
                    if (terrainData != null || terrainObj == null)
                    {
                        terrainMeshTree.terrainData = terrainData;
                    }
                }
            }
            if (meshTreeBase.IsBuilding())
            {
                GUI.enabled  = true;
                m_isBuilding = true;
                EditorGUILayout.LabelField("Building... " + Mathf.FloorToInt(100 * meshTreeBase.GetBuildProgress()).ToString() + "%");
            }
            else
            {
                GUI.enabled = meshTreeBase.IsReadyToBuild();
                if (m_isBuilding)
                {
                    m_isBuilding  = false;
                    m_memoryUsage = meshTreeBase.GetMemoryUsage();
                    EditorUtility.SetDirty(meshTreeBase);
                }
                if (meshTreeBase.IsPrebuilt())
                {
                    m_showBuiltData = EditorGUILayout.Foldout(m_showBuiltData, "Built Tree Info");
                    if (m_showBuiltData)
                    {
                        string memorySize;
                        float  mb = m_memoryUsage / (1024.0f * 1024.0f);
                        if (1.0f <= mb)
                        {
                            memorySize = mb.ToString("f3") + "MB";
                        }
                        else
                        {
                            float kb = m_memoryUsage / 1024.0f;
                            memorySize = kb.ToString("f3") + "KB";
                        }
                        EditorGUILayout.LabelField("Memory", memorySize);
                        EditorGUILayout.LabelField("Node Count", meshTreeBase.GetNodeCount().ToString());
                    }
                }
                if (GUILayout.Button(meshTreeBase.IsPrebuilt() ? "Rebuild" : "Build"))
                {
                    ApplyEditingRenderType();
                    m_isBuilding = true;
                    BuildMeshTree(meshTreeBase);
                }
            }
            GUI.enabled = true;
        }
예제 #5
0
        void OnGUISelectMeshTreeTypeAndCreate()
        {
            EditorGUILayout.LabelField("Type of Mesh Tree");
            if (m_srcMesh is TerrainData)
            {
                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                GUILayout.Label("Terrain Mesh Tree");
                EditorGUILayout.EndHorizontal();
            }
            else
            {
                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                bool bBinaryTree = GUILayout.Toggle(m_bBinaryTree, "Binary Mesh Tree");
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                bool bOctree = GUILayout.Toggle(!m_bBinaryTree, "Oct Mesh Tree");
                EditorGUILayout.EndHorizontal();
                if (bOctree && m_bBinaryTree)
                {
                    m_bBinaryTree = !bOctree;
                }
                else if (bBinaryTree && !m_bBinaryTree)
                {
                    m_bBinaryTree = bBinaryTree;
                }
                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                if (GUILayout.Button("<color=blue>What's the difference?</color>", m_errorStyle))
                {
                    Application.OpenURL("http://nyahoon.com/products/fast-shadow-receiver/create-a-mesh-tree");
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.Separator();

                EditorGUILayout.LabelField("Select Source Object");

                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                bool bSceneObject = GUILayout.Toggle(m_srcMesh == m_rootObject, "Scene Object");
                EditorGUILayout.ObjectField(m_rootObject, typeof(GameObject), true);
                EditorGUILayout.EndHorizontal();
                if (bSceneObject && m_srcMesh != m_rootObject)
                {
                    m_srcMesh = m_rootObject;
                }

                EditorGUILayout.BeginHorizontal();
                GameObject prefabObject = PrefabUtility.GetPrefabParent(m_rootObject) as GameObject;
                GUILayout.Space(30);
                if (prefabObject == null)
                {
                    GUI.enabled = false;
                }
                bool bPrefabObject = GUILayout.Toggle(m_srcMesh == prefabObject, "Prefab Object");
                if (prefabObject != null)
                {
                    EditorGUILayout.ObjectField(prefabObject, typeof(GameObject), true);
                }
                else
                {
                    GUI.enabled = true;
                    if (GUILayout.Button("Create Prefab"))
                    {
                        string path = EditorUtility.SaveFilePanelInProject("Create Prefab", m_rootObject.name, "prefab", "Select a path for the prefab of " + m_rootObject.name);
                        if (!string.IsNullOrEmpty(path))
                        {
                            prefabObject = PrefabUtility.CreatePrefab(path, m_rootObject, ReplacePrefabOptions.ConnectToPrefab);
                            m_srcMesh    = prefabObject;
                            bSceneObject = false;
                        }
                    }
                }
                EditorGUILayout.EndHorizontal();
                if (bPrefabObject && m_srcMesh != prefabObject)
                {
                    m_srcMesh = prefabObject;
                }
                if (m_srcMesh == m_rootObject)
                {
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.Space(30);
                    GUILayout.TextArea("<color=red>If you choose Scene Object, the reference to the object cannot be stored in Mesh Tree settings. So, you will need to set the reference again when you rebuild the mesh tree.</color>", m_errorStyle);
                    EditorGUILayout.EndHorizontal();
                }
                else if (m_srcMesh == prefabObject)
                {
                    // check if m_rootObject is modified from prefabObject.
                    for (int i = 0; i < srcMeshes.Length; ++i)
                    {
                        GameObject go     = srcMeshes[i].gameObject;
                        GameObject prefab = PrefabUtility.GetPrefabParent(go) as GameObject;
                        if (go.layer != prefab.layer)
                        {
                            EditorGUILayout.BeginHorizontal();
                            GUILayout.Space(30);
                            GUILayout.TextArea("<color=red>The layer of the scene object was modified from the prefab.</color>", m_errorStyle);
                            EditorGUILayout.ObjectField(go, typeof(GameObject), true);
                            EditorGUILayout.EndHorizontal();
                            break;
                        }
                        if (go.transform.localPosition != prefab.transform.localPosition || go.transform.localRotation != prefab.transform.localRotation || go.transform.localScale != prefab.transform.localScale)
                        {
                            EditorGUILayout.BeginHorizontal();
                            GUILayout.Space(30);
                            GUILayout.TextArea("<color=red>The transform of the scene object was modified from the prefab.</color>", m_errorStyle);
                            EditorGUILayout.ObjectField(go, typeof(GameObject), true);
                            EditorGUILayout.EndHorizontal();
                            break;
                        }
                    }
                }
            }

            EditorGUILayout.Separator();

            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Create Mesh Tree"))
            {
                string path = GetDefaultPathFromSourceMesh(m_srcMesh, m_bBinaryTree);
                path = EditorUtility.SaveFilePanelInProject("Create New Mesh Tree", System.IO.Path.GetFileName(path), "asset", "Select a path for the new Mesh Tree", System.IO.Path.GetDirectoryName(path));
                if (!string.IsNullOrEmpty(path))
                {
                    MeshTreeBase meshTreeBase;
                    if (m_srcMesh is TerrainData)
                    {
                        TerrainMeshTree meshTree = ScriptableObject.CreateInstance <TerrainMeshTree>();
                        meshTree.terrainData = m_srcMesh as TerrainData;
                        meshTreeBase         = meshTree;
                    }
                    else if (m_bBinaryTree)
                    {
                        BinaryMeshTree meshTree = ScriptableObject.CreateInstance <BinaryMeshTree>();
                        meshTree.srcMesh      = m_srcMesh;
                        meshTree.layerMask    = m_layers;
                        meshTree.scaledOffset = 1.0f;
                        meshTreeBase          = meshTree;
                    }
                    else
                    {
                        OctMeshTree meshTree = ScriptableObject.CreateInstance <OctMeshTree>();
                        meshTree.srcMesh      = m_srcMesh;
                        meshTree.layerMask    = m_layers;
                        meshTree.scaledOffset = 1.0f;
                        meshTreeBase          = meshTree;
                    }
                    AssetDatabase.CreateAsset(meshTreeBase, path);
                    AssetDatabase.Refresh();
                    m_meshTree = AssetDatabase.LoadAssetAtPath(path, typeof(MeshTreeBase)) as MeshTreeBase;
                    if (m_receiver != null)
                    {
                        m_receiver.meshTree = m_meshTree;
                    }
                    if (m_srcMesh is TerrainData)
                    {
                        // just build and close. because TerrainMeshTree doesn't have build options.
                        MeshTreeEditor.BuildMeshTree(m_meshTree);
                        Close();
                    }
                    else
                    {
                        m_serializedObject = new SerializedObject(m_meshTree);
                        ++m_currentPage;
                    }
                }
            }
        }
        public override void OnInspectorGUI()
        {
            MeshShadowReceiver receiver         = target as MeshShadowReceiver;
            Transform          oldMeshTransform = receiver.meshTransform;
            MeshTreeBase       oldMeshTree      = receiver.meshTree;

            if (!string.IsNullOrEmpty(m_errorString))
            {
                EditorGUILayout.TextArea(m_errorString, errorStyle);
            }
            base.OnInspectorGUI();
            SerializedProperty property = serializedObject.FindProperty("m_enablePrediction");
            bool bPredictEnabled        = EditorGUILayout.Toggle("Enable Prediction", property.boolValue);

            if (bPredictEnabled != property.boolValue)
            {
                Undo.RecordObject(target, "Inspector");
                property.boolValue = bPredictEnabled;
                EditorUtility.SetDirty(target);
            }
            if (bPredictEnabled)
            {
                Component predictor = (Component)EditorGUILayout.ObjectField("Predictor", receiver.predictor as Component, typeof(Component), true);
                if (predictor != receiver.predictor)
                {
                    if (predictor == null)
                    {
                        Undo.RecordObject(receiver, "Inspector");
                        receiver.predictor = null;
                        EditorUtility.SetDirty(receiver);
                    }
                    else
                    {
                        Component[] components = predictor.GetComponents <Component>();
                        for (int i = 0; i < components.Length; ++i)
                        {
                            if (components[i] is ITransformPredictor)
                            {
                                Undo.RecordObject(receiver, "Inspector");
                                receiver.predictor = components[i] as ITransformPredictor;
                                EditorUtility.SetDirty(receiver);
                                break;
                            }
                        }
                    }
                }
            }
            property = serializedObject.FindProperty("m_updateOnlyWhenProjectorMoved");
            bool bCheckMove = EditorGUILayout.Toggle("Update Only When Projector Moved", property.boolValue);

            if (bCheckMove != property.boolValue)
            {
                Undo.RecordObject(target, "Inspector");
                property.boolValue = bCheckMove;
                EditorUtility.SetDirty(target);
            }
            if (bCheckMove)
            {
                EditorGUILayout.PropertyField(serializedObject.FindProperty("m_margin"));
            }
            serializedObject.ApplyModifiedProperties();
            if (receiver.meshTree == null && receiver.meshTransform != null)
            {
                if (receiver.meshTransform != oldMeshTransform)
                {
                    // to make it easier to setup mesh shadow receiver, find a mesh tree from meshTransform.
                    MeshTreeBase meshTree = FindMeshTreeFromMeshTransform(receiver.meshTransform);
                    if (meshTree != null)
                    {
                        receiver.meshTree = meshTree;
                        EditorUtility.SetDirty(receiver);
                    }
                }
                // if not found, show create button
                if (receiver.meshTree == null)
                {
                    if (GUILayout.Button("Create a new MeshTree"))
                    {
                        LayerMask layers = -1;
                        if (receiver.unityProjector != null)
                        {
                            layers = receiver.unityProjector.ignoreLayers;
                        }
                        CreateMeshTreeWizard.CreateWizard(receiver.meshTransform, layers, receiver);
                    }
                }
            }
            if (receiver.meshTree != null && receiver.meshTransform != null && (receiver.meshTree != oldMeshTree || receiver.meshTransform != oldMeshTransform))
            {
                string error = receiver.meshTree.CheckError(receiver.meshTransform.gameObject);
                if (!string.IsNullOrEmpty(error))
                {
                    Debug.LogError(error, receiver);
                    m_errorString = "<color=red>" + error + "</color>";
                }
                else
                {
                    m_errorString = null;
                }
            }
        }