I am trying to develop a little tool for the Unity editor using IMGUI and GL, and I came up with a problem.
I have the following code:
public class TestWindow : EditorWindow
{
[MenuItem("TEST/Test")]
static void Open()
{
var window = GetWindow<Test>();
window.Show();
}
void OnGUI()
{
DrawSquare(new Rect(10, 10, 20, 20), Color.red);
DrawSquare(new Rect(10, 40, 20, 20), Color.magenta);
GUIStyle style = new GUIStyle("label");
style.normal.background = Texture2D.whiteTexture;
style.normal.textColor = Color.black;
style.alignment = TextAnchor.MiddleCenter;
GUI.Label(new Rect(40, 10, 80, 50), "Hello World", style);
DrawSquare(new Rect(130, 10, 20, 20), Color.blue);
DrawSquare(new Rect(130, 40, 20, 20), Color.cyan);
}
private static void DrawSquare(Rect rect, Color color)
{
GL.PushMatrix();
GL.Begin(GL.QUADS);
GL.Color(color);
GL.Vertex3(rect.xMin, rect.yMin, 0);
GL.Vertex3(rect.xMax, rect.yMin, 0);
GL.Vertex3(rect.xMax, rect.yMax, 0);
GL.Vertex3(rect.xMin, rect.yMax, 0);
GL.End();
GL.PopMatrix();
}
}
The problem that I am having is that it seems like GUI.Label is blocking the remaining GL calls from being drawn in the editor window. If you run this code, you will see the red and magenta squares, followed by the label, but the blue and cyan squares are not drawn. If you comment/remove the label, then they are. I tried without the style as well and made no difference.
I managed to find out that any GUI call that will draw content into the window will block the other two squares from being drawn. For example, drawing a GUI.Label with empty string text, will still draw the blue and cyan squares.
I saw the C# reference source code from Unity, and I notice that there is a check for empty string before calling some native code, so I assume that the issue comes all the way from C++.
If anyone knows how could I prevent this (maybe there is some GL or C# method that I can use to prevent this) I would appreciate it.
P.S: Worst case scenario I can handle the code to draw all GL stuff first, and then any GUI calls, but I still would like to be able to mix them freely if there is a way.
Related
My GUI Boxes and Labels are set by Coordinates. How would I set them to scale with resolution and screen size as they go off screen on build. The first IF statement the GUI displayed on the top left of screen and then next displays on the top right! Code examples would be very helpful!
public void OnGUI()
{
if ((isClicked) && (cdrwModel))
{
GUI.contentColor = Color.white;
GUI.Label(new Rect(5, 5, 400, 400), "<color=cyan><size=30>This is the </size></color>" + "<color=cyan><size=30>" + this.cdrw + "</size></color>");
GUI.Label(new Rect(15, 35, 400, 400), "Press <TAB> for more information");
if (showGui)
{
GUI.contentColor = Color.white;
GUI.Box(new Rect(1000, 5, 400, 400), "What You Should Know");
GUI.Label(new Rect(1135, 5, 400, 400), "___________________");
GUI.Label(new Rect(1145, 23, 400, 400), "<color=cyan><size=20>The </size></color>" + "<color=cyan><size=20>" + this.cdrw + "</size></color>");
foreach (var file in _puzzlesFile)
{
GUI.contentColor = Color.white;
GUI.Label(new Rect(1000, 50, 400, 400), file);
}
}
}
}
You can use Screen class to get current Screen.width and Screen.height and create something to recalculate your GUI coordinates when it changes. If your game camera doesn't uses entire screen you can get Camera.current.pixelWidth/pixelHeight to do the same.
If you want a responsive UI you should avoid coordinates and use GUILayout instead of GUI class to declare your components, it is way better and easier to adjust without a lot of handmade calculations that can (and probably will) blow your performance out.
Just use Screen.width for width and x position of GUI and Screen.height for height and y position of GUI.
For Example:, for a screen resoulution for 1024:640
GUI.Box(new Rect(Screen.width/10, Screen.height/10, Screen.width/3, Screen.height/5), "What You Should Know");
will create a Gui box same with
GUI.Box(new Rect(102.4f, 64, 341.3f, 128), "What You Should Know");
but first one will be responsive.
Okay this may be really simple, but every attempt I make the console throws up various errors. If i have if(GUI.Button(new Rect(x, y, Screen.width, z), "play")) { How would I go about changing the size of the text. I have a custom GuiSkin used on the text, but I'm unsure how to change the font size of the text without using the inspector - I am using unity. What I am trying to do is change the font size depending on the screen resolution.
If you have create your GUiSkin object, first you can using inspector to change GuiSkin.Button.FontSize. Then apply this setting in you button.
public GUISkin yourGuiSkinObject;
void OnGUI()
{
if(GUI.Button(new Rect(0, 0, 100, 20), "Test", yourGuiSkinObject.button))
{
//Do something.
}
}
Second you can using script to change fontSize. And change the size depend on the screen's height.
public GUISkin yourGuiSkinObject;
void Start()
{
int scale = Screen.height / 20;
yourGuiSkinObject.button.fontsize = scale;
}
void OnGui()
{
if(GUI.Button(new Rect(0, 0, 100, 20), "Test", yourGuiSkinObject.button))
{
}
}
In relation to this question that I asked a few weeks ago now
LinearGradientBrush does not render correctly
Consider the following code:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Rectangle rect = new Rectangle(100, 100, 200, 100);
using(LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Red, Color.Blue, 90))
{
using(Pen pen = new Pen(brush, 1))
{
pen.Alignment = PenAlignment.Inset;
e.Graphics.DrawRectangle(pen, rect);
}
}
}
Produces this result...
Why is there a red line where there should be a blue line, and how do I fix this?
Pen.Alignment = PenAlignment.Inset didn't work!
Pens (or the Graphics DrawMethods) have a tendency to draw outside their boundaries. I think Microsoft considered that a feature, but I never understood it.
Try using a smaller rectangle for the pen:
using (var brush = new LinearGradientBrush(rect, Color.Red, Color.Blue, 90)) {
using (Pen pen = new Pen(brush, 1)) {
e.Graphics.DrawRectangle(pen, new Rectangle(rect.X, rect.Y,
rect.Width - 1, rect.Height - 1));
}
}
OK, to sum it up: DrawRectangle always overdraws by 1 pixel (*) and the LinearGradientBrush therefore restarts with the 1st color.
Yes, Lars got one solution, but here is an interesting alternative:
brush.WrapMode = WrapMode.TileFlipXY;
This makes sure that the gradient doesn't start at the beginning when it has to overdraw but reverses in both directions, so the extra pixel is drawn in the right Color and you don't need to manipulate one of the two the Rectangle Sizes..
Well ok, if you need total precision it is probably second best, as the last row and column will have the same color as their neighbours..but not having to deal with two sizes may be worth it..
(*) Which is why you can't use it to draw a 1x1 square/pixel. Instead you need to use FillRectangle..
Does GDI have any method of setting an overall clipping area, in the same way the GDI+ Graphics object does?
Specifically:
Graphics.DrawString uses GDI+ which includes the clipping bounds system.
TextRenderer.DrawText uses GDI which does not.
My new scrollbar system automatically resizes the Graphics draw area as needed. While tidying up a control which uses it, I switched some inline string rendering calls over to my text layout class, which uses TextRenderer.DrawText. I can't quite remember why I used that instead of just Graphics.DrawString, but before I refactor that I wanted to check if there was some way of fixing the problem as it stands.
public class GraphicsClipExample : Form
{
public GraphicsClipExample()
{
this.ClientSize = new Size(this.ClientSize.Width, 100);
}
protected override void OnPaint(PaintEventArgs e)
{
// update the graphics clip area
e.Graphics.Clear(Color.Cyan);
e.Graphics.SetClip(new Rectangle(0, 0, e.ClipRectangle.Width, 50));
e.Graphics.Clear(Color.Magenta);
Font font = new Font("Calibri", 24);
e.Graphics.DrawString("Testing", font, Brushes.Black, 10, 28);
TextRenderer.DrawText(e.Graphics, "Testing", font, new Point(150, 28), Color.Black);
}
}
This produces the following output:
Is there any way to provide a simple clipping area for GDI overall, or TextRenderer specifically?
Many thanks
Try using the PreserveGraphicsClipping format flag:
TextRenderer.DrawText(e.Graphics, "Testing", font, new Point(150, 28),
Color.Black, Color.Empty,
TextFormatFlags.PreserveGraphicsClipping);
I've got drawing sprites to work with OpenTK in my 2d game engine now. Only problem I'm having is that custom drawn objects with opengl (anything but sprites really) show up as the background color. Example:
I'm Drawing a 2.4f width black line here. There's also a quad and a point in the example, but they do not overlap anything that's actually visible. The line overlaps the magenta sprite, but the color is just wrong. My question is: Am I missing an OpenGL feature, or doing something horrible wrong?
These are the samples of my project concerning drawing: (you can also find the project on https://github.com/Villermen/HatlessEngine if there's questions about the code)
Initialization:
Window = new GameWindow(windowSize.Width, windowSize.Height);
//OpenGL initialization
GL.Enable(EnableCap.PointSmooth);
GL.Hint(HintTarget.PointSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.LineSmooth);
GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.ClearColor(Color.Gray);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Lequal);
GL.ClearDepth(1d);
GL.DepthRange(1d, 0d); //does not seem right, but it works (see it as duct-tape)
Every draw cycle:
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//reset depth and color to be consistent over multiple frames
DrawX.Depth = 0;
DrawX.DefaultColor = Color.Black;
foreach(View view in Resources.Views)
{
CurrentDrawArea = view.Area;
GL.Viewport((int)view.Viewport.Left * Window.Width, (int)view.Viewport.Top * Window.Height, (int)view.Viewport.Right * Window.Width, (int)view.Viewport.Bottom * Window.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(view.Area.Left, view.Area.Right, view.Area.Bottom, view.Area.Top, -1f, 1f);
GL.MatrixMode(MatrixMode.Modelview);
//drawing
foreach (LogicalObject obj in Resources.Objects)
{
//set view's coords for clipping?
obj.Draw();
}
}
GL.Flush();
Window.Context.SwapBuffers();
DrawX.Line:
public static void Line(PointF pos1, PointF pos2, Color color, float width = 1)
{
RectangleF lineRectangle = new RectangleF(pos1.X, pos1.Y, pos2.X - pos1.X, pos2.Y - pos1.Y);
if (lineRectangle.IntersectsWith(Game.CurrentDrawArea))
{
GL.LineWidth(width);
GL.Color3(color);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(pos1.X, pos1.Y, GLDepth);
GL.Vertex3(pos2.X, pos2.Y, GLDepth);
GL.End();
}
}
Edit: If I disable the blendcap before and enable it after drawing the line it does show up with the right color, but I must have it blended.
I forgot to unbind the texture in the texture-drawing method...
GL.BindTexture(TextureTarget.Texture2D, 0);