public static Clamp ( float value, float min, float max ) : float | ||
value | float | |
min | float | |
max | float | |
return | float |
/// <summary> /// Processes an area and applies a gradient calculation to each part of the area. /// </summary> /// <param name="position">The center of the gradient.</param> /// <param name="strength">The width of the gradient spread.</param> /// <param name="angle">The angle to apply the gradient.</param> /// <param name="area">The area to calculate.</param> /// <param name="applyAction">The callback called for each part of the area.</param> public static void GradientFill(Point cellSize, Point position, int strength, int angle, Rectangle area, ColorGradient gradient, Action <int, int, Color> applyAction) { double radians = angle * Math.PI / 180; // = Math.Atan2(x1 - x2, y1 - y2); Vector2 angleVector = new Vector2((float)(Math.Sin(radians) * strength), (float)(Math.Cos(radians) * strength)) / 2; Vector2 location = new Vector2(position.X, position.Y); if (cellSize.X > cellSize.Y) { angleVector.Y *= cellSize.X / cellSize.Y; } else if (cellSize.X < cellSize.Y) { angleVector.X *= cellSize.Y / cellSize.X; } Vector2 endingPoint = location + angleVector; Vector2 startingPoint = location - angleVector; double x1 = (startingPoint.X / (double)area.Width) * 2.0f - 1.0f; double y1 = (startingPoint.Y / (double)area.Height) * 2.0f - 1.0f; double x2 = (endingPoint.X / (double)area.Width) * 2.0f - 1.0f; double y2 = (endingPoint.Y / (double)area.Height) * 2.0f - 1.0f; double start = x1 * angleVector.X + y1 * angleVector.Y; double end = x2 * angleVector.X + y2 * angleVector.Y; for (int x = area.Left; x < area.Width; x++) { for (int y = area.Top; y < area.Height; y++) { // but we need vectors from (-1, -1) to (1, 1) // instead of pixels from (0, 0) to (width, height) double u = (x / (double)area.Width) * 2.0f - 1.0f; double v = (y / (double)area.Height) * 2.0f - 1.0f; double here = u * angleVector.X + v * angleVector.Y; double lerp = (start - here) / (start - end); //lerp = Math.Abs((lerp - (int)lerp)); lerp = MyMathHelper.Clamp((float)lerp, 0f, 1.0f); int counter; for (counter = 0; counter < gradient.Stops.Length && gradient.Stops[counter].Stop < (float)lerp; counter++) { ; } counter--; counter = (int)MyMathHelper.Clamp(counter, 0, gradient.Stops.Length - 2); float newLerp = (gradient.Stops[counter].Stop - (float)lerp) / (gradient.Stops[counter].Stop - gradient.Stops[counter + 1].Stop); applyAction(x, y, ColorHelper.Lerp(gradient.Stops[counter].Color, gradient.Stops[counter + 1].Color, newLerp)); } } }
private void CalcControlSize() { var canvas = this.MinimapCanvas; Size minimapSize = new Size(canvas?.Width ?? 0, canvas?.Height ?? 0); //计算边框 int top = this.resource.N.Height, bottom = this.resource.S.Height, left = this.resource.W.Width, right = this.resource.E.Width; //计算实际大小 Size desireSize = new Size(minimapSize.Width + left + right, minimapSize.Height + top + bottom); //计算标题 if (this.lblStreetName.Text != null) { this.lblStreetName.Measure(new Size(double.MaxValue, double.MaxValue)); var lblRight = Canvas.GetLeft(this.lblStreetName) + this.lblStreetName.DesiredSize.Width; desireSize.Width = Math.Max(desireSize.Width, lblRight); } if (this.lblMapName.Text != null) { this.lblMapName.Measure(new Size(double.MaxValue, double.MaxValue)); var lblRight = Canvas.GetLeft(this.lblMapName) + this.lblMapName.DesiredSize.Width; desireSize.Width = Math.Max(desireSize.Width, lblRight); } this.Width = MathHelper.Clamp(desireSize.Width, this.MinWidth, this.MaxWidth); this.Height = MathHelper.Clamp(desireSize.Height, this.MinHeight, this.MaxHeight); this.MapAreaControl.Width = Math.Max(0, this.Width - left - right); this.MapAreaControl.Height = Math.Max(0, this.Height - top - bottom); }
/// <summary> /// Zooms the camera when Pinch/Stretch is detected. /// </summary> /// <param name="pinchGesture">The pinch gesture.</param> private void ZoomCamera(GestureSample pinchGesture) { // Get the current and the previous location of the two fingers. Vector2 p1New = pinchGesture.Position; Vector2 p1Old = pinchGesture.Position - pinchGesture.Delta; Vector2 p2New = pinchGesture.Position2; Vector2 p2Old = pinchGesture.Position2 - pinchGesture.Delta2; // Get the distance between the current and the previous locations. float dNew = Vector2.Distance(p1New, p2New); float dOld = Vector2.Distance(p1Old, p2Old); // Use the ratio between old and new distance to scale the camera distance. float scale = dOld / dNew; if (!Numeric.IsNaN(scale)) { _cameraDistance *= scale; _cameraDistance = MathHelper.Clamp(_cameraDistance, MinCameraDistance, MaxCameraDistance); } }
/// <overloads> /// <summary> /// Gets a the bounds of the specified object relative to the viewport. /// </summary> /// </overloads> /// /// <summary> /// Gets a the bounds of the specified geometric object relative to the viewport. /// </summary> /// <param name="cameraNode">The camera node.</param> /// <param name="geometricObject">The geometric object.</param> /// <returns> /// The bounds (left, top, right, bottom) where each entry is in the range [0, 1]. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="cameraNode"/> or <paramref name="geometricObject"/> is /// <see langword="null"/>. /// </exception> internal static Vector4F GetBounds(CameraNode cameraNode, IGeometricObject geometricObject) { // Notes: // Do not call this GetBounds() method for spheres. Use the other overload for spheres. // // At first this problem seems trivial, we only have to get the support // points of the geometric object's shape in the directions of the frustum // plane normal vectors. The we project these points to the near plane... // But this does not work because which can be seen if you draw simple // drop down sketch. Actually each eye ray has its own direction therefore // it has its normal and its own support direction! if (cameraNode == null) { throw new ArgumentNullException("cameraNode"); } if (geometricObject == null) { throw new ArgumentNullException("geometricObject"); } Debug.Assert(!(geometricObject.Shape is SphereShape), "Call a different GetBounds() overload for spheres!"); // Projection properties. var camera = cameraNode.Camera; var projection = camera.Projection; float near = projection.Near; float left = projection.Left; float right = projection.Right; float width = projection.Width; float top = projection.Top; float bottom = projection.Bottom; float height = projection.Height; // Get AABB in view space. Pose localToViewPose = cameraNode.PoseWorld.Inverse * geometricObject.Pose; Aabb aabb = geometricObject.Shape.GetAabb(geometricObject.Scale, localToViewPose); // Is the AABB in front of the near plane (= totally clipped)? if (aabb.Minimum.Z >= -near) { return(new Vector4F(0)); } // Does the AABB contain the origin? if (GeometryHelper.HaveContact(aabb, Vector3F.Zero)) { return(new Vector4F(0, 0, 1, 1)); } // Project the AABB far face to the near plane. Vector2F min; min.X = aabb.Minimum.X / -aabb.Minimum.Z * near; min.Y = aabb.Minimum.Y / -aabb.Minimum.Z * near; Vector2F max; max.X = aabb.Maximum.X / -aabb.Minimum.Z * near; max.Y = aabb.Maximum.Y / -aabb.Minimum.Z * near; // If the AABB z extent overlaps the origin, some results are invalid. if (aabb.Maximum.Z > -Numeric.EpsilonF) { if (aabb.Minimum.X < 0) { min.X = left; } if (aabb.Maximum.X > 0) { max.X = right; } if (aabb.Minimum.Y < 0) { min.Y = bottom; } if (aabb.Maximum.Y > 0) { max.Y = top; } } else { // The AABB near face is also in front. Project AABB near face to near plane // and take the most extreme. min.X = Math.Min(min.X, aabb.Minimum.X / -aabb.Maximum.Z * near); min.Y = Math.Min(min.Y, aabb.Minimum.Y / -aabb.Maximum.Z * near); max.X = Math.Max(max.X, aabb.Maximum.X / -aabb.Maximum.Z * near); max.Y = Math.Max(max.Y, aabb.Maximum.Y / -aabb.Maximum.Z * near); } Vector4F bounds; bounds.X = (min.X - left) / width; bounds.Y = (top - max.Y) / height; bounds.Z = (max.X - left) / width; bounds.W = (top - min.Y) / height; bounds.X = MathHelper.Clamp(bounds.X, 0, 1); bounds.Y = MathHelper.Clamp(bounds.Y, 0, 1); bounds.Z = MathHelper.Clamp(bounds.Z, bounds.X, 1); bounds.W = MathHelper.Clamp(bounds.W, bounds.Y, 1); return(bounds); }
/// <summary> /// Gets the bounds of the specified sphere relative to the viewport. /// </summary> /// <param name="cameraNode">The camera node.</param> /// <param name="positionWorld">The sphere center in world space.</param> /// <param name="radius">The sphere radius.</param> /// <returns> /// The bounds (left, top, right, bottom) where each entry is in the range [0, 1]. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="cameraNode"/> is <see langword="null"/>. /// </exception> internal static Vector4F GetBounds(CameraNode cameraNode, Vector3F positionWorld, float radius) { var camera = cameraNode.Camera; var projection = camera.Projection; float near = projection.Near; float left = projection.Left; float width = projection.Width; float top = projection.Top; float height = projection.Height; Vector3F l = cameraNode.PoseWorld.ToLocalPosition(positionWorld); float r = radius; // Default bounds (left, top, right, bottom) var bounds = new Vector4F(0, 0, 1, 1); // ----- Solve for N = (x, 0, z). // Discriminant already divided by 4: float d = (r * r * l.X * l.X - (l.X * l.X + l.Z * l.Z) * (r * r - l.Z * l.Z)); if (d > 0) { // Camera is outside the sphere. float rootD = (float)Math.Sqrt(d); // Now check two possible solutions (+/- rootD): float nx1 = (r * l.X + rootD) / (l.X * l.X + l.Z * l.Z); float nx2 = (r * l.X - rootD) / (l.X * l.X + l.Z * l.Z); float nz1 = (r - nx1 * l.X) / l.Z; float nz2 = (r - nx2 * l.X) / l.Z; // Compute tangent position (px, 0, pz) on the sphere. float pz1 = (l.X * l.X + l.Z * l.Z - r * r) / (l.Z - (nz1 / nx1) * l.X); float pz2 = (l.X * l.X + l.Z * l.Z - r * r) / (l.Z - (nz2 / nx2) * l.X); if (pz1 < 0) { // Plane (nx1, 0, nz1) is within camera frustum. float px = -pz1 * nz1 / nx1; float x = nz1 * near / nx1; // x coordinate on the near plane. float boundsX = (x - left) / width; // Value relative to viewport. (0 = left, 1 = right) // Shrink the scissor rectangle on the left or on the right side. if (px < l.X) { bounds.X = Math.Max(bounds.X, boundsX); } else { bounds.Z = Math.Min(bounds.Z, boundsX); } } if (pz2 < 0) { float px = -pz2 * nz2 / nx2; float x = nz2 * near / nx2; float scissorX = (x - left) / width; if (px < l.X) { bounds.X = Math.Max(bounds.X, scissorX); } else { bounds.Z = Math.Min(bounds.Z, scissorX); } } } // ----- Solve for N = (0, y, z) first. d = (r * r * l.Y * l.Y - (l.Y * l.Y + l.Z * l.Z) * (r * r - l.Z * l.Z)); if (d > 0) { // Camera is outside the sphere. float rootD = (float)Math.Sqrt(d); float ny1 = (r * l.Y + rootD) / (l.Y * l.Y + l.Z * l.Z); float ny2 = (r * l.Y - rootD) / (l.Y * l.Y + l.Z * l.Z); float nz1 = (r - ny1 * l.Y) / l.Z; float nz2 = (r - ny2 * l.Y) / l.Z; float pz1 = (l.Y * l.Y + l.Z * l.Z - r * r) / (l.Z - (nz1 / ny1) * l.Y); float pz2 = (l.Y * l.Y + l.Z * l.Z - r * r) / (l.Z - (nz2 / ny2) * l.Y); if (pz1 < 0) { float py = -pz1 * nz1 / ny1; float y = nz1 * near / ny1; float scissorY = -(y - top) / height; if (py > l.Y) { bounds.Y = Math.Max(bounds.Y, scissorY); } else { bounds.W = Math.Min(bounds.W, scissorY); } } if (pz2 < 0) { float py = -pz2 * nz2 / ny2; float y = nz2 * near / ny2; float scissorY = -(y - top) / height; if (py > l.Y) { bounds.Y = Math.Max(bounds.Y, scissorY); } else { bounds.W = Math.Min(bounds.W, scissorY); } } } bounds.X = MathHelper.Clamp(bounds.X, 0, 1); bounds.Y = MathHelper.Clamp(bounds.Y, 0, 1); bounds.Z = MathHelper.Clamp(bounds.Z, bounds.X, 1); bounds.W = MathHelper.Clamp(bounds.W, bounds.Y, 1); return(bounds); }
private void UpdateSky() { // Update ephemeris model. #if XBOX _ephemeris.Time = new DateTimeOffset(_time.Ticks, TimeSpan.Zero); #else _ephemeris.Time = _time; #endif _ephemeris.Update(); // Update rotation of milky way. We also need to add an offset because the // cube map texture is rotated. _milkyWayNode.PoseWorld = new Pose( (Matrix33F)_ephemeris.EquatorialToWorld.Minor * Matrix33F.CreateRotationZ(ConstantsF.PiOver2 + -0.004f) * Matrix33F.CreateRotationX(ConstantsF.PiOver2 + -0.002f)); // Update rotation of stars. _starfieldNode.PoseWorld = new Pose((Matrix33F)_ephemeris.EquatorialToWorld.Minor); // Update direction of sun. SunDirection = (Vector3F)_ephemeris.SunDirectionRefracted; var sunUp = SunDirection.Orthonormal1; _sunNode.LookAt(SunDirection, sunUp); // Update direction of moon. var moonDirection = (Vector3F)_ephemeris.MoonPosition.Normalized; var moonUp = (Vector3F)_ephemeris.EquatorialToWorld.TransformDirection(Vector3D.Up); _moonNode.LookAt(moonDirection, moonUp); // The moon needs to know the sun position and brightness to compute the moon phase. _moonNode.SunDirection = (Vector3F)_ephemeris.SunPosition.Normalized; _moonNode.SunLight = Ephemeris.ExtraterrestrialSunlight * SunlightScale; // The ScatteringSky needs the sun direction and brightness to compute the sky colors. _scatteringSkyNode.SunDirection = SunDirection; _scatteringSkyNode.SunColor = Ephemeris.ExtraterrestrialSunlight * ScatteringSkyLightScale; // Update the light directions. _sunlightNode.LookAt(-SunDirection, sunUp); _moonlightNode.LookAt(-moonDirection, moonUp); // The ScatteringSkyNode can compute the actual sunlight. SunLight = _scatteringSkyNode.GetSunlight(); // Undo the ScatteringSkyLightScale and apply a custom scale for the sunlight. SunLight = SunLight / ScatteringSkyLightScale * SunlightScale; // The ScatteringSkyNode can also compute the ambient light by sampling the // sky hemisphere. AmbientLight = _scatteringSkyNode.GetAmbientLight(256); AmbientLight = AmbientLight / ScatteringSkyLightScale * SunlightScale; // Desaturate the ambient light to avoid very blue shadows. AmbientLight = InterpolationHelper.Lerp( new Vector3F(Vector3F.Dot(AmbientLight, GraphicsHelper.LuminanceWeights)), AmbientLight, 0.5f); // The Ephemeris model can compute the actual moonlight. Vector3F moonlight, moonAmbient; Ephemeris.GetMoonlight( _scatteringSkyNode.ObserverAltitude, 2.2f, _ephemeris.MoonPosition, (float)_ephemeris.MoonPhaseAngle, out moonlight, out moonAmbient); moonlight *= MoonlightScale; moonAmbient *= MoonlightScale; // Scale sun light to 0 at horizon. var directionalLightColor = SunLight; directionalLightColor *= MathHelper.Clamp(SunDirection.Y / 0.1f, 0, 1); var directionalLight = ((DirectionalLight)_sunlightNode.Light); directionalLight.Color = directionalLightColor; ((DirectionalLight)_moonlightNode.Light).Color = moonlight; ((AmbientLight)_ambientLightNode.Light).Color = AmbientLight + moonAmbient + LightPollution; // Use the sunlight color to create a bright sun disk. var sunDiskColor = SunLight; sunDiskColor.TryNormalize(); _sunNode.GlowColor0 = sunDiskColor * Ephemeris.ExtraterrestrialSunlight.Length * SunlightScale * 10; if (_enableClouds) { // Update lighting info of cloud layer nodes. _cloudLayerNode0.SunDirection = SunDirection; _cloudLayerNode0.SunLight = SunLight; _cloudLayerNode0.AmbientLight = AmbientLight; // The second cloud layer uses simple unlit clouds (only ambient lighting). _cloudLayerNode1.SunDirection = SunDirection; _cloudLayerNode1.SunLight = Vector3F.Zero; _cloudLayerNode1.AmbientLight = AmbientLight + SunLight; // Use the cloud map as the texture for the directional light to create cloud shadows. if (EnableCloudShadows) { directionalLight.Texture = _cloudLayerNode0.CloudMap.Texture; } else { directionalLight.Texture = null; } // Compute a texture offset so that the current sun position and the projected cloud // shadows match. // Since sky dome is always centered on the camera, position the sunlight node in // line with the camera. var cameraPosition = _cameraObject.CameraNode.PoseWorld.Position; var upVector = Vector3F.AreNumericallyEqual(SunDirection, Vector3F.UnitZ) ? Vector3F.UnitY : Vector3F.UnitZ; _sunlightNode.LookAt(cameraPosition + SunDirection, cameraPosition, upVector); // Choose a scale for the cloud shadows. directionalLight.TextureScale = new Vector2F(-1000); // Get the scaled texture offset for the sun direction. directionalLight.TextureOffset = _cloudLayerNode0.GetTextureCoordinates(SunDirection) * directionalLight.TextureScale; } // The ScatteringSkyNode can also estimate a fog color by sampling the horizon colors. Vector3F fogColor = _scatteringSkyNode.GetFogColor(256, FogSampleAngle); // Desaturate the fog color. fogColor = InterpolationHelper.Lerp( new Vector3F(Vector3F.Dot(fogColor, GraphicsHelper.LuminanceWeights)), fogColor, FogSaturation); // Find any FogNode in the scene and update its fog color. foreach (var fogNode in ((Scene)_scene).GetSubtree().OfType <FogNode>()) { var fog = fogNode.Fog; fog.Color0 = fog.Color1 = new Vector4F(fogColor, 1); fog.ScatteringSymmetry = FogScatteringSymmetry; } // TODO: If the fog is dense, reduce the direct light and increase the ambient light. }
protected override void OnDraw(Renderer spriterenderer, double elapsedGameTime, float opacity) { base.OnDraw(spriterenderer, elapsedGameTime, opacity); if (this.MinimapTexture == null || this.MapRegion.Width <= 0 || this.MapRegion.Height <= 0) { return; } var pos = this.RenderPosition; var size = this.RenderSize; spriterenderer.End(); spriterenderer.BeginClipped(new Rect(pos.X, pos.Y, size.Width, size.Height)); Matrix transform; Vector2 cameraPos; Vector2 cameraSize; //绘制canvas { var canvas = this.MinimapTexture; var mapRegion = this.MapRegion; //计算世界坐标系到小地图坐标系的转换 transform = Matrix.CreateTranslation(-mapRegion.X, -mapRegion.Y, 0) * Matrix.CreateScale((float)canvas.Width / mapRegion.Width, (float)canvas.Height / mapRegion.Height, 0); var vp = this.CameraViewPort; cameraPos = Vector2.Transform(new Vector2(vp.X, vp.Y), transform); cameraSize = new Vector2(vp.Width * transform.Scale.X, vp.Height * transform.Scale.Y); //计算小地图显示位置 PointF canvasPos = new PointF(-this.canvasPosCache.X, -this.canvasPosCache.Y); if (this.Width >= canvas.Width) { canvasPos.X = (this.Width - canvas.Width) / 2; } else { if (cameraPos.X < canvasPos.X) { canvasPos.X = cameraPos.X; } else if (cameraPos.X + cameraSize.X > canvasPos.X + this.Width) { canvasPos.X = cameraPos.X + cameraSize.X - this.Width; } canvasPos.X = -MathHelper.Clamp(canvasPos.X, 0, canvas.Width - this.Width); } if (this.Height >= canvas.Height) { canvasPos.Y = (this.Height - canvas.Height) / 2; } else { if (cameraPos.Y < canvasPos.Y) { canvasPos.Y = cameraPos.Y; } else if (cameraPos.Y + cameraSize.Y > canvasPos.Y + this.Height) { canvasPos.Y = cameraPos.Y + cameraSize.Y - this.Height; } canvasPos.Y = -MathHelper.Clamp(canvasPos.Y, 0, canvas.Height - this.Height); } this.canvasPosCache = canvasPos; //计算全局坐标 canvasPos = new PointF(pos.X + canvasPos.X, pos.Y + canvasPos.Y); transform *= Matrix.CreateTranslation(canvasPos.X, canvasPos.Y, 0); //绘制小地图 spriterenderer.Draw(canvas, canvasPos, new Size(canvas.Width, canvas.Height), new ColorW(1f, 1f, 1f, opacity), false); } /* 绘制框线*/ if (CameraRegionVisible) { using (var gb = spriterenderer.CreateGeometryBuffer()) { var pts = new List <PointF>(); pts.Add(new PointF(cameraPos.X, cameraPos.Y)); pts.Add(new PointF(cameraPos.X + cameraSize.X, cameraPos.Y)); pts.Add(new PointF(cameraPos.X + cameraSize.X, cameraPos.Y + cameraSize.Y)); pts.Add(new PointF(cameraPos.X, cameraPos.Y + cameraSize.Y)); pts.Add(pts[0]); gb.FillColor(pts, GeometryPrimitiveType.LineStrip); spriterenderer.DrawGeometryColor(gb, new PointF(pos.X + canvasPosCache.X, pos.Y + canvasPosCache.Y), new ColorW(255, 255, 0), opacity, 0f); } } //绘制小图标 Func <TextureBase, PointF, Rect> drawIconFunc = (texture, worldPos) => { var iconPos = Vector2.Transform(new Vector2(worldPos.X, worldPos.Y), transform); var posF = new PointF( Math.Round(iconPos.X - texture.Width / 2 - 0), Math.Round(iconPos.Y - texture.Height / 2 - 5)); var iconSize = new Size(texture.Width, texture.Height); spriterenderer.Draw(texture, posF, iconSize, new ColorW(1f, 1f, 1f, opacity), false); return(new Rect(posF.X - pos.X, posF.Y - pos.Y, iconSize.Width, iconSize.Height)); }; iconRectCache.Clear(); foreach (var icon in this.Icons) { switch (icon.IconType) { case IconType.Portal: { var texture = this.FindResource("portal") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.EnchantPortal: { var texture = this.FindResource("enchantportal") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.HiddenPortal: { var texture = this.FindResource("hiddenportal") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.Transport: { var texture = this.FindResource("transport") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.Npc: { var texture = this.FindResource("npc") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.EventNpc: { var texture = this.FindResource("eventnpc") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.Shop: { var texture = this.FindResource("shop") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.Trunk: { var texture = this.FindResource("trunk") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; case IconType.ArrowUp: { var texture = this.FindResource("arrowup") as TextureBase; if (texture != null) { var rect = drawIconFunc(texture, icon.WorldPosition); iconRectCache.Add(new IconRect() { Rect = rect, Tooltip = icon.Tooltip }); } } break; } } spriterenderer.EndClipped(); spriterenderer.Begin(); }
private void HandleKeyboardInput(GameTime gameTime) { float dt = (float)gameTime.ElapsedGameTime.TotalSeconds; var keyboardState = Keyboard.GetState(); if (keyboardState.IsKeyDown(Keys.Escape)) { Exit(); } if (keyboardState.IsKeyDown(Keys.P) && _previousKeyboardState.IsKeyUp(Keys.P)) { _pause = !_pause; } if (keyboardState.IsKeyDown(Keys.PageUp) && _previousKeyboardState.IsKeyUp(Keys.PageUp)) { NextPattern(); } else if (keyboardState.IsKeyDown(Keys.PageDown) && _previousKeyboardState.IsKeyUp(Keys.PageDown)) { PreviousPattern(); } if (keyboardState.IsKeyDown(Keys.LeftControl) && _previousKeyboardState.IsKeyUp(Keys.LeftControl)) { AddBullet(); } if (keyboardState.IsKeyDown(Keys.Space)) { AddBullet(); } if (keyboardState.IsKeyDown(Keys.Delete)) { _moverManager.Clear(); } if (keyboardState.IsKeyDown(Keys.E) && _previousKeyboardState.IsKeyUp(Keys.E)) { EditCurrentPatternFile(); } // Mover manager position if (keyboardState.IsKeyDown(Keys.I)) { _moverManagerPosition -= new Vector2(0, 250) * dt; } if (keyboardState.IsKeyDown(Keys.K)) { _moverManagerPosition += new Vector2(0, 250) * dt; } if (keyboardState.IsKeyDown(Keys.J)) { _moverManagerPosition -= new Vector2(250, 0) * dt; } if (keyboardState.IsKeyDown(Keys.L)) { _moverManagerPosition += new Vector2(250, 0) * dt; } // Camera if (keyboardState.IsKeyDown(Keys.NumPad7)) { _camera.Zoom -= dt * 0.5f; } if (keyboardState.IsKeyDown(Keys.NumPad9)) { _camera.Zoom += dt * 0.5f; } _camera.Zoom = MathHelper.Clamp(_camera.Zoom, 0.1f, 10f); if (keyboardState.IsKeyDown(Keys.NumPad8)) { _camera.Position -= new Vector2(0, 250) * dt; } if (keyboardState.IsKeyDown(Keys.NumPad5)) { _camera.Position += new Vector2(0, 250) * dt; } if (keyboardState.IsKeyDown(Keys.NumPad4)) { _camera.Position -= new Vector2(250, 0) * dt; } if (keyboardState.IsKeyDown(Keys.NumPad6)) { _camera.Position += new Vector2(250, 0) * dt; } if (keyboardState.IsKeyDown(Keys.NumPad1)) { Config.GameAeraSize += Config.GameAeraSize * (-0.01f); } if (keyboardState.IsKeyDown(Keys.NumPad3)) { Config.GameAeraSize += Config.GameAeraSize * 0.01f; } _previousKeyboardState = Keyboard.GetState(); }
protected override void OnHandleInput(InputContext context) { var inputService = InputService; if (_isTouchDevice) { // Scrolling on phone/tablet has priority over the actions of the visual children. // Therefore base.OnHandleInput() is called after this code section. Vector2F mousePosition = inputService.MousePosition; float t = (float)context.DeltaTime.TotalSeconds; if (_isDragging) { if (inputService.IsMouseOrTouchHandled || inputService.IsUp(MouseButtons.Left)) { // Dragging ends. _isDragging = false; // Check flick gesture. foreach (var gesture in inputService.Gestures) { if (gesture.GestureType == GestureType.Flick) { // Flick detected. // --> Set a scroll velocity proportional to the flick delta. _scrollVelocity = (Vector2F)(-gesture.Delta * FlickScrollVelocityFactor / t); _scrollVelocity = Vector2F.Clamp(_scrollVelocity, -MaxScrollVelocity, MaxScrollVelocity); inputService.IsMouseOrTouchHandled = true; break; } } } else { // Dragging continues. bool canScrollHorizontally = (ExtentWidth > ViewportWidth); bool canScrollVertically = (ExtentHeight > ViewportHeight); if (!_scrollToleranceExceeded) { // Check if drag tolerance has been exceeded. if (canScrollHorizontally && Math.Abs(mousePosition.X - _scrollStartPosition.X) > _scrollThreshold || canScrollVertically && Math.Abs(mousePosition.Y - _scrollStartPosition.Y) > _scrollThreshold) { // Start dragging. (Use current mouse position to avoid a "jump".) _scrollStartPosition = mousePosition; _scrollToleranceExceeded = true; } } if (_scrollToleranceExceeded) { // Drag content. if (canScrollHorizontally && inputService.MousePositionDelta.X != 0 || canScrollVertically && inputService.MousePositionDelta.Y != 0) { inputService.IsMouseOrTouchHandled = true; Vector2F minOffset = new Vector2F(0, 0); Vector2F maxOffset = new Vector2F(Math.Max(ExtentWidth - ViewportWidth, 0), Math.Max(ExtentHeight - ViewportHeight, 0)); Vector2F minVirtualOffset = minOffset - new Vector2F(SpringLength); Vector2F maxVirtualOffset = maxOffset + new Vector2F(SpringLength); Vector2F newOffset = _scrollStartOffset + _scrollStartPosition - mousePosition; if (canScrollHorizontally) { HorizontalOffset = MathHelper.Clamp(newOffset.X, minOffset.X, maxOffset.X); _virtualOffset.X = MathHelper.Clamp(newOffset.X, minVirtualOffset.X, maxVirtualOffset.X); } if (canScrollVertically) { VerticalOffset = MathHelper.Clamp(newOffset.Y, minOffset.Y, maxOffset.Y); _virtualOffset.Y = MathHelper.Clamp(newOffset.Y, minVirtualOffset.Y, maxVirtualOffset.Y); } _scrollVelocity = -inputService.MousePositionDelta / t; _scrollVelocity = Vector2F.Clamp(_scrollVelocity, -MaxScrollVelocity, MaxScrollVelocity); } } } } else { if (!inputService.IsMouseOrTouchHandled && inputService.IsPressed(MouseButtons.Left, false) && IsMouseOver) { // Dragging starts. _isDragging = true; // Remember the mouse position. _scrollStartPosition = mousePosition; _scrollStartOffset = _virtualOffset; _scrollToleranceExceeded = false; } } if (!inputService.IsMouseOrTouchHandled && inputService.IsDown(MouseButtons.Left)) _scrollVelocity = Vector2F.Zero; } base.OnHandleInput(context); if (!IsLoaded) return; // Mouse wheel scrolls vertically when the mouse cursor is over the scroll viewer. if (!inputService.IsMouseOrTouchHandled && IsMouseOver) { if (inputService.MouseWheelDelta != 0 && VerticalScrollBarVisibility != ScrollBarVisibility.Disabled && _verticalScrollBar != null) { inputService.IsMouseOrTouchHandled = true; var screen = Screen; float offset = inputService.MouseWheelDelta / screen.MouseWheelScrollDelta * screen.MouseWheelScrollLines; offset *= _verticalScrollBar.SmallChange; offset = VerticalOffset - offset; if (offset < 0) offset = 0; if (offset > ExtentHeight - ViewportHeight) offset = ExtentHeight - ViewportHeight; VerticalOffset = offset; } } // Scroll with game pad right stick. if (!inputService.IsGamePadHandled(context.AllowedPlayer)) { var gamePadState = inputService.GetGamePadState(context.AllowedPlayer); Vector2 rightStick = gamePadState.ThumbSticks.Right; float x = rightStick.X; float y = rightStick.Y; if (!Numeric.IsZero(x + y) && IsInActiveWindow()) { if (_horizontalScrollBar != null) { float offset = HorizontalOffset + 0.5f * x * _horizontalScrollBar.SmallChange; offset = MathHelper.Clamp(offset, 0, ExtentWidth - ViewportWidth); HorizontalOffset = offset; } if (_verticalScrollBar != null) { float offset = VerticalOffset - 0.5f * y * _verticalScrollBar.SmallChange; offset = MathHelper.Clamp(offset, 0, ExtentHeight - ViewportHeight); VerticalOffset = offset; } inputService.SetGamePadHandled(context.AllowedPlayer, true); } } }
protected override void OnUpdate(TimeSpan deltaTime) { float t = (float)deltaTime.TotalSeconds; bool canScrollHorizontally = (ExtentWidth > ViewportWidth); bool canScrollVertically = (ExtentHeight > ViewportHeight); Vector2F minOffset = new Vector2F(0, 0); Vector2F maxOffset = new Vector2F(Math.Max(ExtentWidth - ViewportWidth, 0), Math.Max(ExtentHeight - ViewportHeight, 0)); Vector2F minVirtualOffset = minOffset - new Vector2F(SpringLength); Vector2F maxVirtualOffset = maxOffset + new Vector2F(SpringLength); if (!_isDragging) { // The user is currently not interacting with the content: // Move content on with current velocity. // ----- Spring // Apply spring force if user has dragged content beyond the limit. Vector2F springLength = new Vector2F(); if (_virtualOffset.X < minOffset.X) springLength.X = minOffset.X - _virtualOffset.X; else if (_virtualOffset.X > maxOffset.X) springLength.X = maxOffset.X - _virtualOffset.X; if (_virtualOffset.Y < minOffset.Y) springLength.Y = minOffset.Y - _virtualOffset.Y; else if (_virtualOffset.Y > maxOffset.Y) springLength.Y = maxOffset.Y - _virtualOffset.Y; _scrollVelocity = _scrollVelocity + SpringConstant * springLength * t; // ----- Damping // Apply damping to velocity. // See DigitalRune Blog for an explanation of damping formulas: // http://www.digitalrune.com/Support/Blog/tabid/719/EntryId/78/Damping-in-Computer-Games.aspx // Use a stronger damping if the spring is active. Vector2F damping = new Vector2F(springLength.X != 0 ? SpringDamping : ScrollDamping, springLength.Y != 0 ? SpringDamping : ScrollDamping); _scrollVelocity = (Vector2F.One - Vector2F.Min(damping * t, Vector2F.One)) * _scrollVelocity; // ----- Position Update // Compute new scroll offset. Vector2F newOffset = _virtualOffset + _scrollVelocity * t; // ----- Limits // Stop scroll velocity when we hit the max spring length. if (newOffset.X < minVirtualOffset.X || newOffset.X > maxVirtualOffset.X) _scrollVelocity.X = 0; if (newOffset.Y < minVirtualOffset.Y || newOffset.Y > maxVirtualOffset.Y) _scrollVelocity.Y = 0; // ----- Snapping // Snap to min or max offset when the spring pushes the content back. if (_virtualOffset.X < minOffset.X && newOffset.X > minOffset.X) { newOffset.X = minOffset.X; _scrollVelocity.X = 0; } else if (_virtualOffset.X > maxOffset.X && newOffset.X < maxOffset.X) { newOffset.X = maxOffset.X; _scrollVelocity.X = 0; } if (_virtualOffset.Y < minOffset.Y && newOffset.Y > minOffset.Y) { newOffset.Y = minOffset.Y; _scrollVelocity.Y = 0; } else if (_virtualOffset.Y > maxOffset.Y && newOffset.Y < maxOffset.Y) { newOffset.Y = maxOffset.Y; _scrollVelocity.Y = 0; } // ----- Velocity Clamping // When the velocity reaches a min limit, clamp it to zero. Otherwise, the content // never really stops. if (_scrollVelocity.LengthSquared() < MinScrollVelocity * MinScrollVelocity) _scrollVelocity = Vector2F.Zero; // ----- Update HorizontalOffset and VerticalOffset. if (canScrollHorizontally) { HorizontalOffset = MathHelper.Clamp(newOffset.X, minOffset.X, maxOffset.X); _virtualOffset.X = MathHelper.Clamp(newOffset.X, minVirtualOffset.X, maxVirtualOffset.X); } if (canScrollVertically) { VerticalOffset = MathHelper.Clamp(newOffset.Y, minOffset.Y, maxOffset.Y); _virtualOffset.Y = MathHelper.Clamp(newOffset.Y, minVirtualOffset.Y, maxVirtualOffset.Y); } } // ----- Scale Transform if (Content != null) { // Apply scale transform to content if it is pushed beyond the limit. Vector2F scale = Vector2F.One; Vector2F transformOrigin = new Vector2F(0.5f); // Scale content horizontally. if (_virtualOffset.X < minOffset.X) { // User pushes content to the right. scale.X = (ExtentWidth - (minOffset.X - _virtualOffset.X)) / ExtentWidth; transformOrigin.X = 1.0f; } else if (_virtualOffset.X > maxOffset.X) { // User pushes content to the left. scale.X = (ExtentWidth - (_virtualOffset.X - maxOffset.X)) / ExtentWidth; transformOrigin.X = 0.0f; } // Scale content vertically. if (_virtualOffset.Y < minOffset.Y) { // User pushes content down. scale.Y = (ExtentHeight - (minOffset.Y - _virtualOffset.Y)) / ExtentHeight; transformOrigin.Y = 1.0f; } else if (_virtualOffset.Y > maxOffset.Y) { // User pushes content up. scale.Y = (ExtentHeight - (_virtualOffset.Y - maxOffset.Y)) / ExtentHeight; transformOrigin.Y = 0.0f; } Content.RenderScale = scale; Content.RenderTransformOrigin = transformOrigin; } // ----- ScrollBar Opacity Animation if (_isTouchDevice) { // Animate opacity of horizontal and vertical scroll bars. if (!_isDragging && _scrollVelocity.LengthSquared() < 1) { // Fade out. if (_horizontalScrollBar != null && _horizontalScrollBar.IsVisible && _horizontalScrollBar.Opacity > 0) { _horizontalScrollBar.Opacity = MathHelper.Clamp(_horizontalScrollBar.Opacity - ScrollBarFadeVelocity * t, 0, 1); } if (_verticalScrollBar != null && _verticalScrollBar.IsVisible && _verticalScrollBar.Opacity > 0) { _verticalScrollBar.Opacity = MathHelper.Clamp(_verticalScrollBar.Opacity - ScrollBarFadeVelocity * t, 0, 1); } } else if (_scrollVelocity.LengthSquared() >= 1) { // Fade in. if (_horizontalScrollBar != null && _horizontalScrollBar.IsVisible && canScrollHorizontally && _horizontalScrollBar.Opacity < 1) { _horizontalScrollBar.Opacity = MathHelper.Clamp( _horizontalScrollBar.Opacity + 2 * ScrollBarFadeVelocity * t, 0, 1); } if (_verticalScrollBar != null && _verticalScrollBar.IsVisible && canScrollVertically && _verticalScrollBar.Opacity < 1) { _verticalScrollBar.Opacity = MathHelper.Clamp(_verticalScrollBar.Opacity + 2 * ScrollBarFadeVelocity * t, 0, 1); } } } #else // Coerce offsets to allowed range. Vector2F minOffset = new Vector2F(0, 0); Vector2F maxOffset = new Vector2F(Math.Max(ExtentWidth - ViewportWidth, 0), Math.Max(ExtentHeight - ViewportHeight, 0)); float horizontalOffset = HorizontalOffset; if (horizontalOffset < minOffset.X) HorizontalOffset = minOffset.X; else if (horizontalOffset > maxOffset.X) HorizontalOffset = maxOffset.X; float verticalOffset = VerticalOffset; if (verticalOffset < minOffset.Y) VerticalOffset = minOffset.Y; else if (verticalOffset > maxOffset.Y) VerticalOffset = maxOffset.Y; base.OnUpdate(deltaTime); }
public static void Clamp(ref Vector2 value1, ref Vector2 min, ref Vector2 max, out Vector2 result) { result = new Vector2( MathHelper.Clamp(value1.X, min.X, max.X), MathHelper.Clamp(value1.Y, min.Y, max.Y)); }
public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max) { return(new Vector2( MathHelper.Clamp(value1.X, min.X, max.X), MathHelper.Clamp(value1.Y, min.Y, max.Y))); }