minimap Drawing issue with graphics in thread - c#

I am attempting to create a minimap for an overlay. Currently the radar portion is working, targets rotate around the center point at the correct distance. I am having trouble printing the graphic that holds the minimap to screen. Everything other than the map prints to screen using the SlimDx.Direct3D9 library but based on my research the best way to create sub images based on player location was with graphics and this method avoids the OutOfMemory exception with cloning bitmaps and is more efficient.
if (ShowRadar)
{
//Draw background
//DrawFilledBox(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 50, 201f, 201f, Color.DarkOliveGreen, RadarTransparency);
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25);
if (DXTextrureMap != null)
{
//Transpose player posistion to map position and create the current mini map image
x = (int)player_X / 8;// +/- 4000 is max player range /8 = 500 = max map range
z = (int)player_Z / 8;
mini = new Rectangle(x, z, 100, 100);
Graphics g = Graphics.FromImage(originalBitmap);
g.DrawImage(originalBitmap, mini, new Rectangle((int)this.GameWindowRect.Right - 125, (int)this.GameWindowRect.Top + 125, 100, 100), System.Drawing.GraphicsUnit.Pixel);
}//the rest works as it should and displays on screen
if (RadarCenter.Length() > 0f)
{
//Display each entity in correct relational position
foreach (ENTITY entity in Entity)
{
Vector2 pointToRotate = new Vector2(entity.Pos.X, entity.Pos.Z);
Vector2 vector3 = new Vector2(player_X, player_Z);
pointToRotate = vector3 - pointToRotate;
float num30 = pointToRotate.Length() * 0.5f;
num30 = Math.Min(num30, 90f);
pointToRotate.Normalize();
pointToRotate = (Vector2)(pointToRotate * num30);
pointToRotate += RadarCenter;
pointToRotate = RotatePoint(pointToRotate, RadarCenter, player_D, true);
if (entity.Type == 0x04 && RadarPlayers)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.SkyBlue);
}
if ((entity.Type == 0x0C || entity.Type == 0x14 || entity.Type == 0x50 || entity.Type == 0x5b) && RadarAggressive)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.Red);
}
if ((entity.Type == 0x13 || entity.Type == 0x55) && RadarAnimals)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.LightGreen);
}
if ((entity.Type == 0x11 || entity.Type == 0x72 || entity.Type == 0x76) && RadarVehicles)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.HotPink);
}
}
}
//Draw radar border
DrawBoxAbs(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 50, 201f, 201f, 1f, Color.Black);
//Draw radar axis
DrawLine(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 50, this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 251, 1f, Color.Black);
DrawLine(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 150, this.GameWindowRect.Right - 24, this.GameWindowRect.Top + 150, 1f, Color.Black);
//Center point
DrawFilledBox(RadarCenter.X - 1f, RadarCenter.Y - 1f, 3f, 3f, Color.White);
}
What is the correct way to print my graphic?

Makes more sense when you realize a graphic draws on the image it grabs, also had to dispose of some elements. Time to make it rotate then perhaps add headings.
if (DXTextrureMap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
x = (int)player_X / 4;// +/- 4000 is max player range and /4 = 1000 = max map range
z = (int)player_Z / 4;
mini = new Rectangle(x-78, z-78, 157, 157);
croppedBitmap = new Bitmap(200, 200);
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 157, 157), mini, GraphicsUnit.Pixel);
}
//croppedBitmap = Copy(originalBitmap, mini);
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmap.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//croppedImage.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(100f, 100f, 0f), new Vector3(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 135, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}

Related

Skisharp ColorMatrix Invert black/white

I try to draw an image and after the first drawing i want invert the color of the drawing.
If the background of my new square is black i need white and if it white i need black.
In my example i draw 3 squares and make a offset of 10 pixel in the x.
Unfortunately, it does not produce the wanted result.
using var skBitmap = new SKBitmap(100, 40);
using var skCanvas = new SKCanvas(skBitmap);
skCanvas.Clear(SKColors.White);
var color = SKColors.Black;
float[] invertMatrix = {
-1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
using var skPaint = new SKPaint();
skPaint.Color = color;
skPaint.Style = SKPaintStyle.Fill;
skCanvas.DrawRect(10, 10, 20, 20, skPaint);
skPaint.ColorFilter = SKColorFilter.CreateColorMatrix(invertMatrix);
//move +10 in x
skCanvas.DrawRect(20, 10, 20, 20, skPaint);
//move +10 in x
skCanvas.DrawRect(30, 10, 20, 20, skPaint);
It looks like after making your second call to skCanvas.DrawRect you are not updating the colour.
Further, in your drawing 20 pixel wide rectangles, and overlapping them, as your only shifting your new rectangle 10 pixels to the right.
Following your wanted example, your need to make the 4th call to changing the colour back to black, then make a call to skCanvas.DrawRect(40, 10, 20, 20, skPaint).
Given that you are shifting your rectangles by 10 pixels and overlapping them, I suggest you update your calls to produce a thinner initial rectangle at 10 pixels wide.
I have found a solution for my problem unfortunately without a matrix.
using var skBitmap = new SKBitmap(100, 40);
using var skCanvas = new SKCanvas(skBitmap);
skCanvas.Clear(SKColors.White);
var color = SKColors.Black;
using var skPaint = new SKPaint();
skPaint.Color = color;
skPaint.Style = SKPaintStyle.Fill;
skCanvas.DrawRect(10, 10, 20, 20, skPaint);
DrawInvertRectangle(20, 10, 20, 20, skBitmap);
DrawInvertRectangle(30, 10, 20, 20, skBitmap);
private static void DrawInvertRectangle(int x, int y, int width, int height, SKBitmap skBitmap)
{
using var skPaint = new SKPaint();
skPaint.Color = SKColors.Black;
skPaint.Style = SKPaintStyle.Fill;
using var skBitmapInvert = new SKBitmap(skBitmap.Width, skBitmap.Height);
using var skCanvas = new SKCanvas(skBitmapInvert);
skCanvas.DrawRect(x, y, width, height, skPaint);
for (var row = 0; row < skBitmapInvert.Height; row++)
{
for (var column = 0; column < skBitmapInvert.Width; column++)
{
var pixel = skBitmap.GetPixel(column, row);
var pixelInvert = skBitmapInvert.GetPixel(column, row);
if (pixelInvert.Alpha == 255 && pixelInvert.Blue == 0 && pixel.Blue == 255)
{
skBitmap.SetPixel(column, row, SKColors.Black);
}
if (pixelInvert.Alpha == 255 && pixelInvert.Blue == 0 && pixel.Blue == 0)
{
skBitmap.SetPixel(column, row, SKColors.White);
}
}
}
}

How to rotate a camera around its own axis in WPF viewport3D?

I am working on a 3D application in WPF and having trouble with the camera. It should be possible to rotate the camera around its own axis (with other words, look around) using the mouse but I can not get it to work properly. I create the camera with the following code:
PerspectiveCamera perspectiveCamera = new PerspectiveCamera(new Point3D(0, 30, 0), new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), 90);
perspectiveCamera.NearPlaneDistance = 0.001;
perspectiveCamera.FarPlaneDistance = 1000;
center = new TranslateTransform3D(0, 30, 0);
rot_x = new AxisAngleRotation3D(new Vector3D(1, 0, 0), 0);
rot_y = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
rot_z = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
zoom = new ScaleTransform3D(1, 1, 1);
Transform3DGroup t = new Transform3DGroup();
t.Children.Add(zoom);
t.Children.Add(new RotateTransform3D(rot_x));
t.Children.Add(new RotateTransform3D(rot_y));
t.Children.Add(new RotateTransform3D(rot_z));
t.Children.Add(center);
perspectiveCamera.Transform = t;
myViewport3D.Camera = perspectiveCamera;
And then I try to rotate it using the following code:
private void OnViewportMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (mouseLeftIsDown)
{
Point position = e.GetPosition(this);
double rotZAngle = (rot_z.Angle % 360) + oldLeftPosition.X - position.X;
double rotXAngle = (rot_x.Angle % 360) + position.Y - oldLeftPosition.Y;
if (rotZAngle < 0) //rotZAngle is negative, make it positive
{
rotZAngle = 360 + rotZAngle;
}
if (rotXAngle < 0) //rotXAngle is negative, make it positive
{
rotXAngle = 360 + rotXAngle;
}
rot_z.Angle = rotZAngle;
rot_x.Angle = rotXAngle;
oldLeftPosition = position;
}
}
However, it seams that the rotation is not happening around the camera and instead somehere else. The model that I load at position (0,0,0) is becoming visible after I rotate 180 degrees around the z axis which should not be the case.
What am I missing?

c# Rotate bitmap about the center

I build a minimap for an overylay, it correctly updates images based on player location. I am attempting now to make it rotate, although the graphics function rotateTransform, rotates about the upper left corner, how can I rotate about the center?
if (ShowRadar)
{
//Center point
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25 - 15);
//If map exists
if (originalBitmap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
//player location range -4000 - 4000, bitmap range 0 - 8000
x = (int)(Math.Abs(-4000 - player_Z));
z = (int)(Math.Abs(4000 - player_X));
//minimap rec loc on bitmap with a scale of 1000x1000
mini = new Rectangle(x-500, z-500, 1000, 1000);
croppedBitmap = new Bitmap(200, 200);
croppedBitmapFinal = new Bitmap(200, 200);
//draw the crop
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 158, 158), mini, GraphicsUnit.Pixel);
grD.Dispose();
}
//redraw crop as circle
using (Graphics grD = Graphics.FromImage(croppedBitmapFinal))
{
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, croppedBitmapFinal.Width-45, croppedBitmapFinal.Height-45);
grD.SetClip(path);
//apply rotate - direction 0 to 3.15 = 0 to 180, -3.15 to 0 = 180 to 360; so multiply by (180/3.15)
grD.RotateTransform(player_D * 57);
grD.DrawImage(croppedBitmap, 0, 0);
grD.Dispose();
}
//make texture from final bitmap
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmapFinal.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//clean up and print to screen
croppedBitmapFinal.Dispose();
croppedBitmap.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(0f, 0f, 0f), new Vector3(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 35, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}
}
simply had to move the origin like so
if (ShowRadar)
{
//Center point
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25 - 15);
//If map exists
if (originalBitmap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
//player location range -4000 - 4000, bitmap range 0 - 8000
x = (int)(Math.Abs(-4000 - player_Z));
z = (int)(Math.Abs(4000 - player_X));
//minimap rec loc on bitmap with a scale of 1000x1000
mini = new Rectangle(x-500, z-500, 1000, 1000);
croppedBitmap = new Bitmap(200, 200);
croppedBitmapFinal = new Bitmap(200, 200);
//draw the crop
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 158, 158), mini, GraphicsUnit.Pixel);
grD.Dispose();
}
//redraw crop as circle
using (Graphics grD = Graphics.FromImage(croppedBitmapFinal))
{
//add circle clip to graphics
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, croppedBitmapFinal.Width-45, croppedBitmapFinal.Height-45);
grD.SetClip(path);
//move origin to center for rotate
float hw = (croppedBitmapFinal.Width / 2)-22.5f;
float hh = (croppedBitmapFinal.Height / 2)-22.5f;
grD.TranslateTransform(hw, hh);
//apply rotate - direction 0 to 3.15 = 0 to 180, -3.15 to 0 = 180 to 360; so multiply by (180/3.15)
grD.RotateTransform((player_D * 57)-90);
//Move origin back to original pos
grD.TranslateTransform(-hw, -hh);
//Draw to bitmap
grD.DrawImage(croppedBitmap, 0, 0);
grD.Dispose();
}
//make texture from final bitmap
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmapFinal.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//clean up and print to screen
croppedBitmapFinal.Dispose();
croppedBitmap.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(0f, 0f, 0f), new Vector3(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 35, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}

XNA C# Creating a Heads Up Display

I was wondering how to approach creating a HUD.
I currently have health, mana, and experience bar drawn to the screen at set coordinates. Downside of this is that when the camera pans the bars stay at their set coordinates, I want them to adjust to the viewport or not be influenced by positions but just simply drawn to the screen.
Edit
I managed to get the HUD to adjust using the camera's x and y coordinates.
I've created a separate class for drawing the HUD, but now they don't adjust.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using GameOne.Components;
namespace GameOne.GameScreens
{
public class HUD : BaseGameState
{
Player player;
Texture2D HealthBar;
Texture2D HealthBarPositive;
Texture2D HealthBarNegative;
Texture2D ManaBar;
Texture2D ManaBarPositive;
Texture2D ManaBarNegative;
Texture2D ExpBar;
Texture2D ExpBarPositive;
int CurrentHealth = 100;
int CurrentMana = 45;
int CurrentExp = 0;
public HUD(Game game, GameStateManager manager)
: base(game, manager)
{
player = new Player(game);
}
public void LoadContent()
{
base.LoadContent();
HealthBar = Game.Content.Load<Texture2D>(#"GUI\healthBar");
HealthBarPositive = Game.Content.Load<Texture2D>(#"GUI\healthBarPositive");
HealthBarNegative = Game.Content.Load<Texture2D>(#"GUI\healthBarNegative");
ManaBar = Game.Content.Load<Texture2D>(#"GUI\manaBar");
ManaBarPositive = Game.Content.Load<Texture2D>(#"GUI\manaBarPositive");
ManaBarNegative = Game.Content.Load<Texture2D>(#"GUI\manaBarNegative");
ExpBar = Game.Content.Load<Texture2D>(#"GUI\expBar");
ExpBarPositive = Game.Content.Load<Texture2D>(#"GUI\expBarPositive");
}
public void Update(GameTime gameTime)
{
if (InputHandler.KeyDown(Keys.F1))
{
CurrentHealth += 1;
}
if (InputHandler.KeyDown(Keys.F2))
{
CurrentHealth -= 1;
}
if (InputHandler.KeyDown(Keys.F3))
{
CurrentMana += 1;
}
if (InputHandler.KeyDown(Keys.F4))
{
CurrentMana -= 1;
}
if (InputHandler.KeyDown(Keys.F5))
{
CurrentExp += 1;
}
if (InputHandler.KeyDown(Keys.F6))
{
CurrentExp -= 1;
}
CurrentHealth = (int)MathHelper.Clamp(CurrentHealth, 0, 100);
CurrentMana = (int)MathHelper.Clamp(CurrentMana, 0, 45);
CurrentExp = (int)MathHelper.Clamp(CurrentExp, 0, 500);
}
public void Draw(GameTime gameTime)
{
GameRef.SpriteBatch.Draw(
HealthBarNegative,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 630, 150, 15),
Color.White);
GameRef.SpriteBatch.Draw(
HealthBarPositive,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 630, 150 * (int)CurrentHealth / 100, 15),
Color.White);
GameRef.SpriteBatch.Draw(
HealthBar,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 630, 150, 15),
Color.White);
GameRef.SpriteBatch.Draw(
ManaBarNegative,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 650, 150, 15),
Color.White);
GameRef.SpriteBatch.Draw(
ManaBarPositive,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 650, 150 * (int)CurrentMana / 45, 15),
Color.White);
GameRef.SpriteBatch.Draw(
ManaBar,
new Rectangle((int)player.Camera.Position.X + 150, (int)player.Camera.Position.Y + 650, 150, 15),
Color.White);
GameRef.SpriteBatch.Draw(
ExpBarPositive,
new Rectangle((int)player.Camera.Position.X + 10, (int)player.Camera.Position.Y + 680, 1260 * (int)CurrentExp / 500, 15),
Color.White);
GameRef.SpriteBatch.Draw(
ExpBar,
new Rectangle((int)player.Camera.Position.X + 10, (int)player.Camera.Position.Y + 680, 1260, 15),
Color.White);
}
}
}
When you are drawing your Health bars, is it inside a Spritebatch.Begin( ... ) where you specify your camera matrix?
If you draw it in its own Spritebatch.Begin, without the camera, the position of the health bars will stay relative to the screen.
When you extend you class with DrawableGameComponent, you can use the property DrawOrder to set it topmost.
The class will be a bit different, you have to overload Update, draw and LoadContent, but it will be all the same.
This is only usefull if you use a HUD with SpriteBatch.

How do I draw an annulus (doughnut) using GDI+?

I have been trying to draw an annulus (ring with thickness) with a transparent hole and a gradient rim in C# with very little success. Does anyone have any suggestions on how to do this?
here's a nice Blend Utility
Here's the Final result - thanks to BlueMonkMN
Rectangle GetSquareRec(double radius, int x, int y)
{
double r = radius;
double side = Math.Sqrt(Math.Pow(r, 2) / 2);
Rectangle rec = new Rectangle(x - ((int)side), y - ((int)side), (int)(side * 2) + x, (int)(side * 2) + y);
return rec;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gTarget = e.Graphics;
gTarget.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pTemp = new GraphicsPath();
Rectangle r = GetSquareRec(200, 225, 225);
pTemp.AddEllipse(r);
pTemp.AddEllipse(GetSquareRec(50, 225, 225));
Color[] colors = new Color[5];
colors[0] = Color.FromArgb(192, 192, 192);
colors[1] = Color.FromArgb(105, 0, 0);
colors[2] = Color.FromArgb(169, 169, 169);
colors[3] = Color.FromArgb(0, 0, 0);
colors[4] = Color.FromArgb(0, 0, 0);
float[] positions = new float[5];
positions[0] = 0f;
positions[1] = 0.1f;
positions[2] = 0.35f;
positions[3] = 0.5f;
positions[4] = 1f;
ColorBlend Cb = new ColorBlend();
Cb.Colors = colors;
Cb.Positions = positions;
PathGradientBrush pgb = new PathGradientBrush(pTemp);
pgb.InterpolationColors = Cb;
pgb.CenterPoint = new PointF(r.X + (r.Width / 2), r.Y + (r.Height / 2));
gTarget.FillPath(pgb, pTemp);
}
http://www.freeimagehosting.net/uploads/th.515733e62e.jpg
This is how I did it in the Scrolling Game Development Kit:
pTemp = new GraphicsPath();
pTemp.AddEllipse(Start.X, Start.Y, End.X - Start.X, End.Y - Start.Y);
pTemp.AddEllipse((Start.X * 3 + End.X) / 4f,
(Start.Y * 3 + End.Y) / 4f,
(End.X - Start.X) / 2f,
(End.Y - Start.Y) / 2f);
PathGradientBrush pgb = new PathGradientBrush(pTemp);
Blend b = new Blend();
b.Factors = new float[] { 0, 1, 1 };
b.Positions = new float[] { 0, .5F, 1 };
pgb.Blend = b;
pgb.CenterColor = ((SolidBrush)CurrentBrush).Color;
pgb.SurroundColors = new Color[] {CurrentPen.Color};
gTarget.FillPath(pgb, pTemp);
pgb.Dispose();
pTemp.Dispose();
(source: enigmadream.com)
I edited the original SGDK code for this sample because originally I wasn't smart enough to scale the gradient to exclude the hole, but now I guess I am :).
If you would rather see the gradient like this:
(source: enigmadream.com)
Then change the blend code to look like this:
Blend blend = new Blend();
blend.Factors = new float[] { 0, 1, 0, 0 };
blend.Positions = new float[] { 0, 0.25F, .5F, 1 };
pgb.Blend = blend;
You may use two calls to Graphics.DrawArc combined, drawing the top and bottom or left and right portions of the annulus, one portion at a time.

Categories