Jump to content
  • Advertisement

C# GUI WPF + OpenGL 3.1

8Observer8

1197 views

We will see how to place OpenTK.GLControl on WPF window to make GUI application with 2D/3D graphics using modern OpenGL 3.

This is the result VS project: EditedTriangle_WPFOpenGL31CSharp.zip

EditedTriangle_WPFOpenGL31CSharp_Result.png.ee287ff2ace0598d55c51f32540d8876.png

How to create the project from scratch

Note 1: RMB - Right Mouse Button click
Note 2: Good Color calculator for normalized values: http://doc.instantreality.org/tools/color_calculator/

  • Create WPF application, with the name "EditedTriangle". See the screenshot:
Spoiler

EditedTriangle_WPFOpenGL31CSharp_NewProject.png.3f540ad7ae9d7549ca58724fd8ce393c.png

  • Download OpenTK.GLControl.zip and OpenTK.zip
  • Create the empty "Libs" folder in the solution folder (where the ".sln" is placed)
  • Unzip "OpenTK" and "OpenTK.GLControl" folder in the "Libs" folder
  • Add references to "OpenTK.dll" and "OpenTK.GLControl.dll". For this: RMB on "References" -> select "Add Reference..." -> select "Browse" -> click the "Browse..." button -> select DLL's. See the screenshot with the result:
Spoiler

EditedTriangle_WPFOpenGL31CSharp_ReferenceManager.png.8af7d941f5bd6b5697792287d09a69c5.png

  • Add "Assemblies". For this: select "Assemblies" -> "Framework" -> check:
System.Drawing
System.Windows.Forms
WindowsFormsIntegration

, see the screenshot with the result:

Spoiler

EditedTriangle_WPFOpenGL31CSharp_Assemblies.png.527a86dc69d4109c08ec2fb91d29f6e1.png

  • Click "OK" button
  • Open the file "MainWindow.xaml" in VS. Add this line as an attribute of the "Window" element:
xmlns:opentk="clr-namespace:OpenTK;assembly=OpenTK.GLControl"
  • Place this code inside of <Window></Window> element:
    <Grid>
        <DockPanel LastChildFill="True">
            <StackPanel DockPanel.Dock="Right">
                <Button x:Name="buttonSetBGColor" Content="Set BG Color" Margin="5" Click="buttonSetBGColor_Click"></Button>
                <Button x:Name="buttonSetTRColor" Content="Set TR Color" Margin="5" Click="buttonSetTRColor_Click"></Button>
            </StackPanel>
            <WindowsFormsHost Margin="5" Initialized="WindowsFormsHost_Initialized">
                <opentk:GLControl x:Name="glControl" Load="glControl_Load" Paint="glControl_Paint" />
            </WindowsFormsHost>
        </DockPanel>
    </Grid>
  • Copy the code below to "MainWindow.xaml.cs" and run the project

MainWindow.xaml.cs

using System;
using System.Windows;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;

namespace EditedTriangle
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private int _numOfVertices = 0;
        private int _uColorLocation;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void glControl_Load(object sender, EventArgs e)
        {
            glControl.MakeCurrent();

            // Get a shader program ID
            int shaderProgram = InitShadersAndGetProgram();

            _numOfVertices = InitVertexBuffers();

            _uColorLocation = GL.GetUniformLocation(shaderProgram, "uColor");
            if (_uColorLocation < 0)
            {
                MessageBox.Show("Failed to get uColorLocation variable");
                return;
            }

            // Set a triangle color
            GL.Uniform3(_uColorLocation, 0.945f, 0.745f, 0.356f);

            // Set a color for clearing the glCotrol
            GL.ClearColor(new Color4(0.286f, 0.576f, 0.243f, 1f));
        }

        private void glControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            GL.Viewport(0, 0, glControl.Width, glControl.Height);

            // Clear the glControl with set color
            GL.Clear(ClearBufferMask.ColorBufferBit);

            if (_numOfVertices != 0)
            {
                GL.DrawArrays(PrimitiveType.Triangles, 0, _numOfVertices);
            }

            // Swap the front and back buffers
            glControl.SwapBuffers();
        }

        private int InitVertexBuffers()
        {
            float[] vertices = new float[]
            {
                0.0f, 0.5f,
                -0.5f, -0.5f,
                0.5f, -0.5f
            };
            int n = 3;

            int vbo;
            GL.GenBuffers(1, out vbo);

            // Get an array size in bytes
            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            int sizeInBytes = vertices.Length * sizeof(float);
            // Send the vertex array to a video card memory
            GL.BufferData(BufferTarget.ArrayBuffer, sizeInBytes, vertices, BufferUsageHint.StaticDraw);
            // Config the aPosition variable
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(0);

            return n;
        }

        private int InitShadersAndGetProgram()
        {
            string vertexShaderSource =
                "#version 140\n" +
                "in vec2 aPosition;" +
                "void main()" +
                "{" +
                "    gl_Position = vec4(aPosition, 1.0, 1.0);" +
                "}";

            string fragmentShaderSource =
                "#version 140\n" +
                "out vec4 fragColor;" +
                "uniform vec3 uColor;" +
                "void main()" +
                "{" +
                "    fragColor = vec4(uColor, 1.0);" +
                "}";

            // Vertex Shader
            int vShader = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vShader, vertexShaderSource);
            GL.CompileShader(vShader);
            // Check compilation
            string vShaderInfo = GL.GetShaderInfoLog(vShader);
            if (!vShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(vShaderInfo);
                return -1;
            }

            // Fragment Shader
            int fShader = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fShader, fragmentShaderSource);
            GL.CompileShader(fShader);
            string fShaderInfo = GL.GetShaderInfoLog(fShader);
            if (!fShaderInfo.StartsWith("No errors"))
            {
                MessageBox.Show(fShaderInfo);
                return -1;
            }

            int program = GL.CreateProgram();
            GL.AttachShader(program, vShader);
            GL.AttachShader(program, fShader);
            GL.LinkProgram(program);
            GL.UseProgram(program);

            return program;
        }

        private void buttonSetBGColor_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new System.Windows.Forms.ColorDialog();

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                GL.ClearColor(dialog.Color);
                glControl.Invalidate();
            }

        }

        private void buttonSetTRColor_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new System.Windows.Forms.ColorDialog();

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                float r = dialog.Color.R / 255f;
                float g = dialog.Color.G / 255f;
                float b = dialog.Color.B / 255f;
                GL.Uniform3(_uColorLocation, r, g, b);
                glControl.Invalidate();
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="EditedTriangle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:EditedTriangle"
        xmlns:opentk="clr-namespace:OpenTK;assembly=OpenTK.GLControl"
        mc:Ignorable="d"
        Title="Triangle" Height="256" Width="290">
    <Grid>
        <DockPanel LastChildFill="True">
            <StackPanel DockPanel.Dock="Right">
                <Button x:Name="buttonSetBGColor" Content="Set BG Color" Margin="5" Click="buttonSetBGColor_Click"></Button>
                <Button x:Name="buttonSetTRColor" Content="Set TR Color" Margin="5" Click="buttonSetTRColor_Click"></Button>
            </StackPanel>
            <WindowsFormsHost Margin="5">
                <opentk:GLControl x:Name="glControl" Load="glControl_Load" Paint="glControl_Paint" />
            </WindowsFormsHost>
        </DockPanel>
    </Grid>
</Window>

 



0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By ThisIsFix
      YouTube Video Tutorial: https://www.youtube.com/watch?v=et6BAdlxECw&t=7s
      using System.Collections; using System.Collections.Generic; using UnityEngine; public class BillboardFX : MonoBehaviour { public Transform camTransform; Quaternion originalRotation; void Start() { originalRotation = transform.rotation; } void Update() { transform.rotation = camTransform.rotation * originalRotation; } } Script Link: https://github.com/ThisIsFix/Unity-Billboard
    • By tamlam
      I have a vector saving 5 cube coordinates in oxy plan.
      Task: 1st cube roll 90 degrees, then disappear then 2nd cube roll 90 dg … until the last cube in the vector.
      Problem: whenever the program runs, all the cube will appear and roll the same time.
      Q: How can I solve this problem?
      I checked the value in for loop (temp), and value of (count) as below:
      angle 1 count 1
      temp0
      temp1
      temp2
      temp3
      temp4
      temp5
      temp6
      temp7
      angle 2 count 2
      temp0
      temp1
      temp2
      temp3
      temp4
      temp5
      temp6
      temp7 => I do not know why the for loop does not wait for for the “presskey” action to increase the angle value before move to the next temp value.
      int Count(0); void Display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); SetLight(); glPushMatrix(); ConclusiveAxis(); DrawGrid(); for (int temp = 0; temp < cube.coordinates.size(); temp++) { cout<< "temp" << temp << endl; if ((cube.coordinates[temp] == true)) { sDirection = true; if (Count <= 90) { glPushMatrix(); glTranslatef(cube.coordinates[temp].x - 0.5, cube.coordinates[temp].y - 0.5, 0.0); glRotatef(angle, 0, 1, 0); glTranslatef(-0.5, 0.5, 0.5); glColor3f(0.5, 1.5, 1.0); glutSolidCube(1); glPopMatrix(); } else if (Count > 90) { angle = 0; Count = 0; } else { sDirection = false; } } else { } } glPopMatrix(); glutSwapBuffers(); } void SpecialKey(unsigned char key, int x, int y) { switch (key) { case 'r': //condition for next check if (sDirection == true) { angle++; glutPostRedisplay(); Count++; cout << "angle " << angle << " count " << Count << endl; } else { glutPostRedisplay(NULL); } break; } } void OpenGLCallBackAnimation(void) { GLSettings0.PickObject = PickObject0; OpenGLInitialize(0, GLSettings0, 300, 150, 1000, 650, "window"); glutDisplayFunc(Display); glutMouseFunc(MouseButton); glutMotionFunc(OnMouseMotion); glutPassiveMotionFunc(PassiveMotion); glutKeyboardFunc(SpecialKey); //keyboard call glutMouseWheelFunc(OpenGLMouseWheel0); glutReshapeFunc(OpenGLReshape0); //glutTimerFunc(0, time_callback, 0); OpenGLPostprocessor(GLSettings0); }  
    • By IGStudios
      We are currently working in a new indie Survival Crafting game, the project "Putrid" is looking for focused resource farming and modifiable environment.
      The project have an advanced state of development with many items and assets.
      We want a programmer with experience in c# to handle the main mechanics of the game.


      here's our Discord: https://discord.gg/hZQYapv
      and here's my mail for any doubts: tyxefield@gmail.com
    • By Alessandro
      Hello, I need to pick the model (local coordinates) when I click on an object on screen. I'm using the following code to get the world coordinates:
      glLoadIdentity(); // Resets The Matrix glGetDoublev(GL_MODELVIEW_MATRIX, mv); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, vp); glReadPixels(xPos, viewport[3] - yPos, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &wz); gluUnProject(xPos, viewport[3] - yPos, wz, mv, proj, vp, &cx, &cy, &cz); Point P(cx, cy, cz); P does hold the correct world coordinates values.
      Now at this point, in order to get the model local coordinates, I thought I could simply multiply the point P by the inverse of the modelview matrix (mv),  but the calculations that come out are all wrong.
      Is this the correct approach or am I mistaken about this last point (world to local transformation)?
      I am attaching also the routine used to calculate the matrix inverse...

      Thanks for any hint!
      bool GLWorld::gluInvertMatrix(const double m[16], double invOut[16]) { double inv[16], det; int i; inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; if (det == 0) return false; det = 1.0 / det; for (i = 0; i < 16; i++) invOut[i] = inv[i] * det; return true; }  
    • By RamblingBaba
      Hello,
      So I have been programming with C++ for 2.5 years now. I have been interested in checking out C# for fun. My question is pretty simple. Does C# use a form typically for their games? The simple game examples I have watched on YouTube reminds me A LOT of Visual Basic. 
      Was this just an easy way to show you C#? Or using the form is a big part of C#? Or is it nothing more than using something like MFC and completely irrelevant to making games?
      Thanks
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!