Full Code of opentk/LearnOpenTK for AI

master 7b876bdb79fc cached
118 files
270.9 KB
81.8k tokens
178 symbols
1 requests
Download .txt
Showing preview only (301K chars total). Download the full file or copy to clipboard to get everything.
Repository: opentk/LearnOpenTK
Branch: master
Commit: 7b876bdb79fc
Files: 118
Total size: 270.9 KB

Directory structure:
gitextract_wlj10zxr/

├── .gitattributes
├── .gitignore
├── Chapter1/
│   ├── 1-CreatingAWindow/
│   │   ├── 1-CreatingAWindow.csproj
│   │   ├── Program.cs
│   │   └── Window.cs
│   ├── 2-HelloTriangle/
│   │   ├── 2-HelloTriangle.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 3-ElementBufferObjects/
│   │   ├── 3-ElementBufferObjects.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-InsAndOuts/
│   │   ├── 4-Shaders-InsAndOuts.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-MoreAttributes/
│   │   ├── 4-Shaders-MoreAttributes.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-Uniforms/
│   │   ├── 4-Shaders-Uniforms.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-Textures/
│   │   ├── 5-Textures.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 6-MultipleTextures/
│   │   ├── 6-MultipleTextures.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 7-Transformations/
│   │   ├── 7-Transformations.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 8-CoordinatesSystems/
│   │   ├── 8-CoordinatesSystems.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   └── 9-Camera/
│       ├── 9-Camera.csproj
│       ├── Program.cs
│       ├── Shaders/
│       │   ├── shader.frag
│       │   └── shader.vert
│       └── Window.cs
├── Chapter2/
│   ├── 1-Colors/
│   │   ├── 1-Colors.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 2-BasicLighting/
│   │   ├── 2-BasicLighting.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 3-Materials/
│   │   ├── 3-Materials.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-LightingMaps/
│   │   ├── 4-LightingMaps.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-DirectionalLights/
│   │   ├── 5-LightCasters-DirectionalLights.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-PointLights/
│   │   ├── 5-LightCasters-PointLights.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-Spotlight/
│   │   ├── 5-LightCasters-Spotlight.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   └── 6-MultipleLights/
│       ├── 6-MultipleLights.csproj
│       ├── OpenTK.dll.config
│       ├── Program.cs
│       ├── Shaders/
│       │   ├── lighting.frag
│       │   ├── shader.frag
│       │   └── shader.vert
│       └── Window.cs
├── Common/
│   ├── Camera.cs
│   ├── Common.csproj
│   ├── Shader.cs
│   └── Texture.cs
├── LICENSE
├── LearnOpenTK.sln
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# DNX
project.lock.json
project.fragment.lock.json
artifacts/

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush
.cr/

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

================================================
FILE: Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/1-CreatingAWindow/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Creating a Window",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            // To create a new window, create a class that extends GameWindow, then call Run() on it.
            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }

            // And that's it! That's all it takes to create a window with OpenTK.
        }
    }
}


================================================
FILE: Chapter1/1-CreatingAWindow/Window.cs
================================================

using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // This is where all OpenGL code will be written.
    // OpenToolkit allows for several functions to be overriden to extend functionality; this is how we'll be writing code.
    public class Window : GameWindow
    {
        // A simple constructor to let us set properties like window size, title, FPS, etc. on the window.
        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        // This function runs on every update frame.
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            // Check if the Escape button is currently being pressed.
            if (KeyboardState.IsKeyDown(Keys.Escape))
            {
                // If it is, close the window.
                Close();
            }

            base.OnUpdateFrame(e);
        }
    }
}


================================================
FILE: Chapter1/2-HelloTriangle/2-HelloTriangle.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>

  <ItemGroup>
    <None Update="Shaders\shader.frag">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="Shaders\shader.vert">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/2-HelloTriangle/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Creating a Window",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/2-HelloTriangle/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

void main()
{
    outputColor = vec4(1.0, 1.0, 0.0, 1.0);
}

================================================
FILE: Chapter1/2-HelloTriangle/Shaders/shader.vert
================================================
// For more information on how shaders work, check out the web version of this tutorial.
// I'll include a simpler summary here.

// First non-comment line should always be a #version statement; this just tells the GLSL compiler what version it should use.
#version 330 core

// GLSL's syntax is somewhat like C, but it has a few differences.

// There are four different types of variables in GLSL: input, output, uniform, and internal.
// - Input variables are sent from the buffer, in a way defined by GL.VertexAttribPointer.
// - Output variables are sent from this shader to the next one in the chain (which will be the fragment shader most of the time).
// - Uniforms will be touched on in the next tutorial.
// - Internal variables are defined in the shader file and only used there.


// The vertex shader is run once for every vertex. In C# pseudocode, it might look something like:
// foreach(var vertex in vertices)
//   shader(vertex)


// This defines our input variable, aPosition.
// It starts with the line "layout(location = 0)". This defines where this input variable will be located, which is needed for GL.VertexAttribPointer.
// However, you can omit it, and replace this with just "in vec3 aPosition". If you do that, you'll have to replace the 0 in GL.VertexAttribPointer with
//   a call to GL.GetAttribLocation(shaderHandle, attributeName)
// Next, the keyword "in" defines this as an input variable. We'll have an example of the "out" keyword in the next tutorial.
// Then, the keyword "vec3" means this is a vector with 3 floats inside.

layout(location = 0) in vec3 aPosition;


// Like C, we have an entrypoint function. In this case, it takes void and returns void, and must be named main.
// You can do all sorts of calculations here to modify your vertices, but right now, we don't need to do any of that.
// gl_Position is the final vertex position; pass a vec4 to it and you're done.
// Keep in mind that we only pass a vec3 to this shader; the fourth component of a vertex is known as "w".
// It's only used in some more advanced OpenGL functions; it's not needed here.
// So with a call to the vec4 function, we just give it a constant value of 1.0.

void main(void)
{
    gl_Position = vec4(aPosition, 1.0);
}

================================================
FILE: Chapter1/2-HelloTriangle/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // Be warned, there is a LOT of stuff here. It might seem complicated, but just take it slow and you'll be fine.
    // OpenGL's initial hurdle is quite large, but once you get past that, things will start making more sense.
    public class Window : 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, // Bottom-left vertex
             0.5f, -0.5f, 0.0f, // Bottom-right vertex
             0.0f,  0.5f, 0.0f  // Top vertex
        };

        // These are the handles to OpenGL objects. A handle is an integer representing where the object lives on the
        // graphics card. Consider them sort of like a pointer; we can't do anything with them directly, but we can
        // send them to OpenGL functions that need them.

        // What these objects are will be explained in OnLoad.
        private int _vertexBufferObject;

        private int _vertexArrayObject;

        // This class is a wrapper around a shader, which helps us manage it.
        // The shader class's code is in the Common project.
        // What shaders are and what they're used for will be explained later in this tutorial.
        private Shader _shader;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        // Now, we start initializing OpenGL.
        protected override void OnLoad()
        {
            base.OnLoad();

            // This will be the color of the background after we clear it, in normalized colors.
            // Normalized colors are mapped on a range of 0.0 to 1.0, with 0.0 representing black, and 1.0 representing
            // the largest possible value for that channel.
            // This is a deep green.
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            // We need to send our vertices over to the graphics card so OpenGL can use them.
            // To do this, we need to create what's called a Vertex Buffer Object (VBO).
            // These allow you to upload a bunch of data to a buffer, and send the buffer to the graphics card.
            // This effectively sends all the vertices at the same time.

            // First, we need to create a buffer. This function returns a handle to it, but as of right now, it's empty.
            _vertexBufferObject = GL.GenBuffer();

            // Now, bind the buffer. OpenGL uses one global state, so after calling this,
            // all future calls that modify the VBO will be applied to this buffer until another buffer is bound instead.
            // The first argument is an enum, specifying what type of buffer we're binding. A VBO is an ArrayBuffer.
            // There are multiple types of buffers, but for now, only the VBO is necessary.
            // The second argument is the handle to our buffer.
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);

            // Finally, upload the vertices to the buffer.
            // Arguments:
            //   Which buffer the data should be sent to.
            //   How much data is being sent, in bytes. You can generally set this to the length of your array, multiplied by sizeof(array type).
            //   The vertices themselves.
            //   How the buffer will be used, so that OpenGL can write the data to the proper memory space on the GPU.
            //   There are three different BufferUsageHints for drawing:
            //     StaticDraw: This buffer will rarely, if ever, update after being initially uploaded.
            //     DynamicDraw: This buffer will change frequently after being initially uploaded.
            //     StreamDraw: This buffer will change on every frame.
            //   Writing to the proper memory space is important! Generally, you'll only want StaticDraw,
            //   but be sure to use the right one for your use case.
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            // One notable thing about the buffer we just loaded data into is that it doesn't have any structure to it. It's just a bunch of floats (which are actaully just bytes).
            // The opengl driver doesn't know how this data should be interpreted or how it should be divided up into vertices. To do this opengl introduces the idea of a 
            // Vertex Array Obejct (VAO) which has the job of keeping track of what parts or what buffers correspond to what data. In this example we want to set our VAO up so that 
            // it tells opengl that we want to interpret 12 bytes as 3 floats and divide the buffer into vertices using that.
            // To do this we generate and bind a VAO (which looks deceptivly similar to creating and binding a VBO, but they are different!).
            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            // Now, we need to setup how the vertex shader will interpret the VBO data; you can send almost any C datatype (and a few non-C ones too) to it.
            // While this makes them incredibly flexible, it means we have to specify how that data will be mapped to the shader's input variables.

            // To do this, we use the GL.VertexAttribPointer function
            // This function has two jobs, to tell opengl about the format of the data, but also to associate the current array buffer with the VAO.
            // This means that after this call, we have setup this attribute to source data from the current array buffer and interpret it in the way we specified.
            // Arguments:
            //   Location of the input variable in the shader. the layout(location = 0) line in the vertex shader explicitly sets it to 0.
            //   How many elements will be sent to the variable. In this case, 3 floats for every vertex.
            //   The data type of the elements set, in this case float.
            //   Whether or not the data should be converted to normalized device coordinates. In this case, false, because that's already done.
            //   The stride; this is how many bytes are between the last element of one vertex and the first element of the next. 3 * sizeof(float) in this case.
            //   The offset; this is how many bytes it should skip to find the first element of the first vertex. 0 as of right now.
            // Stride and Offset are just sort of glossed over for now, but when we get into texture coordinates they'll be shown in better detail.
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);

            // Enable variable 0 in the shader.
            GL.EnableVertexAttribArray(0);

            // We've got the vertices done, but how exactly should this be converted to pixels for the final image?
            // Modern OpenGL makes this pipeline very free, giving us a lot of freedom on how vertices are turned to pixels.
            // The drawback is that we actually need two more programs for this! These are called "shaders".
            // Shaders are tiny programs that live on the GPU. OpenGL uses them to handle the vertex-to-pixel pipeline.
            // Check out the Shader class in Common to see how we create our shaders, as well as a more in-depth explanation of how shaders work.
            // shader.vert and shader.frag contain the actual shader code.
            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

            // Now, enable the shader.
            // Just like the VBO, this is global, so every function that uses a shader will modify this one until a new one is bound instead.
            _shader.Use();

            // Setup is now complete! Now we move to the OnRenderFrame function to finally draw the triangle.
        }

        // Now that initialization is done, let's create our render loop.
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            // This clears the image, using what you set as GL.ClearColor earlier.
            // OpenGL provides several different types of data that can be rendered.
            // You can clear multiple buffers by using multiple bit flags.
            // However, we only modify the color, so ColorBufferBit is all we need to clear.
            GL.Clear(ClearBufferMask.ColorBufferBit);

            // To draw an object in OpenGL, it's typically as simple as binding your shader,
            // setting shader uniforms (not done here, will be shown in a future tutorial)
            // binding the VAO,
            // and then calling an OpenGL function to render.

            // Bind the shader
            _shader.Use();

            // Bind the VAO
            GL.BindVertexArray(_vertexArrayObject);

            // And then call our drawing function.
            // For this tutorial, we'll use GL.DrawArrays, which is a very simple rendering function.
            // Arguments:
            //   Primitive type; What sort of geometric primitive the vertices represent.
            //     OpenGL used to support many different primitive types, but almost all of the ones still supported
            //     is some variant of a triangle. Since we just want a single triangle, we use Triangles.
            //   Starting index; this is just the start of the data you want to draw. 0 here.
            //   How many vertices you want to draw. 3 for a triangle.
            GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

            // OpenTK windows are what's known as "double-buffered". In essence, the window manages two buffers.
            // One is rendered to while the other is currently displayed by the window.
            // This avoids screen tearing, a visual artifact that can happen if the buffer is modified while being displayed.
            // After drawing, call this function to swap the buffers. If you don't, it won't display what you've rendered.
            SwapBuffers();

            // And that's all you have to do for rendering! You should now see a yellow triangle on a black screen.
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            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);

            // Delete all the resources.
            GL.DeleteBuffer(_vertexBufferObject);
            GL.DeleteVertexArray(_vertexArrayObject);

            GL.DeleteProgram(_shader.Handle);

            base.OnUnload();
        }
    }
}


================================================
FILE: Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/3-ElementBufferObjects/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Element Buffer Objects",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/3-ElementBufferObjects/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

void main()
{
    outputColor = vec4(1.0, 1.0, 0.0, 1.0);
}

================================================
FILE: Chapter1/3-ElementBufferObjects/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

void main(void)
{
    gl_Position = vec4(aPosition, 1.0);
}

================================================
FILE: Chapter1/3-ElementBufferObjects/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // So you've drawn the first triangle. But what about drawing multiple?
    // You may consider just adding more vertices to the array, and that would technically work, but say you're drawing a rectangle.
    // It only needs four vertices, but since OpenGL works in triangles, you'd need to define 6.
    // Not a huge deal, but it quickly adds up when you get to more complex models. For example, a cube only needs 8 vertices, but
    // doing it that way would need 36 vertices!

    // OpenGL provides a way to reuse vertices, which can heavily reduce memory usage on complex objects.
    // This is called an Element Buffer Object. This tutorial will be all about how to set one up.
    public class Window : GameWindow
    {
        // We modify the vertex array to include four vertices for our rectangle.
        private readonly float[] _vertices =
        {
             0.5f,  0.5f, 0.0f, // top right
             0.5f, -0.5f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, // top left
        };

        // Then, we create a new array: indices.
        // This array controls how the EBO will use those vertices to create triangles
        private readonly uint[] _indices =
        {
            // Note that indices start at 0!
            0, 1, 3, // The first triangle will be the top-right half of the triangle
            1, 2, 3  // Then the second will be the bottom-left half of the triangle
        };

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        // Add a handle for the EBO
        private int _elementBufferObject;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);

            // We create/bind the Element Buffer Object EBO the same way as the VBO, except there is a major difference here which can be REALLY confusing.
            // The binding spot for ElementArrayBuffer is not actually a global binding spot like ArrayBuffer is. 
            // Instead it's actually a property of the currently bound VertexArrayObject, and binding an EBO with no VAO is undefined behaviour.
            // This also means that if you bind another VAO, the current ElementArrayBuffer is going to change with it.
            // Another sneaky part is that you don't need to unbind the buffer in ElementArrayBuffer as unbinding the VAO is going to do this,
            // and unbinding the EBO will remove it from the VAO instead of unbinding it like you would for VBOs or VAOs.
            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            // We also upload data to the EBO the same way as we did with VBOs.
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
            // The EBO has now been properly setup. Go to the Render function to see how we draw our rectangle now!

            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            _shader.Use();

            // Because ElementArrayObject is a property of the currently bound VAO,
            // the buffer you will find in the ElementArrayBuffer will change with the currently bound VAO.
            GL.BindVertexArray(_vertexArrayObject);

            // Then replace your call to DrawTriangles with one to DrawElements
            // Arguments:
            //   Primitive type to draw. Triangles in this case.
            //   How many indices should be drawn. Six in this case.
            //   Data type of the indices. The indices are an unsigned int, so we want that here too.
            //   Offset in the EBO. Set this to 0 because we want to draw the whole thing.
            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);
            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/4-Shaders-InsAndOuts/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Shaders In and Outs!",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/4-Shaders-InsAndOuts/Shaders/shader.frag
================================================
#version 330 core

out vec4 outputColor;

// This is where the color variable we declared and assigned in vertex shader 
// Gets pass to, this is enabled by using the in keyword 
// Keep in mind the vec type must match in order for this to work

in vec4 vertexColor;

void main()
{
    outputColor = vertexColor;
}

================================================
FILE: Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert
================================================
#version 330 core

// the position variable has attribute position 0
layout(location = 0) in vec3 aPosition; 

// This variable uses the keyword out in order to pass on the value to the 
// next shader down in the chain, in this case the frag shader
out vec4 vertexColor;

void main(void)
{
	// see how we directly give a vec3 to vec4's constructor
    gl_Position = vec4(aPosition, 1.0);

	// Here we assign the variable a dark red color to the out variable
	vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}

================================================
FILE: Chapter1/4-Shaders-InsAndOuts/Window.cs
================================================
using System;
using OpenTK.Graphics.OpenGL4;
using LearnOpenTK.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using System.Diagnostics;

namespace LearnOpenTK

{   // Here we'll be elaborating on what shaders can do from the Hello World project we worked on before.
    // Specifically we'll be showing how shaders deal with input and output from the main program 
    // and between each other.
    public class Window : GameWindow
    {

        private readonly float[] _vertices =
        {
            -0.5f, -0.5f, 0.0f, // Bottom-left vertex
             0.5f, -0.5f, 0.0f, // Bottom-right vertex
             0.0f,  0.5f, 0.0f  // Top vertex
        };

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexBufferObject = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);

            // Vertex attributes are the data we send as input into the vertex shader from the main program.
            // So here we're checking to see how many vertex attributes our hardware can handle.
            // OpenGL at minimum supports 16 vertex attributes. This only needs to be called 
            // when your intensive attribute work and need to know exactly how many are available to you.
            GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
            Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");

            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            _shader.Use();

            GL.BindVertexArray(_vertexArrayObject);

            GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/4-Shaders-MoreAttributes/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Shaders More Attributes!",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/4-Shaders-MoreAttributes/Shaders/shader.frag
================================================
#version 330 core

out vec4 outputColor;

in vec3 ourColor;

void main()
{
    outputColor = vec4(ourColor, 1.0);
}

================================================
FILE: Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert
================================================
#version 330 core

// the position variable has attribute position 0
layout(location = 0) in vec3 aPosition;  

// This is where the color values we assigned in the main program goes to
layout(location = 1) in vec3 aColor;

out vec3 ourColor; // output a color to the fragment shader

void main(void)
{
	// see how we directly give a vec3 to vec4's constructor
    gl_Position = vec4(aPosition, 1.0); 

	// We use the outColor variable to pass on the color information to the frag shader
	ourColor = aColor;
}

================================================
FILE: Chapter1/4-Shaders-MoreAttributes/Window.cs
================================================
using System;
using OpenTK.Graphics.OpenGL4;
using LearnOpenTK.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using System.Diagnostics;

namespace LearnOpenTK
{
    // In this project, we will be assigning 3 colors to the triangle, one for each vertex.
    // The output will be an interpolated value based on the distance from each vertex.
    // If you want to look more into it, the in-between step is called a Rasterizer.
    public class Window : GameWindow
    {

        // We're assigning three different colors at the asscoiate vertex position:
        // blue for the top, green for the bottom left and red for the bottom right.
        private readonly float[] _vertices =
        {
             // positions        // colors
             0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // bottom right
            -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // bottom left
             0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // top 
        };

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        // Now, we start initializing OpenGL.
        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexBufferObject = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            // Just like before, we create a pointer for the 3 position components of our vertices.
            // The only difference here is that we need to account for the 3 color values in the stride variable.
            // Therefore, the stride contains the size of 6 floats instead of 3.
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);

            // We create a new pointer for the color values.
            // Much like the previous pointer, we assign 6 in the stride value.
            // We also need to correctly set the offset to get the color values.
            // The color data starts after the position data, so the offset is the size of 3 floats.
            GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
            // We then enable color attribute (location=1) so it is available to the shader.
            GL.EnableVertexAttribArray(1);

            GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
            Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");

            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            _shader.Use();

            GL.BindVertexArray(_vertexArrayObject);

            GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/4-Shaders-Uniforms/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Shaders Uniforms!",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/4-Shaders-Uniforms/Shaders/shader.frag
================================================
#version 330 core

out vec4 outputColor;


// The Uniform keyword allows you to access a shader variable at any stage of the shader chain
// It's also accessible across all of the main program
// Whatever you set this variable to it keeps it until you either reset the value or updated it

uniform vec4 ourColor; 

void main()
{
    outputColor = ourColor;
}

================================================
FILE: Chapter1/4-Shaders-Uniforms/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;  // the position variable has attribute position 0

void main(void)
{
	// see how we directly give a vec3 to vec4's constructor
    gl_Position = vec4(aPosition, 1.0); 
}

================================================
FILE: Chapter1/4-Shaders-Uniforms/Window.cs
================================================
using System;
using System.Diagnostics;
using OpenTK.Graphics.OpenGL4;
using LearnOpenTK.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;

namespace LearnOpenTK
{
    // This project will explore how to use uniform variable type which allows you to assign values
    // to shaders at any point during the project.
    public class Window : GameWindow
    {

        private readonly float[] _vertices =
        {
            -0.5f, -0.5f, 0.0f, // Bottom-left vertex
             0.5f, -0.5f, 0.0f, // Bottom-right vertex
             0.0f,  0.5f, 0.0f  // Top vertex
        };

        // So we're going make the triangle pulsate between a color range.
        // In order to do that, we'll need a constantly changing value.
        // The stopwatch is perfect for this as it is constantly going up.
        private Stopwatch _timer;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexBufferObject = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);

            GL.GetInteger(GetPName.MaxVertexAttribs, out int maxAttributeCount);
            Debug.WriteLine($"Maximum number of vertex attributes supported: {maxAttributeCount}");

            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            // We start the stopwatch here as this method is only called once.
            _timer = new Stopwatch();
            _timer.Start();
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            _shader.Use();

            // Here, we get the total seconds that have elapsed since the last time this method has reset
            // and we assign it to the timeValue variable so it can be used for the pulsating color.
            double timeValue = _timer.Elapsed.TotalSeconds;

            // We're increasing / decreasing the green value we're passing into
            // the shader based off of timeValue we created in the previous line,
            // as well as using some built in math functions to help the change be smoother.
            float greenValue = (float)Math.Sin(timeValue) / 2.0f + 0.5f;

            // This gets the uniform variable location from the frag shader so that we can 
            // assign the new green value to it.
            int vertexColorLocation = GL.GetUniformLocation(_shader.Handle, "ourColor");

            // Here we're assigning the ourColor variable in the frag shader 
            // via the OpenGL Uniform method which takes in the value as the individual vec values (which total 4 in this instance).
            GL.Uniform4(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

            // You can alternatively use this overload of the same function.
            // GL.Uniform4(vertexColorLocation, new OpenTK.Mathematics.Color4(0f, greenValue, 0f, 0f));

            // Bind the VAO
            GL.BindVertexArray(_vertexArrayObject);

            GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/5-Textures/5-Textures.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/5-Textures/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Textures",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/5-Textures/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

in vec2 texCoord;

// A sampler2d is the representation of a texture in a shader.
// Each sampler is bound to a texture unit (texture units are described in Texture.cs on the Use function).
// By default, the unit is 0, so no code-related setup is actually needed.
// Multiple samplers will be demonstrated in section 1.5.
uniform sampler2D texture0;

void main()
{
    // To use a texture, you call the texture() function.
    // It takes two parameters: the sampler to use, and a vec2, used as texture coordinates.
    outputColor = texture(texture0, texCoord);
}

================================================
FILE: Chapter1/5-Textures/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

// We add another input variable for the texture coordinates.

layout(location = 1) in vec2 aTexCoord;

// ...However, they aren't needed for the vertex shader itself.
// Instead, we create an output variable so we can send that data to the fragment shader.

out vec2 texCoord;

void main(void)
{
    // Then, we further the input texture coordinate to the output one.
    // texCoord can now be used in the fragment shader.
    
    texCoord = aTexCoord;

    gl_Position = vec4(aPosition, 1.0);
}

================================================
FILE: Chapter1/5-Textures/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public class Window : GameWindow
    {
        // Because we're adding a texture, we modify the vertex array to include texture coordinates.
        // Texture coordinates range from 0.0 to 1.0, with (0.0, 0.0) representing the bottom left, and (1.0, 1.0) representing the top right.
        // The new layout is three floats to create a vertex, then two floats to create the coordinates.
        private readonly float[] _vertices =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        private readonly uint[] _indices =
        {
            0, 1, 3,
            1, 2, 3
        };

        private int _elementBufferObject;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        // For documentation on this, check Texture.cs.
        private Texture _texture;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

            // The shaders have been modified to include the texture coordinates, check them out after finishing the OnLoad function.
            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            // Because there's now 5 floats between the start of the first vertex and the start of the second,
            // we modify the stride from 3 * sizeof(float) to 5 * sizeof(float).
            // This will now pass the new vertex array to the buffer.
            var vertexLocation = _shader.GetAttribLocation("aPosition");
            GL.EnableVertexAttribArray(vertexLocation);
            GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

            // Next, we also setup texture coordinates. It works in much the same way.
            // We add an offset of 3, since the texture coordinates comes after the position data.
            // We also change the amount of data to 2 because there's only 2 floats for texture coordinates.
            var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texCoordLocation);
            GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

            _texture = Texture.LoadFromFile("Resources/container.png");
            _texture.Use(TextureUnit.Texture0);
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.BindVertexArray(_vertexArrayObject);

            _texture.Use(TextureUnit.Texture0);
            _shader.Use();

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/6-MultipleTextures/6-MultipleTextures.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/6-MultipleTextures/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Multiple Textures",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/6-MultipleTextures/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;
uniform sampler2D texture1;

void main()
{
    outputColor = mix(texture(texture0, texCoord), texture(texture1, texCoord), 0.2);
}

================================================
FILE: Chapter1/6-MultipleTextures/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

void main(void)
{
    texCoord = aTexCoord;

    gl_Position = vec4(aPosition, 1.0);
}

================================================
FILE: Chapter1/6-MultipleTextures/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        private readonly uint[] _indices =
        {
            0, 1, 3,
            1, 2, 3
        };

        private int _elementBufferObject;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        private Texture _texture;

        private Texture _texture2;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

            // shader.frag has been modified yet again, take a look at it as well.
            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            var vertexLocation = _shader.GetAttribLocation("aPosition");
            GL.EnableVertexAttribArray(vertexLocation);
            GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

            var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texCoordLocation);
            GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

            _texture = Texture.LoadFromFile("Resources/container.png");
            // Texture units are explained in Texture.cs, at the Use function.
            // First texture goes in texture unit 0.
            _texture.Use(TextureUnit.Texture0);

            // This is helpful because System.Drawing reads the pixels differently than OpenGL expects.
            _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
            // Then, the second goes in texture unit 1.
            _texture2.Use(TextureUnit.Texture1);

            // Next, we must setup the samplers in the shaders to use the right textures.
            // The int we send to the uniform indicates which texture unit the sampler should use.
            _shader.SetInt("texture0", 0);
            _shader.SetInt("texture1", 1);
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.BindVertexArray(_vertexArrayObject);

            _texture.Use(TextureUnit.Texture0);
            _texture2.Use(TextureUnit.Texture1);
            _shader.Use();

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/7-Transformations/7-Transformations.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/7-Transformations/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Transformations",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/7-Transformations/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;
uniform sampler2D texture1;

void main()
{
    outputColor = mix(texture(texture0, texCoord), texture(texture1, texCoord), 0.2);
}

================================================
FILE: Chapter1/7-Transformations/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

// Add a uniform for the transformation matrix.
uniform mat4 transform;

void main(void)
{
    texCoord = aTexCoord;

    // Then all you have to do is multiply the vertices by the transformation matrix, and you'll see your transformation in the scene!
    gl_Position = vec4(aPosition, 1.0) * transform;
}

================================================
FILE: Chapter1/7-Transformations/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // So you can setup OpenGL, you can draw basic shapes without wasting vertices, and you can texture them.
    // There's one big thing left, though: moving the shapes.
    // To do this, we use linear algebra to move the vertices in the vertex shader.

    // Just as a disclaimer: this tutorial will NOT explain linear algebra or matrices; those topics are wayyyyy too complex to do with comments.
    // If you want a more detailed understanding of what's going on here, look at the web version of this tutorial instead.
    // A deep understanding of linear algebra won't be necessary for this tutorial as OpenTK includes built-in matrix types that abstract over the actual math.

    // Head down to RenderFrame to see how we can apply transformations to our shape.
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        private readonly uint[] _indices =
        {
            0, 1, 3,
            1, 2, 3
        };

        private int _elementBufferObject;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        private Texture _texture;

        private Texture _texture2;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

            // shader.vert has been modified, take a look at it as well.
            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            var vertexLocation = _shader.GetAttribLocation("aPosition");
            GL.EnableVertexAttribArray(vertexLocation);
            GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

            var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texCoordLocation);
            GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

            _texture = Texture.LoadFromFile("Resources/container.png");
            _texture.Use(TextureUnit.Texture0);

            _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
            _texture2.Use(TextureUnit.Texture1);

            _shader.SetInt("texture0", 0);
            _shader.SetInt("texture1", 1);
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit);

            GL.BindVertexArray(_vertexArrayObject);

            // Note: The matrices we'll use for transformations are all 4x4.

            // We start with an identity matrix. This is just a simple matrix that doesn't move the vertices at all.
            var transform = Matrix4.Identity;

            // The next few steps just show how to use OpenTK's matrix functions, and aren't necessary for the transform matrix to actually work.
            // If you want, you can just pass the identity matrix to the shader, though it won't affect the vertices at all.

            // A fact to note about matrices is that the order of multiplications matter. "matrixA * matrixB" and "matrixB * matrixA" mean different things.
            // A VERY important thing to know is that OpenTK matrices are so called row-major. We won't go into the full details here, but here is a good place to read more about it:
            // https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/row-major-vs-column-major-vector
            // What it means for us is that we can think of matrix multiplication as going left to right.
            // So "rotate * translate" means rotate (around the origin) first and then translate, as opposed to "translate * rotate" which means translate and then rotate (around the origin).

            // To combine two matrices, you multiply them. Here, we combine the transform matrix with another one created by OpenTK to rotate it by 20 degrees.
            // Note that all Matrix4.CreateRotation functions take radians, not degrees. Use MathHelper.DegreesToRadians() to convert to radians, if you want to use degrees.
            transform = transform * Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(20f));

            // Next, we scale the matrix. This will make the rectangle slightly larger.
            transform = transform * Matrix4.CreateScale(1.1f);

            // Then, we translate the matrix, which will move it slightly towards the top-right.
            // Note that we aren't using a full coordinate system yet, so the translation is in normalized device coordinates.
            // The next tutorial will be about how to set one up so we can use more human-readable numbers.
            transform = transform * Matrix4.CreateTranslation(0.1f, 0.1f, 0.0f);

            _texture.Use(TextureUnit.Texture0);
            _texture2.Use(TextureUnit.Texture1);
            _shader.Use();

            // Now that the matrix is finished, pass it to the vertex shader.
            // Go over to shader.vert to see how we finally apply this to the vertices.
            _shader.SetMatrix4("transform", transform);

            // And that's it for now! In the next tutorial, we'll see how to setup a full coordinates system.

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/8-CoordinatesSystems/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Coordinates Systems",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/8-CoordinatesSystems/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;
uniform sampler2D texture1;

void main()
{
    outputColor = mix(texture(texture0, texCoord), texture(texture1, texCoord), 0.2);
}

================================================
FILE: Chapter1/8-CoordinatesSystems/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main(void)
{
    texCoord = aTexCoord;

    gl_Position = vec4(aPosition, 1.0) * model * view * projection;
}


================================================
FILE: Chapter1/8-CoordinatesSystems/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // We can now move around objects. However, how can we move our "camera", or modify our perspective?
    // In this tutorial, I'll show you how to setup a full projection/view/model (PVM) matrix.
    // In addition, we'll make the rectangle rotate over time.
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        private readonly uint[] _indices =
        {
            0, 1, 3,
            1, 2, 3
        };

        private int _elementBufferObject;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        private Texture _texture;

        private Texture _texture2;

        // We create a double to hold how long has passed since the program was opened.
        private double _time;

        // Then, we create two matrices to hold our view and projection. They're initialized at the bottom of OnLoad.
        // The view matrix is what you might consider the "camera". It represents the current viewport in the window.
        private Matrix4 _view;

        // This represents how the vertices will be projected. It's hard to explain through comments,
        // so check out the web version for a good demonstration of what this does.
        private Matrix4 _projection;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            // We enable depth testing here. If you try to draw something more complex than one plane without this,
            // you'll notice that polygons further in the background will occasionally be drawn over the top of the ones in the foreground.
            // Obviously, we don't want this, so we enable depth testing. We also clear the depth buffer in GL.Clear over in OnRenderFrame.
            GL.Enable(EnableCap.DepthTest);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

            // shader.vert has been modified. Take a look at it after the explanation in OnRenderFrame.
            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            var vertexLocation = _shader.GetAttribLocation("aPosition");
            GL.EnableVertexAttribArray(vertexLocation);
            GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

            var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texCoordLocation);
            GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

            _texture = Texture.LoadFromFile("Resources/container.png");
            _texture.Use(TextureUnit.Texture0);

            _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
            _texture2.Use(TextureUnit.Texture1);

            _shader.SetInt("texture0", 0);
            _shader.SetInt("texture1", 1);

            // For the view, we don't do too much here. Next tutorial will be all about a Camera class that will make it much easier to manipulate the view.
            // For now, we move it backwards three units on the Z axis.
            _view = Matrix4.CreateTranslation(0.0f, 0.0f, -3.0f);

            // For the matrix, we use a few parameters.
            //   Field of view. This determines how much the viewport can see at once. 45 is considered the most "realistic" setting, but most video games nowadays use 90
            //   Aspect ratio. This should be set to Width / Height.
            //   Near-clipping. Any vertices closer to the camera than this value will be clipped.
            //   Far-clipping. Any vertices farther away from the camera than this value will be clipped.
            _projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45f), Size.X / (float) Size.Y, 0.1f, 100.0f);

            // Now, head over to OnRenderFrame to see how we setup the model matrix.
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            // We add the time elapsed since last frame, times 4.0 to speed up animation, to the total amount of time passed.
            _time += 4.0 * e.Time;

            // We clear the depth buffer in addition to the color buffer.
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vertexArrayObject);

            _texture.Use(TextureUnit.Texture0);
            _texture2.Use(TextureUnit.Texture1);
            _shader.Use();

            // Finally, we have the model matrix. This determines the position of the model.
            var model = Matrix4.Identity * Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));

            // Then, we pass all of these matrices to the vertex shader.
            // You could also multiply them here and then pass, which is faster, but having the separate matrices available is used for some advanced effects.

            // IMPORTANT: OpenTK's matrix types are transposed from what OpenGL would expect - rows and columns are reversed.
            // They are then transposed properly when passed to the shader. 
            // This means that we retain the same multiplication order in both OpenTK c# code and GLSL shader code.
            // If you pass the individual matrices to the shader and multiply there, you have to do in the order "model * view * projection".
            // You can think like this: first apply the modelToWorld (aka model) matrix, then apply the worldToView (aka view) matrix, 
            // and finally apply the viewToProjectedSpace (aka projection) matrix.
            _shader.SetMatrix4("model", model);
            _shader.SetMatrix4("view", _view);
            _shader.SetMatrix4("projection", _projection);

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
        }
    }
}

================================================
FILE: Chapter1/9-Camera/9-Camera.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>


================================================
FILE: Chapter1/9-Camera/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Camera",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter1/9-Camera/Shaders/shader.frag
================================================
#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;
uniform sampler2D texture1;

void main()
{
    outputColor = mix(texture(texture0, texCoord), texture(texture1, texCoord), 0.2);
}

================================================
FILE: Chapter1/9-Camera/Shaders/shader.vert
================================================
#version 330 core

layout(location = 0) in vec3 aPosition;

layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main(void)
{
    texCoord = aTexCoord;

    gl_Position = vec4(aPosition, 1.0) * model * view * projection;
}


================================================
FILE: Chapter1/9-Camera/Window.cs
================================================
using System;
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // We now have a rotating rectangle but how can we make the view move based on the users input?
    // In this tutorial we will take a look at how you could implement a camera class
    // and start responding to user input.
    // You can move to the camera class to see a lot of the new code added.
    // Otherwise you can move to Load to see how the camera is initialized.

    // In reality, we can't move the camera but we actually move the rectangle.
    // This will explained more in depth in the web version, however it pretty much gives us the same result
    // as if the view itself was moved.
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Position         Texture coordinates
             0.5f,  0.5f, 0.0f, 1.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        private readonly uint[] _indices =
        {
            0, 1, 3,
            1, 2, 3
        };

        private int _elementBufferObject;

        private int _vertexBufferObject;

        private int _vertexArrayObject;

        private Shader _shader;

        private Texture _texture;

        private Texture _texture2;

        // The view and projection matrices have been removed as we don't need them here anymore.
        // They can now be found in the new camera class.

        // We need an instance of the new camera class so it can manage the view and projection matrix code.
        // We also need a boolean set to true to detect whether or not the mouse has been moved for the first time.
        // Finally, we add the last position of the mouse so we can calculate the mouse offset easily.
        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        private double _time;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(_vertexArrayObject);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);

            _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            _shader.Use();

            var vertexLocation = _shader.GetAttribLocation("aPosition");
            GL.EnableVertexAttribArray(vertexLocation);
            GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

            var texCoordLocation = _shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texCoordLocation);
            GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));

            _texture = Texture.LoadFromFile("Resources/container.png");
            _texture.Use(TextureUnit.Texture0);

            _texture2 = Texture.LoadFromFile("Resources/awesomeface.png");
            _texture2.Use(TextureUnit.Texture1);

            _shader.SetInt("texture0", 0);
            _shader.SetInt("texture1", 1);

            // We initialize the camera so that it is 3 units back from where the rectangle is.
            // We also give it the proper aspect ratio.
            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            // We make the mouse cursor invisible and captured so we can have proper FPS-camera movement.
            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            _time += 4.0 * e.Time;

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vertexArrayObject);

            _texture.Use(TextureUnit.Texture0);
            _texture2.Use(TextureUnit.Texture1);
            _shader.Use();

            var model = Matrix4.Identity * Matrix4.CreateRotationX((float)MathHelper.DegreesToRadians(_time));
            _shader.SetMatrix4("model", model);
            _shader.SetMatrix4("view", _camera.GetViewMatrix());
            _shader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused) // Check to see if the window is focused
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }

            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            // Get the mouse state
            var mouse = MouseState;

            if (_firstMove) // This bool variable is initially set to true.
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                // Calculate the offset of the mouse position
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                // Apply the camera pitch and yaw (we clamp the pitch in the camera class)
                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity; // Reversed since y-coordinates range from bottom to top
            }
        }

        // In the mouse wheel function, we manage all the zooming of the camera.
        // This is simply done by changing the FOV of the camera.
        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            // We need to update the aspect ratio once the window has been resized.
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/1-Colors/1-Colors.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/1-Colors/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/1-Colors/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Colors",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/1-Colors/Shaders/lighting.frag
================================================
#version 330 core
out vec4 FragColor;

uniform vec3 objectColor;
uniform vec3 lightColor;

void main()
{
    // For our physically based coloring we simply want to multiply the color of the light with the objects color
    // A much better and in depth explanation of this in the web tutorials.
    FragColor = vec4(lightColor * objectColor, 1.0);
}

================================================
FILE: Chapter2/1-Colors/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/1-Colors/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
}

================================================
FILE: Chapter2/1-Colors/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // In this chapter we will focus on how to use lighting to make our games and other applications more lifelike

    // In this first part the focus will mainly be on setting up a scene for testing the different coloring options.
    // We draw two cubes one at 0,0,0 for testing our light shader, the second one is drawn where we have the light.
    // Furthermore in the shaders we have set up some basic physically based coloring.
    public class Window : GameWindow
    {
        // The vertices are now used to draw cubes.
        // For this example, we aren't using texture coordinates.
        // You can use textures with lighting (and we will get onto this), but for simplicity's sake, we'll just use a solid color here
        private readonly float[] _vertices =
        {
            // Position
            -0.5f, -0.5f, -0.5f, // Front face
             0.5f, -0.5f, -0.5f,
             0.5f,  0.5f, -0.5f,
             0.5f,  0.5f, -0.5f,
            -0.5f,  0.5f, -0.5f,
            -0.5f, -0.5f, -0.5f,

            -0.5f, -0.5f,  0.5f, // Back face
             0.5f, -0.5f,  0.5f,
             0.5f,  0.5f,  0.5f,
             0.5f,  0.5f,  0.5f,
            -0.5f,  0.5f,  0.5f,
            -0.5f, -0.5f,  0.5f,

            -0.5f,  0.5f,  0.5f, // Left face
            -0.5f,  0.5f, -0.5f,
            -0.5f, -0.5f, -0.5f,
            -0.5f, -0.5f, -0.5f,
            -0.5f, -0.5f,  0.5f,
            -0.5f,  0.5f,  0.5f,

             0.5f,  0.5f,  0.5f, // Right face
             0.5f,  0.5f, -0.5f,
             0.5f, -0.5f, -0.5f,
             0.5f, -0.5f, -0.5f,
             0.5f, -0.5f,  0.5f,
             0.5f,  0.5f,  0.5f,

            -0.5f, -0.5f, -0.5f, // Bottom face
             0.5f, -0.5f, -0.5f,
             0.5f, -0.5f,  0.5f,
             0.5f, -0.5f,  0.5f,
            -0.5f, -0.5f,  0.5f,
            -0.5f, -0.5f, -0.5f,

            -0.5f,  0.5f, -0.5f, // Top face
             0.5f,  0.5f, -0.5f,
             0.5f,  0.5f,  0.5f,
             0.5f,  0.5f,  0.5f,
            -0.5f,  0.5f,  0.5f,
            -0.5f,  0.5f, -0.5f
        };

        // This is the position of both the light and the place the lamp cube will be drawn in the scene
        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        // I renamed the vertex array object since we now want two VAO's one for the model (the big cube for testing light shaders),
        // and one for the lamp so we can see where the light source comes from.
        // In an actual application you would probably either not draw the lamp at all or draw it with a model of a lamp of some sort.
        private int _vaoModel;

        private int _vaoLamp;

        // We also need two shaders, one for the lamp and one for our lighting material.
        // The lighting shader is where most of this chapter will take place as this is where a lot of the lighting "magic" happens.
        private Shader _lampShader;

        private Shader _lightingShader;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            // Load the two different shaders, they use the same vertex shader program. However they have two different fragment shaders.
            // This is because the lamp only uses a basic shader to turn it white, it wouldn't make sense to have the lamp lit in other colors.
            // The lighting shaders uses the lighting.frag shader which is what a large part of this chapter will be about
            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

            {
                // Initialize the vao for the model
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                var vertexLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(vertexLocation);
                GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            }

            {
                // Initialize the vao for the lamp, this is mostly the same as the code for the model cube
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                // Set the vertex attributes (only position data for our lamp)
                var vertexLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(vertexLocation);
                GL.VertexAttribPointer(vertexLocation, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            }

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            // Draw the model/cube with the lighting shader
            GL.BindVertexArray(_vaoModel);

            _lightingShader.Use();

            // Matrix4.Identity is used as the matrix, since we just want to draw it at 0, 0, 0
            _lightingShader.SetMatrix4("model", Matrix4.Identity);
            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("objectColor", new Vector3(1.0f, 0.5f, 0.31f));
            _lightingShader.SetVector3("lightColor", new Vector3(1.0f, 1.0f, 1.0f));

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            // Draw the lamp, this is mostly the same as for the model cube
            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.CreateScale(0.2f); // We scale the lamp cube down a bit to make it less dominant
            lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            var mouse = MouseState;

            if (_firstMove)
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity;
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/2-BasicLighting/2-BasicLighting.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/2-BasicLighting/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/2-BasicLighting/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Basic lighting",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/2-BasicLighting/Shaders/lighting.frag
================================================
#version 330 core
out vec4 FragColor;

//In order to calculate some basic lighting we need a few things per model basis, and a few things per fragment basis:
uniform vec3 objectColor; //The color of the object.
uniform vec3 lightColor; //The color of the light.
uniform vec3 lightPos; //The position of the light.
uniform vec3 viewPos; //The position of the view and/or of the player.

in vec3 Normal; //The normal of the fragment is calculated in the vertex shader.
in vec3 FragPos; //The fragment position.

void main()
{
    //The ambient color is the color where the light does not directly hit the object.
    //You can think of it as an underlying tone throughout the object. Or the light coming from the scene/the sky (not the sun).
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    //We calculate the light direction, and make sure the normal is normalized.
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos); //Note: The light is pointing from the light to the fragment

    //The diffuse part of the phong model.
    //This is the part of the light that gives the most, it is the color of the object where it is hit by light.
    float diff = max(dot(norm, lightDir), 0.0); //We make sure the value is non negative with the max function.
    vec3 diffuse = diff * lightColor;


    //The specular light is the light that shines from the object, like light hitting metal.
    //The calculations are explained much more detailed in the web version of the tutorials.
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); //The 32 is the shininess of the material.
    vec3 specular = specularStrength * spec * lightColor;

    //At last we add all the light components together and multiply with the color of the object. Then we set the color
    //and makes sure the alpha value is 1
    vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
    
    //Note we still use the light color * object color from the last tutorial.
    //This time the light values are in the phong model (ambient, diffuse and specular)
}

================================================
FILE: Chapter2/2-BasicLighting/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/2-BasicLighting/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec3 FragPos;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
    FragPos = vec3(vec4(aPos, 1.0) * model);
    Normal = aNormal * mat3(transpose(inverse(model)));
}

================================================
FILE: Chapter2/2-BasicLighting/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // In this tutorial we set up some basic lighting and look at how the phong model works
    // For more insight into how it all works look at the web version. If you are just here for the source,
    // most of the changes are in the shaders, specifically most of the changes are in the fragment shader as this is
    // where the lighting calculations happens.
    public class Window : GameWindow
    {
        // Here we now have added the normals of the vertices
        // Remember to define the layouts to the VAO's
        private readonly float[] _vertices =
        {
             // Position          Normal
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, // Front face
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f, // Back face
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f, // Left face
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f, // Right face
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f, // Bottom face
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f, // Top face
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
        };

        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        private int _vaoModel;

        private int _vaoLamp;

        private Shader _lampShader;

        private Shader _lightingShader;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

            {
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                var positionLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                // Remember to change the stride as we now have 6 floats per vertex
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);

                // We now need to define the layout of the normal so the shader can use it
                var normalLocation = _lightingShader.GetAttribLocation("aNormal");
                GL.EnableVertexAttribArray(normalLocation);
                GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
            }

            {
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                var positionLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                // Also change the stride here as we now have 6 floats per vertex. Now we don't define the normal for the lamp VAO
                // this is because it isn't used, it might seem like a waste to use the same VBO if they dont have the same data
                // The two cubes still use the same position, and since the position is already in the graphics memory it is actually
                // better to do it this way. Look through the web version for a much better understanding of this.
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
            }

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vaoModel);

            _lightingShader.Use();

            _lightingShader.SetMatrix4("model", Matrix4.Identity);
            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("objectColor", new Vector3(1.0f, 0.5f, 0.31f));
            _lightingShader.SetVector3("lightColor", new Vector3(1.0f, 1.0f, 1.0f));
            _lightingShader.SetVector3("lightPos", _lightPos);
            _lightingShader.SetVector3("viewPos", _camera.Position);

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
            lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            var mouse = MouseState;

            if (_firstMove)
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity;
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/3-Materials/3-Materials.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/3-Materials/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/3-Materials/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Materials",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/3-Materials/Shaders/lighting.frag
================================================
#version 330 core
//The material is a collection of some values that we talked about in the last tutorial,
//some crucial elements to the phong model.
struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float shininess; //Shininess is the power the specular light is raised to
};
//The light contains all the values from the light source, how the ambient diffuse and specular values are from the light source.
//This is technically what we were using in the last episode as we were only applying the phong model directly to the light.
struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
//We create the light and the material struct as uniforms.
uniform Light light;
uniform Material material;
//We still need the view position.
uniform vec3 viewPos;

out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;

void main()
{
    //ambient
    vec3 ambient = light.ambient * material.ambient; //Remember to use the material here.

    //diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * (diff * material.diffuse); //Remember to use the material here.

    //specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * (spec * material.specular); //Remember to use the material here.

    //Now the result sum has changed a bit, since we now set the objects color in each element, we now dont have to
    //multiply the light with the object here, instead we do it for each element seperatly. This allows much better control
    //over how each element is applied to different objects.
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

================================================
FILE: Chapter2/3-Materials/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/3-Materials/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec3 FragPos;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
    FragPos = vec3(vec4(aPos, 1.0) * model);
    Normal = aNormal * mat3(transpose(inverse(model)));
}

================================================
FILE: Chapter2/3-Materials/Window.cs
================================================
using System;
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;
using System.Diagnostics;

namespace LearnOpenTK
{
    // In this tutorial we take the code from the last tutorial and create some level of abstraction over it allowing more
    // control of the interaction between the light and the material.
    // At the end of the web version of the tutorial we also had a bit of fun creating a disco light that changes
    // color of the cube over time.
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
             // Position          Normal
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f, // Front face
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f, // Back face
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f, // Left face
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f, // Right face
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f, // Bottom face
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f, // Top face
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
        };

        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        private int _vaoModel;

        private int _vaoLamp;

        private Shader _lampShader;

        private Shader _lightingShader;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

            {
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                var positionLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);

                var normalLocation = _lightingShader.GetAttribLocation("aNormal");
                GL.EnableVertexAttribArray(normalLocation);
                GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
            }
            
            {
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                var positionLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
            }

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vaoModel);

            _lightingShader.Use();

            _lightingShader.SetMatrix4("model", Matrix4.Identity);
            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("viewPos", _camera.Position);

            // Here we set the material values of the cube, the material struct is just a container so to access
            // the underlying values we simply type "material.value" to get the location of the uniform
            _lightingShader.SetVector3("material.ambient", new Vector3(1.0f, 0.5f, 0.31f));
            _lightingShader.SetVector3("material.diffuse", new Vector3(1.0f, 0.5f, 0.31f));
            _lightingShader.SetVector3("material.specular", new Vector3(0.5f, 0.5f, 0.5f));
            _lightingShader.SetFloat("material.shininess", 32.0f);

            // This is where we change the lights color over time using the sin function
            Vector3 lightColor;
            float time = DateTime.Now.Second + DateTime.Now.Millisecond / 1000f;
            lightColor.X = (MathF.Sin(time * 2.0f) + 1) / 2f;
            lightColor.Y = (MathF.Sin(time * 0.7f) + 1) / 2f;
            lightColor.Z = (MathF.Sin(time * 1.3f) + 1) / 2f;

            // The ambient light is less intensive than the diffuse light in order to make it less dominant
            Vector3 ambientColor = lightColor * new Vector3(0.2f);
            Vector3 diffuseColor = lightColor * new Vector3(0.5f);

            _lightingShader.SetVector3("light.position", _lightPos);
            _lightingShader.SetVector3("light.ambient", ambientColor);
            _lightingShader.SetVector3("light.diffuse", diffuseColor);
            _lightingShader.SetVector3("light.specular", new Vector3(1.0f, 1.0f, 1.0f));

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.Identity;
            lampMatrix *= Matrix4.CreateScale(0.2f);
            lampMatrix *= Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            var mouse = MouseState;

            if (_firstMove)
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity;
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/4-LightingMaps/4-LightingMaps.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
    <None Update="Resources\container2.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="Resources\container2_specular.png">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/4-LightingMaps/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/4-LightingMaps/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Lighting maps",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/4-LightingMaps/Shaders/lighting.frag
================================================
#version 330 core
// Now the diffuse and the specular values are controlled by textures, this is what we in graphics call mapping something.
// This means they are now based on textures instead of a color, and can be controlled much better per fragment.
// This also allows us the ability to texture our objects again.

// Note: We dont have a value for the ambient, as that is mostly the same the diffuse in pretty much every single situation.
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};
struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;
uniform Material material;
uniform vec3 viewPos;

out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;

// Now we need the texture coordinates, however we only need one set even though we have 2 textures,
// as every fragment should have the same texture position no matter what texture we are using.
in vec2 TexCoords;

void main()
{
    // Each of the 3 different components now use a texture for the material values instead of the object wide color they had before.
    // Note: The ambient and the diffuse share the same texture.
    // ambient
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

    // Diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

    // Specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

================================================
FILE: Chapter2/4-LightingMaps/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/4-LightingMaps/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
    FragPos = vec3(vec4(aPos, 1.0) * model);
    Normal = aNormal * mat3(transpose(inverse(model)));
    TexCoords = aTexCoords;
}

================================================
FILE: Chapter2/4-LightingMaps/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // In this tutorial we take a look at how we can use textures to make the light settings we set up in the last episode
    // different per fragment instead of making them per object.
    // Remember to check out the shaders for how we converted to using textures there.
    public class Window : GameWindow
    {
        // Since we are going to use textures we of course have to include two new floats per vertex, the texture coords.
        private readonly float[] _vertices =
        {
            // Positions          Normals              Texture coords
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
        };

        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        private int _vaoModel;

        private int _vaoLamp;

        private Shader _lampShader;

        private Shader _lightingShader;

        // The texture containing information for the diffuse map, this would more commonly
        // just be called the color/texture of the object.
        private Texture _diffuseMap;

        // The specular map is a black/white representation of how specular each part of the texture is.
        private Texture _specularMap;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

            {
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                // All of the vertex attributes have been updated to now have a stride of 8 float sizes.
                var positionLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);

                var normalLocation = _lightingShader.GetAttribLocation("aNormal");
                GL.EnableVertexAttribArray(normalLocation);
                GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));

                // The texture coords have now been added too, remember we only have 2 coordinates as the texture is 2d,
                // so the size parameter should only be 2 for the texture coordinates.
                var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
                GL.EnableVertexAttribArray(texCoordLocation);
                GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
            }
            
            {
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                // The lamp shader should have its stride updated aswell, however we dont actually
                // use the texture coords for the lamp, so we dont need to add any extra attributes.
                var positionLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
            }
            
            // Our two textures are loaded in from memory, you should head over and
            // check them out and compare them to the results.
            _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
            _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vaoModel);

            // The two textures need to be used, in this case we use the diffuse map as our 0th texture
            // and the specular map as our 1st texture.
            _diffuseMap.Use(TextureUnit.Texture0);
            _specularMap.Use(TextureUnit.Texture1);
            _lightingShader.Use();

            _lightingShader.SetMatrix4("model", Matrix4.Identity);
            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("viewPos", _camera.Position);

            // Here we specify to the shaders what textures they should refer to when we want to get the positions.
            _lightingShader.SetInt("material.diffuse", 0);
            _lightingShader.SetInt("material.specular", 1);
            _lightingShader.SetFloat("material.shininess", 32.0f);

            _lightingShader.SetVector3("light.position", _lightPos);
            _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
            _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
            _lightingShader.SetVector3("light.specular", new Vector3(1.0f));

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.Identity;
            lampMatrix *= Matrix4.CreateScale(0.2f);
            lampMatrix *= Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            var mouse = MouseState;

            if (_firstMove)
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity;
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);
            
            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/Program.cs
================================================
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new OpenTK.Mathematics.Vector2i(800, 600),
                Title = "LearnOpenTK - Light caster - directional",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag
================================================
#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};
struct Light {
    //For a directional light we dont need the lights position to calculate the direction.
    //Since the direction is the same no matter the position of the fragment we also dont need that.
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;
uniform Material material;
uniform vec3 viewPos;

out vec4 FragColor;

in vec3 Normal;
in vec2 TexCoords;

void main()
{
    // ambient
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(-light.direction);//We still normalize the light direction since we techically dont know,
                                                    //wether it was normalized for us or not.
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

    // specular
    vec3 viewDir = normalize(viewPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec2 TexCoords;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
    Normal = aNormal * mat3(transpose(inverse(model)));
    TexCoords = aTexCoords;
}

================================================
FILE: Chapter2/5-LightCasters-DirectionalLights/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // This tutorial is split up into multiple different bits, one for each type of light.

    // The following is the code for the directional light, a light that has a direction but no position.
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Positions          Normals              Texture coords
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
        };

        // We draw multiple different cubes and it helps to store all
        // their positions in an array for later when we want to draw them
        private readonly Vector3[] _cubePositions =
        {
            new Vector3(0.0f, 0.0f, 0.0f),
            new Vector3(2.0f, 5.0f, -15.0f),
            new Vector3(-1.5f, -2.2f, -2.5f),
            new Vector3(-3.8f, -2.0f, -12.3f),
            new Vector3(2.4f, -0.4f, -3.5f),
            new Vector3(-1.7f, 3.0f, -7.5f),
            new Vector3(1.3f, -2.0f, -2.5f),
            new Vector3(1.5f, 2.0f, -2.5f),
            new Vector3(1.5f, 0.2f, -1.5f),
            new Vector3(-1.3f, 1.0f, -1.5f)
        };

        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        private int _vaoModel;

        private int _vaoLamp;

        private Shader _lampShader;

        private Shader _lightingShader;

        private Texture _diffuseMap;

        private Texture _specularMap;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            
            {
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                var positionLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);

                var normalLocation = _lightingShader.GetAttribLocation("aNormal");
                GL.EnableVertexAttribArray(normalLocation);
                GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));

                var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
                GL.EnableVertexAttribArray(texCoordLocation);
                GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
            }

            {
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);

                var positionLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
            }

            _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
            _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vaoModel);

            _diffuseMap.Use(TextureUnit.Texture0);
            _specularMap.Use(TextureUnit.Texture1);
            _lightingShader.Use();

            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("viewPos", _camera.Position);

            _lightingShader.SetInt("material.diffuse", 0);
            _lightingShader.SetInt("material.specular", 1);
            _lightingShader.SetFloat("material.shininess", 32.0f);

            // Directional light needs a direction, in this example we just use (-0.2, -1.0, -0.3f) as the lights direction
            _lightingShader.SetVector3("light.direction", new Vector3(-0.2f, -1.0f, -0.3f));
            _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
            _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
            _lightingShader.SetVector3("light.specular", new Vector3(1.0f));

            // We want to draw all the cubes at their respective positions
            for (int i = 0; i < _cubePositions.Length; i++)
            {
                // Then we translate said matrix by the cube position
                Matrix4 model = Matrix4.CreateTranslation(_cubePositions[i]);
                // We then calculate the angle and rotate the model around an axis
                float angle = 20.0f * i;
                model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
                // Remember to set the model at last so it can be used by opentk
                _lightingShader.SetMatrix4("model", model);

                // At last we draw all our cubes
                GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
            }

            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
            lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (float)e.Time; // Up
            }
            if (input.IsKeyDown(Keys.LeftShift))
            {
                _camera.Position -= _camera.Up * cameraSpeed * (float)e.Time; // Down
            }

            var mouse = MouseState;

            if (_firstMove)
            {
                _lastPos = new Vector2(mouse.X, mouse.Y);
                _firstMove = false;
            }
            else
            {
                var deltaX = mouse.X - _lastPos.X;
                var deltaY = mouse.Y - _lastPos.Y;
                _lastPos = new Vector2(mouse.X, mouse.Y);

                _camera.Yaw += deltaX * sensitivity;
                _camera.Pitch -= deltaY * sensitivity;
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            _camera.Fov -= e.OffsetY;
        }

        protected override void OnResize(ResizeEventArgs e)
        {
            base.OnResize(e);

            GL.Viewport(0, 0, Size.X, Size.Y);
            _camera.AspectRatio = Size.X / (float)Size.Y;
        }
    }
}


================================================
FILE: Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <RootNamespace>LearnOpenTK</RootNamespace>
    <AssemblyName>LearnOpenTK</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Include="Resources/**" CopyToOutputDirectory="PreserveNewest" />
    <None Include="Shaders/**" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="OpenTK" Version="4.8.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Common\Common.csproj" />
  </ItemGroup>
</Project>

================================================
FILE: Chapter2/5-LightCasters-PointLights/OpenTK.dll.config
================================================
<configuration>
  <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
  <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
  <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
  <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
  <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
  <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
  <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
  <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
  <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
  <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
  <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
  <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
  <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
  <!-- XQuartz compatibility (X11 on Mac) -->
  <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
  <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
  <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
  <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
  <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
  <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>


================================================
FILE: Chapter2/5-LightCasters-PointLights/Program.cs
================================================
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    public static class Program
    {
        private static void Main()
        {
            var nativeWindowSettings = new NativeWindowSettings()
            {
                ClientSize = new Vector2i(800, 600),
                Title = "LearnOpenTK - Light casters - point lights",
                // This is needed to run on macos
                Flags = ContextFlags.ForwardCompatible,
            };

            using (var window = new Window(GameWindowSettings.Default, nativeWindowSettings))
            {
                window.Run();
            }
        }
    }
}


================================================
FILE: Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag
================================================
#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};
//This light structure is pretty much the same as the one from the last few parts of the tutorials
struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    //In the web version you can see why we need the constant the linear and the quadratic values.
    //However to keep a brief explanation here, it is to make the light more dim the further you go away.
    //These are constants defining the graph the intensity of the light follows.
    float constant;
    float linear;
    float quadratic;
};

uniform Light light;
uniform Material material;
uniform vec3 viewPos;

out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;

void main()
{

    //ambient
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

    //diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

    //specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    
    //attenuation
    //The attenuation is the term we use when talking about how dim the light gets over distance
    float distance    = length(light.position - FragPos);
    //This formula is the so called attenuation formula used to calculate the attenuation
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    //To apply the attenuation simply multiply it into each of the elements
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

================================================
FILE: Chapter2/5-LightCasters-PointLights/Shaders/shader.frag
================================================
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0); // set all 4 vector values to 1.0
}

================================================
FILE: Chapter2/5-LightCasters-PointLights/Shaders/shader.vert
================================================
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;

void main()
{
    gl_Position = vec4(aPos, 1.0) * model * view * projection;
    FragPos = vec3(vec4(aPos, 1.0) * model);
    Normal = aNormal * mat3(transpose(inverse(model)));
    TexCoords = aTexCoords;
}

================================================
FILE: Chapter2/5-LightCasters-PointLights/Window.cs
================================================
using LearnOpenTK.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace LearnOpenTK
{
    // This tutorial is split up into multiple different bits, one for each type of light.

    // The following is the code for the point light, a point light is in essence the same as we made in tutorial 1-4
    // except it diminishes over distance (attenuation)
    public class Window : GameWindow
    {
        private readonly float[] _vertices =
        {
            // Positions          Normals              Texture coords
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
        };

        // We draw multiple different cubes and it helps to store all
        // their positions in an array for later when we want to draw them
        private readonly Vector3[] _cubePositions =
        {
            new Vector3(0.0f, 0.0f, 0.0f),
            new Vector3(2.0f, 5.0f, -15.0f),
            new Vector3(-1.5f, -2.2f, -2.5f),
            new Vector3(-3.8f, -2.0f, -12.3f),
            new Vector3(2.4f, -0.4f, -3.5f),
            new Vector3(-1.7f, 3.0f, -7.5f),
            new Vector3(1.3f, -2.0f, -2.5f),
            new Vector3(1.5f, 2.0f, -2.5f),
            new Vector3(1.5f, 0.2f, -1.5f),
            new Vector3(-1.3f, 1.0f, -1.5f)
        };

        private readonly Vector3 _lightPos = new Vector3(1.2f, 1.0f, 2.0f);

        private int _vertexBufferObject;

        private int _vaoModel;

        private int _vaoLamp;

        private Shader _lampShader;

        private Shader _lightingShader;

        private Texture _diffuseMap;

        private Texture _specularMap;

        private Camera _camera;

        private bool _firstMove = true;

        private Vector2 _lastPos;

        public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
            : base(gameWindowSettings, nativeWindowSettings)
        {
        }

        protected override void OnLoad()
        {
            base.OnLoad();

            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            GL.Enable(EnableCap.DepthTest);

            _vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            _lightingShader = new Shader("Shaders/shader.vert", "Shaders/lighting.frag");
            _lampShader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");
            
            {
                _vaoModel = GL.GenVertexArray();
                GL.BindVertexArray(_vaoModel);

                var positionLocation = _lightingShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);

                var normalLocation = _lightingShader.GetAttribLocation("aNormal");
                GL.EnableVertexAttribArray(normalLocation);
                GL.VertexAttribPointer(normalLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 3 * sizeof(float));

                var texCoordLocation = _lightingShader.GetAttribLocation("aTexCoords");
                GL.EnableVertexAttribArray(texCoordLocation);
                GL.VertexAttribPointer(texCoordLocation, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 6 * sizeof(float));
            }

            {
                _vaoLamp = GL.GenVertexArray();
                GL.BindVertexArray(_vaoLamp);

                var positionLocation = _lampShader.GetAttribLocation("aPos");
                GL.EnableVertexAttribArray(positionLocation);
                GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0);
            }

            _diffuseMap = Texture.LoadFromFile("Resources/container2.png");
            _specularMap = Texture.LoadFromFile("Resources/container2_specular.png");

            _camera = new Camera(Vector3.UnitZ * 3, Size.X / (float)Size.Y);

            CursorState = CursorState.Grabbed;
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.BindVertexArray(_vaoModel);

            _diffuseMap.Use(TextureUnit.Texture0);
            _specularMap.Use(TextureUnit.Texture1);
            _lightingShader.Use();

            _lightingShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lightingShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            _lightingShader.SetVector3("viewPos", _camera.Position);

            _lightingShader.SetInt("material.diffuse", 0);
            _lightingShader.SetInt("material.specular", 1);
            _lightingShader.SetFloat("material.shininess", 32.0f);

            _lightingShader.SetVector3("light.position", _lightPos);
            _lightingShader.SetFloat("light.constant", 1.0f);
            _lightingShader.SetFloat("light.linear", 0.09f);
            _lightingShader.SetFloat("light.quadratic", 0.032f);
            _lightingShader.SetVector3("light.ambient", new Vector3(0.2f));
            _lightingShader.SetVector3("light.diffuse", new Vector3(0.5f));
            _lightingShader.SetVector3("light.specular", new Vector3(1.0f));

            // We want to draw all the cubes at their respective positions
            for (int i = 0; i < _cubePositions.Length; i++)
            {
                // First we create a model from an identity matrix
                // Then we translate said matrix by the cube position
                Matrix4 model = Matrix4.CreateTranslation(_cubePositions[i]);
                // We then calculate the angle and rotate the model around an axis
                float angle = 20.0f * i;
                model = model * Matrix4.CreateFromAxisAngle(new Vector3(1.0f, 0.3f, 0.5f), angle);
                // Remember to set the model at last so it can be used by opentk
                _lightingShader.SetMatrix4("model", model);

                // At last we draw all our cubes
                GL.DrawArrays(PrimitiveType.Triangles, 0, 36);
            }

            GL.BindVertexArray(_vaoLamp);

            _lampShader.Use();

            Matrix4 lampMatrix = Matrix4.CreateScale(0.2f);
            lampMatrix = lampMatrix * Matrix4.CreateTranslation(_lightPos);

            _lampShader.SetMatrix4("model", lampMatrix);
            _lampShader.SetMatrix4("view", _camera.GetViewMatrix());
            _lampShader.SetMatrix4("projection", _camera.GetProjectionMatrix());

            GL.DrawArrays(PrimitiveType.Triangles, 0, 36);

            SwapBuffers();
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);

            if (!IsFocused)
            {
                return;
            }

            var input = KeyboardState;

            if (input.IsKeyDown(Keys.Escape))
            {
                Close();
            }

            const float cameraSpeed = 1.5f;
            const float sensitivity = 0.2f;

            if (input.IsKeyDown(Keys.W))
            {
                _camera.Position += _camera.Front * cameraSpeed * (float)e.Time; // Forward
            }
            if (input.IsKeyDown(Keys.S))
            {
                _camera.Position -= _camera.Front * cameraSpeed * (float)e.Time; // Backwards
            }
            if (input.IsKeyDown(Keys.A))
            {
                _camera.Position -= _camera.Right * cameraSpeed * (float)e.Time; // Left
            }
            if (input.IsKeyDown(Keys.D))
            {
                _camera.Position += _camera.Right * cameraSpeed * (float)e.Time; // Right
            }
            if (input.IsKeyDown(Keys.Space))
            {
                _camera.Position += _camera.Up * cameraSpeed * (floa
Download .txt
gitextract_wlj10zxr/

├── .gitattributes
├── .gitignore
├── Chapter1/
│   ├── 1-CreatingAWindow/
│   │   ├── 1-CreatingAWindow.csproj
│   │   ├── Program.cs
│   │   └── Window.cs
│   ├── 2-HelloTriangle/
│   │   ├── 2-HelloTriangle.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 3-ElementBufferObjects/
│   │   ├── 3-ElementBufferObjects.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-InsAndOuts/
│   │   ├── 4-Shaders-InsAndOuts.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-MoreAttributes/
│   │   ├── 4-Shaders-MoreAttributes.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-Shaders-Uniforms/
│   │   ├── 4-Shaders-Uniforms.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-Textures/
│   │   ├── 5-Textures.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 6-MultipleTextures/
│   │   ├── 6-MultipleTextures.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 7-Transformations/
│   │   ├── 7-Transformations.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 8-CoordinatesSystems/
│   │   ├── 8-CoordinatesSystems.csproj
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   └── 9-Camera/
│       ├── 9-Camera.csproj
│       ├── Program.cs
│       ├── Shaders/
│       │   ├── shader.frag
│       │   └── shader.vert
│       └── Window.cs
├── Chapter2/
│   ├── 1-Colors/
│   │   ├── 1-Colors.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 2-BasicLighting/
│   │   ├── 2-BasicLighting.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 3-Materials/
│   │   ├── 3-Materials.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 4-LightingMaps/
│   │   ├── 4-LightingMaps.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-DirectionalLights/
│   │   ├── 5-LightCasters-DirectionalLights.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-PointLights/
│   │   ├── 5-LightCasters-PointLights.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   ├── 5-LightCasters-Spotlight/
│   │   ├── 5-LightCasters-Spotlight.csproj
│   │   ├── OpenTK.dll.config
│   │   ├── Program.cs
│   │   ├── Shaders/
│   │   │   ├── lighting.frag
│   │   │   ├── shader.frag
│   │   │   └── shader.vert
│   │   └── Window.cs
│   └── 6-MultipleLights/
│       ├── 6-MultipleLights.csproj
│       ├── OpenTK.dll.config
│       ├── Program.cs
│       ├── Shaders/
│       │   ├── lighting.frag
│       │   ├── shader.frag
│       │   └── shader.vert
│       └── Window.cs
├── Common/
│   ├── Camera.cs
│   ├── Common.csproj
│   ├── Shader.cs
│   └── Texture.cs
├── LICENSE
├── LearnOpenTK.sln
└── README.md
Download .txt
SYMBOL INDEX (178 symbols across 41 files)

FILE: Chapter1/1-CreatingAWindow/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/1-CreatingAWindow/Window.cs
  class Window (line 10) | public class Window : GameWindow
    method Window (line 13) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnUpdateFrame (line 19) | protected override void OnUpdateFrame(FrameEventArgs e)

FILE: Chapter1/2-HelloTriangle/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/2-HelloTriangle/Window.cs
  class Window (line 11) | public class Window : GameWindow
    method Window (line 39) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 45) | protected override void OnLoad()
    method OnRenderFrame (line 127) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 167) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 179) | protected override void OnResize(ResizeEventArgs e)
    method OnUnload (line 201) | protected override void OnUnload()

FILE: Chapter1/3-ElementBufferObjects/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/3-ElementBufferObjects/Window.cs
  class Window (line 17) | public class Window : GameWindow
    method Window (line 46) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 51) | protected override void OnLoad()
    method OnRenderFrame (line 83) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 106) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 118) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/4-Shaders-InsAndOuts/Program.cs
  class Program (line 7) | class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/4-Shaders-InsAndOuts/Window.cs
  class Window (line 14) | public class Window : GameWindow
    method Window (line 30) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 35) | protected override void OnLoad()
    method OnRenderFrame (line 63) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 78) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 90) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/4-Shaders-MoreAttributes/Program.cs
  class Program (line 7) | class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/4-Shaders-MoreAttributes/Window.cs
  class Window (line 14) | public class Window : GameWindow
    method Window (line 33) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 39) | protected override void OnLoad()
    method OnRenderFrame (line 74) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 89) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 101) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/4-Shaders-Uniforms/Program.cs
  class Program (line 7) | class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/4-Shaders-Uniforms/Window.cs
  class Window (line 13) | public class Window : GameWindow
    method Window (line 34) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 39) | protected override void OnLoad()
    method OnRenderFrame (line 67) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 103) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 115) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/5-Textures/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/5-Textures/Window.cs
  class Window (line 9) | public class Window : GameWindow
    method Window (line 40) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 45) | protected override void OnLoad()
    method OnRenderFrame (line 84) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 100) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 112) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/6-MultipleTextures/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/6-MultipleTextures/Window.cs
  class Window (line 9) | public class Window : GameWindow
    method Window (line 38) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 43) | protected override void OnLoad()
    method OnRenderFrame (line 88) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 105) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 117) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/7-Transformations/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/7-Transformations/Window.cs
  class Window (line 19) | public class Window : GameWindow
    method Window (line 48) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 53) | protected override void OnLoad()
    method OnRenderFrame (line 92) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 141) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 153) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/8-CoordinatesSystems/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/8-CoordinatesSystems/Window.cs
  class Window (line 13) | public class Window : GameWindow
    method Window (line 53) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 58) | protected override void OnLoad()
    method OnRenderFrame (line 115) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 152) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnResize (line 164) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter1/9-Camera/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter1/9-Camera/Window.cs
  class Window (line 20) | public class Window : GameWindow
    method Window (line 63) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 68) | protected override void OnLoad()
    method OnRenderFrame (line 115) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 139) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 207) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 214) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/1-Colors/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/1-Colors/Window.cs
  class Window (line 15) | public class Window : GameWindow
    method Window (line 90) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 95) | protected override void OnLoad()
    method OnRenderFrame (line 139) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 177) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 239) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 246) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/2-BasicLighting/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/2-BasicLighting/Window.cs
  class Window (line 14) | public class Window : GameWindow
    method Window (line 82) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 87) | protected override void OnLoad()
    method OnRenderFrame (line 135) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 172) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 234) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 241) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/3-Materials/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/3-Materials/Window.cs
  class Window (line 16) | public class Window : GameWindow
    method Window (line 82) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 87) | protected override void OnLoad()
    method OnRenderFrame (line 129) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 187) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 249) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 256) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/4-LightingMaps/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/4-LightingMaps/Window.cs
  class Window (line 13) | public class Window : GameWindow
    method Window (line 87) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 92) | protected override void OnLoad()
    method OnRenderFrame (line 148) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 197) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 259) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 266) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/5-LightCasters-DirectionalLights/Program.cs
  class Program (line 6) | public static class Program
    method Main (line 8) | private static void Main()

FILE: Chapter2/5-LightCasters-DirectionalLights/Window.cs
  class Window (line 13) | public class Window : GameWindow
    method Window (line 99) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 104) | protected override void OnLoad()
    method OnRenderFrame (line 155) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 213) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 275) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 282) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/5-LightCasters-PointLights/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/5-LightCasters-PointLights/Window.cs
  class Window (line 14) | public class Window : GameWindow
    method Window (line 100) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 105) | protected override void OnLoad()
    method OnRenderFrame (line 154) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 215) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 277) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 284) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/5-LightCasters-Spotlight/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/5-LightCasters-Spotlight/Window.cs
  class Window (line 16) | public class Window : GameWindow
    method Window (line 102) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 107) | protected override void OnLoad()
    method OnRenderFrame (line 156) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 219) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 281) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 288) | protected override void OnResize(ResizeEventArgs e)

FILE: Chapter2/6-MultipleLights/Program.cs
  class Program (line 7) | public static class Program
    method Main (line 9) | private static void Main()

FILE: Chapter2/6-MultipleLights/Window.cs
  class Window (line 13) | public class Window : GameWindow
    method Window (line 104) | public Window(GameWindowSettings gameWindowSettings, NativeWindowSetti...
    method OnLoad (line 109) | protected override void OnLoad()
    method OnRenderFrame (line 158) | protected override void OnRenderFrame(FrameEventArgs e)
    method OnUpdateFrame (line 245) | protected override void OnUpdateFrame(FrameEventArgs e)
    method OnMouseWheel (line 307) | protected override void OnMouseWheel(MouseWheelEventArgs e)
    method OnResize (line 314) | protected override void OnResize(ResizeEventArgs e)

FILE: Common/Camera.cs
  class Camera (line 13) | public class Camera
    method Camera (line 31) | public Camera(Vector3 position, float aspectRatio)
    method GetViewMatrix (line 90) | public Matrix4 GetViewMatrix()
    method GetProjectionMatrix (line 96) | public Matrix4 GetProjectionMatrix()
    method UpdateVectors (line 102) | private void UpdateVectors()

FILE: Common/Shader.cs
  class Shader (line 11) | public class Shader
    method Shader (line 21) | public Shader(string vertPath, string fragPath)
    method CompileShader (line 89) | private static void CompileShader(int shader)
    method LinkProgram (line 104) | private static void LinkProgram(int program)
    method Use (line 119) | public void Use()
    method GetAttribLocation (line 126) | public int GetAttribLocation(string attribName)
    method SetInt (line 145) | public void SetInt(string name, int data)
    method SetFloat (line 156) | public void SetFloat(string name, float data)
    method SetMatrix4 (line 172) | public void SetMatrix4(string name, Matrix4 data)
    method SetVector3 (line 183) | public void SetVector3(string name, Vector3 data)

FILE: Common/Texture.cs
  class Texture (line 11) | public class Texture
    method LoadFromFile (line 15) | public static Texture LoadFromFile(string path)
    method Texture (line 76) | public Texture(int glHandle)
    method Use (line 85) | public void Use(TextureUnit unit)
Condensed preview — 118 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (295K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".gitignore",
    "chars": 4305,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
  },
  {
    "path": "Chapter1/1-CreatingAWindow/1-CreatingAWindow.csproj",
    "chars": 345,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/1-CreatingAWindow/Program.cs",
    "chars": 867,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/1-CreatingAWindow/Window.cs",
    "chars": 1058,
    "preview": "\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.GraphicsLibraryFramework;\nusing OpenTK.Windowing.Desktop;\n\nnames"
  },
  {
    "path": "Chapter1/2-HelloTriangle/2-HelloTriangle.csproj",
    "chars": 810,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/2-HelloTriangle/Program.cs",
    "chars": 682,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/2-HelloTriangle/Shaders/shader.frag",
    "chars": 96,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nvoid main()\n{\n    outputColor = vec4(1.0, 1.0, 0.0, 1.0);\n}"
  },
  {
    "path": "Chapter1/2-HelloTriangle/Shaders/shader.vert",
    "chars": 2246,
    "preview": "// For more information on how shaders work, check out the web version of this tutorial.\n// I'll include a simpler summa"
  },
  {
    "path": "Chapter1/2-HelloTriangle/Window.cs",
    "chars": 12786,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Graphics"
  },
  {
    "path": "Chapter1/3-ElementBufferObjects/3-ElementBufferObjects.csproj",
    "chars": 540,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/3-ElementBufferObjects/Program.cs",
    "chars": 687,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/3-ElementBufferObjects/Shaders/shader.frag",
    "chars": 96,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nvoid main()\n{\n    outputColor = vec4(1.0, 1.0, 0.0, 1.0);\n}"
  },
  {
    "path": "Chapter1/3-ElementBufferObjects/Shaders/shader.vert",
    "chars": 119,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\nvoid main(void)\n{\n    gl_Position = vec4(aPosition, 1.0);\n}"
  },
  {
    "path": "Chapter1/3-ElementBufferObjects/Window.cs",
    "chars": 5513,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Graphics"
  },
  {
    "path": "Chapter1/4-Shaders-InsAndOuts/4-Shaders-InsAndOuts.csproj",
    "chars": 540,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/4-Shaders-InsAndOuts/Program.cs",
    "chars": 671,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    c"
  },
  {
    "path": "Chapter1/4-Shaders-InsAndOuts/Shaders/shader.frag",
    "chars": 314,
    "preview": "#version 330 core\n\nout vec4 outputColor;\n\n// This is where the color variable we declared and assigned in vertex shader "
  },
  {
    "path": "Chapter1/4-Shaders-InsAndOuts/Shaders/shader.vert",
    "chars": 501,
    "preview": "#version 330 core\n\n// the position variable has attribute position 0\nlayout(location = 0) in vec3 aPosition; \n\n// This v"
  },
  {
    "path": "Chapter1/4-Shaders-InsAndOuts/Window.cs",
    "chars": 3148,
    "preview": "using System;\nusing OpenTK.Graphics.OpenGL4;\nusing LearnOpenTK.Common;\nusing OpenTK.Windowing.Desktop;\nusing OpenTK.Win"
  },
  {
    "path": "Chapter1/4-Shaders-MoreAttributes/4-Shaders-MoreAttributes.csproj",
    "chars": 540,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/4-Shaders-MoreAttributes/Program.cs",
    "chars": 675,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    c"
  },
  {
    "path": "Chapter1/4-Shaders-MoreAttributes/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\n\nout vec4 outputColor;\n\nin vec3 ourColor;\n\nvoid main()\n{\n    outputColor = vec4(ourColor, 1.0);\n}"
  },
  {
    "path": "Chapter1/4-Shaders-MoreAttributes/Shaders/shader.vert",
    "chars": 509,
    "preview": "#version 330 core\n\n// the position variable has attribute position 0\nlayout(location = 0) in vec3 aPosition;  \n\n// This "
  },
  {
    "path": "Chapter1/4-Shaders-MoreAttributes/Window.cs",
    "chars": 3946,
    "preview": "using System;\nusing OpenTK.Graphics.OpenGL4;\nusing LearnOpenTK.Common;\nusing OpenTK.Windowing.Desktop;\nusing OpenTK.Win"
  },
  {
    "path": "Chapter1/4-Shaders-Uniforms/4-Shaders-Uniforms.csproj",
    "chars": 540,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/4-Shaders-Uniforms/Program.cs",
    "chars": 668,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    c"
  },
  {
    "path": "Chapter1/4-Shaders-Uniforms/Shaders/shader.frag",
    "chars": 358,
    "preview": "#version 330 core\n\nout vec4 outputColor;\n\n\n// The Uniform keyword allows you to access a shader variable at any stage of"
  },
  {
    "path": "Chapter1/4-Shaders-Uniforms/Shaders/shader.vert",
    "chars": 229,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;  // the position variable has attribute position 0\n\nvoid main"
  },
  {
    "path": "Chapter1/4-Shaders-Uniforms/Window.cs",
    "chars": 4387,
    "preview": "using System;\nusing System.Diagnostics;\nusing OpenTK.Graphics.OpenGL4;\nusing LearnOpenTK.Common;\nusing OpenTK.Windowing"
  },
  {
    "path": "Chapter1/5-Textures/5-Textures.csproj",
    "chars": 615,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/5-Textures/Program.cs",
    "chars": 673,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/5-Textures/Shaders/shader.frag",
    "chars": 602,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nin vec2 texCoord;\n\n// A sampler2d is the representation of a texture in a shader.\n/"
  },
  {
    "path": "Chapter1/5-Textures/Shaders/shader.vert",
    "chars": 558,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\n// We add another input variable for the texture coordinates"
  },
  {
    "path": "Chapter1/5-Textures/Window.cs",
    "chars": 4538,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Graphics"
  },
  {
    "path": "Chapter1/6-MultipleTextures/6-MultipleTextures.csproj",
    "chars": 615,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/6-MultipleTextures/Program.cs",
    "chars": 682,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/6-MultipleTextures/Shaders/shader.frag",
    "chars": 214,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nin vec2 texCoord;\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvoid ma"
  },
  {
    "path": "Chapter1/6-MultipleTextures/Shaders/shader.vert",
    "chars": 207,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\nlayout(location = 1) in vec2 aTexCoord;\n\nout vec2 texCoord;\n"
  },
  {
    "path": "Chapter1/6-MultipleTextures/Window.cs",
    "chars": 4302,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Graphics"
  },
  {
    "path": "Chapter1/7-Transformations/7-Transformations.csproj",
    "chars": 615,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/7-Transformations/Program.cs",
    "chars": 680,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/7-Transformations/Shaders/shader.frag",
    "chars": 214,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nin vec2 texCoord;\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvoid ma"
  },
  {
    "path": "Chapter1/7-Transformations/Shaders/shader.vert",
    "chars": 427,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\nlayout(location = 1) in vec2 aTexCoord;\n\nout vec2 texCoord;\n"
  },
  {
    "path": "Chapter1/7-Transformations/Window.cs",
    "chars": 7231,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter1/8-CoordinatesSystems/8-CoordinatesSystems.csproj",
    "chars": 615,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/8-CoordinatesSystems/Program.cs",
    "chars": 684,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/8-CoordinatesSystems/Shaders/shader.frag",
    "chars": 214,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nin vec2 texCoord;\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvoid ma"
  },
  {
    "path": "Chapter1/8-CoordinatesSystems/Shaders/shader.vert",
    "chars": 301,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\nlayout(location = 1) in vec2 aTexCoord;\n\nout vec2 texCoord;\n"
  },
  {
    "path": "Chapter1/8-CoordinatesSystems/Window.cs",
    "chars": 7746,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter1/9-Camera/9-Camera.csproj",
    "chars": 615,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter1/9-Camera/Program.cs",
    "chars": 671,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter1/9-Camera/Shaders/shader.frag",
    "chars": 214,
    "preview": "#version 330\n\nout vec4 outputColor;\n\nin vec2 texCoord;\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvoid ma"
  },
  {
    "path": "Chapter1/9-Camera/Shaders/shader.vert",
    "chars": 301,
    "preview": "#version 330 core\n\nlayout(location = 0) in vec3 aPosition;\n\nlayout(location = 1) in vec2 aTexCoord;\n\nout vec2 texCoord;\n"
  },
  {
    "path": "Chapter1/9-Camera/Window.cs",
    "chars": 8235,
    "preview": "using System;\nusing LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing"
  },
  {
    "path": "Chapter2/1-Colors/1-Colors.csproj",
    "chars": 539,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/1-Colors/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/1-Colors/Program.cs",
    "chars": 671,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/1-Colors/Shaders/lighting.frag",
    "chars": 349,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nuniform vec3 objectColor;\nuniform vec3 lightColor;\n\nvoid main()\n{\n    // For our "
  },
  {
    "path": "Chapter2/1-Colors/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/1-Colors/Shaders/shader.vert",
    "chars": 198,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\n\nuniform mat4 model;\nuniform mat4 view;\nuniform mat4 projection;\n\n"
  },
  {
    "path": "Chapter2/1-Colors/Window.cs",
    "chars": 9523,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing "
  },
  {
    "path": "Chapter2/2-BasicLighting/2-BasicLighting.csproj",
    "chars": 539,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/2-BasicLighting/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/2-BasicLighting/Program.cs",
    "chars": 679,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/2-BasicLighting/Shaders/lighting.frag",
    "chars": 2274,
    "preview": "#version 330 core\nout vec4 FragColor;\n\n//In order to calculate some basic lighting we need a few things per model basis,"
  },
  {
    "path": "Chapter2/2-BasicLighting/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/2-BasicLighting/Shaders/shader.vert",
    "chars": 374,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\n\nuniform mat4 model;\nunifor"
  },
  {
    "path": "Chapter2/2-BasicLighting/Window.cs",
    "chars": 9583,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter2/3-Materials/3-Materials.csproj",
    "chars": 539,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/3-Materials/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/3-Materials/Program.cs",
    "chars": 674,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/3-Materials/Shaders/lighting.frag",
    "chars": 1926,
    "preview": "#version 330 core\n//The material is a collection of some values that we talked about in the last tutorial,\n//some crucia"
  },
  {
    "path": "Chapter2/3-Materials/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/3-Materials/Shaders/shader.vert",
    "chars": 374,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\n\nuniform mat4 model;\nunifor"
  },
  {
    "path": "Chapter2/3-Materials/Window.cs",
    "chars": 10102,
    "preview": "using System;\nusing LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing"
  },
  {
    "path": "Chapter2/4-LightingMaps/4-LightingMaps.csproj",
    "chars": 798,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/4-LightingMaps/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/4-LightingMaps/Program.cs",
    "chars": 678,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/4-LightingMaps/Shaders/lighting.frag",
    "chars": 1891,
    "preview": "#version 330 core\n// Now the diffuse and the specular values are controlled by textures, this is what we in graphics cal"
  },
  {
    "path": "Chapter2/4-LightingMaps/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/4-LightingMaps/Shaders/shader.vert",
    "chars": 464,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nlayout (location = 2) in ve"
  },
  {
    "path": "Chapter2/4-LightingMaps/Window.cs",
    "chars": 11371,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/5-LightCasters-DirectionalLights.csproj",
    "chars": 614,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/Program.cs",
    "chars": 684,
    "preview": "using OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    public static class Program"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/Shaders/lighting.frag",
    "chars": 1390,
    "preview": "#version 330 core\nstruct Material {\n    sampler2D diffuse;\n    sampler2D specular;\n    float     shininess;\n};\nstruct Li"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/Shaders/shader.vert",
    "chars": 401,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nlayout (location = 2) in ve"
  },
  {
    "path": "Chapter2/5-LightCasters-DirectionalLights/Window.cs",
    "chars": 11480,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/5-LightCasters-PointLights.csproj",
    "chars": 614,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/Program.cs",
    "chars": 693,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/Shaders/lighting.frag",
    "chars": 2057,
    "preview": "#version 330 core\nstruct Material {\n    sampler2D diffuse;\n    sampler2D specular;\n    float     shininess;\n};\n//This li"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/Shaders/shader.vert",
    "chars": 464,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nlayout (location = 2) in ve"
  },
  {
    "path": "Chapter2/5-LightCasters-PointLights/Window.cs",
    "chars": 11576,
    "preview": "using LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/5-LightCasters-Spotlight.csproj",
    "chars": 614,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/Program.cs",
    "chars": 690,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/Shaders/lighting.frag",
    "chars": 2416,
    "preview": "#version 330 core\nstruct Material {\n    sampler2D diffuse;\n    sampler2D specular;\n    float     shininess;\n};\n//The spo"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/Shaders/shader.vert",
    "chars": 464,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nlayout (location = 2) in ve"
  },
  {
    "path": "Chapter2/5-LightCasters-Spotlight/Window.cs",
    "chars": 11977,
    "preview": "using System;\nusing LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing"
  },
  {
    "path": "Chapter2/6-MultipleLights/6-MultipleLights.csproj",
    "chars": 614,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <RootNamespace>LearnOpenTK<"
  },
  {
    "path": "Chapter2/6-MultipleLights/OpenTK.dll.config",
    "chars": 1788,
    "preview": "<configuration>\n  <dllmap os=\"linux\" dll=\"opengl32.dll\" target=\"libGL.so.1\"/>\n  <dllmap os=\"linux\" dll=\"glu32.dll\" targe"
  },
  {
    "path": "Chapter2/6-MultipleLights/Program.cs",
    "chars": 680,
    "preview": "using OpenTK.Mathematics;\nusing OpenTK.Windowing.Common;\nusing OpenTK.Windowing.Desktop;\n\nnamespace LearnOpenTK\n{\n    p"
  },
  {
    "path": "Chapter2/6-MultipleLights/Shaders/lighting.frag",
    "chars": 5297,
    "preview": "#version 330 core\n//In this tutorial it might seem like a lot is going on, but really we just combine the last tutorials"
  },
  {
    "path": "Chapter2/6-MultipleLights/Shaders/shader.frag",
    "chars": 115,
    "preview": "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); // set all 4 vector values to 1.0\n}"
  },
  {
    "path": "Chapter2/6-MultipleLights/Shaders/shader.vert",
    "chars": 464,
    "preview": "#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\nlayout (location = 2) in ve"
  },
  {
    "path": "Chapter2/6-MultipleLights/Window.cs",
    "chars": 13474,
    "preview": "using System;\nusing LearnOpenTK.Common;\nusing OpenTK.Graphics.OpenGL4;\nusing OpenTK.Mathematics;\nusing OpenTK.Windowing"
  },
  {
    "path": "Common/Camera.cs",
    "chars": 4919,
    "preview": "using OpenTK.Mathematics;\nusing System;\n\nnamespace LearnOpenTK.Common\n{\n    // This is the camera class as it could be s"
  },
  {
    "path": "Common/Common.csproj",
    "chars": 390,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <RootNamespace>LearnOpenTK.Common</RootNamespace>\n    <Assembly"
  },
  {
    "path": "Common/Shader.cs",
    "chars": 7983,
    "preview": "using System;\nusing System.IO;\nusing System.Text;\nusing System.Collections.Generic;\nusing OpenTK.Graphics.OpenGL4;\nusin"
  },
  {
    "path": "Common/Texture.cs",
    "chars": 5319,
    "preview": "using OpenTK.Graphics.OpenGL4;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing PixelFormat = OpenTK.Graphics."
  },
  {
    "path": "LICENSE",
    "chars": 18648,
    "preview": "Attribution 4.0 International\n\n=======================================================================\n\nCreative Commons"
  },
  {
    "path": "LearnOpenTK.sln",
    "chars": 12788,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3030"
  },
  {
    "path": "README.md",
    "chars": 340,
    "preview": "# LearnOpenTK\nFor a more comprehensive written tutorial go to the [Learn section of OpenTK.net](https://opentk.net/learn"
  }
]

About this extraction

This page contains the full source code of the opentk/LearnOpenTK GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 118 files (270.9 KB), approximately 81.8k tokens, and a symbol index with 178 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!