/// <summary> /// Synchronizes the camera to the controller's current state</summary> /// <param name="camera">Camera</param> protected override void ControllerToCamera(Camera camera) { Vec3F lookAt = Camera.LookAt; Vec3F up = Camera.Up; if (camera.ViewType == ViewTypes.Perspective) { QuatF rotation = m_rotation * m_currentRotation; rotation = rotation.Inverse; Matrix4F transform = new Matrix4F(rotation); lookAt = new Vec3F(0, 0, -1); up = new Vec3F(0, 1, 0); transform.Transform(ref lookAt); transform.Transform(ref up); } float eyeOffset = m_distanceFromLookAt; float lookAtOffset = 0; if (m_distanceFromLookAt < m_dollyThreshold) // do we need to start dollying? { eyeOffset = m_distanceFromLookAt; lookAtOffset = m_distanceFromLookAt - m_dollyThreshold; } Camera.Set( m_lookAtPoint - (eyeOffset * lookAt), // eye m_lookAtPoint - (lookAtOffset * lookAt), // lookAt up); // up base.ControllerToCamera(camera); }
/// <summary> /// Transform the vectors in the given array. /// </summary> /// <param name="vectors">An array of vectors to transform.</param> /// <param name="transformation">The transformation.</param> /// <remarks> /// This method changes the vector values in the <paramref name="vectors"/> array. /// </remarks> public static void TransformArray(Vector3FArrayList vectors, Matrix4F transformation) { for (int i = 0; i < vectors.Count; i++) { vectors[i] = Matrix4F.Transform(transformation, vectors[i]); } }
/// <summary> /// Transform the vectors in the given array and put the result in another array. /// </summary> /// <param name="vectors">An array of vectors to transform.</param> /// <param name="transformation">The transformation.</param> /// <param name="result">An array of vectors to put the transformation results in (should be empty).</param> public static void TransformArray(Vector3FArrayList vectors, Matrix4F transformation, Vector3FArrayList result) { for (int i = 0; i < vectors.Count; i++) { result.Add(Matrix4F.Transform(transformation, vectors[i])); } }
/// <summary> /// Sorts the provided list by camera space z distance. /// Assumes a right-handed coordinate system, so the farthest nodes have the most negative values.</summary> /// <param name="list">List of alpha TraverseNodes to sort</param> /// <param name="viewMatrix">Current view matrix to transform bounding box centroids by</param> public static void SortByCameraSpaceDepth(List <TraverseNode> list, Matrix4F viewMatrix) { KeyValuePair <float, TraverseNode>[] camSpaceDistances = new KeyValuePair <float, TraverseNode> [list.Count]; for (int i = 0; i < list.Count; ++i) { TraverseNode node = list[i]; Vec3F worldSpaceCentroid = node.WorldSpaceBoundingBox.Centroid; Vec3F centerPointInCameraSpace; viewMatrix.Transform(worldSpaceCentroid, out centerPointInCameraSpace); camSpaceDistances[i] = new KeyValuePair <float, TraverseNode>(centerPointInCameraSpace.Z, node); } Array.Sort(camSpaceDistances, new CamSpaceDistanceComparer()); list.Clear(); foreach (KeyValuePair <float, TraverseNode> entry in camSpaceDistances) { list.Add(entry.Value); } }
/// <summary> /// Compute new transform using /// position of all the control points</summary> internal void ComputeTranslation() { if (m_computingTranslation) { return; } try { m_computingTranslation = true; var points = ControlPoints; if (points.Count == 0) { return; } // center in local space. Vec3F localcenter = points[0].Translation; ITransformable xformcurve = this.As <ITransformable>(); Matrix4F localToParent = xformcurve.Transform; // center in parent space. Vec3F center; localToParent.Transform(localcenter, out center); xformcurve.Translation = center; foreach (var cpt in points) { cpt.Translation -= localcenter; } } finally { m_computingTranslation = false; } }
/// <summary> /// Projects the specified x and y, in normalized window coordinates, onto the grid, /// and snaps it to the nearest grid vertex if necessary. /// Normalized window coordinates are in the range [-0.5,0.5] with +x pointing to the /// right and +y pointing up.</summary> /// <param name="x">Window x in normalized window coords</param> /// <param name="y">Window y in normalized window coords</param> /// <param name="camera">Camera</param> /// <returns>Projection of x and y onto the grid, in world space.</returns> public Vec3F Project(float x, float y, Camera camera) { Ray3F ray = camera.CreateRay(x, y); Matrix4F V = new Matrix4F(camera.ViewMatrix); V.Mul(m_invAxisSystem, V); if (camera.Frustum.IsOrtho) { V = new Matrix4F(m_V); V.Translation = camera.ViewMatrix.Translation; } // origin Vec3F delta = new Vec3F(0, Height, 0); V.Transform(delta, out delta); Vec3F o = delta; // Up vec Vec3F axis = V.YAxis; Vec3F projPt = ray.IntersectPlane(axis, -Vec3F.Dot(o, axis)); // Transform back into world space Matrix4F Inv = new Matrix4F(); Inv.Invert(camera.ViewMatrix); Inv.Transform(projPt, out projPt); if (Snap) { projPt = SnapPoint(projPt); } return projPt; }
private DomNode CreatePrototype(IEnumerable <IGameObject> gobs) { DomNode[] originals = new DomNode[1]; List <IGameObject> copyList = new List <IGameObject>(); AABB bound = new AABB(); foreach (IGameObject gameObject in SelectedGobs) { IBoundable boundable = gameObject.As <IBoundable>(); bound.Extend(boundable.BoundingBox); Matrix4F world = TransformUtils.ComputeWorldTransform(gameObject); originals[0] = gameObject.As <DomNode>(); DomNode[] copies = DomNode.Copy(originals); IGameObject copy = copies[0].As <IGameObject>(); TransformUtils.SetTransform(copy, world); copyList.Add(copy); } DomNode gobchild = null; if (copyList.Count > 1) {// create group IGame game = m_contextRegistry.GetActiveContext <IGame>(); IGameObjectGroup gobgroup = game.CreateGameObjectGroup(); gobgroup.Translation = bound.Center; gobgroup.UpdateTransform(); Matrix4F worldInv = new Matrix4F(); worldInv.Invert(gobgroup.Transform); foreach (IGameObject gob in copyList) { Vec3F translate = gob.Translation; worldInv.Transform(ref translate); gob.Translation = translate; gob.UpdateTransform(); gobgroup.GameObjects.Add(gob); } gobchild = gobgroup.As <DomNode>(); } else { gobchild = copyList[0].As <DomNode>(); } gobchild.InitializeExtensions(); gobchild.As <IGameObject>().Translation = new Vec3F(0, 0, 0); DomNode prototype = null; if (gobchild != null) { prototype = new DomNode(Schema.prototypeType.Type, Schema.prototypeRootElement); prototype.SetChild(Schema.prototypeType.gameObjectChild, gobchild); } return(prototype); }
/// <summary> /// project the v from 3d space to viewport space /// using the given wvp matrix. /// </summary> public Point Project(Matrix4F wvp, Vec3F v) { float w = v.X * wvp.M14 + v.Y * wvp.M24 + v.Z * wvp.M34 + wvp.M44; wvp.Transform(ref v); v = v / w; Point pt = new Point(); pt.X = (int)((v.X + 1) * 0.5f * Width); pt.Y = (int)((1.0f - v.Y) * 0.5f * Height); return(pt); }
/// <summary> /// Handles mouse-move events</summary> /// <param name="sender">Control that raised original event</param> /// <param name="e">Event args</param> /// <returns>true, if controller handled the event</returns> public override bool MouseMove(object sender, MouseEventArgs e) { if (m_dragging && InputScheme.ActiveControlScheme.IsControllingCamera(Control.ModifierKeys, e)) { float dx = (float)(e.X - m_lastMousePoint.X) / 150.0f; float dy = (float)(e.Y - m_lastMousePoint.Y) / 150.0f; if (InputScheme.ActiveControlScheme.IsElevating(Control.ModifierKeys, e)) { // move camera up/down Vec3F p = Camera.Eye; p.Y += (dy < 0) ? m_scale : -m_scale; Camera.Set(p); } else if (InputScheme.ActiveControlScheme.IsTurning(Control.ModifierKeys, e)) { // pitch and yaw camera Matrix4F mat = Matrix4F.RotAxisRH(Camera.Right, -dy); // pitch along camera right Matrix4F yaw = new Matrix4F(); yaw.RotY(-dx); mat.Mul(yaw, mat); Vec3F lookAt = Camera.LookAt; Vec3F up = Camera.Up; mat.Transform(ref lookAt); mat.Transform(ref up); Vec3F position = Camera.Eye; float d = Camera.DistanceFromLookAt; Camera.Set(position, position + lookAt * d, up); } m_lastMousePoint = e.Location; return(true); } return(base.MouseMove(sender, e)); }
/// <summary> /// Groups the specified GameObjects</summary> /// <param name="gobs">GameObjects to be grouped</param> /// <remarks>Creates a new GameObjectGroup and moves all /// the GameObjects into it.</remarks> public IGameObjectGroup Group(IEnumerable <IGameObject> gobs) { // extra check. if (!CanGroup(gobs)) { return(null); } IGame game = null; AABB groupBox = new AABB(); List <IGameObject> gameObjects = new List <IGameObject>(); foreach (IGameObject gameObject in gobs) { if (game == null) { game = gameObject.As <DomNode>().GetRoot().As <IGame>(); } gameObjects.Add(gameObject); IBoundable boundable = gameObject.As <IBoundable>(); groupBox.Extend(boundable.BoundingBox); } IGameObjectGroup group = game.CreateGameObjectGroup(); DomNode node = group.As <DomNode>(); node.InitializeExtensions(); ITransformable transformable = node.As <ITransformable>(); transformable.Translation = groupBox.Center; Matrix4F invWorld = new Matrix4F(); invWorld.Invert(transformable.Transform); game.RootGameObjectFolder.GameObjects.Add(group); foreach (IGameObject gameObject in gameObjects) { ITransformable xformable = gameObject.As <ITransformable>(); Matrix4F world = ComputeWorldTransform(xformable); SetTransform(xformable, world); group.GameObjects.Add(gameObject); Vec3F trans = world.Translation; invWorld.Transform(ref trans); xformable.Translation = trans; } return(group); }
/// <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> /// Transforms the given world or local point into viewport (Windows) coordinates</summary> /// <param name="localPoint">World or local point to be transformed</param> /// <param name="localToScreen">Tranformation matrix composed of object-to-world times /// world-to-view times view-to-projection</param> /// <param name="viewportWidth">The viewport width, for example Control.Width or /// IRenderAction.ViewportWidth</param> /// <param name="viewportHeight">The viewport height, for example Control.Height or /// IRenderAction.ViewportHeight</param> /// <returns>The viewport or Window coordinate in the range [0,Width] and [0,Height] /// where the origin is the upper left corner of the viewport. The coordinate could be /// outside of this range if localPoint is not visible.</returns> /// <example> /// To calculate localToScreen using an object's local-to-world and a Camera: /// localToScreen = Matrix4F.Multiply(localToWorld, camera.ViewMatrix); /// localToScreen.Mul(localToScreen, camera.ProjectionMatrix); /// </example> public static Vec2F TransformToViewport( Vec3F localPoint, Matrix4F localToScreen, float viewportWidth, float viewportHeight) { // transform to clip space and do perspective divide. Result is in range of [-1, 1] Vec4F xScreen = new Vec4F(localPoint); localToScreen.Transform(xScreen, out xScreen); xScreen = Vec4F.Mul(xScreen, 1.0f / xScreen.W); // get viewport coordinates. Convert [-1, 1] to [0, view size] Vec2F xViewport = new Vec2F( (xScreen.X + 1) * 0.5f * viewportWidth, (1 - (xScreen.Y + 1) * 0.5f) * viewportHeight); return(xViewport); }
/// <summary> /// unproject vector from screen space to object space. /// </summary> public Vec3F Unproject(Vec3F scrPt, Matrix4F wvp) { float width = ClientSize.Width; float height = ClientSize.Height; Matrix4F invWVP = new Matrix4F(); invWVP.Invert(wvp); Vec3F worldPt = new Vec3F(); worldPt.X = scrPt.X / width * 2.0f - 1f; worldPt.Y = -(scrPt.Y / height * 2.0f - 1f); worldPt.Z = scrPt.Z; float w = worldPt.X * invWVP.M14 + worldPt.Y * invWVP.M24 + worldPt.Z * invWVP.M34 + invWVP.M44; invWVP.Transform(ref worldPt); worldPt = worldPt / w; return(worldPt); }
public static void CalcAxisLengths(Camera camera, Vec3F objectPosW, out float s) { float axisRatio = 0.24f; float worldHeight; // World height on origin's z value if (camera.Frustum.IsOrtho) { worldHeight = (camera.Frustum.Top - camera.Frustum.Bottom) / 2; } else { Matrix4F view = camera.ViewMatrix; Vec3F objPosV; view.Transform(objectPosW, out objPosV); worldHeight = -objPosV.Z * (float)Math.Tan(camera.Frustum.FovY / 2.0f); } s = (axisRatio * worldHeight); }
public IControlPoint InsertPoint(uint index, float x, float y, float z) { IControlPoint cpt = CreateControlPoint(); int numSteps = GetAttribute<int>(Schema.curveType.stepsAttribute); int interpolationType = GetAttribute<int>(Schema.curveType.interpolationTypeAttribute); if (interpolationType != 0 && numSteps > 0) { index = index / (uint)numSteps; } Path<DomNode> path = new Path<DomNode>(DomNode.GetPath()); Matrix4F toworld = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F worldToLocal = new Matrix4F(); worldToLocal.Invert(toworld); Vec3F pos = new Vec3F(x, y, z); worldToLocal.Transform(ref pos); cpt.Translation = pos; ControlPoints.Insert((int)index + 1, cpt); return cpt; }
/// <summary> /// Calculates required scale such that when it applied to an /// object. The object maintain a constant size in pixel regardless of /// its distance from camera. /// Used for computing size of 3d manipulators.</summary> /// <param name="camera"></param> /// <param name="objectPosW"></param> /// <param name="sizeInPixels"></param> /// <param name="viewHeight"></param> public static float CalcAxisScale(Camera camera, Vec3F objectPosW, float sizeInPixels, float viewHeight) { float worldHeight; // World height on origin's z value if (camera.Frustum.IsOrtho) { worldHeight = (camera.Frustum.Top - camera.Frustum.Bottom); } else { Matrix4F view = camera.ViewMatrix; Vec3F objPosV; view.Transform(objectPosW, out objPosV); worldHeight = 2.0f * Math.Abs(objPosV.Z) * (float)Math.Tan(camera.Frustum.FovY / 2.0f); } return(sizeInPixels * (worldHeight / viewHeight)); }
public IControlPoint InsertPoint(uint index, float x, float y, float z) { IControlPoint cpt = CreateControlPoint(); int numSteps = GetAttribute <int>(Schema.curveType.stepsAttribute); int interpolationType = GetAttribute <int>(Schema.curveType.interpolationTypeAttribute); if (interpolationType != 0 && numSteps > 0) { index = index / (uint)numSteps; } Path <DomNode> path = new Path <DomNode>(DomNode.GetPath()); Matrix4F toworld = TransformUtils.CalcPathTransform(path, path.Count - 1); Matrix4F worldToLocal = new Matrix4F(); worldToLocal.Invert(toworld); Vec3F pos = new Vec3F(x, y, z); worldToLocal.Transform(ref pos); cpt.Translation = pos; ControlPoints.Insert((int)index + 1, cpt); return(cpt); }
/// <summary> /// Dispatches untyped items. Replaces DispatchNotTyped(). To get the same behavior as /// the old DispatchNotTyped(), set the TypeFilter property to null prior to calling.</summary> /// <param name="traverseList">The traverse list</param> /// <param name="camera">The camera</param> protected void DispatchTraverseList(ICollection <TraverseNode> traverseList, Camera camera) { // Prepare for geometric picking -- create the ray in world space and reset geometric hit-list. // First create the ray in viewing coordinates and transform to world coordinates. float nx = (m_x / (float)m_width) - 0.5f; //normalized x float ny = 0.5f - (m_y / (float)m_height); //normalized y Ray3F rayWorld = camera.CreateRay(nx, ny); Matrix4F worldToView = camera.ViewMatrix; Matrix4F viewToWorld = new Matrix4F(); viewToWorld.Invert(worldToView); rayWorld.Transform(viewToWorld); ClearHitList(); // for geometric picking. will be cleared for each HitRecord. List <uint> userData = new List <uint>(1); // Dispatch traverse list int index = 0; foreach (TraverseNode node in traverseList) { // First test for filtering. IRenderObject renderObject = node.RenderObject; if (FilterByType(renderObject)) { IIntersectable intersectable = renderObject.GetIntersectable(); IGeometricPick geometricPick = intersectable as IGeometricPick; if (geometricPick != null) { // Picking by geometry. Matrix4F objToWorld = new Matrix4F(node.Transform); Matrix4F worldToObj = new Matrix4F(); worldToObj.Invert(objToWorld); Matrix4F viewToObj = Matrix4F.Multiply(viewToWorld, worldToObj); if (m_frustumPick) { //The pick frustum is in view space. Transform to world space then object space. Frustum frustumObj = new Frustum(m_viewFrust0); frustumObj.Transform(viewToObj); //Multi-pick. Get everything in the pick frustum (m_viewFrust0). Vec3F eyeObj; worldToObj.Transform(camera.Eye, out eyeObj); userData.Clear(); if (geometricPick.IntersectFrustum(frustumObj, eyeObj, node.RenderState, userData)) { // Prepare a multi-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); m_geoHitList.Add(hit); } } else { //Single pick. We care about distance from camera eye. //Make a copy of the ray in world-space and tranform it to object space. Ray3F rayObj = rayWorld; //remember, Ray3F is a value type, not a reference type. rayObj.Transform(worldToObj); // Do the intersection test in object space. userData.Clear(); Vec3F intersectionPt, surfaceNormal; Vec3F nearestVert; bool intersected; intersected = geometricPick.IntersectRay( rayObj, camera, node.RenderState, objToWorld, this, out intersectionPt, out nearestVert, out surfaceNormal, userData); if (intersected) { // Transform to world space and then to screen space. objToWorld.Transform(intersectionPt, out intersectionPt); objToWorld.Transform(nearestVert, out nearestVert); // Prepare a single-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); // This is the one difference from OpenGL pick. We have the world pt already. hit.WorldIntersection = intersectionPt; hit.NearestVert = nearestVert; // Another difference is that it's possible to get the surface normal. if (surfaceNormal != Vec3F.ZeroVector) { objToWorld.TransformNormal(surfaceNormal, out surfaceNormal); surfaceNormal.Normalize(); hit.Normal = surfaceNormal; } m_geoHitList.Add(hit); } } } else { // Picking by "rendering", using OpenGL pick. PushMatrix(node.Transform, false); Gl.glPushName(index); IRenderPick pickInterface = renderObject as IRenderPick; if (pickInterface != null) { pickInterface.PickDispatch(node.GraphPath, node.RenderState, this, camera); } else { renderObject.Dispatch(node.GraphPath, node.RenderState, this, camera); } Gl.glPopName(); PopMatrix(); } } index++; } }
/// <summary> /// Dispatches untyped items. Replaces DispatchNotTyped(). To get the same behavior as /// the old DispatchNotTyped(), set the TypeFilter property to null prior to calling.</summary> /// <param name="traverseList">The traverse list</param> /// <param name="camera">The camera</param> protected void DispatchTraverseList(ICollection<TraverseNode> traverseList, Camera camera) { // Prepare for geometric picking -- create the ray in world space and reset geometric hit-list. // First create the ray in viewing coordinates and transform to world coordinates. float nx = (m_x / (float)m_width) - 0.5f;//normalized x float ny = 0.5f - (m_y / (float)m_height);//normalized y Ray3F rayWorld = camera.CreateRay(nx, ny); Matrix4F worldToView = camera.ViewMatrix; Matrix4F viewToWorld = new Matrix4F(); viewToWorld.Invert(worldToView); rayWorld.Transform(viewToWorld); ClearHitList(); // for geometric picking. will be cleared for each HitRecord. List<uint> userData = new List<uint>(1); // Dispatch traverse list int index = 0; foreach (TraverseNode node in traverseList) { // First test for filtering. IRenderObject renderObject = node.RenderObject; if (FilterByType(renderObject)) { IIntersectable intersectable = renderObject.GetIntersectable(); IGeometricPick geometricPick = intersectable as IGeometricPick; if (geometricPick != null) { // Picking by geometry. Matrix4F objToWorld = new Matrix4F(node.Transform); Matrix4F worldToObj = new Matrix4F(); worldToObj.Invert(objToWorld); Matrix4F viewToObj = Matrix4F.Multiply(viewToWorld, worldToObj); if (m_frustumPick) { //The pick frustum is in view space. Transform to world space then object space. Frustum frustumObj = new Frustum(m_viewFrust0); frustumObj.Transform(viewToObj); //Multi-pick. Get everything in the pick frustum (m_viewFrust0). Vec3F eyeObj; worldToObj.Transform(camera.Eye, out eyeObj); userData.Clear(); if (geometricPick.IntersectFrustum(frustumObj, eyeObj, node.RenderState, userData)) { // Prepare a multi-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); m_geoHitList.Add(hit); } } else { //Single pick. We care about distance from camera eye. //Make a copy of the ray in world-space and tranform it to object space. Ray3F rayObj = rayWorld; //remember, Ray3F is a value type, not a reference type. rayObj.Transform(worldToObj); // Do the intersection test in object space. userData.Clear(); Vec3F intersectionPt, surfaceNormal; Vec3F nearestVert; bool intersected; intersected = geometricPick.IntersectRay( rayObj, camera, node.RenderState, objToWorld, this, out intersectionPt, out nearestVert, out surfaceNormal, userData); if (intersected) { // Transform to world space and then to screen space. objToWorld.Transform(intersectionPt, out intersectionPt); objToWorld.Transform(nearestVert, out nearestVert); // Prepare a single-pick HitRecord, as if OpenGL had calculated this. HitRecord hit = new HitRecord( node.GraphPath, renderObject, objToWorld, userData.ToArray()); // This is the one difference from OpenGL pick. We have the world pt already. hit.WorldIntersection = intersectionPt; hit.NearestVert = nearestVert; // Another difference is that it's possible to get the surface normal. if (surfaceNormal != Vec3F.ZeroVector) { objToWorld.TransformNormal(surfaceNormal, out surfaceNormal); surfaceNormal.Normalize(); hit.Normal = surfaceNormal; } m_geoHitList.Add(hit); } } } else { // Picking by "rendering", using OpenGL pick. PushMatrix(node.Transform, false); Gl.glPushName(index); IRenderPick pickInterface = renderObject as IRenderPick; if (pickInterface != null) { pickInterface.PickDispatch(node.GraphPath, node.RenderState, this, camera); } else { renderObject.Dispatch(node.GraphPath, node.RenderState, this, camera); } Gl.glPopName(); PopMatrix(); } } index++; } }
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; } } }
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 a sphere by the given matrix</summary> /// <param name="m">Matrix</param> /// <returns>Transformed sphere</returns> public Sphere3F Transform(Matrix4F m) { m.Transform(Center, out Center); // Calculate the scale float l1 = m.XAxis.Length; float l2 = m.YAxis.Length; float l3 = m.ZAxis.Length; float scale = l1; if (scale < l2) { scale = l2; } if (scale < l3) { scale = l3; } Radius *= scale; return this; }
/// <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(); }
/// <summary> /// Groups the specified GameObjects</summary> /// <param name="gobs">GameObjects to be grouped</param> /// <remarks>Creates a new GameObjectGroup and moves all /// the GameObjects into it.</remarks> public IGameObjectGroup Group(IEnumerable<IGameObject> gobs) { // extra check. if (!CanGroup(gobs)) return null; IGame game = null; AABB groupBox = new AABB(); List<IGameObject> gameObjects = new List<IGameObject>(); foreach (IGameObject gameObject in gobs) { if (game == null) { game = gameObject.As<DomNode>().GetRoot().As<IGame>(); } gameObjects.Add(gameObject); IBoundable boundable = gameObject.As<IBoundable>(); groupBox.Extend(boundable.BoundingBox); } IGameObjectGroup group = game.CreateGameObjectGroup(); DomNode node = group.As<DomNode>(); node.InitializeExtensions(); ITransformable transformable = node.As<ITransformable>(); transformable.Translation = groupBox.Center; Matrix4F invWorld = new Matrix4F(); invWorld.Invert(transformable.Transform); game.RootGameObjectFolder.GameObjects.Add(group); foreach (IGameObject gameObject in gameObjects) { ITransformable xformable = gameObject.As<ITransformable>(); Matrix4F world = ComputeWorldTransform(xformable); SetTransform(xformable, world); group.GameObjects.Add(gameObject); Vec3F trans = world.Translation; invWorld.Transform(ref trans); xformable.Translation = trans; } return group; }
/// <summary> /// Transforms the given world or local point into viewport (Windows) coordinates</summary> /// <param name="localPoint">World or local point to be transformed</param> /// <param name="localToScreen">Tranformation matrix composed of object-to-world times /// world-to-view times view-to-projection</param> /// <param name="viewportWidth">The viewport width, for example Control.Width or /// IRenderAction.ViewportWidth</param> /// <param name="viewportHeight">The viewport height, for example Control.Height or /// IRenderAction.ViewportHeight</param> /// <returns>The viewport or Window coordinate in the range [0,Width] and [0,Height] /// where the origin is the upper left corner of the viewport. The coordinate could be /// outside of this range if localPoint is not visible.</returns> /// <example> /// To calculate localToScreen using an object's local-to-world and a Camera: /// localToScreen = Matrix4F.Multiply(localToWorld, camera.ViewMatrix); /// localToScreen.Mul(localToScreen, camera.ProjectionMatrix); /// </example> public static Vec2F TransformToViewport( Vec3F localPoint, Matrix4F localToScreen, float viewportWidth, float viewportHeight) { // transform to clip space and do perspective divide. Result is in range of [-1, 1] Vec4F xScreen = new Vec4F(localPoint); localToScreen.Transform(xScreen, out xScreen); xScreen = Vec4F.Mul(xScreen, 1.0f / xScreen.W); // get viewport coordinates. Convert [-1, 1] to [0, view size] Vec2F xViewport = new Vec2F( (xScreen.X + 1) * 0.5f * viewportWidth, (1 - (xScreen.Y + 1) * 0.5f) * viewportHeight); return xViewport; }
/// <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> /// Transforms this frustum by the given matrix</summary> /// <param name="m">Transformation matrix. Can be a nearly-general transform and include non-uniform /// scaling and shearing.</param> public void Transform(Matrix4F m) { Matrix4F transposeOfInverse = new Matrix4F(m); transposeOfInverse.Invert(transposeOfInverse); transposeOfInverse.Transpose(transposeOfInverse); for (int i = 0; i < 6; i++) { m.Transform(m_planes[i], transposeOfInverse, out m_planes[i]); } }
private DomNode CreatePrototype(IEnumerable<IGameObject> gobs) { DomNode[] originals = new DomNode[1]; List<IGameObject> copyList = new List<IGameObject>(); AABB bound = new AABB(); foreach (IGameObject gameObject in SelectedGobs) { IBoundable boundable = gameObject.As<IBoundable>(); bound.Extend(boundable.BoundingBox); Matrix4F world = TransformUtils.ComputeWorldTransform(gameObject); originals[0] = gameObject.As<DomNode>(); DomNode[] copies = DomNode.Copy(originals); IGameObject copy = copies[0].As<IGameObject>(); TransformUtils.SetTransform(copy, world); copyList.Add(copy); } DomNode gobchild = null; if (copyList.Count > 1) {// create group IGame game = m_contextRegistry.GetActiveContext<IGame>(); IGameObjectGroup gobgroup = game.CreateGameObjectGroup(); gobgroup.Translation = bound.Center; gobgroup.UpdateTransform(); Matrix4F worldInv = new Matrix4F(); worldInv.Invert(gobgroup.Transform); foreach (IGameObject gob in copyList) { Vec3F translate = gob.Translation; worldInv.Transform(ref translate); gob.Translation = translate; gob.UpdateTransform(); gobgroup.GameObjects.Add(gob); } gobchild = gobgroup.As<DomNode>(); } else { gobchild = copyList[0].As<DomNode>(); } gobchild.InitializeExtensions(); gobchild.As<IGameObject>().Translation = new Vec3F(0, 0, 0); DomNode prototype = null; if (gobchild != null) { prototype = new DomNode(Schema.prototypeType.Type, Schema.prototypeRootElement); prototype.SetChild(Schema.prototypeType.gameObjectChild, gobchild); } return prototype; }
/// <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; }
protected Vector4F mul(Vector4F vector, Matrix4F transform) { return(Matrix4F.Transform(transform, vector)); //return Vector4F.Transform(vector, transform); }
/// <summary> /// unproject vector from screen space to object space. /// </summary> public Vec3F Unproject(Vec3F scrPt, Matrix4F wvp) { float width = ClientSize.Width; float height = ClientSize.Height; Matrix4F invWVP = new Matrix4F(); invWVP.Invert(wvp); Vec3F worldPt = new Vec3F(); worldPt.X = scrPt.X / width * 2.0f - 1f; worldPt.Y = -(scrPt.Y / height * 2.0f - 1f); worldPt.Z = scrPt.Z; float w = worldPt.X * invWVP.M14 + worldPt.Y * invWVP.M24 + worldPt.Z * invWVP.M34 + invWVP.M44; invWVP.Transform(ref worldPt); worldPt = worldPt / w; return worldPt; }
/// <summary> /// project the v from 3d space to viewport space /// using the given wvp matrix. /// </summary> public Point Project(Matrix4F wvp, Vec3F v) { float w = v.X * wvp.M14 + v.Y * wvp.M24 + v.Z * wvp.M34 + wvp.M44; wvp.Transform(ref v); v = v / w; Point pt = new Point(); pt.X = (int)((v.X + 1) * 0.5f * Width); pt.Y = (int)((1.0f - v.Y) * 0.5f * Height); return pt; }
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); } } }