/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as a string. See SnapFromModes property. /// Pass in 'null' to get the default which is the offset to the pivot point.</param> /// <param name="axisType">Whether the Y axis is up or down</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, string snapFromMode, AxisSystemType axisType) { SnapFromMode mode = GetSnapFromMode(snapFromMode); return(CalcSnapFromOffset(node, mode, axisType)); }
/// <summary> /// Gets the vector in world space that points "up"</summary> /// <param name="axis">The axis system indicating which axis is up</param> /// <returns>The vector that points up</returns> public static Vec3F GetUpVector(AxisSystemType axis) { if (axis == AxisSystemType.YIsUp) { return(new Vec3F(0, 1, 0)); } else { return(new Vec3F(0, 0, 1)); } }
/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as an enum</param> /// <param name="axisType">Axis system type (y or z is up)</param> /// <param name="pivot">Pass in either node.RotatePivot or node.ScalePivot</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, SnapFromMode snapFromMode, AxisSystemType axisType, Vec3F pivot) { switch (snapFromMode) { case SnapFromMode.Pivot: { Vec3F offset; Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().Ancestry); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); node.Transform.TransformVector(pivot, out offset); //local-to-parent parentToWorld.TransformVector(offset, out offset); //parent-to-world return(offset); //world } case SnapFromMode.Origin: return(new Vec3F(0, 0, 0)); case SnapFromMode.BottomCenter: { Box box = node.BoundingBox; Vec3F btmWorld; if (axisType == AxisSystemType.YIsUp) { btmWorld = new Vec3F( (box.Min.X + box.Max.X) * 0.5f, box.Min.Y, (box.Min.Z + box.Max.Z) * 0.5f); } else { btmWorld = new Vec3F( (box.Min.X + box.Max.X) * 0.5f, (box.Min.Y + box.Max.Y) * 0.5f, box.Min.Z); } Vec3F origin = node.Transform.Translation; Vec3F offset = btmWorld - origin; //local space offset Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); parentToWorld.TransformVector(offset, out offset); return(offset); } default: throw new ArgumentException("Invalid snap-from node"); } }
/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as an enum</param> /// <param name="axisType">Whether the Y axis is up or down</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, SnapFromMode snapFromMode, AxisSystemType axisType) { Vec3F rotatePivot = new Vec3F(); if ((node.TransformationType & TransformationTypes.RotatePivot) != 0) { rotatePivot = node.RotatePivot; } return(CalcSnapFromOffset(node, snapFromMode, axisType, rotatePivot)); }
/// <summary> /// Gets the vector in world space that points "up"</summary> /// <param name="axis">The axis system indicating which axis is up</param> /// <returns>The vector that points up</returns> public static Vec3F GetUpVector(AxisSystemType axis) { if (axis == AxisSystemType.YIsUp) return new Vec3F(0, 1, 0); else return new Vec3F(0, 0, 1); }
/// <summary> /// Given an object's current Euler angles and a surface normal, calculates /// the Euler angles necessary to rotate the object so that its up-vector is /// aligned with the surface normal</summary> /// <param name="originalEulers">Original Euler angles, from an IRenderableNode.Rotation, for example</param> /// <param name="surfaceNormal">A unit vector that the object should be rotate-snapped to</param> /// <param name="upAxis">Whether the Y axis is up or down</param> /// <returns>The resulting angles to be assigned to IRenderableNode.Rotation</returns> /// <remarks> /// Note that QuatF was attempted to be used, but I could not get it to work reliably /// with the Matrix3F.GetEulerAngles(). Numerical instability? The basis vector /// method below works well, except for when the target surface is 90 degrees different /// than the starting up vector. In this case, the rotation around the up vector is lost /// (gimbal lock), but the results are always valid in the sense that the up vector /// is aligned with the surface normal. --Ron Little</remarks> public static Vec3F RotateToVector(Vec3F originalEulers, Vec3F surfaceNormal, AxisSystemType upAxis) { // get basis vectors for the current rotation Matrix3F rotMat = new Matrix3F(); rotMat.Rotation(originalEulers); Vec3F a1 = rotMat.XAxis; Vec3F a2 = rotMat.YAxis; Vec3F a3 = rotMat.ZAxis; // calculate destination basis vectors Vec3F b1, b2, b3; if (upAxis == AxisSystemType.YIsUp) { // a2 is the current up vector. b2 is the final up vector. // now, find either a1 or a3, whichever is most orthogonal to surface b2 = new Vec3F(surfaceNormal); float a1DotS = Vec3F.Dot(a1, surfaceNormal); float a3DotS = Vec3F.Dot(a3, surfaceNormal); if (Math.Abs(a1DotS) < Math.Abs(a3DotS)) { b1 = new Vec3F(a1); b3 = Vec3F.Cross(b1, b2); b1 = Vec3F.Cross(b2, b3); } else { b3 = new Vec3F(a3); b1 = Vec3F.Cross(b2, b3); b3 = Vec3F.Cross(b1, b2); } } else { // a3 is the current up vector. b3 is the final up vector. // now, find either a1 or a2, whichever is most orthogonal to surface b3 = new Vec3F(surfaceNormal); float a1DotS = Vec3F.Dot(a1, surfaceNormal); float a2DotS = Vec3F.Dot(a2, surfaceNormal); if (Math.Abs(a1DotS) < Math.Abs(a2DotS)) { b1 = new Vec3F(a1); b2 = Vec3F.Cross(b3, b1); b1 = Vec3F.Cross(b2, b3); } else { b2 = new Vec3F(a2); b1 = Vec3F.Cross(b2, b3); b2 = Vec3F.Cross(b3, b1); } } // in theory, this isn't necessary, but in practice... b1.Normalize(); b2.Normalize(); b3.Normalize(); // construct new rotation matrix and extract euler angles rotMat.XAxis = b1; rotMat.YAxis = b2; rotMat.ZAxis = b3; Vec3F newEulers = new Vec3F(); rotMat.GetEulerAngles(out newEulers.X, out newEulers.Y, out newEulers.Z); return newEulers; }
/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as an enum</param> /// <param name="axisType">Whether the Y axis is up or down</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, SnapFromMode snapFromMode, AxisSystemType axisType) { Vec3F rotatePivot = new Vec3F(); if ((node.TransformationType & TransformationTypes.RotatePivot) != 0) rotatePivot = node.RotatePivot; return CalcSnapFromOffset(node, snapFromMode, axisType, rotatePivot); }
/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as a string. See SnapFromModes property. /// Pass in 'null' to get the default which is the offset to the pivot point.</param> /// <param name="axisType">Whether the Y axis is up or down</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, string snapFromMode, AxisSystemType axisType) { SnapFromMode mode = GetSnapFromMode(snapFromMode); return CalcSnapFromOffset(node, mode, axisType); }
/// <summary> /// For an object 'node' that is being manipulated, this function returns /// the offset (in world space) from the object's origin to to the point that is being /// "snapped from". This calculation is determined by the snapping modes.</summary> /// <param name="node">The object that is being snapped-to some other object</param> /// <param name="snapFromMode">The "snap from" mode, as an enum</param> /// <param name="axisType">Axis system type (y or z is up)</param> /// <param name="pivot">Pass in either node.RotatePivot or node.ScalePivot</param> /// <returns>Offset from this object's origin to the "snap from" point, in world space</returns> /// <remarks>Must be kept in sync with SnapFromModes property.</remarks> public static Vec3F CalcSnapFromOffset( ITransformable node, SnapFromMode snapFromMode, AxisSystemType axisType, Vec3F pivot) { switch (snapFromMode) { case SnapFromMode.Pivot: { Vec3F offset; Path<DomNode> path = new Path<DomNode>(node.Cast<DomNode>().Ancestry); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); node.Transform.TransformVector(pivot, out offset); //local-to-parent parentToWorld.TransformVector(offset, out offset); //parent-to-world return offset; //world } case SnapFromMode.Origin: return new Vec3F(0, 0, 0); case SnapFromMode.BottomCenter: { Box box = node.BoundingBox; Vec3F btmWorld; if (axisType == AxisSystemType.YIsUp) { btmWorld = new Vec3F( (box.Min.X + box.Max.X) * 0.5f, box.Min.Y, (box.Min.Z + box.Max.Z) * 0.5f); } else { btmWorld = new Vec3F( (box.Min.X + box.Max.X) * 0.5f, (box.Min.Y + box.Max.Y) * 0.5f, box.Min.Z); } Vec3F origin = node.Transform.Translation; Vec3F offset = btmWorld - origin; //local space offset Path<DomNode> path = new Path<DomNode>(node.Cast<DomNode>().GetPath()); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); parentToWorld.TransformVector(offset, out offset); return offset; } default: throw new ArgumentException("Invalid snap-from node"); } }
/// <summary> /// Given an object's current Euler angles and a surface normal, calculates /// the Euler angles necessary to rotate the object so that its up-vector is /// aligned with the surface normal</summary> /// <param name="originalEulers">Original Euler angles, from an IRenderableNode.Rotation, for example</param> /// <param name="surfaceNormal">A unit vector that the object should be rotate-snapped to</param> /// <param name="upAxis">Whether the Y axis is up or down</param> /// <returns>The resulting angles to be assigned to IRenderableNode.Rotation</returns> /// <remarks> /// Note that QuatF was attempted to be used, but I could not get it to work reliably /// with the Matrix3F.GetEulerAngles(). Numerical instability? The basis vector /// method below works well, except for when the target surface is 90 degrees different /// than the starting up vector. In this case, the rotation around the up vector is lost /// (gimbal lock), but the results are always valid in the sense that the up vector /// is aligned with the surface normal. --Ron Little</remarks> public static Vec3F RotateToVector(Vec3F originalEulers, Vec3F surfaceNormal, AxisSystemType upAxis) { // get basis vectors for the current rotation Matrix3F rotMat = new Matrix3F(); rotMat.Rotation(originalEulers); Vec3F a1 = rotMat.XAxis; Vec3F a2 = rotMat.YAxis; Vec3F a3 = rotMat.ZAxis; // calculate destination basis vectors Vec3F b1, b2, b3; if (upAxis == AxisSystemType.YIsUp) { // a2 is the current up vector. b2 is the final up vector. // now, find either a1 or a3, whichever is most orthogonal to surface b2 = new Vec3F(surfaceNormal); float a1DotS = Vec3F.Dot(a1, surfaceNormal); float a3DotS = Vec3F.Dot(a3, surfaceNormal); if (Math.Abs(a1DotS) < Math.Abs(a3DotS)) { b1 = new Vec3F(a1); b3 = Vec3F.Cross(b1, b2); b1 = Vec3F.Cross(b2, b3); } else { b3 = new Vec3F(a3); b1 = Vec3F.Cross(b2, b3); b3 = Vec3F.Cross(b1, b2); } } else { // a3 is the current up vector. b3 is the final up vector. // now, find either a1 or a2, whichever is most orthogonal to surface b3 = new Vec3F(surfaceNormal); float a1DotS = Vec3F.Dot(a1, surfaceNormal); float a2DotS = Vec3F.Dot(a2, surfaceNormal); if (Math.Abs(a1DotS) < Math.Abs(a2DotS)) { b1 = new Vec3F(a1); b2 = Vec3F.Cross(b3, b1); b1 = Vec3F.Cross(b2, b3); } else { b2 = new Vec3F(a2); b1 = Vec3F.Cross(b2, b3); b2 = Vec3F.Cross(b3, b1); } } // in theory, this isn't necessary, but in practice... b1.Normalize(); b2.Normalize(); b3.Normalize(); // construct new rotation matrix and extract euler angles rotMat.XAxis = b1; rotMat.YAxis = b2; rotMat.ZAxis = b3; Vec3F newEulers = new Vec3F(); rotMat.GetEulerAngles(out newEulers.X, out newEulers.Y, out newEulers.Z); return(newEulers); }