Пример #1
0
        /// <summary>
        /// Initializes the <see cref="Candlelight.SerializedPropertyX"/> class.
        /// </summary>
        static SerializedPropertyX()
        {
            // get all of the PropertyDrawer types
            List <System.Type> drawerTypes =
                ObjectX.AllTypes.Where(t => t.IsSubclassOf(typeof(PropertyDrawer))).ToList();

            // associate object/attribute types with their respective drawers
            foreach (System.Type drawerType in drawerTypes)
            {
                CustomPropertyDrawer[] attrs = ObjectX.GetCustomAttributes <CustomPropertyDrawer>(drawerType);
                if (attrs.Length > 0)
                {
                    System.Type baseType = customPropertyDrawerTypeField.GetValue(attrs[0]) as System.Type;
                    if (!drawersForEachType.ContainsKey(baseType))
                    {
                        drawersForEachType.Add(baseType, drawerType);
                    }
                    else
                    {
                        drawersForEachType[baseType] = drawerType;
                    }
                    if ((bool)customPropertyDrawerUseForChildrenField.GetValue(attrs[0]))
                    {
                        foreach (System.Type type in ObjectX.AllTypes)
                        {
                            if (!drawersForEachType.ContainsKey(type) && type.IsSubclassOf(baseType))
                            {
                                drawersForEachType.Add(type, drawerType);
                            }
                        }
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Displays a translation handle at the specified location with a customizable size and control ID that also
        /// respects the current scene GUI alpha.
        /// </summary>
        /// <remarks>
        /// Unity's built-in translation handle does not allow size specification, always renders fully opaque, and will
        /// result in control ID conflicts if there are multiple instances on the screen at once.
        /// </remarks>
        /// <param name="baseId">Base identifier. Each axis its own unique hash based off this value.</param>
        /// <param name="position">Position.</param>
        /// <param name="orientation">Orientation.</param>
        /// <param name="size">Size.</param>
        public static Vector3 Translation(int baseId, Vector3 position, Quaternion orientation, float size)
        {
            Color oldColor = Handles.color;

            size          = SceneGUI.GetFixedHandleSize(position, size);
            Handles.color = EditorGUIX.xHandleColor * SceneGUI.CurrentAlphaScalar;
            GUI.SetNextControlName(
                ObjectX.GenerateHashCode(baseId, s_TranslationHandleHash, (int)EditAxis.X).ToString()
                );
            position      = Handles.Slider(position, orientation * Vector3.right, size, Handles.ArrowCap, 1f);
            Handles.color = EditorGUIX.yHandleColor * SceneGUI.CurrentAlphaScalar;
            GUI.SetNextControlName(
                ObjectX.GenerateHashCode(baseId, s_TranslationHandleHash, (int)EditAxis.Y).ToString()
                );
            position      = Handles.Slider(position, orientation * Vector3.up, size, Handles.ArrowCap, 1f);
            Handles.color = EditorGUIX.zHandleColor * SceneGUI.CurrentAlphaScalar;
            GUI.SetNextControlName(
                ObjectX.GenerateHashCode(baseId, s_TranslationHandleHash, (int)EditAxis.Z).ToString()
                );
            position = Handles.Slider(position, orientation * Vector3.forward, size, Handles.ArrowCap, 1f);
            // TODO: add 2-axis sliders
//			position = Handles.DoPlanarHandle(Handles.PlaneHandle.xzPlane, position, orientation, size * 0.25f);
//			position = Handles.DoPlanarHandle(Handles.PlaneHandle.xyPlane, position, orientation, size * 0.25f);
//			position = Handles.DoPlanarHandle(Handles.PlaneHandle.yzPlane, position, orientation, size * 0.25f);
            Handles.color = oldColor;
            return(position);
        }
Пример #3
0
 /// <summary>
 /// Serves as a hash function for a <see cref="Candlelight.RichTextStyle"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
 /// hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                m_SizeScalar.GetHashCode(),
                m_FontStyle.GetHashCode(),
                m_ShouldReplaceColor.GetHashCode(),
                m_ReplacementColor.GetHashCode()
                ));
 }
Пример #4
0
            /// <summary>
            /// Serves as a hash function for a <see cref="KeywordsGlossary.Entry"/> object.
            /// </summary>
            /// <returns>
            /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as
            /// a hash table.
            /// </returns>
            public override int GetHashCode()
            {
                int result = ObjectX.GenerateHashCode(
                    m_Definition.GetHashCode(),
                    m_MainForm.GetHashCode(),
                    (m_OtherForms == null ? 0 : m_OtherForms.Count).GetHashCode()
                    );

                if (m_OtherForms != null)
                {
                    for (int i = 0; i < m_OtherForms.Count; ++i)
                    {
                        result = ObjectX.GenerateHashCode(result, m_OtherForms[i].GetHashCode());
                    }
                }
                return(result);
            }
Пример #5
0
        /// <summary>
        /// Displays the helix twist curve handles.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixTwistHandles(int baseId, Helix helix)
        {
            float          v;
            AnimationCurve width    = helix.Width;
            AnimationCurve newTwist = new AnimationCurve(helix.Twist.keys);

            Keyframe[] newKeys = newTwist.keys;
            for (int i = 0; i < newKeys.Length; ++i)
            {
                // compute the disc center
                Vector3 discCenter = Vector3.forward * newKeys[i].time * helix.Length;
                v = newKeys[i].value;
                // create an arc handle
                float helixWidth = width.Evaluate(newKeys[i].time);
                v = ArcHandles.SolidAngle(
                    ObjectX.GenerateHashCode(baseId, s_TwistHandleHash, i, 1),
                    v, discCenter,
                    Quaternion.LookRotation(Vector3.right, Vector3.forward),
                    helixWidth,
                    string.Format("{0:0} Degrees", v)
                    );
                Handles.DrawWireDisc(discCenter, Vector3.forward, helixWidth);
                newKeys[i].value = v;
                // create time adjustment handles for intermediary keys
                if (i == 0 || i == newKeys.Length - 1)
                {
                    continue;
                }
                v = newKeys[i].time * helix.Length;
                int id = ObjectX.GenerateHashCode(baseId, s_TwistHandleHash, i, 2);
                v = LinearHandles.Cone(id, v, Vector3.zero, Vector3.forward, capScale: s_TimeAdjustHandleSize);
                v = Mathf.Clamp(
                    v / helix.Length,
                    Mathf.Epsilon * 2f,
                    1f - Mathf.Epsilon * 2f
                    );
                if (Mathf.Abs(v - newKeys[i].time) > s_RequiredMinHandleChange)
                {
                    newKeys[i].time = v;
                }
                // TODO: Fix problems with keys moving over each other
            }
            newTwist.keys = newKeys;
            helix.Twist   = newTwist;
        }
Пример #6
0
        /// <summary>
        /// Display a helix length handle.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixLengthHandle(int baseId, Helix helix)
        {
            float length = helix.Length;
            int   id     = ObjectX.GenerateHashCode(baseId, s_LengthHandleHash);

            length = LinearHandles.Arrow(
                id,
                length,
                Vector3.zero,
                Vector3.forward,
                string.Format("Length: {0:0.###}", length),
                s_LengthHandleSize
                );
            if (Mathf.Abs(length - helix.Length) < s_RequiredMinHandleChange)
            {
                length = helix.Length;
            }
            helix.Length = length;
        }
Пример #7
0
        /// <summary>
        /// Generates a hash code for the serialized properties in a list or array of
        /// <see cref="IPropertyBackingFieldCompatible"/> objects.
        /// </summary>
        /// <returns>A hash code.</returns>
        /// <param name="listField">List field.</param>
        /// <typeparam name="T">The element type.</typeparam>
        public static int GenerateSerializedPropertiesHash <T>(IList <T> listField)
            where T : IPropertyBackingFieldCompatible
        {
            int typeCode = typeof(T).GetHashCode();

            if (listField == null)
            {
                // a null and empty collection are the same where the inspector is concerned
                return(ObjectX.GenerateHashCode(typeCode, 0.GetHashCode()));
            }
            int result = ObjectX.GenerateHashCode(typeCode, listField.Count.GetHashCode());

            for (int i = 0; i < listField.Count; ++i)
            {
                result = ObjectX.GenerateHashCode(
                    result, listField[i] == null ? typeCode : listField[i].GetSerializedPropertiesHash()
                    );
            }
            return(result);
        }
Пример #8
0
        /// <summary>
        /// Displays helix width handles.
        /// </summary>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="helix">Helix.</param>
        private static void DoHelixWidthHandles(int baseId, Helix helix)
        {
            float          v;
            AnimationCurve twist    = helix.Twist;
            AnimationCurve newWidth = helix.Width;

            Keyframe[] newKeys = newWidth.keys;
            for (int i = 0; i < newKeys.Length; ++i)
            {
                // compute the disc center
                Vector3 discCenter = Vector3.forward * newKeys[i].time * helix.Length;
                // compute the angle of the handle around the length
                Quaternion angle = Quaternion.AngleAxis(twist.Evaluate(newKeys[i].time), Vector3.forward);
                // create a cone handle with a slight offset to not overlap the arc handle
                v = newKeys[i].value + s_WidthHandleOffset;
                int id = ObjectX.GenerateHashCode(baseId, s_WidthHandleHash, i, 1);
                v = LinearHandles.Cone(id, v, discCenter, angle * Vector3.right, capScale: s_WidthHandleSize);
                newKeys[i].value = v - s_WidthHandleOffset;
                // create time adjustment handles for intermediary keys
                if (i == 0 || i == newKeys.Length - 1)
                {
                    continue;
                }
                v  = newKeys[i].time * helix.Length;
                id = ObjectX.GenerateHashCode(baseId, s_WidthHandleHash, i, 2);
                v  = LinearHandles.Cone(id, v, Vector3.zero, Vector3.forward, capScale: s_TimeAdjustHandleSize);
                v  = Mathf.Clamp(
                    v / helix.Length,
                    Mathf.Epsilon * 2f,
                    1f - Mathf.Epsilon * 2f
                    );
                if (Mathf.Abs(v - newKeys[i].time) > s_RequiredMinHandleChange)
                {
                    newKeys[i].time = v;
                }
                // TODO: Fix problems with keys moving over each other
            }
            newWidth.keys = newKeys;
            helix.Width   = newWidth;
        }
Пример #9
0
 /// <summary>
 /// Serves as a hash function for a <see cref="ColorGradient"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
 /// hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                m_MinColor.GetHashCode(), m_MaxColor.GetHashCode(), m_InterpolationSpace.GetHashCode()
                ));
 }
Пример #10
0
 /// <summary>
 /// Determines whether the specified <see cref="System.Object"/> is equal to the current
 /// <see cref="ColorGradient"/>.
 /// </summary>
 /// <param name="obj">
 /// The <see cref="System.Object"/> to compare with the current <see cref="ColorGradient"/>.
 /// </param>
 /// <returns>
 /// <see langword="true"/> if the specified <see cref="System.Object"/> is equal to the current
 /// <see cref="ColorGradient"/>; otherwise, <see langword="false"/>.
 /// </returns>
 public override bool Equals(object obj)
 {
     return(ObjectX.Equals(ref this, obj));
 }
Пример #11
0
 /// <summary>
 /// Serves as a hash function for a <see cref="KeywordsGlossary.Entry"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as
 /// a hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                m_Definition.GetHashCode(), m_MainForm.GetHashCode(), ObjectX.GenerateHashCode(m_OtherForms)
                ));
 }
Пример #12
0
 /// <summary>
 /// Serves as a hash function for a <see cref="KeywordsGlossary.InflectedForm"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as
 /// a hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(m_PartOfSpeech.GetHashCode(), this.Word.GetHashCode()));
 }
Пример #13
0
 /// <summary>
 /// Serves as a hash function for a <see cref="ImmutableRectOffset"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
 /// hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                m_Left.GetHashCode(), m_Right.GetHashCode(), m_Top.GetHashCode(), m_Bottom.GetHashCode()
                ));
 }
Пример #14
0
 /// <summary>
 /// Serves as a hash function for a <see cref="UnityVersion"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
 /// hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                this.MajorVersion.GetHashCode(), this.MinorVersion.GetHashCode(), this.MaintenanceVersion.GetHashCode()
                ));
 }
Пример #15
0
 /// <summary>
 /// Serves as a hash function for a <see cref="ColorHSV"/> object.
 /// </summary>
 /// <returns>
 /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
 /// hash table.
 /// </returns>
 public override int GetHashCode()
 {
     return(ObjectX.GenerateHashCode(
                m_Hue.GetHashCode(), m_Saturation.GetHashCode(), m_Value.GetHashCode(), m_Alpha.GetHashCode()
                ));
 }
Пример #16
0
 /// <summary>
 /// Displays a wire disc handle.
 /// </summary>
 /// <returns>The disc radius.</returns>
 /// <param name="id">Control identifier.</param>
 /// <param name="radius">Radius.</param>
 /// <param name="origin">Origin.</param>
 /// <param name="orientation">Orientation.</param>
 /// <param name="label">Label.</param>
 public static float WireDisc(int id, float radius, Vector3 origin, Quaternion orientation, string label = "")
 {
     return(DoDisc(
                ObjectX.GenerateHashCode(id, s_WireDiscHash), radius, origin, orientation, label, FillMode.Wire
                ));
 }
Пример #17
0
 /// <summary>
 /// Displays a wire sphere handle.
 /// </summary>
 /// <returns>The sphere radius.</returns>
 /// <param name="id">Control identifier.</param>
 /// <param name="radius">Radius.</param>
 /// <param name="origin">Origin.</param>
 /// <param name="label">Label.</param>
 public static float WireSphere(int id, float radius, Vector3 origin, string label = "")
 {
     return(DoSphere(ObjectX.GenerateHashCode(id, s_WireSphereHash), radius, origin, label, FillMode.Wire));
 }
Пример #18
0
        /// <summary>
        /// Displays a shape handle of the specified type.
        /// </summary>
        /// <remarks> If the user is holding Alt, the center will stay locked in place.</remarks>
        /// <param name="baseId">Base identifier.</param>
        /// <param name="size">Size.</param>
        /// <param name="center">Center.</param>
        /// <param name="orientation">Orientation.</param>
        /// <param name="type">Type.</param>
        private static void DoShapeHandle(
            int baseId, ref Vector3 size, ref Vector3 center, Quaternion orientation, WireShapeType type
            )
        {
            // set handle matrix
            Matrix4x4 oldMatrix = Handles.matrix;

            Handles.matrix *= Matrix4x4.TRS(center, orientation, Vector3.one);
            // generate control Ids
            int rightId   = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.X, 1);
            int leftId    = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.X, 2);
            int upId      = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.Y, 1);
            int downId    = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.Y, 2);
            int forwardId = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.Z, 1);
            int backId    = ObjectX.GenerateHashCode(baseId, (int)type, (int)EditAxis.Z, 2);
            int currentId = GUIUtility.hotControl;
            int nearestId = HandleUtility.nearestControl;

            // cache the center and size when a handle is clicked to use for shift/alt dragging
            switch (Event.current.type)
            {
            case EventType.MouseDown:
                if (
                    nearestId == leftId || nearestId == rightId ||
                    nearestId == upId || nearestId == downId ||
                    nearestId == forwardId || nearestId == backId
                    )
                {
                    s_OnClickCenter[baseId] = center;
                    s_OnClickSize[baseId]   = size;
                }
                break;

            case EventType.MouseUp:
                s_OnClickCenter.Remove(baseId);
                s_OnClickSize.Remove(baseId);
                currentId = 0;
                break;
            }
            // make sure size is not negative on any dimension
            size = new Vector3(Mathf.Max(0f, size.x), Mathf.Max(0f, size.y), Mathf.Max(0f, size.z));
            // display right/left handles
            float   right  = size.x * 0.5f;
            float   left   = -size.x * 0.5f;
            Vector3 offset = Handles.matrix.GetScale();

            offset = new Vector3(
                offset.x == 0f ? 0f : 1f / offset.x,
                offset.y == 0f ? 0f : 1f / offset.y,
                offset.z == 0f ? 0f : 1f / offset.z
                );
            offset *= s_HandleOffset;
            right   = LinearHandles.Dot(
                rightId, val: right, origin: Vector3.right * offset.x, direction: Vector3.right
                );
            right = Mathf.Max(right, left);
            left  =
                LinearHandles.Dot(leftId, val: left, origin: Vector3.left * offset.x, direction: Vector3.right);
            left = Mathf.Min(right, left);
            // display up/down handles
            float up   = size.y * 0.5f;
            float down = -size.y * 0.5f;

            up   = LinearHandles.Dot(upId, val: up, origin: Vector3.up * offset.y, direction: Vector3.up);
            up   = Mathf.Max(up, down);
            down = LinearHandles.Dot(downId, val: down, origin: Vector3.down * offset.y, direction: Vector3.up);
            down = Mathf.Min(up, down);
            // display forward/back handles
            float forward = size.z * 0.5f;
            float back    = -size.z * 0.5f;

            forward = LinearHandles.Dot(
                forwardId, val: forward, origin: Vector3.forward * offset.z, direction: Vector3.forward
                );
            forward = Mathf.Max(forward, back);
            back    =
                LinearHandles.Dot(backId, val: back, origin: Vector3.back * offset.z, direction: Vector3.forward);
            back = Mathf.Min(forward, back);
            // store which axes are being edited
            EditAxis editAxis = EditAxis.None;

            if (currentId == rightId || currentId == leftId)
            {
                editAxis = EditAxis.X;
            }
            else if (currentId == upId || currentId == downId)
            {
                editAxis = EditAxis.Y;
            }
            else if (currentId == forwardId || currentId == backId)
            {
                editAxis = EditAxis.Z;
            }
            // apply constraints to size based on shape type
            size.x = Mathf.Max(0f, right - left);
            size.y = Mathf.Max(0f, up - down);
            size.z = Mathf.Max(0f, forward - back);
            Vector3 deltaCenter = 0.5f * new Vector3(right + left, up + down, forward + back);
            float   delta;

            switch (type)
            {
            case WireShapeType.Capsule:
                switch (editAxis)
                {
                case EditAxis.X:
                    delta          = Mathf.Min(size.x, size.y) - size.x;
                    size.x        += delta;
                    size.z         = size.x;
                    deltaCenter.x += delta * 0.5f;
                    break;

                case EditAxis.Y:
                    delta          = Mathf.Max(size.y, size.x, size.z) - size.y;
                    size.y        += delta;
                    deltaCenter.y += delta * 0.5f;
                    break;

                case EditAxis.Z:
                    delta          = Mathf.Min(size.z, size.y) - size.z;
                    size.z        += delta;
                    size.x         = size.z;
                    deltaCenter.z += delta * 0.5f;
                    break;
                }
                break;

            case WireShapeType.Cylinder:
                switch (editAxis)
                {
                case EditAxis.X:
                    delta          = Mathf.Min(size.x, size.y) - size.x;
                    size.x        += delta;
                    size.z         = size.x;
                    deltaCenter.x += delta * 0.5f;
                    break;

                case EditAxis.Z:
                    delta          = Mathf.Min(size.z, size.y) - size.z;
                    size.z        += delta;
                    size.x         = size.z;
                    deltaCenter.z += delta * 0.5f;
                    break;
                }
                break;

            case WireShapeType.Sphere:
                switch (editAxis)
                {
                case EditAxis.X:
                    size.y = size.z = size.x;
                    break;

                case EditAxis.Y:
                    size.x = size.z = size.y;
                    break;

                case EditAxis.Z:
                    size.x = size.y = size.z;
                    break;
                }
                break;
            }
            // apply new center
            center += orientation * deltaCenter;
            // scale uniformly using cached size if holding shift key
            if (s_OnClickSize.ContainsKey(baseId) && Event.current.shift)
            {
                float scaleFactor = 1f;
                switch (editAxis)
                {
                case EditAxis.X:
                    scaleFactor = s_OnClickSize[baseId].x > 0f ? size.x / s_OnClickSize[baseId].x : 0f;
                    break;

                case EditAxis.Y:
                    scaleFactor = s_OnClickSize[baseId].y > 0f ? size.y / s_OnClickSize[baseId].y : 0f;
                    break;

                case EditAxis.Z:
                    scaleFactor = s_OnClickSize[baseId].z > 0f ? size.z / s_OnClickSize[baseId].z : 0f;
                    break;
                }
                size = s_OnClickSize[baseId] * scaleFactor;
            }
            // use cached center if holding alt key
            if (s_OnClickCenter.ContainsKey(baseId) && Event.current.alt)
            {
                center = s_OnClickCenter[baseId];
            }
            // draw wire shape
            switch (type)
            {
            case WireShapeType.Box:
                DrawWireBox(size);
                break;

            case WireShapeType.Capsule:
                DrawWireCylinder(new CylinderProperties(height: size.y, radius: size.x * 0.5f), CylinderCap.Capsule);
                break;

            case WireShapeType.Cylinder:
                DrawWireCylinder(new CylinderProperties(height: size.y, radius: size.x * 0.5f), CylinderCap.Cylinder);
                break;

            case WireShapeType.Sphere:
                float radius = size.x * 0.5f;
                Handles.DrawWireDisc(Vector3.zero, Vector3.right, radius);
                Handles.DrawWireDisc(Vector3.zero, Vector3.up, radius);
                Handles.DrawWireDisc(Vector3.zero, Vector3.forward, radius);
                // TODO: get this working in non-identity orientations
//				Vector3 nml = Handles.matrix.MultiplyPoint(Vector3.zero) - Camera.current.transform.position;
//				float sqrMagRecip = 1f / nml.sqrMagnitude;
//				float sqrRadius = radius * radius;
//				radius = Mathf.Sqrt(sqrRadius - (sqrRadius * sqrRadius * sqrMagRecip));
//				Handles.DrawWireDisc(
//					Vector3.zero - sqrRadius * nml * sqrMagRecip, Handles.matrix.inverse.MultiplyVector(nml), radius
//				);
                break;
            }
            // reset handle matrix
            Handles.matrix = oldMatrix;
        }