private bool CanManipulate(ITransformable node) { bool result = node != null && (node.TransformationType & TransformationTypes.Pivot) != 0 && node.Cast <IVisible>().Visible && node.Cast <ILockable>().IsLocked == false; return(result); }
private bool CanManipulate(ITransformable node, FilterDelegate filter) { bool result = node != null && node.Cast <IVisible>().Visible && node.Cast <ILockable>().IsLocked == false && filter(node); return(result); }
/// <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"); } }
protected override Matrix4F GetManipulatorMatrix() { ITransformable node = GetManipulatorNode(TransformationTypes.Translation); if (node == null) { return(null); } ISnapSettings snapSettings = (ISnapSettings)DesignView; Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F localToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F toworld = new Matrix4F(); if (snapSettings.ManipulateLocalAxis) { toworld.Set(localToWorld); toworld.Normalize(toworld); } else { toworld.Translation = localToWorld.Translation; } Vec3F offset = TransformUtils.CalcSnapFromOffset(node, snapSettings.SnapFrom); // Offset by pivot Matrix4F P = new Matrix4F(); P.Translation = offset; toworld.Mul(toworld, P); return(toworld); }
protected override Matrix4F GetManipulatorMatrix() { ITransformable node = GetManipulatorNode(TransformationTypes.Scale); if (node == null) { return(null); } Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F localToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); // local transform Matrix4F toworld = new Matrix4F(localToWorld); // Offset by pivot Matrix4F P = new Matrix4F(); P.Translation = node.Pivot; toworld.Mul(P, toworld); // Normalize toworld.Normalize(toworld); return(toworld); }
public static Vec3F CalcSnapFromOffset(ITransformable node, SnapFromMode snapFrom) { // AABB in local space. AABB box = node.As <IBoundable>().LocalBoundingBox; Vec3F offsetLocal = box.Center; switch (snapFrom) { case SnapFromMode.Pivot: offsetLocal = node.Pivot; break; case SnapFromMode.Origin: offsetLocal = box.Center; break; case SnapFromMode.TopCenter: offsetLocal.Y = box.Max.Y; break; case SnapFromMode.BottomCenter: offsetLocal.Y = box.Min.Y; break; case SnapFromMode.FrontCenter: offsetLocal.Z = box.Max.Z; break; case SnapFromMode.BackCenter: offsetLocal.Z = box.Min.Z; break; case SnapFromMode.LeftCenter: offsetLocal.X = box.Min.X; break; case SnapFromMode.RightCenter: offsetLocal.X = box.Max.X; break; default: throw new ArgumentOutOfRangeException("Invalid snap-from node"); } Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F toWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); Vec3F offset; toWorld.TransformVector(offsetLocal, out offset); //local-to-world return(offset); //world }
/// <summary> /// Adjusts child transform, making it the concatenation with its parent's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void RemoveChild(ITransformable parent, ITransformable child) { Path <DomNode> path = new Path <DomNode>(parent.Cast <DomNode>().GetPath()); Matrix4F parentMatrix = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F childMatrix = child.Transform; Matrix4F newChildMatrix = Matrix4F.Multiply(childMatrix, parentMatrix); Vec3F newTranslation = child.Translation; parentMatrix.Transform(ref newTranslation); Vec3F newRotation = new Vec3F(); newChildMatrix.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; Vec3F newScale = newChildMatrix.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newChildMatrix due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newChildMatrix into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newChildMatrix.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }
protected override Matrix4F GetManipulatorMatrix() { ITransformable node = GetManipulatorNode(TransformationTypes.Pivot); if (node == null) { return(null); } Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F localToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); // Offset by pivot Matrix4F Pv = new Matrix4F(); Pv.Set(node.Pivot); localToWorld.Mul(Pv, localToWorld); localToWorld.OrthoNormalize(localToWorld); return(new Matrix4F(localToWorld.Translation)); }
protected override Matrix4F GetManipulatorMatrix() { ITransformable node = GetManipulatorNode(TransformationTypes.Translation); if (node == null) { return(null); } ISnapSettings snapSettings = (ISnapSettings)DesignView; Path <DomNode> path = new Path <DomNode>(node.Cast <DomNode>().GetPath()); Matrix4F localToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F toworld = new Matrix4F(); if (snapSettings.ManipulateLocalAxis) { toworld.Set(localToWorld); toworld.Normalize(toworld); } else { toworld.Translation = localToWorld.Translation; } // DavidJ -- note -- this "pivot" behaviour was inherited from another manipulator // but appears to be broken! Check coordinate space of value returned from CalcSnapFromOffset // Vec3F offset = TransformUtils.CalcSnapFromOffset(node, snapSettings.SnapFrom); // // // Offset by pivot // Matrix4F P = new Matrix4F(); // P.Translation = offset; // toworld.Mul(toworld,P); return(toworld); }
public static Vec3F CalcSnapFromOffset(ITransformable node, SnapFromMode snapFrom) { // AABB in local space. AABB box = node.As<IBoundable>().LocalBoundingBox; Vec3F offsetLocal = box.Center; switch (snapFrom) { case SnapFromMode.Pivot: offsetLocal = node.Pivot; break; case SnapFromMode.Origin: offsetLocal = box.Center; break; case SnapFromMode.TopCenter: offsetLocal.Y = box.Max.Y; break; case SnapFromMode.BottomCenter: offsetLocal.Y = box.Min.Y; break; case SnapFromMode.FrontCenter: offsetLocal.Z = box.Max.Z; break; case SnapFromMode.BackCenter: offsetLocal.Z = box.Min.Z; break; case SnapFromMode.LeftCenter: offsetLocal.X = box.Min.X; break; case SnapFromMode.RightCenter: offsetLocal.X = box.Max.X; break; default: throw new ArgumentOutOfRangeException("Invalid snap-from node"); } Path<DomNode> path = new Path<DomNode>(node.Cast<DomNode>().GetPath()); Matrix4F toWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); Vec3F offset; toWorld.TransformVector(offsetLocal, out offset); //local-to-world return offset; //world }
/// <summary> /// Adjusts child transform, making it the concatenation with its parent's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void RemoveChild(ITransformable parent, ITransformable child) { Path<DomNode> path = new Path<DomNode>(parent.Cast<DomNode>().GetPath()); Matrix4F parentMatrix = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F childMatrix = child.Transform; Matrix4F newChildMatrix = Matrix4F.Multiply(childMatrix, parentMatrix); Vec3F newTranslation = child.Translation; parentMatrix.Transform(ref newTranslation); Vec3F newRotation = new Vec3F(); newChildMatrix.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; Vec3F newScale = newChildMatrix.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newChildMatrix due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newChildMatrix into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newChildMatrix.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }
/// <summary> /// Adjusts child transform, making it relative to new parent node's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void AddChild(ITransformable parent, ITransformable child) { Path <DomNode> path = new Path <DomNode>(parent.Cast <DomNode>().GetPath()); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); // We want 'child' to appear in the same place in the world after adding to 'parent'. // local-point * original-local-to-world = world-point // new-local-point * new-local-to-parent * parent-to-world = world-point // ==> new-local-to-parent * parent-to-world = original-local-to-world // (multiply both sides by inverse of parent-to-world; call it world-to-parent) // ==> new-local-to-parent = original-local-to-world * world-to-parent Matrix4F worldToParent = new Matrix4F(); worldToParent.Invert(parentToWorld); Matrix4F originalLocalToWorld = child.Transform; Matrix4F newLocalToParent = Matrix4F.Multiply(originalLocalToWorld, worldToParent); // The translation component of newLocalToParent consists of pivot translation // as well as the child.Translation. So, start with the original child.Translation // and transform it into our new space. Vec3F newTranslation = child.Translation; worldToParent.Transform(ref newTranslation); // There's only one way of getting rotation info, so get it straight from matrix. Vec3F newRotation = new Vec3F(); newLocalToParent.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; // Likewise with scale. Vec3F newScale = newLocalToParent.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newLocalToParent due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newLocalToParent into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newLocalToParent.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. Storing the fix in RotatePivotTranslation // is done elsewhere, as well. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }
/// <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"); } }
private bool CanManipulate(ITransformable node) { bool result = node != null && (node.TransformationType & TransformationTypes.Pivot) != 0 && node.Cast<IVisible>().Visible && node.Cast<ILockable>().IsLocked == false; return result; }
private void RenderProperties(IEnumerable <object> objects, bool renderCaption, bool renderBound, bool renderPivot) { bool renderAny = renderCaption || renderBound || renderPivot; if (renderAny == false) { return; } Util3D.RenderFlag = BasicRendererFlags.WireFrame; Matrix4F vp = Camera.ViewMatrix * Camera.ProjectionMatrix; foreach (object obj in objects) { IBoundable bnode = obj.As <IBoundable>(); if (bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } INameable nnode = obj.As <INameable>(); ITransformable trans = obj.As <ITransformable>(); if (renderBound) { Util3D.DrawAABB(bnode.BoundingBox); } if (renderCaption && nnode != null) { Vec3F topCenter = bnode.BoundingBox.Center; topCenter.Y = bnode.BoundingBox.Max.Y; Point pt = Project(vp, topCenter); GameEngine.DrawText2D(nnode.Name, Util3D.CaptionFont, pt.X, pt.Y, Color.White); } } if (renderPivot) { Util3D.RenderFlag = BasicRendererFlags.WireFrame | BasicRendererFlags.DisableDepthTest; // create few temp matrics to Matrix4F toWorld = new Matrix4F(); Matrix4F PV = new Matrix4F(); Matrix4F sc = new Matrix4F(); Matrix4F bl = new Matrix4F(); Matrix4F recXform = new Matrix4F(); foreach (object obj in objects) { ITransformable trans = obj.As <ITransformable>(); IBoundable bnode = obj.As <IBoundable>(); if (trans == null || bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } Path <DomNode> path = new Path <DomNode>(trans.Cast <DomNode>().GetPath()); toWorld.Set(Vec3F.ZeroVector); TransformUtils.CalcPathTransform(toWorld, path, path.Count - 1); // Offset by pivot PV.Set(trans.Pivot); toWorld.Mul(PV, toWorld); Vec3F pos = toWorld.Translation; float s; Util.CalcAxisLengths(Camera, pos, out s); s /= 12.0f; sc.Scale(s); Util.CreateBillboard(bl, pos, Camera.WorldEye, Camera.Up, Camera.LookAt); Matrix4F.Multiply(sc, bl, recXform); Util3D.DrawPivot(recXform, Color.Yellow); } } }
private bool CanManipulate(ITransformable node, FilterDelegate filter) { bool result = node != null && node.Cast<IVisible>().Visible && node.Cast<ILockable>().IsLocked == false && filter(node); return result; }
private void RenderProperties(GUILayer.SimpleRenderingContext context, IEnumerable <object> objects, bool renderCaption, bool renderBound, bool renderPivot) { if (renderCaption || renderBound) { Util3D.SetRenderFlag(context, BasicRendererFlags.WireFrame); Matrix4F vp = Camera.ViewMatrix * Camera.ProjectionMatrix; foreach (object obj in objects) { IBoundable bnode = obj.As <IBoundable>(); if (bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } INameable nnode = obj.As <INameable>(); ITransformable trans = obj.As <ITransformable>(); if (renderBound) { Util3D.DrawAABB(context, bnode.BoundingBox); } if (renderCaption && nnode != null) { Vec3F topCenter = bnode.BoundingBox.Center; topCenter.Y = bnode.BoundingBox.Max.Y; Point pt = Project(vp, topCenter); GameEngine.DrawText2D(nnode.Name, Util3D.CaptionFont, pt.X, pt.Y, Color.White); } } } if (renderPivot) { Util3D.SetRenderFlag(context, BasicRendererFlags.WireFrame | BasicRendererFlags.DisableDepthTest); // create few temp matrics to Matrix4F toWorld = new Matrix4F(); Matrix4F PV = new Matrix4F(); Matrix4F sc = new Matrix4F(); Matrix4F bl = new Matrix4F(); Matrix4F recXform = new Matrix4F(); foreach (object obj in objects) { ITransformable trans = obj.As <ITransformable>(); IBoundable bnode = obj.As <IBoundable>(); if (trans == null || bnode == null || bnode.BoundingBox.IsEmpty || obj.Is <IGameObjectFolder>()) { continue; } Path <DomNode> path = new Path <DomNode>(trans.Cast <DomNode>().GetPath()); toWorld.Set(Vec3F.ZeroVector); TransformUtils.CalcPathTransform(toWorld, path, path.Count - 1); // Offset by pivot PV.Set(trans.Pivot); toWorld.Mul(PV, toWorld); Vec3F pos = toWorld.Translation; const float pivotDiameter = 16; // in pixels float s = Util.CalcAxisScale(Camera, pos, pivotDiameter, Height); sc.Scale(s); Util.CreateBillboard(bl, pos, Camera.WorldEye, Camera.Up, Camera.LookAt); recXform = sc * bl; Util3D.DrawPivot(context, recXform, Color.Yellow); } } }
/// <summary> /// Adjusts child transform, making it relative to new parent node's transform. /// Is recursive, looking for parents that also implement IRenderableNode.</summary> /// <param name="parent">Parent node</param> /// <param name="child">Child node</param> public static void AddChild(ITransformable parent, ITransformable child) { Path<DomNode> path = new Path<DomNode>(parent.Cast<DomNode>().GetPath()); Matrix4F parentToWorld = TransformUtils.CalcPathTransform(path, path.Count - 1); // We want 'child' to appear in the same place in the world after adding to 'parent'. // local-point * original-local-to-world = world-point // new-local-point * new-local-to-parent * parent-to-world = world-point // ==> new-local-to-parent * parent-to-world = original-local-to-world // (multiply both sides by inverse of parent-to-world; call it world-to-parent) // ==> new-local-to-parent = original-local-to-world * world-to-parent Matrix4F worldToParent = new Matrix4F(); worldToParent.Invert(parentToWorld); Matrix4F originalLocalToWorld = child.Transform; Matrix4F newLocalToParent = Matrix4F.Multiply(originalLocalToWorld, worldToParent); // The translation component of newLocalToParent consists of pivot translation // as well as the child.Translation. So, start with the original child.Translation // and transform it into our new space. Vec3F newTranslation = child.Translation; worldToParent.Transform(ref newTranslation); // There's only one way of getting rotation info, so get it straight from matrix. Vec3F newRotation = new Vec3F(); newLocalToParent.GetEulerAngles(out newRotation.X, out newRotation.Y, out newRotation.Z); child.Rotation = newRotation; // Likewise with scale. Vec3F newScale = newLocalToParent.GetScale(); child.Scale = newScale; // We can compose together all of the separate transformations now. Matrix4F newTransform = CalcTransform( newTranslation, newRotation, newScale, child.ScalePivot, child.ScalePivotTranslation, child.RotatePivot, child.RotatePivotTranslation); // However, the composed matrix may not equal newLocalToParent due to rotating // or scaling around a pivot. In the general case, it may be impossible to // decompose newLocalToParent into all of these separate components. For example, // a sheer transformation cannot be reproduced by a single rotation and scale. // But for common cases, only the translation is out-of-sync now, so apply a fix. Vec3F desiredTranslation = newLocalToParent.Translation; Vec3F currentTranslation = newTransform.Translation; Vec3F fixupTranslation = desiredTranslation - currentTranslation; Matrix4F fixupTransform = new Matrix4F(fixupTranslation); newTransform.Mul(newTransform, fixupTransform); // Save the fix and the final transform. Storing the fix in RotatePivotTranslation // is done elsewhere, as well. child.Translation = newTranslation + fixupTranslation; child.Transform = newTransform; }