/// <summary> /// Converts the screen coordinates to world coordiantes using the specified attitude matrix. /// </summary> /// <param name="pointOnScreen"> /// The point in screen coordinates to translate. /// </param> /// <param name="attitude"> /// The attitude matrix to use for translations. /// </param> /// <returns> /// A <see cref="Vector3"/> in world space. /// </returns> public Vector3 ScreenToWorld(Point pointOnScreen, Matrix attitude) { // Ensure that the viewport has been created EnsureViewport(); // Use the attitude Matrix saved in the OnMouseLeftButtonUp handler. // Rotate it 90 degrees around the X axis to put it in the XNA Framework coordinate system. attitude = Matrix.CreateRotationX(MathHelper.PiOver2) * attitude; // for the near point we specified zero as the z component of the point which means this point is as close as // possible to the camera, while one for the second point means it’s as far as possible from the camera Vector3 nearPoint = new Vector3((float)pointOnScreen.X, (float)pointOnScreen.Y, 0); Vector3 farPoint = new Vector3((float)pointOnScreen.X, (float)pointOnScreen.Y, 1); // Convert the near and far points from mouse space to 3D space nearPoint = viewport.Unproject(nearPoint, projection, view, attitude); farPoint = viewport.Unproject(farPoint, projection, view, attitude); // Now that we have the near and far points in 3D space, create a directional vector between the two points // and normalize it. Normalizing makes the vector only one unit in length but still pointing in the same // direction. Vector3 direction = farPoint - nearPoint; direction.Normalize(); // Now we take our direction and multiply it by 10, which essentially just moves it out almost to the edge of // our sceeen (our edge is 12 as defined by FarClippingPlane above). 10 units is also where all the labels like // 'front', 'back', 'left', etc. are positioned. This way if the user taps on one of the labels, their new label // will be at the same location and will move in sync. Vector3 unprojected = direction * 10; // Return the unprojected location return(unprojected); }
private void EnsureViewport() { // Only do this once if (viewport.Width == 0 || previousOrientation != currentOrientation || viewPortNeedsRebuilding) { // Initialize the viewport and matrixes for 3d projection. // Create the ViewPort based on the size of ourselves (the view) and not the screen. That's // becase the View should be sized to the camera preview, which is not the same size as // the Page or Window. viewport = new Viewport(0, 0, (int)this.ActualWidth, (int)this.ActualHeight); viewPortNeedsRebuilding = false; float aspect = viewport.AspectRatio; projection = Matrix.CreatePerspectiveFieldOfView(1, aspect, (float)NearClippingPlane, (float)FarClippingPlane); // Rotate items by adjusting camera's up vector Vector3 cameraUpVector = Vector3.Up; switch (currentOrientation) { case ControlOrientation.Clockwise270Degrees: cameraUpVector = Vector3.Right; break; case ControlOrientation.Clockwise90Degrees: cameraUpVector = Vector3.Left; break; #if WIN_RT case ControlOrientation.Clockwise180Degrees: cameraUpVector = Vector3.Down; break; #endif } view = Matrix.CreateLookAt(new Vector3(0, 0, 1), Vector3.Zero, cameraUpVector); previousOrientation = currentOrientation; } }
/// <summary> /// Occurs when the value of the <see cref="Attitude"/> property has changed. /// </summary> /// <param name="e"> /// A <see cref="DependencyPropertyChangedEventArgs"/> containing event information. /// </param> protected override void OnAttitudeChanged(DependencyPropertyChangedEventArgs e) { base.OnAttitudeChanged(e); // Ensure that the viewport has been created EnsureViewport(); // Get the RotationMatrix from the MotionReading. // Rotate it 90 degrees around the X axis to put it in the XNA Framework coordinate system. Matrix xnaAttitude = Matrix.CreateRotationX(MathHelper.PiOver2) * Attitude; // Loop through our items for (int i = 0; i < Items.Count; i++) { // Get the WorldVIewItem that matches the index WorldViewItem wvItem = ItemContainerGenerator.ContainerFromIndex(i) as WorldViewItem; // If we couldn't get a WorldViewItem for the index, skip if (wvItem == null) { continue; } // Get the ARItem that the WorldViewItem represents ARItem arItem = (ARItem)wvItem.DataContext; // Create a World matrix for the ARItems WorldLocation Matrix world = Matrix.CreateWorld(arItem.WorldLocation, new Vector3(0, 0, 1), new Vector3(0, 1, 0)); // Use Viewport.Project to project the ARItems location in 3D space into screen coordinates Vector3 projected = viewport.Project(Vector3.Zero, projection, view, world * xnaAttitude); // If the point is outside of this range, it is behind the camera if (projected.Z > 1 || projected.Z < 0) { // Out of range, just hide wvItem.Visibility = Visibility.Collapsed; } else { // In range so show wvItem.Visibility = Visibility.Visible; /* * // Create a TranslateTransform to position the WorldViewItem * TranslateTransform tt = new TranslateTransform(); * * // Offset by half of the WorldViewItems size to center it on the point * tt.X = projected.X - (wvItem.ActualWidth / 2); * tt.Y = projected.Y - (wvItem.ActualHeight / 2); * * // Set the transform, which moves the item * wvItem.RenderTransform = tt; */ // Create a CompositeTransform to position and scale the WorldViewItem CompositeTransform ct = new CompositeTransform(); // Offset by half of the WorldViewItems size to center it on the point // TODO: Expose vertical limit property #if WIN_RT ct.TranslateX = projected.X - (wvItem.ActualWidth / 2) - (this.ActualWidth / 2); // Ricky; Keep the y value within the screen double y = projected.Y - (wvItem.ActualHeight / 2); double h2 = this.Height / 2; y = (y < -h2) ? -h2 : ((y > h2) ? h2 : y); ct.TranslateY = y; #endif #if WINDOWS_PHONE ct.TranslateX = projected.X - (wvItem.ActualWidth / 2); ct.TranslateY = projected.Y - (wvItem.ActualHeight / 2); #endif #if WP7 double scale = MathHelper.Lerp((float)MinItemScale, (float)MaxItemScale, ((float)FarClippingPlane - Math.Abs(arItem.WorldLocation.Z)) / (float)FarClippingPlane); #else double scale = MathHelper.Lerp(MinItemScale, MaxItemScale, (FarClippingPlane - Math.Abs(arItem.WorldLocation.Z)) / FarClippingPlane); #endif ct.ScaleX = scale; ct.ScaleY = scale; // Set the transform, which moves the item wvItem.RenderTransform = ct; // Set the items z-index so that the closest item is always rendered on top Canvas.SetZIndex(wvItem, (int)(scale * 255)); } } }