public static MaxValue ( Vector2 v ) : float | ||
v | Vector2 |
/// A |
return | float |
/* * Draw a ball at a part's location * */ void DrawBall(BodyPart part, float scaleFactor) { if (part == null) { return; } Handles.SphereCap(0, part.bone.position, part.bone.rotation, _ballSize * VectorHelpers.MaxValue(part.bone.root.localScale)); // draw a line to the parent part if it is defined Handles.color = linesColor; if (part.parentPart != null) { Handles.DrawLine(part.bone.position, part.parentPart.bone.position); } }
/* * Convert this part's shapeSize into the opposite part's space * */ public void PasteShapeSizeToOpposite(bool applyScale) { Vector3 s = TransformPointToOpposite(shapeSize, applyScale); if (s.sqrMagnitude == 0f) { return; // early out if there is invalid scale } oppositePart.shapeSize = new Vector3(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z)); if (oppositePart.shapeType == ShapeType.Sphere) { oppositePart.shapeSize = Vector3.one * VectorHelpers.MaxValue(oppositePart.shapeSize); } else if (oppositePart.shapeType == ShapeType.Capsule) { CapsuleDirection currentDirection = oppositePart.capsuleDirection; oppositePart.capsuleDirection = capsuleDirection; oppositePart.FlipCapsule(currentDirection); } }
/* * Draw custom viewport render * */ void OnSceneGUI() { // only draw handles if array parts are not null, otherwise console spams crap when component is added while object is selected if (biped.spine != null && biped.spine.Length > 0) { // get the current scaleFactor Matrix4x4 matrix = biped.transform.localToWorldMatrix; float scaleFactor = Mathf.Sqrt(VectorHelpers.MaxValue(new Vector3( new Vector3(matrix.m00, matrix.m01, matrix.m02).sqrMagnitude, new Vector3(matrix.m10, matrix.m11, matrix.m12).sqrMagnitude, new Vector3(matrix.m20, matrix.m21, matrix.m22).sqrMagnitude))); // draw balls and lines for all defined parts Handles.color = ballsColor; foreach (BodyPart part in biped.allParts) { DrawBall(part, scaleFactor); } // draw a shape gizmo for each body part Handles.color = shapeColor; foreach (BodyPart part in biped.allParts) { BodyPartEditor.DrawShapeHandle(part, false); } // draw joint gizmos for all the body parts foreach (BodyPart part in biped.allParts) { BodyPartEditor.DrawJointHandle(part, false); } } // begin GUI or transform handles will be disabled Handles.BeginGUI(); // create the viewport controls ViewportControls.BeginArea(BodyPartEditor.viewportControlsWidth, GUIAnchor.TopLeft); { // common controls for BodyParts (shape, center, joints) BodyPartEditor.ViewportCommonControls(); // only update values if they change to prevent constant updating of player pref keys float fVal; GUILayout.BeginHorizontal(); { GUILayout.Label(string.Format("Transform Display Size: {0:0.00}", ballSize), GUILayout.Width(BodyPartEditor.viewportControlsWidth * 0.65f)); fVal = GUILayout.HorizontalSlider(ballSize, 0f, 1f); if (fVal != ballSize) { ballSize = fVal; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); { GUILayout.Label(string.Format("Body Mass: {0:0.00}", biped.mass), GUILayout.Width(BodyPartEditor.viewportControlsWidth * 0.65f)); fVal = GUILayout.HorizontalSlider(biped.mass, 1f, 500f); if (fVal != biped.mass) { biped.mass = fVal; biped.DistributeMass(); } } GUILayout.EndHorizontal(); // if the editor is playing, then create testing controls if (Application.isPlaying) { // padding after previous controls GUILayout.Space(ViewportControls.viewportPadding * 2f); // only display ragdoll controls if minimum requirements have been met if (biped.ValidateMinimumRequirements()) { // if the biped is currently ragdoll, then create a button to remove the ragdoll if (biped.isRagdoll) { if (GUILayout.Button("Remove Ragdoll")) { biped.RemoveRagdoll(); } } // otherwise create a button to turn the biped into a ragdoll else { if (GUILayout.Button("Create Ragdoll")) { biped.CreateRagdoll(BodyPartEditor.jointResistance, 1f); } } // only update an active ragdoll's spring value if the resistance slider changes float oldResist = BodyPartEditor.jointResistance; float oldForce = BodyPartEditor.maxForce; BodyPartEditor.ViewportResistanceSlider(); BodyPartEditor.ViewportMaxForceSlider(); if ((oldResist != BodyPartEditor.jointResistance || oldForce != BodyPartEditor.maxForce) && biped.isRagdoll) { JointDrive drive; foreach (BodyPart part in biped.allParts) { if (part == null || part.joint == null) { continue; } drive = part.joint.slerpDrive; drive.maximumForce = BodyPartEditor.maxForce; drive.positionSpring = BodyPartEditor.jointResistance; part.joint.slerpDrive = drive; } } // if the biped is out of ragdoll, include a button to return to the snapshot if (!biped.isRagdoll) { if (GUILayout.Button("Restore Pose to Snapshot")) { foreach (BodyPart part in biped.allParts) { if (part == null) { continue; } part.ResetToInitialRotation(); part.ResetToPositionSnapshot(); } } } } else { GUILayout.Label("Minimum biped definition not specified. Please stop the game and ensure that biped minimum requirements have been met."); } } // otherwise, create additional setup controls else { GUILayout.Label("Bone Naming Convention:"); GUILayout.BeginVertical(); { namingConvention = (DCCApplication)GUILayout.SelectionGrid((int)namingConvention, System.Enum.GetNames(typeof(DCCApplication)), 2); if (GUILayout.Button(string.Format("Set Up Biped Using {0} Names", namingConvention))) { biped.AutomateSetup(namingConvention); } } GUILayout.EndVertical(); } } ViewportControls.EndArea(); // display symmetry status at the top of the viewport BodyPartEditor.ViewportStatus(); // finish GUI Handles.EndGUI(); }
/* * Draw a collider gizmo for a specified part * */ public static void DrawShapeHandle(BodyPart part, bool drawOpposite) { // early out if the part is null or the part has no shape if (part == null || part.shapeType == ShapeType.None || !part.isCollider) { return; } // store the current color Color oldColor = Handles.color; // set undo snapshot BodyPart[] parts = new BodyPart[1]; if (isSymmetrical && part.oppositePart != null) { parts = new BodyPart[2]; parts[1] = part.oppositePart; parts[1].oppositePart = part; // BUG: No idea why I need to do this } parts[0] = part; Undo.SetSnapshotTarget(parts, string.Format("Change Shape")); // create shape handles if (isShapeHandleEnabled) { // use radius if shape is capsule or sphere float radius = 0f; // compute the size to draw the shape handle based on the part's scale Vector3 shapeHandleSize = GetShapeHandleSize(part); // draw the correct handle based on shapeType switch (part.shapeType) { case ShapeType.Box: // create handles ShapeHandles.WireBox(ref shapeHandleSize, part.bone.TransformPoint(part.shapeCenter), part.bone.rotation, ""); // apply the result DoShapeSizeThresholdTest(part, shapeHandleSize); // handle symmetry if (parts.Length > 1) { part.PasteShapeSizeToOpposite(isScaleSymmetrical); if (drawOpposite) { // get the opposite part's dimensions in case its local scale is different shapeHandleSize = GetShapeHandleSize(parts[1]); // ghost the opposite part CustomHandleUtilities.SetHandleColor(oldColor, symmetryAlpha); ShapeHandles.WireBox(ref shapeHandleSize, parts[1].bone.TransformPoint(parts[1].shapeCenter), parts[1].bone.rotation, ""); // apply the result DoShapeSizeThresholdTest(parts[1], shapeHandleSize); parts[1].PasteShapeSizeToOpposite(isScaleSymmetrical); } } break; case ShapeType.Capsule: // get handle properties float height = 0f; Quaternion capsuleOrientation = Quaternion.identity; GetCapsuleProperties(part, ref height, ref radius, ref capsuleOrientation); // draw handle ShapeHandles.WireCapsule(ref radius, ref height, part.bone.TransformPoint(part.shapeCenter), part.bone.rotation * part.shapeRotation * capsuleOrientation, ""); // apply result DoShapeSizeThresholdTest(part, CapsuleToSize(part, height, radius)); // handle symmetry if (parts.Length > 1) { part.PasteShapeSizeToOpposite(isScaleSymmetrical); if (drawOpposite) { // get the opposite part's dimensions in case its local scale is different GetCapsuleProperties(parts[1], ref height, ref radius, ref capsuleOrientation); // ghost the opposite part CustomHandleUtilities.SetHandleColor(oldColor, symmetryAlpha); ShapeHandles.WireCapsule(ref radius, ref height, parts[1].bone.TransformPoint(parts[1].shapeCenter), parts[1].bone.rotation * parts[1].shapeRotation * capsuleOrientation, ""); // apply the result DoShapeSizeThresholdTest(parts[1], CapsuleToSize(parts[1], height, radius)); parts[1].PasteShapeSizeToOpposite(isScaleSymmetrical); } } break; case ShapeType.Sphere: // create a simple radius handle float oldRadius = Mathf.Max( part.shapeSize.x * part.bone.lossyScale.x * 0.5f, part.shapeSize.y * part.bone.lossyScale.y * 0.5f, part.shapeSize.z * part.bone.lossyScale.z * 0.5f); radius = Handles.RadiusHandle(part.bone.rotation * part.shapeRotation, part.bone.TransformPoint(part.shapeCenter), oldRadius); if (Mathf.Abs(radius - oldRadius) > changeThreshold) { float scaleFactor = 1f / VectorHelpers.MaxValue(part.bone.lossyScale); part.shapeSize.x = 2f * radius * scaleFactor; part.shapeSize.y = 2f * radius * scaleFactor; part.shapeSize.z = 2f * radius * scaleFactor; oldRadius = radius; } // handle symmetry if (parts.Length > 1) { part.PasteShapeSizeToOpposite(isScaleSymmetrical); if (drawOpposite) { // ghost the opposite part CustomHandleUtilities.SetHandleColor(oldColor, symmetryAlpha); oldRadius = Mathf.Max( parts[1].shapeSize.x * parts[1].bone.lossyScale.x * 0.5f, parts[1].shapeSize.y * parts[1].bone.lossyScale.y * 0.5f, parts[1].shapeSize.z * parts[1].bone.lossyScale.z * 0.5f); radius = Handles.RadiusHandle(parts[1].bone.rotation * part.shapeRotation, parts[1].bone.TransformPoint(parts[1].shapeCenter), oldRadius); if (Mathf.Abs(radius - oldRadius) > changeThreshold) { parts[1].shapeSize.x = 2f * radius; parts[1].shapeSize.y = 2f * radius; parts[1].shapeSize.z = 2f * radius; parts[1].PasteShapeSizeToOpposite(isScaleSymmetrical); } } } break; } } // center handles if (isCenterHandleEnabled) { // position handle for the center Vector3 center = part.bone.InverseTransformPoint(Handles.PositionHandle(part.bone.TransformPoint(part.shapeCenter), part.bone.rotation * part.shapeRotation)); // rotation handle // Quaternion rotation = Quaternion.Inverse(part.bone.rotation)*Handles.RotationHandle(part.bone.rotation*part.shapeRotation, part.bone.TransformPoint(part.shapeCenter)); // handle symmetry if (parts.Length > 1) { center = part.TransformPointToOpposite(center, isScaleSymmetrical); // rotation = part.TransformRotationToOpposite(rotation); if (drawOpposite) { center = parts[1].bone.InverseTransformPoint(Handles.PositionHandle(parts[1].bone.TransformPoint(center), parts[1].bone.rotation * part.shapeRotation)); // rotation = Quaternion.Inverse(parts[1].bone.rotation)*Handles.RotationHandle(parts[1].bone.rotation*parts[1].shapeRotation, parts[1].bone.TransformPoint(parts[1].shapeCenter)); } center = parts[1].TransformPointToOpposite(center, isScaleSymmetrical); // rotation = parts[1].TransformRotationToOpposite(rotation); } // apply results if ((part.shapeCenter - center).sqrMagnitude > changeThreshold * changeThreshold) { part.shapeCenter = center; } // if (Quaternion.Angle(part.shapeRotation, rotation)>changeThreshold) part.shapeRotation = rotation; } if (parts.Length > 1) { // update values parts[1].shapeType = part.shapeType; Vector3 oldValue = parts[1].shapeCenter; Vector3 newValue = part.TransformPointToOpposite(part.shapeCenter, isScaleSymmetrical); if ((oldValue - newValue).sqrMagnitude > changeThreshold * changeThreshold) { parts[1].shapeCenter = newValue; } oldValue = parts[1].shapeSize; part.PasteShapeSizeToOpposite(isScaleSymmetrical); if ((oldValue - parts[1].shapeSize).sqrMagnitude < changeThreshold * changeThreshold) { parts[1].shapeSize = oldValue; } // TODO: mirror rotation if/when collider rotation is implemented } CustomHandleUtilities.SetHandleColor(oldColor); foreach (BodyPart p in parts) { EditorUtility.SetDirty(p); } }