How to draw circle on texture in unity? - c#

I try to find and show corners using opencv and unity3d. I capture by unity camera. I send texture2d to c++ code that uses opencv. I detect corners using opencv(harris corner detector). And c++ code send to unity code corners points(x,y position on image).
Finally, I want to show these points. I try to draw circle on texture2d in unity. I use below code. But unity says that Type UnityEngine.Texture2D does not contain a definition for DrawCircle and no extension method DrawCircle of type UnityEngine.Texture2D could be found
How can I draw simple shape on unity3d?
Texture2D texture = new Texture2D(w, h,TextureFormat.RGB24 , false);
texture.DrawCircle(100, 100, 20, Color.green);
// Apply all SetPixel calls
texture.Apply();
mesh_renderer.material.mainTexture = texture;

More optimized solution from #ChrisH
(Original one slowed down my pc for 2 minutes when I tried to draw 300 circles on 1000x1000 texture while new one does it immediately due to avoiding extra iterations)
public static Texture2D DrawCircle(this Texture2D tex, Color color, int x, int y, int radius = 3)
{
float rSquared = radius * radius;
for (int u = x - radius; u < x + radius + 1; u++)
for (int v = y - radius; v < y + radius + 1; v++)
if ((x - u) * (x - u) + (y - v) * (y - v) < rSquared)
tex.SetPixel(u, v, color);
return tex;
}

Just make an extension method for Texture2d.
public static class Tex2DExtension
{
public static Texture2D Circle(this Texture2D tex, int x, int y, int r, Color color)
{
float rSquared = r * r;
for (int u=0; u<tex.width; u++) {
for (int v=0; v<tex.height; v++) {
if ((x-u)*(x-u) + (y-v)*(y-v) < rSquared) tex.SetPixel(u,v,color);
}
}
return tex;
}
}

Related

Getting weird results from terrain generation sys using 2D Perlin Noise

I am trying to make a terrain generation system in Unity, similar to Minecraft's, but using Unity's Perlin Noise function (so only 2D noise).
So I have a 16x16x16 chunk with a vector2int that has it's position (so like, if x & z = 0, then the blocks inside are from 0 to 16 in world coordinates).
This is how I'm trying to generate the height map of a chunk:
public void generate(float scale) {
GameObject root = new GameObject("Root");
// this.z & this.x are the chunk coordinates, size is 16
for(int z = this.z * size; z < (this.z + size); ++z) {
for (int x = this.x * size; x < (this.x + size); ++x) {
float[] coord = new float[2] { (float)x / size * scale,
(float)z / size * scale };
Debug.LogFormat("<color='blue'>Perlin coords |</color> x: {0}; y: {1}", coord[0], coord[1]);
float value = Mathf.PerlinNoise(coord[0], coord[1]);
// temporary
GameObject Cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Cube.transform.position = new Vector3(x, value, z);
Cube.transform.parent = root.transform;
}
}
return;
}
The results are... bad. See for yourself:
What can I do?
It looks good, looks just scrunched on the y transform.
float value = Mathf.PerlinNoise(coord[0], coord[1]);
This is going to give you problems, I'm not sure what coord[0] and coord[1] are but Mathf.PerlinNoise will return a random float between coord[0] and coord[1], so a random float will never be able to produce well aligned tiles.
Better off doing something like
int numTilesHigh = Random.Range(0,15);
for (int i = 0; i < numTilesHigh; i++) {
GameObject Cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Cube.transform.position = new Vector3(x, <cube height> * i, z);
Cube.transform.parent = root.transform;
}
ps I kind of like your screen shot, not in a minecraft way but it does look cool : - )

Ellipse Texture not doing what is expected

I am generating dynamic textures in monogame for simple shapes. Yes I know the disadvantages to this system, but I am just experimenting with building my own physics engine. I am trying to generate the texture for an ellipse as is described here.
I have a function PaintDescriptor that takes an x and y pixel coordinate and gives back what color it should be. Red is just while I am debugging, and normally it would be Color.Transparent.
public override Color PaintDescriptor(int x, int y)
{
float c = (float)Width / 2;
float d = (float)Height / 2;
return pow((x - c) / c, 2) + pow((y - d) / d, 2) <= 1 ? BackgroundColor : Color.Red;
}
Now this works if Width == Height, so, a circle. However, if they are not equal, it generates a texture with some ellipse like shapes, but also with banding/striping.
I have tried seeing if my width and height were switched, and ive tried several other things. One thing to note is that where in the normal coordinate system on desmos I have (y + d) / d, but since the screen's y axis is flipped, I have to flip the y offset in the code: (y - d) / d. The rest of the relating code for texture generation and drawing is here:
public Texture2D GenerateTexture(GraphicsDevice device, Func<int, int, Color> paint)
{
Texture2D texture = new Texture2D(device, Width, Height);
Color[] data = new Color[Width * Height];
for (int pixel = 0; pixel < data.Count(); pixel++)
data[pixel] = paint(pixel / Width, pixel % Height);
texture.SetData(data);
return texture;
}
public void Draw(float scale = 1, float layerdepth = 0, SpriteEffects se = SpriteEffects.None)
{
if (SBRef == null)
throw new Exception("No reference to spritebatch object");
SBRef.Draw(Texture, new Vector2(X, Y), null, null, null, 0, new Vector2(scale, scale), Color.White, se, layerdepth);
}
public float pow(float num, float power) //this is a redirect of math.pow to make code shorter and more readable
{
return (float)Math.Pow(num, power);
}
Why doesnt this match desmos? Why does it not make an ellipse?
EDIT: I forgot to mention, but one possible solution I have come across is to always draw a circle, and then scale it to the desired width and height. This is not acceptable for me for one because of some possible blurriness in drawing, or other artifacts, but more mainly because I want to understand whatever im not currently getting with this solution.
After sleeping and coming back with a fresh mindset for like the 10th time, I found the answer. in the function GenerateTexture:
data[pixel] = paint(pixel / Width, pixel % Height);
should be
data[pixel] = paint(pixel % Width, pixel / Height);

Drawing Bezier curves in MonoGame (XNA) produces scratchy lines

I recently got into using MonoGame, and I love the library.
However, I seem to be having some issues with drawing bezier curves
The result that my code produces looks something like this
Look bad, no?
The lines aren't smooth at all.
Let me show you some of the code:
//This is what I call to get all points between which to draw.
public static List<Point> computeCurvePoints(int steps)
{
List<Point> curvePoints = new List<Point>();
for (float x = 0; x < 1; x += 1 / (float)steps)
{
curvePoints.Add(getBezierPointRecursive(x, pointsQ));
}
return curvePoints;
}
//Calculates a point on the bezier curve based on the timeStep.
private static Point getBezierPointRecursive(float timeStep, Point[] ps)
{
if (ps.Length > 2)
{
List<Point> newPoints = new List<Point>();
for (int x = 0; x < ps.Length-1; x++)
{
newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep));
}
return getBezierPointRecursive(timeStep, newPoints.ToArray());
}
else
{
return interpolatedPoint(ps[0], ps[1], timeStep);
}
}
//Gets the linearly interpolated point at t between two given points (without manual rounding).
//Bad results!
private static Point interpolatedPoint(Point p1, Point p2, float t)
{
Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());
return new Point((int)roundedVector.X, (int)roundedVector.Y);
}
//Method used to draw a line between two points.
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D pixel, Vector2 begin, Vector2 end, Color color, int width = 1)
{
Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length() + width, width);
Vector2 v = Vector2.Normalize(begin - end);
float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
spriteBatch.Draw(pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
}
//DrawLine() is called as following. "pixel" is just a Texture2D with a single black pixel.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
for(int x = 0; x < curvePoints.Count-1; x++)
{
DrawExtenstion.DrawLine(spriteBatch, pixel, curvePoints[x].ToVector2(), curvePoints[x + 1].ToVector2(), Color.Black, 2);
}
spriteBatch.End();
base.Draw(gameTime);
}
I managed to make the line a bit smoother by adding some manual Math.Round() calls to my interpolatedPoint method
//Gets the linearly interpolated point at t between two given points (with manual rounding).
//Better results (but still not good).
private static Point interpolatedPoint(Point p1, Point p2, float t)
{
Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());
return new Point((int)Math.Round(roundedVector.X), (int)Math.Round(roundedVector.Y));
}
This produces the following result:
I had to remove one picture since Stackoverflow doesn't let me use more than two links
Are there any ways I can get this curve to be absolutely smooth?
Perhaps there is a problem with the DrawLine method?
Thanks in advance.
EDIT:
Okay, I managed to make the curve look a lot better by doing all the calculations with Vector2Ds and only converting it to a Point at the moment that it needs to be drawn
It still isn't perfect though :/
As Mike 'Pomax' Kamermans said,
it seems to have been a problem with the 2D surface not allowing subpixel drawing and thus causing rounding issues
Following craftworkgames' advice, I adapted the algorithm to draw the curve in 3D using a BasicEffect. This also allows for antialiasing, which smoothes out the curve a lot.
The result is the following:
A lot better!
Thank you very much for the helpful advice!
EDIT:
Here is the code I used for doing this.
I would also like to add that this webpage (http://gamedevelopment.tutsplus.com/tutorials/create-a-glowing-flowing-lava-river-using-bezier-curves-and-shaders--gamedev-919) helped me a lot while writing this code.
Also, please note that some of the names I used for defining the methods might not really make sense or can be confusing. This was something I quickly put together on an evening.
//Used for generating the mesh for the curve
//First object is vertex data, second is indices (both as arrays)
public static object[] computeCurve3D(int steps)
{
List<VertexPositionTexture> path = new List<VertexPositionTexture>();
List<int> indices = new List<int>();
List<Vector2> curvePoints = new List<Vector2>();
for (float x = 0; x < 1; x += 1 / (float)steps)
{
curvePoints.Add(getBezierPointRecursive(x, points3D));
}
float curveWidth = 0.003f;
for(int x = 0; x < curvePoints.Count; x++)
{
Vector2 normal;
if(x == 0)
{
//First point, Take normal from first line segment
normal = getNormalizedVector(getLineNormal(curvePoints[x+1] - curvePoints[x]));
}
else if (x + 1 == curvePoints.Count)
{
//Last point, take normal from last line segment
normal = getNormalizedVector(getLineNormal(curvePoints[x] - curvePoints[x-1]));
} else
{
//Middle point, interpolate normals from adjacent line segments
normal = getNormalizedVertexNormal(getLineNormal(curvePoints[x] - curvePoints[x - 1]), getLineNormal(curvePoints[x + 1] - curvePoints[x]));
}
path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * curveWidth, 0), new Vector2()));
path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * -curveWidth, 0), new Vector2()));
}
for(int x = 0; x < curvePoints.Count-1; x++)
{
indices.Add(2 * x + 0);
indices.Add(2 * x + 1);
indices.Add(2 * x + 2);
indices.Add(2 * x + 1);
indices.Add(2 * x + 3);
indices.Add(2 * x + 2);
}
return new object[] {
path.ToArray(),
indices.ToArray()
};
}
//Recursive algorithm for getting the bezier curve points
private static Vector2 getBezierPointRecursive(float timeStep, Vector2[] ps)
{
if (ps.Length > 2)
{
List<Vector2> newPoints = new List<Vector2>();
for (int x = 0; x < ps.Length - 1; x++)
{
newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep));
}
return getBezierPointRecursive(timeStep, newPoints.ToArray());
}
else
{
return interpolatedPoint(ps[0], ps[1], timeStep);
}
}
//Gets the interpolated Vector2 based on t
private static Vector2 interpolatedPoint(Vector2 p1, Vector2 p2, float t)
{
return Vector2.Multiply(p2 - p1, t) + p1;
}
//Gets the normalized normal of a vertex, given two adjacent normals (2D)
private static Vector2 getNormalizedVertexNormal(Vector2 v1, Vector2 v2) //v1 and v2 are normals
{
return getNormalizedVector(v1 + v2);
}
//Normalizes the given Vector2
private static Vector2 getNormalizedVector(Vector2 v)
{
Vector2 temp = new Vector2(v.X, v.Y);
v.Normalize();
return v;
}
//Gets the normal of a given Vector2
private static Vector2 getLineNormal(Vector2 v)
{
Vector2 normal = new Vector2(v.Y, -v.X);
return normal;
}
//Drawing method in main Game class
//curveData is a private object[] that is initialized in the constructor (by calling computeCurve3D)
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
var camPos = new Vector3(0, 0, 0.1f);
var camLookAtVector = Vector3.Forward;
var camUpVector = Vector3.Up;
effect.View = Matrix.CreateLookAt(camPos, camLookAtVector, camUpVector);
float aspectRatio = graphics.PreferredBackBufferWidth / (float)graphics.PreferredBackBufferHeight;
float fieldOfView = MathHelper.PiOver4;
float nearClip = 0.1f;
float farClip = 200f;
//Orthogonal
effect.Projection = Matrix.CreateOrthographic(480 * aspectRatio, 480, nearClip, farClip);
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
effect.World = Matrix.CreateScale(200);
graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList,
(VertexPositionTexture[])curveData[0],
0,
((VertexPositionTexture[])curveData[0]).Length,
(int[])curveData[1],
0,
((int[])curveData[1]).Length/3);
}
base.Draw(gameTime);
}
Also, this image may be able to show what the code does a little bit better
So, I needed something like this working with SpriteBatch, so I poked around at the original code a bit (with the Point -> Vector2 and rounding changes.
If you render every other segment as a different color, and with a large enough width and low enough steps, you can see why it resulted in jagged lines with larger values of width. It turns out the lines go past where they should end!
Lines going past their end:
This is because the DrawLine function adds width to length of the segment. However, without this, you see a bunch of disconnected segments for anything that actually curves.
Lines being disconnected:
There's probably some math you can do to get the appropriate value to add here, based on the angle of the connecting points. I don't know math well enough for that, so I'm just using a fixed value for them all. (10 seems to be the sweet spot for the image I posted, although it isn't perfect due to the low step count.)
(The following is DrawLine adjusted with the width being added, to using a constant instead.)
// Method used to draw a line between two points.
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D pixel, Vector2 begin, Vector2 end, Color color, int width = 1)
{
Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length() + 10, width);
Vector2 v = Vector2.Normalize(begin - end);
float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
spriteBatch.Draw(pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
}

XNA Getting colour data from a spritesheet is crashing the game

I'm building a small top down shooter in XNA using C#, and I am trying to implement per-pixel collision detection. I have the following code to do that, along a standard bounding box detection that returns the rectangle containing the collision.
private bool perPixel(Rectangle object1, Color[] dataA, Rectangle object2, Color[] dataB)
{
//Bounds of collision
int top = Math.Max(object1.Top, object2.Top);
int bottom = Math.Min(object1.Bottom, object2.Bottom);
int left = Math.Max(object1.Left, object2.Left);
int right = Math.Min(object1.Right, object2.Right);
//Check every pixel
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
//Check alpha values
Color colourA = dataA[(x - object1.Left) + (y - object1.Top) * object1.Width];
Color colourB = dataB[(x - object2.Left) + (y - object2.Top) * object2.Width];
if (colourA.A != 0 && colourB.A != 0)
{
return true;
}
}
}
return false;
}
I'm pretty sure that that will work, but I am also trying to get some of the objects to check against from a sprite sheet, and I'm trying to use this code to get the colour data, but it is getting an error saying that "The size of the data passed in is too large or small for this resource".
Color[] pacmanColour = new Color[frameSize.X * frameSize.Y];
pacman.GetData(0, new Rectangle(currentFrame.X * frameSize.X, currentFrame.Y * frameSize.Y, frameSize.X, frameSize.Y), pacmanColour,
currentFrame.X * currentFrame.Y, (sheetSize.X * sheetSize.Y));
What am I doing wrong?
Let me show you my method for dealing with Texture2D Colors
I used the following technique for loading premade structures from files
//Load the texture from the content pipeline
Texture2D texture = Content.Load<Texture2D>("Your Texture Name and Directory");
//Convert the 1D array, to a 2D array for accessing data easily (Much easier to do Colors[x,y] than Colors[i],because it specifies an easy to read pixel)
Color[,] Colors = TextureTo2DArray(texture);
And the function...
Color[,] TextureTo2DArray(Texture2D texture)
{
Color[] colors1D = new Color[texture.Width * texture.Height]; //The hard to read,1D array
texture.GetData(colors1D); //Get the colors and add them to the array
Color[,] colors2D = new Color[texture.Width, texture.Height]; //The new, easy to read 2D array
for (int x = 0; x < texture.Width; x++) //Convert!
for (int y = 0; y < texture.Height; y++)
colors2D[x, y] = colors1D[x + y * texture.Width];
return colors2D; //Done!
}
It will return a simple to use 2D array of colors, So you can simply check if Colors[1,1] (For pixel 1,1) equals whatever.

Draw simple circle in XNA

I want to draw a 2d, filled, circle. I've looked everywhere and cannot seem to find anything that will even remotely help me draw a circle. I simply want to specify a height and width and location on my canvas.
Anyone know how?
Thanks!
XNA doesn't normally have an idea of a canvas you can paint on. Instead you can either create a circle in your favorite paint program and render it as a sprite or create a series vertexes in a 3D mesh to approximate a circle and render that.
You could also check out the sample framework that Jeff Weber uses in Farseer:
http://www.codeplex.com/FarseerPhysics
The demos have a dynamic texture generator that let's him make circles and rectangles (which the samples then use as the visualization of the physics simulation). You could just re-use that :-)
Had the same problem, as others already suggested you need to draw a square or rectangle with a circle texture on it. Here follows my method to create a circle texture runtime. Not the most efficient or fancy way to do it, but it works.
Texture2D createCircleText(int radius)
{
Texture2D texture = new Texture2D(GraphicsDevice, radius, radius);
Color[] colorData = new Color[radius*radius];
float diam = radius / 2f;
float diamsq = diam * diam;
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius; y++)
{
int index = x * radius + y;
Vector2 pos = new Vector2(x - diam, y - diam);
if (pos.LengthSquared() <= diamsq)
{
colorData[index] = Color.White;
}
else
{
colorData[index] = Color.Transparent;
}
}
}
texture.SetData(colorData);
return texture;
}
Out of the box, there's no support for this in XNA. I'm assuming you're coming from some GDI background and just want to see something moving around onscreen. In a real game though, this is seldom if ever needed.
There's some helpful info here:
http://forums.xna.com/forums/t/7414.aspx
My advice to you would be to just fire up paint or something, and create the basic shapes yourself and use the Content Pipeline.
Another option (if you want to use a more complex gradient brush or something) is to draw a quad aligned to the screen and use a pixel shader.
What I did to solve this was to paint a rectangular texture, leaving the area of the rectangle which doesn't contain the circle transparent. You check to see if a point in the array is contained within a circle originating from the center of the rectangle.
Using the color data array is a bit weird because its not a 2D array. My solution was to bring in some 2D array logic into the scenario.
public Texture2D GetColoredCircle(float radius, Color desiredColor)
{
radius = radius / 2;
int width = (int)radius * 2;
int height = width;
Vector2 center = new Vector2(radius, radius);
Circle circle = new Circle(center, radius,false);
Color[] dataColors = new Color[width * height];
int row = -1; //increased on first iteration to zero!
int column = 0;
for (int i = 0; i < dataColors.Length; i++)
{
column++;
if(i % width == 0) //if we reach the right side of the rectangle go to the next row as if we were using a 2D array.
{
row++;
column = 0;
}
Vector2 point = new Vector2(row, column); //basically the next pixel.
if(circle.ContainsPoint(point))
{
dataColors[i] = desiredColor; //point lies within the radius. Paint it.
}
else
{
dataColors[i] = Color.Transparent; //point lies outside, leave it transparent.
}
}
Texture2D texture = new Texture2D(GraphicsDevice, width, height);
texture.SetData(0, new Rectangle(0, 0, width, height), dataColors, 0, width * height);
return texture;
}
And here's the method to check whether or not a point is contained within your circle:
public bool ContainsPoint(Vector2 point)
{
return ((point - this.Center).Length() <= this.Radius);
}
Hope this helps!
public Texture2D createCircleText(int radius, GraphicsDevice Devise,Color color,int tickenes)
{
Texture2D texture = new Texture2D(Devise, radius, radius);
Color[] colorData = new Color[radius * radius];
if (tickenes >= radius) tickenes = radius - 5;
float diam = radius / 2f;
float diamsq = diam * diam;
float intdiam = (radius-tickenes) / 2f;
float intdiamsq = intdiam * intdiam;
for (int x = 0; x < radius; x++)
{
for (int y = 0; y < radius; y++)
{
int index = x * radius + y;
Vector2 pos = new Vector2(x - diam, y - diam);
if (pos.LengthSquared() <= diamsq)
{
colorData[index] = color;
}
else
{
colorData[index] = Color.Transparent;
}
if (pos.LengthSquared() <= intdiamsq)
{
colorData[index] = Color.Transparent;
}
}
}
texture.SetData(colorData);
return texture;
}

Categories