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 Shader _shader; // - Resources that only need to be created once private Mesh _cubeMesh; private Texture _cubeTexture; private float _rotation; // -- Scene Objects private RenderObject _cubeObject; 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(); //GL.Disable(EnableCap.CullFace); //GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); // Set The background color to a nice blue. GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f); GL.Enable(EnableCap.DepthTest); _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); _cubeMesh = ShapeFactory.CreateTexturedCube(); _cubeTexture = new Texture("Textures/placeholder.png"); _cubeObject = new RenderObject(_cubeMesh, _shader, _cubeTexture); _cubeObject.Position = new Vector3(0.0f, 0.0f, -1.0f); _cubeObject.Scale = new Vector3(0.5f, 0.5f, 0.5f); } // 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); _cubeObject.Rotation = Quaternion.FromEulerAngles(_rotation * 0.5f, _rotation, 0); // // --- Set up Camera --- Matrix4 view = Matrix4.CreateTranslation(0.0f, 0.0f, -3.0f); float aspectRatio = (float)Size.X / Size.Y; // 2. Add a safety check for divide-by-zero if (Size.Y == 0 || Size.X == 0) { aspectRatio = 1.0f; // Default to 1:1 if window is minimized } Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView( MathHelper.DegreesToRadians(45f), aspectRatio, 0.1f, 100.0f ); _cubeObject.Draw(view, projection); SwapBuffers(); } protected override void OnUpdateFrame(FrameEventArgs e) { base.OnUpdateFrame(e); _rotation += (float)e.Time ; var input = KeyboardState; if (input.IsKeyDown(Keys.Escape)) { Close(); } } 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(); } } }