public void SetLighting(LightSettings lightSettings, CameraBase camera) { Matrix4x3 cameraM = Matrix4x3.Identity; if (lightSettings.IsAnyLightAffixedToCamera) { // if a light is affixed to the camera, its position is considered to be in camera coordinates // but here we need the light in world coordinates // cameraM transforms from camera coordinates to world coordinates cameraM = camera.InverseLookAtRHMatrix; } // first ambient light var al = lightSettings.AmbientLight; SetAmbientLight(al.ColorBelow.Color, al.ColorAbove.Color, al.LightAmplitude, al.IsAffixedToCamera ? cameraM.Transform(al.DirectionBelowToAbove) : al.DirectionBelowToAbove); for (int idx = 0; idx < 4; ++idx) { var l = lightSettings.GetDiscreteLight(idx); if (null == l) { ClearSingleLight(idx); } else if (l is DirectionalLight) { var dl = (DirectionalLight)l; SetDirectionalLight( idx, dl.Color.Color, dl.LightAmplitude, dl.IsAffixedToCamera ? cameraM.Transform(dl.DirectionToLight) : dl.DirectionToLight ); } else if (l is PointLight) { var pl = (PointLight)l; SetPointLight( idx, pl.Color.Color, pl.LightAmplitude, pl.IsAffixedToCamera ? cameraM.Transform(pl.Position) : pl.Position, pl.Range ); } else if (l is SpotLight) { var sl = (SpotLight)l; // calculation of SpotCosInnerConeRcp: it is in reality not 1/CosInnerConeAngle, but it is 1/(Cos(InnerConeAngle) - Cos(OuterConeAngle)) double diffCos = Math.Cos(sl.InnerConeAngle) - Math.Cos(sl.OuterConeAngle); double SpotCosInnerConeRcp = diffCos >= 1E-18 ? 1 / diffCos : 1E18; SetSpotLight( idx, sl.Color.Color, sl.LightAmplitude, sl.IsAffixedToCamera ? cameraM.Transform(sl.Position) : sl.Position, sl.IsAffixedToCamera ? cameraM.Transform(sl.DirectionToLight) : sl.DirectionToLight, sl.Range, Math.Cos(sl.OuterConeAngle), SpotCosInnerConeRcp ); } else { throw new NotImplementedException(string.Format("The type of lighting ({0}) is not implemented here.")); } } AssembleLights(); }
public void SetCamera(CameraBase camera) { if (null == camera) throw new ArgumentNullException(nameof(camera)); _camera = camera; }
/// <summary> /// Handles the mouse up event onto the graph in the controller class. /// </summary> /// <param name="position">Mouse position. X and Y components are the current relative mouse coordinates, the Z component is the screen's aspect ratio.</param> /// <param name="e">MouseEventArgs.</param> public virtual void EhView_GraphPanelMouseUp(PointD3D position, MouseButtonEventArgs e) { _mouseState.OnMouseUp(position, e); if (e.ChangedButton == MouseButton.Middle && e.MiddleButton == MouseButtonState.Released) { _middleButtonPressed_InitialCamera = null; } }
/// <summary> /// Handles the mouse move event onto the graph in the controller class. /// </summary> /// <param name="position">Mouse position.</param> /// <param name="e">MouseEventArgs.</param> public virtual void EhView_GraphPanelMouseMove(PointD3D position, MouseEventArgs e) { _mouseState.OnMouseMove(position, e); if (e.MiddleButton == MouseButtonState.Released) { _middleButtonPressed_InitialCamera = null; } else if (null != _middleButtonPressed_InitialCamera) { switch (_middleButtonCurrentAction) { case MiddelButtonAction.RotateCamera: { double dx = position.X - _middleButtonPressed_InitialPosition.X; double dy = position.Y - _middleButtonPressed_InitialPosition.Y; //Doc.Camera = CameraRotateDegrees(_middleButtonPressed_InitialCamera, dx * 540, -dy * 540); Doc.Camera = ModelRotateDegrees(_middleButtonPressed_InitialCamera, _doc.RootLayer.Size, dx * 540, -dy * 540); } break; case MiddelButtonAction.MoveCamera: { double dx = position.X - _middleButtonPressed_InitialPosition.X; double dy = position.Y - _middleButtonPressed_InitialPosition.Y; Doc.Camera = CameraMoveRelative(_middleButtonPressed_InitialCamera, dx, dy); } break; case MiddelButtonAction.ZoomCamera: { double dy = position.Y - _middleButtonPressed_InitialPosition.Y; Doc.Camera = CameraZoomByMouseWheel(_middleButtonPressed_InitialCamera, 0.5, 0.5, position.Z, (dy * 5)); } break; } } }
/// <summary> /// Moves the camera horizontally and vertically. /// </summary> /// <param name="camera">The camera prior to the movement.</param> /// <param name="stepX">The movement in horizontal direction. A value of 1 means movement corresponding to the full width of the scene.</param> /// <param name="stepY">The movement in horizontal direction. A value of 1 means movement corresponding to the full height of the scene.</param> /// <returns>The new camera after the movement.</returns> public static CameraBase CameraMoveRelative(CameraBase camera, double stepX, double stepY) { VectorD3D xaxis = VectorD3D.CreateNormalized(VectorD3D.CrossProduct(camera.TargetToEyeVectorNormalized, camera.UpVector)); VectorD3D yaxis = VectorD3D.CreateNormalized(VectorD3D.CrossProduct(camera.TargetToEyeVector, VectorD3D.CrossProduct(camera.TargetToEyeVector, camera.UpVector))); if (camera is OrthographicCamera) { var oldCamera = (OrthographicCamera)camera; var shift = (xaxis * stepX + yaxis * stepY) * (oldCamera.WidthAtZNear); camera = oldCamera.WithEyeTarget(oldCamera.EyePosition + shift, oldCamera.TargetPosition + shift); } else if (camera is PerspectiveCamera) { var oldCamera = (PerspectiveCamera)camera; var shift = (xaxis * stepX + yaxis * stepY) * (oldCamera.WidthAtZNear * oldCamera.Distance / oldCamera.ZNear); camera = oldCamera.WithEyeTarget(oldCamera.EyePosition + shift, oldCamera.TargetPosition + shift); } return camera; }
/// <summary> /// Handles the mouse down event onto the graph in the controller class. /// </summary> /// <param name="position">Mouse position. X and Y components are the current relative mouse coordinates, the Z component is the screen's aspect ratio.</param> /// <param name="e">MouseEventArgs.</param> public virtual void EhView_GraphPanelMouseDown(PointD3D position, MouseButtonEventArgs e) { _mouseState.OnMouseDown(position, e); bool isSHIFTpressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift); bool isCTRLpressed = Keyboard.Modifiers.HasFlag(ModifierKeys.Control); if (e.ChangedButton == MouseButton.Middle && e.MiddleButton == MouseButtonState.Pressed) { _middleButtonPressed_InitialPosition = position; _middleButtonPressed_InitialCamera = _doc.Camera; if (!isSHIFTpressed && !isCTRLpressed) _middleButtonCurrentAction = MiddelButtonAction.RotateCamera; else if (isSHIFTpressed && !isCTRLpressed) _middleButtonCurrentAction = MiddelButtonAction.MoveCamera; else if (!isSHIFTpressed && isCTRLpressed) _middleButtonCurrentAction = MiddelButtonAction.ZoomCamera; else _middleButtonPressed_InitialCamera = null; // if inconsistent keys, then no action at all } }
/// <summary> /// Adjusts the zNear and zFar parameter of the camera to make sure that our scene is viewed appropriately, and nothing is cut away. /// </summary> /// <param name="cam">The cam.</param> /// <returns></returns> public CameraBase AdjustZNearZFar(CameraBase cam) { var currentDistanceToRootLayerCenter = ((VectorD3D)(cam.EyePosition - 0.5 * Doc.RootLayer.Size)).Length; var rootLayerRadius = 0.5 * Doc.RootLayer.Size.Length; double zNear, zFar; double rootLayerRadiusTimesFour = 4 * rootLayerRadius; if (currentDistanceToRootLayerCenter <= 2 * rootLayerRadius) { zNear = rootLayerRadius / 100; zFar = rootLayerRadiusTimesFour; } else { zNear = rootLayerRadius / 2; zFar = Math.Max(2 * currentDistanceToRootLayerCenter, rootLayerRadius * 4); zFar = rootLayerRadiusTimesFour * Math.Ceiling(zFar / rootLayerRadiusTimesFour); } return cam.WithZNearZFarWithoutChangingViewAngle(zNear, zFar); }
public static CameraBase ModelRotateDegrees(CameraBase cam, VectorD3D rootLayerSize, double stepX, double stepY) { double angleRadianZ = stepX * Math.PI / 180.0; double angleRadianX = stepY * Math.PI / 180.0; var shift = 0.5 * rootLayerSize; var l = cam.LookAtRHMatrix; // in the world coordinate space: i) translate to root layer center, ii) rotate around z-axis and iii) translate back var m01 = Matrix4x3.NewTranslation(-shift); var m02 = Matrix4x3.NewRotationFromAxisAndAngleRadian(new VectorD3D(0, 0, 1), angleRadianZ, PointD3D.Empty); var m03 = Matrix4x3.NewTranslation(shift); var mstart = m01.WithAppendedTransformation(m02).WithAppendedTransformation(m03); // in LookAt coordinate space: i) translate to root layer center, ii) rotate around x-axis and iii) translate back var diff11 = (VectorD3D)cam.LookAtRHMatrix.Transform((PointD3D)shift); var m11 = Matrix4x3.NewTranslation(-diff11); var m12 = Matrix4x3.NewRotationFromAxisAndAngleRadian(new VectorD3D(1, 0, 0), angleRadianX, PointD3D.Empty); var m13 = Matrix4x3.NewTranslation(diff11); var mend = m11.WithAppendedTransformation(m12).WithAppendedTransformation(m13); var t = mstart.WithAppendedTransformation(l).WithAppendedTransformation(mend); return cam.WithLookAtRHMatrix(t); }
/// <summary> /// Rotates the camera. /// </summary> /// <param name="cam">Initial camera, i.e. camera prior to the rotation</param> /// <param name="stepX">The rotation around the vertical axis in degrees.</param> /// <param name="stepY">The rotation around the horizontal axis in degrees.</param> public static CameraBase CameraRotateDegrees(CameraBase cam, double stepX, double stepY) { // the axis to turn the camera around is in case of stepY the Cross of UpVector and eyeVector // in case of stepX it is the cross of the Upvector and the cross of UpVector and eyeVector if (stepX != 0) { double angleRadian = stepX * Math.PI / 180.0; VectorD3D axis = VectorD3D.CreateNormalized(VectorD3D.CrossProduct(cam.TargetToEyeVector, VectorD3D.CrossProduct(cam.TargetToEyeVector, cam.UpVector))); var matrix = Matrix4x3.NewRotationFromAxisAndAngleRadian(axis, angleRadian, cam.TargetPosition); var newEye = matrix.Transform(cam.EyePosition); var newUp = matrix.Transform(cam.UpVector); cam = cam.WithUpEye(newUp, newEye); } if (stepY != 0) { double angleRadian = stepY * Math.PI / 180.0; VectorD3D axis = VectorD3D.CreateNormalized(VectorD3D.CrossProduct(cam.TargetToEyeVectorNormalized, cam.UpVector)); var matrix = Matrix4x3.NewRotationFromAxisAndAngleRadian(axis, angleRadian, cam.TargetPosition); var newEye = matrix.Transform(cam.EyePosition); var newUp = matrix.Transform(cam.UpVector); cam = cam.WithUpEye(newUp, newEye); } return cam; }
protected static CameraBase CameraZoomByMouseWheel(CameraBase camera, double relX, double relY, double aspectRatio, double delta) { if (camera is OrthographicCamera) { var cam = camera as OrthographicCamera; var eye = cam.TargetToEyeVectorNormalized; var up = cam.UpVectorPerpendicularToEyeVectorNormalized; var widthBefore = cam.WidthAtZNear; var widthAfter = widthBefore * Math.Pow(2, delta); var tam1h = relX - 0.5; var tbm1h = relY - 0.5; var shift = new VectorD3D( -(widthAfter - widthBefore) * (aspectRatio * tbm1h * up.X + eye.Z * tam1h * up.Y - eye.Y * tam1h * up.Z), (widthAfter - widthBefore) * (eye.Z * tam1h * up.X - aspectRatio * tbm1h * up.Y - eye.X * tam1h * up.Z), -(widthAfter - widthBefore) * (eye.Y * tam1h * up.X - eye.X * tam1h * up.Y + aspectRatio * tbm1h * up.Z) ); var oldCamera = (OrthographicCamera)camera; var newCamera = ((OrthographicCamera)camera).WithEyeTargetWidth(oldCamera.EyePosition + shift, oldCamera.TargetPosition + shift, widthAfter); return newCamera; } else if (camera is PerspectiveCamera) { double rx = 2 * relX - 1; double ry = 2 * relY - 1; double distanceFactor = Math.Pow(2, delta); camera = ((PerspectiveCamera)camera).ZoomByGettingCloserToTarget(distanceFactor, rx, ry, aspectRatio); } return camera; }
/// <summary> /// Gets the principal coordinate system that results of the camera facing a layer. The plane of the layer that best faced the camera is used for the calculations. /// The normal of that layer is returned as z-axis, the vector that best matches the up-vector of the camera is becoming the y-axis, /// and the x-axis results from the z-axis and the y-axis. /// </summary> /// <param name="camera">The camera.</param> /// <param name="activeLayer">The active layer of the graph document.</param> /// <param name="transformation">Matrix that contains the principal axes as described above. The axes coordinates are in the coordinates of the layer provided in the argument <paramref name="activeLayer"/>.</param> /// <exception cref="InvalidProgramException">There should always be a plane of a rectangle that can be hit!</exception> public static void GetCoordinateSystemBasedOnLayerPlaneFacingTheCamera(CameraBase camera, HostLayer activeLayer, out Matrix3x3 transformation) { PointD3D hitposition = new PointD3D(0.5, 0.5, 1); // this hit position is arbitrary, every other position should work similarly var activeLayerTransformation = activeLayer.TransformationFromRootToHere(); var hitData = new HitTestPointData(camera.GetHitRayMatrix(hitposition)); hitData = hitData.NewFromAdditionalTransformation(activeLayerTransformation); // now hitdata are in layer cos var targetToEye = hitData.WorldTransformation.Transform(camera.TargetToEyeVectorNormalized); // targetToEye in layer coordinates var upEye = hitData.WorldTransformation.Transform(camera.UpVectorPerpendicularToEyeVectorNormalized); // camera up vector in layer coordinates // get the face which has the best dot product between the eye vector of the camera and the plane's normal var layerRect = new RectangleD3D(PointD3D.Empty, activeLayer.Size); double maxval = double.MinValue; PlaneD3D maxPlane = PlaneD3D.Empty; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, targetToEye); if (val > maxval) { maxval = val; maxPlane = plane; } } // bool isHit = hitData.IsPlaneHitByRay(maxPlane, out hitPointOnPlaneInActiveLayerCoordinates); // hitPointOnPlane is in layer coordinates too // if (!isHit) // throw new InvalidProgramException("There should always be a plane of a rectangle that can be hit!"); VectorD3D zaxis = maxPlane.Normal; VectorD3D yaxis = upEye; // Find y axis perpendicular to zaxis maxval = double.MinValue; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, upEye); if (val > maxval && 0 == VectorD3D.DotProduct(plane.Normal, zaxis)) { maxval = val; yaxis = plane.Normal; } } var xaxis = VectorD3D.CrossProduct(yaxis, zaxis); // now we have all information about the spatial position and orientation of the text: // hitPointOnPlane is the position of the text // maxPlane.Normal is the face orientation of the text // maxUpVector is the up orientation of the text xaxis = xaxis.Normalized; yaxis = yaxis.Normalized; zaxis = zaxis.Normalized; transformation = new Matrix3x3( xaxis.X, yaxis.X, zaxis.X, xaxis.Y, yaxis.Y, zaxis.Y, xaxis.Z, yaxis.Z, zaxis.Z ); }