using OpenTK.Graphics.OpenGL4; using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; using LearnOpenTK.Common; using OpenTK.Mathematics; using TheLabs.Shapes; namespace TheLabs { public class MyExampleWindow : GameWindow { // Create the vertices for our triangle. These are listed in normalized device coordinates (NDC) // In NDC, (0, 0) is the center of the screen. // Negative X coordinates move to the left, positive X move to the right. // Negative Y coordinates move to the bottom, positive Y move to the top. // OpenGL only supports rendering in 3D, so to create a flat triangle, the Z coordinate will be kept as 0. private readonly float[] _vertices = { -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // Bottom-right vertex 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Top vertex }; private readonly float[] _quadvertices = { // x, y, z -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-right vertex 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top vertex -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex 0.0f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-right vertex 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top vertex }; private Cube _cube; private Circle _circle; private Cylinder _cylinder; private Shader _shader; private float _rotation; public MyExampleWindow(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings) : base(gameWindowSettings, nativeWindowSettings) { } // Now, we start initializing OpenGL. protected override void OnLoad() { // This is called when the window is created and is where we can set up OpenGL resources. base.OnLoad(); // Set The background color to a nice blue. GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f); // Enable depth buffering. GL.Enable(EnableCap.DepthTest); // Create and compile our shader program from the shader source files _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); // Create a cube _circle = new Circle(); _cylinder = new Cylinder(); // Use the shader program. This is similar to "activating" the shader program. _shader.Use(); } // Now that initialization is done, let's create our render loop. protected override void OnRenderFrame(FrameEventArgs e) { // This is called once per frame and is where all the rendering code goes. base.OnRenderFrame(e); // Clear the color buffer and the depth buffer GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit); // Create the model, view, and projection matrices Matrix4 view = Matrix4.CreateTranslation(0.0f, 0.0f, -3.0f); Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView( MathHelper.DegreesToRadians(45f), Size.X / (float)Size.Y, 0.1f, 100.0f ); // Draw the cube for (int i = 0; i < 3; i++) { _cube = new Cube(); _cube.Position = new Vector3(0.5f, 0.5f * i * 0.5f, 0.0f); _cube.Draw(_shader, view, projection, _rotation); } //_circle.Draw(_shader, model); //_cylinder.Draw(_shader, model); SwapBuffers(); GL.GetError(); } protected override void OnUpdateFrame(FrameEventArgs e) { base.OnUpdateFrame(e); var input = KeyboardState; if (input.IsKeyDown(Keys.Escape)) { Close(); } if (input.IsKeyDown(Keys.R)) { _rotation += 0.8f * (float)e.Time; // Update rotation only when the condition is met } else if (input.IsKeyDown(Keys.T)) { _rotation -= 0.8f * (float)e.Time; // Update rotation only when the condition is met } } protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); // When the window gets resized, we have to call GL.Viewport to resize OpenGL's viewport to match the new size. // If we don't, the NDC will no longer be correct. GL.Viewport(0, 0, Size.X, Size.Y); } // Now, for cleanup. // You should generally not do cleanup of opengl resources when exiting an application, // as that is handled by the driver and operating system when the application exits. // // There are reasons to delete opengl resources, but exiting the application is not one of them. // This is provided here as a reference on how resource cleanup is done in opengl, but // should not be done when exiting the application. // // Places where cleanup is appropriate would be: to delete textures that are no // longer used for whatever reason (e.g. a new scene is loaded that doesn't use a texture). // This would free up video ram (VRAM) that can be used for new textures. // // The coming chapters will not have this code. protected override void OnUnload() { // Unbind all the resources by binding the targets to 0/null. GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindVertexArray(0); GL.UseProgram(0); GL.DeleteProgram(_shader.Handle); base.OnUnload(); } } }