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> /// 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"); } }
public override void OnDragging(ViewControl vc, Point scrPt) { if (m_hitRegion == HitRegion.None || m_activeOp == null || m_activeOp.NodeList.Count == 0) { return; } // create ray in view space. Ray3F rayV = vc.GetWorldRay(scrPt); using (var intersectionScene = GameEngine.GetEditorSceneManager().GetIntersectionScene()) { Vec3F intersectionPt; if (!CalculateTerrainIntersection(vc, rayV, intersectionScene, out intersectionPt)) { return; } if (m_pendingStartPt) { m_startPt = intersectionPt; m_pendingStartPt = false; } else { bool clampToSurface = Control.ModifierKeys == Keys.Shift; Vec3F translate = new Vec3F(intersectionPt.X - m_startPt.X, intersectionPt.Y - m_startPt.Y, 0.0f); for (int i = 0; i < m_activeOp.NodeList.Count; i++) { ITransformable node = m_activeOp.NodeList[i]; Path <DomNode> path = new Path <DomNode>(Adapters.Cast <DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); Vec3F newWorldPos = m_originalTranslations[i] + translate; float terrainHeight = 0.0f; if (GUILayer.EditorInterfaceUtils.GetTerrainHeight( out terrainHeight, intersectionScene, newWorldPos.X, newWorldPos.Y)) { newWorldPos.Z = terrainHeight + (clampToSurface ? 0.0f : m_originalHeights[i]); Vec3F localTranslation; parentWorldToLocal.TransformVector(newWorldPos, out localTranslation); node.Translation = localTranslation; } } } } }
public override void OnDragging(ViewControl vc, Point scrPt) { if (m_cancelDrag || m_hitRegion == HitRegion.None || NodeList.Count == 0) { return; } bool hitAxis = m_hitRegion == HitRegion.XAxis || m_hitRegion == HitRegion.YAxis || m_hitRegion == HitRegion.ZAxis; Matrix4F view = vc.Camera.ViewMatrix; Matrix4F proj = vc.Camera.ProjectionMatrix; Matrix4F vp = view * proj; // create ray in world space. Ray3F rayW = vc.GetRay(scrPt, vp); // create ray in view space. Ray3F rayV = vc.GetRay(scrPt, proj); Vec3F translate = m_translatorControl.OnDragging(rayV); ISnapSettings snapSettings = (ISnapSettings)DesignView; bool snapToGeom = Control.ModifierKeys == m_snapGeometryKey; if (snapToGeom) { Vec3F manipPos = HitMatrix.Translation; Vec3F manipMove; if (hitAxis) { //Make rayw to point toward moving axis and starting // from manipulator’s world position. rayW.Direction = Vec3F.Normalize(translate); rayW.Origin = manipPos; manipMove = Vec3F.ZeroVector; m_cancelDrag = true; //stop further snap-to's } else { manipMove = rayW.ProjectPoint(manipPos) - manipPos; } for (int i = 0; i < NodeList.Count; i++) { ITransformable node = NodeList[i]; Vec3F snapOffset = TransformUtils.CalcSnapFromOffset(node, snapSettings.SnapFrom); Path <DomNode> path = new Path <DomNode>(Adapters.Cast <DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Vec3F orgPosW; parentLocalToWorld.Transform(m_originalValues[i], out orgPosW); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); rayW.MoveToIncludePoint(orgPosW + snapOffset + manipMove); HitRecord[] hits = GameEngine.RayPick(view, proj, rayW, true); bool cansnap = false; HitRecord target = new HitRecord(); if (hits.Length > 0) { // find hit record. foreach (var hit in hits) { if (m_snapFilter.CanSnapTo(node, GameEngine.GetAdapterFromId(hit.instanceId))) { target = hit; cansnap = true; break; } } } if (cansnap) { Vec3F pos; if (target.hasNearestVert && snapSettings.SnapVertex) { pos = target.nearestVertex; } else { pos = target.hitPt; } pos -= snapOffset; parentWorldToLocal.Transform(ref pos); Vec3F diff = pos - node.Transform.Translation; node.Translation += diff; bool rotateOnSnap = snapSettings.RotateOnSnap && target.hasNormal && (node.TransformationType & TransformationTypes.Rotation) != 0; if (rotateOnSnap) { Vec3F localSurfaceNormal; parentWorldToLocal.TransformNormal(target.normal, out localSurfaceNormal); node.Rotation = TransformUtils.RotateToVector( m_originalRotations[i], localSurfaceNormal, AxisSystemType.YIsUp); } } } } else { IGrid grid = DesignView.Context.Cast <IGame>().Grid; bool snapToGrid = Control.ModifierKeys == m_snapGridKey && grid.Visible && vc.Camera.ViewType == ViewTypes.Perspective; float gridHeight = grid.Height; // translate. for (int i = 0; i < NodeList.Count; i++) { ITransformable node = NodeList[i]; Path <DomNode> path = new Path <DomNode>(Adapters.Cast <DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); Vec3F localTranslation; parentWorldToLocal.TransformVector(translate, out localTranslation); Vec3F trans = m_originalValues[i] + localTranslation; if (snapToGrid) { if (grid.Snap) { trans = grid.SnapPoint(trans); } else { trans.Y = gridHeight; } } node.Translation = trans; } } }
/// <summary> /// Transforms the ray by the given matrix. The Direction member will be normalized. There are /// no particular restrictions on M. Any transformation done to a point in world space or /// object space can be done on this ray, including any combination of rotations, translations, /// uniform scales, non-uniform scales, etc.</summary> /// <param name="M">Transformation matrix</param> public void Transform(Matrix4F M) { M.Transform(Origin, out Origin); M.TransformVector(Direction, out Direction); Direction.Normalize(); }
public override void OnDragging(ViewControl vc, Point scrPt) { if (m_hitRegion == HitRegion.None || m_activeOp == null || m_activeOp.NodeList.Count == 0) return; // create ray in view space. Ray3F rayV = vc.GetWorldRay(scrPt); using (var intersectionScene = GameEngine.GetEditorSceneManager().GetIntersectionScene()) { Vec3F intersectionPt; if (!CalculateTerrainIntersection(vc, rayV, intersectionScene, out intersectionPt)) return; if (m_pendingStartPt) { m_startPt = intersectionPt; m_pendingStartPt = false; } else { bool clampToSurface = Control.ModifierKeys == Keys.Shift; Vec3F translate = new Vec3F(intersectionPt.X - m_startPt.X, intersectionPt.Y - m_startPt.Y, 0.0f); for (int i = 0; i < m_activeOp.NodeList.Count; i++) { ITransformable node = m_activeOp.NodeList[i]; Path<DomNode> path = new Path<DomNode>(Adapters.Cast<DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); Vec3F newWorldPos = m_originalTranslations[i] + translate; float terrainHeight = 0.0f; if (GUILayer.EditorInterfaceUtils.GetTerrainHeight( out terrainHeight, intersectionScene, newWorldPos.X, newWorldPos.Y)) { newWorldPos.Z = terrainHeight + (clampToSurface ? 0.0f : m_originalHeights[i]); Vec3F localTranslation; parentWorldToLocal.TransformVector(newWorldPos, out localTranslation); node.Translation = localTranslation; } } } } }
public static void CreateTorus(float innerRadius, float outerRadius, uint rings, uint sides, List <Vec3F> pos, List <Vec3F> nor, List <Vec2F> tex, List <uint> indices) { uint ringStride = rings + 1; uint sideStride = sides + 1; // radiusC: distance to center of the ring float radiusC = (innerRadius + outerRadius) * 0.5f; //radiusR: the radius of the ring float radiusR = (outerRadius - radiusC); for (uint i = 0; i <= rings; i++) { float u = (float)i / rings; float outerAngle = i * MathHelper.TwoPi / rings; // xform from ring space to torus space. Matrix4F trans = new Matrix4F(); trans.Translation = new Vec3F(radiusC, 0, 0); Matrix4F roty = new Matrix4F(); roty.RotY(outerAngle); Matrix4F transform = trans * roty; // create vertices for each ring. for (uint j = 0; j <= sides; j++) { float v = (float)j / sides; float innerAngle = j * MathHelper.TwoPi / sides + MathHelper.Pi; float dx = (float)Math.Cos(innerAngle); float dy = (float)Math.Sin(innerAngle); // normal, position ,and texture coordinates Vec3F n = new Vec3F(dx, dy, 0); Vec3F p = n * radiusR; if (tex != null) { Vec2F t = new Vec2F(u, v); tex.Add(t); } transform.Transform(ref p); transform.TransformVector(n, out n); pos.Add(p); nor.Add(n); // And create indices for two triangles. uint nextI = (i + 1) % ringStride; uint nextJ = (j + 1) % sideStride; indices.Add(nextI * sideStride + j); indices.Add(i * sideStride + nextJ); indices.Add(i * sideStride + j); indices.Add(nextI * sideStride + j); indices.Add(nextI * sideStride + nextJ); indices.Add(i * sideStride + nextJ); } } }
public override void OnDragging(ViewControl vc, Point scrPt) { if (m_hitRegion == HitRegion.None || m_activeOp == null || m_activeOp.NodeList.Count == 0) { return; } // create ray in view space. Ray3F rayV = vc.GetWorldRay(scrPt); using (var intersectionScene = GameEngine.GetEditorSceneManager().GetIntersectionScene()) { PickResult intersectionPt; if (!CalculateTerrainIntersection(vc, rayV, intersectionScene, out intersectionPt)) { return; } if (m_pendingStartPt) { m_startPt = intersectionPt._pt.Value; m_pendingStartPt = false; } else { ISnapSettings snapSettings = (ISnapSettings)DesignView; bool clampToSurface = Control.ModifierKeys == Keys.Shift; var pt = intersectionPt._pt.Value; Vec3F translate = new Vec3F(pt.X - m_startPt.X, pt.Y - m_startPt.Y, 0.0f); for (int i = 0; i < m_activeOp.NodeList.Count; i++) { ITransformable node = m_activeOp.NodeList[i]; Path <DomNode> path = new Path <DomNode>(Adapters.Cast <DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); Vec3F newWorldPos = m_originalTranslations[i] + translate; float terrainHeight = 0.0f; GUILayer.Vector3 terrainNormal; if (GUILayer.EditorInterfaceUtils.GetTerrainHeightAndNormal( out terrainHeight, out terrainNormal, intersectionScene, newWorldPos.X, newWorldPos.Y)) { newWorldPos.Z = terrainHeight + (clampToSurface ? 0.0f : m_originalHeights[i]); Vec3F localTranslation; parentWorldToLocal.TransformVector(newWorldPos, out localTranslation); node.Translation = localTranslation; // There are two ways to orient the "up" vector of the object. // One way is to decompose the 3x3 rotation matrix into a up vector + a rotation around // that vector. Then we retain the rotation, and just move the up vector. // Another way is to take the 3x3 matrix, and adjust it's up vector. Then just perform // the Gram–Schmidt algorithm to re-orthogonalize it. // We'll use the code from the SCEE level editor. This isn't the ideal math (there's a // lot of room for floating point creep) but it should work. var currentUp = node.Transform.ZAxis; if (snapSettings.TerrainAlignment == TerrainAlignmentMode.TerrainUp) { node.Rotation = TransformUtils.RotateToVector( node.Rotation, new Vec3F(terrainNormal.X, terrainNormal.Y, terrainNormal.Z), AxisSystemType.ZIsUp); } else if (snapSettings.TerrainAlignment == TerrainAlignmentMode.WorldUp) { // Prevent the change if the normal is already very close to straight up if (Math.Abs(currentUp.X - 0.0f) > 1e-4f || Math.Abs(currentUp.Y - 0.0f) > 1e-4f || Math.Abs(currentUp.Z - 1.0f) > 1e-4f) { node.Rotation = TransformUtils.RotateToVector( node.Rotation, new Vec3F(0.0f, 0.0f, 1.0f), AxisSystemType.ZIsUp); } } node.UpdateTransform(); } } } } }
public override void OnDragging(ViewControl vc, Point scrPt) { if (m_cancelDrag || m_hitRegion == HitRegion.None || NodeList.Count == 0) return; bool hitAxis = m_hitRegion == HitRegion.XAxis || m_hitRegion == HitRegion.YAxis || m_hitRegion == HitRegion.ZAxis; Matrix4F view = vc.Camera.ViewMatrix; Matrix4F proj = vc.Camera.ProjectionMatrix; Matrix4F vp = view * proj; // create ray in world space. Ray3F rayW = vc.GetRay(scrPt, vp); // create ray in view space. Ray3F rayV = vc.GetRay(scrPt, proj); Vec3F translate = m_translatorControl.OnDragging(rayV); ISnapSettings snapSettings = (ISnapSettings)DesignView; bool snapToGeom = Control.ModifierKeys == m_snapGeometryKey; if (snapToGeom) { Vec3F manipPos = HitMatrix.Translation; Vec3F manipMove; if (hitAxis) { //Make rayw to point toward moving axis and starting // from manipulator’s world position. rayW.Direction = Vec3F.Normalize(translate); rayW.Origin = manipPos; manipMove = Vec3F.ZeroVector; m_cancelDrag = true; //stop further snap-to's } else { manipMove = rayW.ProjectPoint(manipPos) - manipPos; } for (int i = 0; i < NodeList.Count; i++) { ITransformable node = NodeList[i]; Vec3F snapOffset = TransformUtils.CalcSnapFromOffset(node, snapSettings.SnapFrom); Path<DomNode> path = new Path<DomNode>(Adapters.Cast<DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Vec3F orgPosW; parentLocalToWorld.Transform(m_originalValues[i], out orgPosW); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); rayW.MoveToIncludePoint(orgPosW + snapOffset + manipMove); HitRecord[] hits = GameEngine.RayPick(view, proj, rayW, true); bool cansnap = false; HitRecord target = new HitRecord(); if (hits.Length > 0) { // find hit record. foreach (var hit in hits) { if (m_snapFilter.CanSnapTo(node, GameEngine.GetAdapterFromId(hit.instanceId))) { target = hit; cansnap = true; break; } } } if (cansnap) { Vec3F pos; if (target.hasNearestVert && snapSettings.SnapVertex) { pos = target.nearestVertex; } else { pos = target.hitPt; } pos -= snapOffset; parentWorldToLocal.Transform(ref pos); Vec3F diff = pos - node.Transform.Translation; node.Translation += diff; bool rotateOnSnap = snapSettings.RotateOnSnap && target.hasNormal && (node.TransformationType & TransformationTypes.Rotation) != 0; if (rotateOnSnap) { Vec3F localSurfaceNormal; parentWorldToLocal.TransformNormal(target.normal, out localSurfaceNormal); node.Rotation = TransformUtils.RotateToVector( m_originalRotations[i], localSurfaceNormal, AxisSystemType.YIsUp); } } } } else { IGrid grid = DesignView.Context.Cast<IGame>().Grid; bool snapToGrid = Control.ModifierKeys == m_snapGridKey && grid.Visible && vc.Camera.ViewType == ViewTypes.Perspective; float gridHeight = grid.Height; // translate. for (int i = 0; i < NodeList.Count; i++) { ITransformable node = NodeList[i]; Path<DomNode> path = new Path<DomNode>(Adapters.Cast<DomNode>(node).GetPath()); Matrix4F parentLocalToWorld = TransformUtils.CalcPathTransform(path, path.Count - 2); Matrix4F parentWorldToLocal = new Matrix4F(); parentWorldToLocal.Invert(parentLocalToWorld); Vec3F localTranslation; parentWorldToLocal.TransformVector(translate, out localTranslation); Vec3F trans = m_originalValues[i] + localTranslation; if(snapToGrid) { if(grid.Snap) trans = grid.SnapPoint(trans); else trans.Y = gridHeight; } node.Translation = trans; } } }