From e77901d4f057589d31b332618f812e0365b17ae1 Mon Sep 17 00:00:00 2001
From: "github-classroom[bot]"
<66690702+github-classroom[bot]@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:05:42 +0000
Subject: [PATCH] Initial commit
---
.gitignore | 398 +++++++++++++++++
README.md | 49 +++
.../DoWhateverYouWant.csproj | 31 ++
.../LearnOpenTK/Common/Shader.cs | 189 +++++++++
TheRepo/DoWhateverYouWant/MyExampleWindow.cs | 215 ++++++++++
TheRepo/DoWhateverYouWant/MyMatrix.cs | 92 ++++
TheRepo/DoWhateverYouWant/MyVector.cs | 57 +++
TheRepo/DoWhateverYouWant/Program.cs | 24 ++
TheRepo/DoWhateverYouWant/Shaders/shader.frag | 8 +
TheRepo/DoWhateverYouWant/Shaders/shader.vert | 41 ++
TheRepo/TheLabs/LearnOpenTK/Common/Shader.cs | 189 +++++++++
TheRepo/TheLabs/MyExampleWindow.cs | 215 ++++++++++
TheRepo/TheLabs/MyMatrix.cs | 92 ++++
TheRepo/TheLabs/MyVector.cs | 57 +++
TheRepo/TheLabs/Program.cs | 24 ++
TheRepo/TheLabs/Shaders/shader.frag | 8 +
TheRepo/TheLabs/Shaders/shader.vert | 41 ++
TheRepo/TheLabs/TheLabs.csproj | 31 ++
TheRepo/TheRepo.sln | 37 ++
TheRepo/VectorAndMatrixTests/MatrixTests.cs | 400 ++++++++++++++++++
.../VectorAndMatrixTests.csproj | 27 ++
TheRepo/VectorAndMatrixTests/VectorTests.cs | 218 ++++++++++
images/busses.png | Bin 0 -> 12096 bytes
23 files changed, 2443 insertions(+)
create mode 100644 .gitignore
create mode 100644 README.md
create mode 100644 TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj
create mode 100644 TheRepo/DoWhateverYouWant/LearnOpenTK/Common/Shader.cs
create mode 100644 TheRepo/DoWhateverYouWant/MyExampleWindow.cs
create mode 100644 TheRepo/DoWhateverYouWant/MyMatrix.cs
create mode 100644 TheRepo/DoWhateverYouWant/MyVector.cs
create mode 100644 TheRepo/DoWhateverYouWant/Program.cs
create mode 100644 TheRepo/DoWhateverYouWant/Shaders/shader.frag
create mode 100644 TheRepo/DoWhateverYouWant/Shaders/shader.vert
create mode 100644 TheRepo/TheLabs/LearnOpenTK/Common/Shader.cs
create mode 100644 TheRepo/TheLabs/MyExampleWindow.cs
create mode 100644 TheRepo/TheLabs/MyMatrix.cs
create mode 100644 TheRepo/TheLabs/MyVector.cs
create mode 100644 TheRepo/TheLabs/Program.cs
create mode 100644 TheRepo/TheLabs/Shaders/shader.frag
create mode 100644 TheRepo/TheLabs/Shaders/shader.vert
create mode 100644 TheRepo/TheLabs/TheLabs.csproj
create mode 100644 TheRepo/TheRepo.sln
create mode 100644 TheRepo/VectorAndMatrixTests/MatrixTests.cs
create mode 100644 TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj
create mode 100644 TheRepo/VectorAndMatrixTests/VectorTests.cs
create mode 100644 images/busses.png
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8a30d25
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,398 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.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
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# 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
+# Note: 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
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable 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
+*.appx
+*.appxbundle
+*.appxupload
+
+# 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
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# 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
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# 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/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..dacb0e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# Examples of Code Snippets and Figures to support your explanations
+```c#
+public static void Main()
+{
+ var nativeWindowSettings = new NativeWindowSettings
+ {
+ Size = new Vector2i(800, 600),
+ Title = "My OpenTK Example Program"
+ };
+
+ using (var window = new MyExampleWindow(GameWindowSettings.Default,
+ nativeWindowSettings))
+ {
+ window.Run();
+ }
+}
+```
+*Snippet 1 - from Program.cs Line 12*
+
+
+*Figure 1 - The Busses*
+
+
+
+ *Figure 2 - The Busses*
+
+
+# Your reflection
+As you develop your scene, you should reflect on your progress by editing this document in each of the sections below. You should include information that makes it clear what you have implemented and demonstrates your understanding of the concepts and techniques that you have applied.
+# Implement a MyVector object that passes all the provided tests.
+Your reflection
+# Implement a MyMatrix object that passes all the provided tests.
+Your reflection
+# Create a Primitive class that is capable of generating Triangles, Quads, Cubes, Cylinders, and Spheres.
+Your reflection
+# Use the OpenTK Matrix4 and Vector3/4 structs to implement transformations (translate, rotate, scale) in your scene.
+Your reflection
+# Implement a Scene Graph to store and execute your transformations and drawings.
+Your reflection
+# Animation
+Your reflection
+# Lighting
+Your reflection
+# Textures
+Your reflection
+# Cameras
+Your reflection
diff --git a/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj b/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj
new file mode 100644
index 0000000..c30b43d
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj
@@ -0,0 +1,31 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
diff --git a/TheRepo/DoWhateverYouWant/LearnOpenTK/Common/Shader.cs b/TheRepo/DoWhateverYouWant/LearnOpenTK/Common/Shader.cs
new file mode 100644
index 0000000..b1e6b8b
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/LearnOpenTK/Common/Shader.cs
@@ -0,0 +1,189 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using OpenTK.Graphics.OpenGL4;
+using OpenTK.Mathematics;
+
+namespace LearnOpenTK.Common
+{
+ // A simple class meant to help create shaders.
+ public class Shader
+ {
+ public readonly int Handle;
+
+ private readonly Dictionary _uniformLocations;
+
+ // This is how you create a simple shader.
+ // Shaders are written in GLSL, which is a language very similar to C in its semantics.
+ // The GLSL source is compiled *at runtime*, so it can optimize itself for the graphics card it's currently being used on.
+ // A commented example of GLSL can be found in shader.vert.
+ public Shader(string vertPath, string fragPath)
+ {
+ // There are several different types of shaders, but the only two you need for basic rendering are the vertex and fragment shaders.
+ // The vertex shader is responsible for moving around vertices, and uploading that data to the fragment shader.
+ // The vertex shader won't be too important here, but they'll be more important later.
+ // The fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel.
+ // The fragment shader is what we'll be using the most here.
+
+ // Load vertex shader and compile
+ var shaderSource = File.ReadAllText(vertPath);
+
+ // GL.CreateShader will create an empty shader (obviously). The ShaderType enum denotes which type of shader will be created.
+ var vertexShader = GL.CreateShader(ShaderType.VertexShader);
+
+ // Now, bind the GLSL source code
+ GL.ShaderSource(vertexShader, shaderSource);
+
+ // And then compile
+ CompileShader(vertexShader);
+
+ // We do the same for the fragment shader.
+ shaderSource = File.ReadAllText(fragPath);
+ var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
+ GL.ShaderSource(fragmentShader, shaderSource);
+ CompileShader(fragmentShader);
+
+ // These two shaders must then be merged into a shader program, which can then be used by OpenGL.
+ // To do this, create a program...
+ Handle = GL.CreateProgram();
+
+ // Attach both shaders...
+ GL.AttachShader(Handle, vertexShader);
+ GL.AttachShader(Handle, fragmentShader);
+
+ // And then link them together.
+ LinkProgram(Handle);
+
+ // When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
+ // Detach them, and then delete them.
+ GL.DetachShader(Handle, vertexShader);
+ GL.DetachShader(Handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ // The shader is now ready to go, but first, we're going to cache all the shader uniform locations.
+ // Querying this from the shader is very slow, so we do it once on initialization and reuse those values
+ // later.
+
+ // First, we have to get the number of active uniforms in the shader.
+ GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);
+
+ // Next, allocate the dictionary to hold the locations.
+ _uniformLocations = new Dictionary();
+
+ // Loop over all the uniforms,
+ for (var i = 0; i < numberOfUniforms; i++)
+ {
+ // get the name of this uniform,
+ var key = GL.GetActiveUniform(Handle, i, out _, out _);
+
+ // get the location,
+ var location = GL.GetUniformLocation(Handle, key);
+
+ // and then add it to the dictionary.
+ _uniformLocations.Add(key, location);
+ }
+ }
+
+ private static void CompileShader(int shader)
+ {
+ // Try to compile the shader
+ GL.CompileShader(shader);
+
+ // Check for compilation errors
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
+ }
+ }
+
+ private static void LinkProgram(int program)
+ {
+ // We link the program
+ GL.LinkProgram(program);
+
+ // Check for linking errors
+ GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetProgramInfoLog(program)` to get information about the error.
+ throw new Exception($"Error occurred whilst linking Program({program})");
+ }
+ }
+
+ // A wrapper function that enables the shader program.
+ public void Use()
+ {
+ GL.UseProgram(Handle);
+ }
+
+ // The shader sources provided with this project use hardcoded layout(location)-s. If you want to do it dynamically,
+ // you can omit the layout(location=X) lines in the vertex shader, and use this in VertexAttribPointer instead of the hardcoded values.
+ public int GetAttribLocation(string attribName)
+ {
+ return GL.GetAttribLocation(Handle, attribName);
+ }
+
+ // Uniform setters
+ // Uniforms are variables that can be set by user code, instead of reading them from the VBO.
+ // You use VBOs for vertex-related data, and uniforms for almost everything else.
+
+ // Setting a uniform is almost always the exact same, so I'll explain it here once, instead of in every method:
+ // 1. Bind the program you want to set the uniform on
+ // 2. Get a handle to the location of the uniform with GL.GetUniformLocation.
+ // 3. Use the appropriate GL.Uniform* function to set the uniform.
+
+ ///
+ /// Set a uniform int on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetInt(string name, int data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform1(_uniformLocations[name], data);
+ }
+
+ ///
+ /// Set a uniform float on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetFloat(string name, float data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform1(_uniformLocations[name], data);
+ }
+
+ ///
+ /// Set a uniform Matrix4 on this shader
+ ///
+ /// The name of the uniform
+ /// The data to set
+ ///
+ ///
+ /// The matrix is transposed before being sent to the shader.
+ ///
+ ///
+ public void SetMatrix4(string name, Matrix4 data)
+ {
+ GL.UseProgram(Handle);
+ GL.UniformMatrix4(_uniformLocations[name], true, ref data);
+ }
+
+ ///
+ /// Set a uniform Vector3 on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetVector3(string name, Vector3 data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform3(_uniformLocations[name], data);
+ }
+ }
+}
diff --git a/TheRepo/DoWhateverYouWant/MyExampleWindow.cs b/TheRepo/DoWhateverYouWant/MyExampleWindow.cs
new file mode 100644
index 0000000..d9e1795
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/MyExampleWindow.cs
@@ -0,0 +1,215 @@
+using OpenTK.Graphics.OpenGL4;
+using OpenTK.Windowing.Common;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using OpenTK.Windowing.Desktop;
+using LearnOpenTK.Common;
+
+namespace TheLabs
+{
+ public class MyExampleWindow : GameWindow
+ {
+ // Create the vertices for our triangle. These are listed in normalized device coordinates (NDC)
+ // In NDC, (0, 0) is the center of the screen.
+ // Negative X coordinates move to the left, positive X move to the right.
+ // Negative Y coordinates move to the bottom, positive Y move to the top.
+ // OpenGL only supports rendering in 3D, so to create a flat triangle, the Z coordinate will be kept as 0.
+ private readonly float[] _vertices =
+ {
+ -0.5f, -0.5f, 0.0f, // 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 MyExampleWindow(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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TheRepo/DoWhateverYouWant/MyMatrix.cs b/TheRepo/DoWhateverYouWant/MyMatrix.cs
new file mode 100644
index 0000000..4577231
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/MyMatrix.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using OpenTK.Mathematics;
+
+public class MyMatrix
+{
+
+ public MyMatrix(float pRow0Column0,
+ float pRow0Column1,
+ float pRow0Column2,
+ float pRow0Column3,
+ float pRow1Column0,
+ float pRow1Column1,
+ float pRow1Column2,
+ float pRow1Column3,
+ float pRow2Column0,
+ float pRow2Column1,
+ float pRow2Column2,
+ float pRow2Column3,
+ float pRow3Column0,
+ float pRow3Column1,
+ float pRow3Column2,
+ float pRow3Column3)
+ {
+
+ }
+
+ public float GetElement(int pRow, int pColumn)
+ {
+ return -1;
+ }
+ public void SetElement(int pRow, int pColumn, float pValue)
+ {
+
+ }
+
+
+
+ public static MyMatrix CreateIdentity()
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateTranslation(MyVector pTranslation)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateScale(MyVector pScale)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationX(float pAngle)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationY(float pAngle)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationZ(float pAngle)
+ {
+ return null;
+ }
+
+ public MyVector Multiply(MyVector pVector)
+ {
+ return null;
+ }
+
+ public MyMatrix Multiply(MyMatrix pMatrix)
+ {
+ return null;
+ }
+
+ public MyMatrix Inverse()
+ {
+ return null;
+ }
+
+ // this method takes our MyMatrix and converts it to an OpenTK Matrix4
+ // this looks a little odd as the OpenTK Matrix4 constructor takes the elements in row major order
+ public Matrix4 ToMatrix4()
+ {
+ return Matrix4.Identity;
+ }
+
+
+}
diff --git a/TheRepo/DoWhateverYouWant/MyVector.cs b/TheRepo/DoWhateverYouWant/MyVector.cs
new file mode 100644
index 0000000..79b420e
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/MyVector.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+
+public class MyVector
+{
+ public float X { get; private set; }
+ public float Y { get; private set; }
+ public float Z { get; private set; }
+ // this W component has only been included to make it easier to do Matrix multiplications
+ public float W { get; private set; }
+ public MyVector(float pX, float pY, float pZ, float pW = 1)
+ {
+
+ }
+
+ public MyVector Add(MyVector pVector)
+ {
+ return null;
+ }
+ public MyVector Subtract(MyVector pVector)
+ {
+ return null;
+ }
+ public MyVector Multiply(float pScalar)
+ {
+ return null;
+ }
+ public MyVector Divide(float pScalar)
+ {
+ return null;
+ }
+ public float Magnitude()
+ {
+ return -1;
+ }
+ public MyVector Normalise()
+ {
+ return null;
+ }
+ public float DotProduct(MyVector pVector)
+ {
+ return -1;
+ }
+ public MyVector Interpolate(MyVector pVector, float pInterpolation)
+ {
+ return null;
+ }
+ public float AngleBetween(MyVector pVector)
+ {
+ return -1;
+ }
+ public MyVector CrossProduct(MyVector pVector)
+ {
+ return null;
+ }
+
+}
diff --git a/TheRepo/DoWhateverYouWant/Program.cs b/TheRepo/DoWhateverYouWant/Program.cs
new file mode 100644
index 0000000..0c4e298
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/Program.cs
@@ -0,0 +1,24 @@
+using OpenTK.Mathematics;
+using OpenTK.Windowing.Desktop;
+using System;
+
+namespace TheLabs
+{
+ public static class Program
+ {
+ public static void Main()
+ {
+ var nativeWindowSettings = new NativeWindowSettings
+ {
+ Size = new Vector2i(800, 600),
+ Title = "My OpenTK Example Program"
+ };
+
+ using (var window = new MyExampleWindow(GameWindowSettings.Default,
+ nativeWindowSettings))
+ {
+ window.Run();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TheRepo/DoWhateverYouWant/Shaders/shader.frag b/TheRepo/DoWhateverYouWant/Shaders/shader.frag
new file mode 100644
index 0000000..db749de
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/Shaders/shader.frag
@@ -0,0 +1,8 @@
+#version 330
+
+out vec4 outputColor;
+
+void main()
+{
+ outputColor = vec4(1.0, 1.0, 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/TheRepo/DoWhateverYouWant/Shaders/shader.vert b/TheRepo/DoWhateverYouWant/Shaders/shader.vert
new file mode 100644
index 0000000..e9e0e7b
--- /dev/null
+++ b/TheRepo/DoWhateverYouWant/Shaders/shader.vert
@@ -0,0 +1,41 @@
+// 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);
+}
\ No newline at end of file
diff --git a/TheRepo/TheLabs/LearnOpenTK/Common/Shader.cs b/TheRepo/TheLabs/LearnOpenTK/Common/Shader.cs
new file mode 100644
index 0000000..b1e6b8b
--- /dev/null
+++ b/TheRepo/TheLabs/LearnOpenTK/Common/Shader.cs
@@ -0,0 +1,189 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using OpenTK.Graphics.OpenGL4;
+using OpenTK.Mathematics;
+
+namespace LearnOpenTK.Common
+{
+ // A simple class meant to help create shaders.
+ public class Shader
+ {
+ public readonly int Handle;
+
+ private readonly Dictionary _uniformLocations;
+
+ // This is how you create a simple shader.
+ // Shaders are written in GLSL, which is a language very similar to C in its semantics.
+ // The GLSL source is compiled *at runtime*, so it can optimize itself for the graphics card it's currently being used on.
+ // A commented example of GLSL can be found in shader.vert.
+ public Shader(string vertPath, string fragPath)
+ {
+ // There are several different types of shaders, but the only two you need for basic rendering are the vertex and fragment shaders.
+ // The vertex shader is responsible for moving around vertices, and uploading that data to the fragment shader.
+ // The vertex shader won't be too important here, but they'll be more important later.
+ // The fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel.
+ // The fragment shader is what we'll be using the most here.
+
+ // Load vertex shader and compile
+ var shaderSource = File.ReadAllText(vertPath);
+
+ // GL.CreateShader will create an empty shader (obviously). The ShaderType enum denotes which type of shader will be created.
+ var vertexShader = GL.CreateShader(ShaderType.VertexShader);
+
+ // Now, bind the GLSL source code
+ GL.ShaderSource(vertexShader, shaderSource);
+
+ // And then compile
+ CompileShader(vertexShader);
+
+ // We do the same for the fragment shader.
+ shaderSource = File.ReadAllText(fragPath);
+ var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
+ GL.ShaderSource(fragmentShader, shaderSource);
+ CompileShader(fragmentShader);
+
+ // These two shaders must then be merged into a shader program, which can then be used by OpenGL.
+ // To do this, create a program...
+ Handle = GL.CreateProgram();
+
+ // Attach both shaders...
+ GL.AttachShader(Handle, vertexShader);
+ GL.AttachShader(Handle, fragmentShader);
+
+ // And then link them together.
+ LinkProgram(Handle);
+
+ // When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
+ // Detach them, and then delete them.
+ GL.DetachShader(Handle, vertexShader);
+ GL.DetachShader(Handle, fragmentShader);
+ GL.DeleteShader(fragmentShader);
+ GL.DeleteShader(vertexShader);
+
+ // The shader is now ready to go, but first, we're going to cache all the shader uniform locations.
+ // Querying this from the shader is very slow, so we do it once on initialization and reuse those values
+ // later.
+
+ // First, we have to get the number of active uniforms in the shader.
+ GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);
+
+ // Next, allocate the dictionary to hold the locations.
+ _uniformLocations = new Dictionary();
+
+ // Loop over all the uniforms,
+ for (var i = 0; i < numberOfUniforms; i++)
+ {
+ // get the name of this uniform,
+ var key = GL.GetActiveUniform(Handle, i, out _, out _);
+
+ // get the location,
+ var location = GL.GetUniformLocation(Handle, key);
+
+ // and then add it to the dictionary.
+ _uniformLocations.Add(key, location);
+ }
+ }
+
+ private static void CompileShader(int shader)
+ {
+ // Try to compile the shader
+ GL.CompileShader(shader);
+
+ // Check for compilation errors
+ GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
+ var infoLog = GL.GetShaderInfoLog(shader);
+ throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
+ }
+ }
+
+ private static void LinkProgram(int program)
+ {
+ // We link the program
+ GL.LinkProgram(program);
+
+ // Check for linking errors
+ GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
+ if (code != (int)All.True)
+ {
+ // We can use `GL.GetProgramInfoLog(program)` to get information about the error.
+ throw new Exception($"Error occurred whilst linking Program({program})");
+ }
+ }
+
+ // A wrapper function that enables the shader program.
+ public void Use()
+ {
+ GL.UseProgram(Handle);
+ }
+
+ // The shader sources provided with this project use hardcoded layout(location)-s. If you want to do it dynamically,
+ // you can omit the layout(location=X) lines in the vertex shader, and use this in VertexAttribPointer instead of the hardcoded values.
+ public int GetAttribLocation(string attribName)
+ {
+ return GL.GetAttribLocation(Handle, attribName);
+ }
+
+ // Uniform setters
+ // Uniforms are variables that can be set by user code, instead of reading them from the VBO.
+ // You use VBOs for vertex-related data, and uniforms for almost everything else.
+
+ // Setting a uniform is almost always the exact same, so I'll explain it here once, instead of in every method:
+ // 1. Bind the program you want to set the uniform on
+ // 2. Get a handle to the location of the uniform with GL.GetUniformLocation.
+ // 3. Use the appropriate GL.Uniform* function to set the uniform.
+
+ ///
+ /// Set a uniform int on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetInt(string name, int data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform1(_uniformLocations[name], data);
+ }
+
+ ///
+ /// Set a uniform float on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetFloat(string name, float data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform1(_uniformLocations[name], data);
+ }
+
+ ///
+ /// Set a uniform Matrix4 on this shader
+ ///
+ /// The name of the uniform
+ /// The data to set
+ ///
+ ///
+ /// The matrix is transposed before being sent to the shader.
+ ///
+ ///
+ public void SetMatrix4(string name, Matrix4 data)
+ {
+ GL.UseProgram(Handle);
+ GL.UniformMatrix4(_uniformLocations[name], true, ref data);
+ }
+
+ ///
+ /// Set a uniform Vector3 on this shader.
+ ///
+ /// The name of the uniform
+ /// The data to set
+ public void SetVector3(string name, Vector3 data)
+ {
+ GL.UseProgram(Handle);
+ GL.Uniform3(_uniformLocations[name], data);
+ }
+ }
+}
diff --git a/TheRepo/TheLabs/MyExampleWindow.cs b/TheRepo/TheLabs/MyExampleWindow.cs
new file mode 100644
index 0000000..d9e1795
--- /dev/null
+++ b/TheRepo/TheLabs/MyExampleWindow.cs
@@ -0,0 +1,215 @@
+using OpenTK.Graphics.OpenGL4;
+using OpenTK.Windowing.Common;
+using OpenTK.Windowing.GraphicsLibraryFramework;
+using OpenTK.Windowing.Desktop;
+using LearnOpenTK.Common;
+
+namespace TheLabs
+{
+ public class MyExampleWindow : GameWindow
+ {
+ // Create the vertices for our triangle. These are listed in normalized device coordinates (NDC)
+ // In NDC, (0, 0) is the center of the screen.
+ // Negative X coordinates move to the left, positive X move to the right.
+ // Negative Y coordinates move to the bottom, positive Y move to the top.
+ // OpenGL only supports rendering in 3D, so to create a flat triangle, the Z coordinate will be kept as 0.
+ private readonly float[] _vertices =
+ {
+ -0.5f, -0.5f, 0.0f, // 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 MyExampleWindow(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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TheRepo/TheLabs/MyMatrix.cs b/TheRepo/TheLabs/MyMatrix.cs
new file mode 100644
index 0000000..4577231
--- /dev/null
+++ b/TheRepo/TheLabs/MyMatrix.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using OpenTK.Mathematics;
+
+public class MyMatrix
+{
+
+ public MyMatrix(float pRow0Column0,
+ float pRow0Column1,
+ float pRow0Column2,
+ float pRow0Column3,
+ float pRow1Column0,
+ float pRow1Column1,
+ float pRow1Column2,
+ float pRow1Column3,
+ float pRow2Column0,
+ float pRow2Column1,
+ float pRow2Column2,
+ float pRow2Column3,
+ float pRow3Column0,
+ float pRow3Column1,
+ float pRow3Column2,
+ float pRow3Column3)
+ {
+
+ }
+
+ public float GetElement(int pRow, int pColumn)
+ {
+ return -1;
+ }
+ public void SetElement(int pRow, int pColumn, float pValue)
+ {
+
+ }
+
+
+
+ public static MyMatrix CreateIdentity()
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateTranslation(MyVector pTranslation)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateScale(MyVector pScale)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationX(float pAngle)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationY(float pAngle)
+ {
+ return null;
+ }
+
+ public static MyMatrix CreateRotationZ(float pAngle)
+ {
+ return null;
+ }
+
+ public MyVector Multiply(MyVector pVector)
+ {
+ return null;
+ }
+
+ public MyMatrix Multiply(MyMatrix pMatrix)
+ {
+ return null;
+ }
+
+ public MyMatrix Inverse()
+ {
+ return null;
+ }
+
+ // this method takes our MyMatrix and converts it to an OpenTK Matrix4
+ // this looks a little odd as the OpenTK Matrix4 constructor takes the elements in row major order
+ public Matrix4 ToMatrix4()
+ {
+ return Matrix4.Identity;
+ }
+
+
+}
diff --git a/TheRepo/TheLabs/MyVector.cs b/TheRepo/TheLabs/MyVector.cs
new file mode 100644
index 0000000..79b420e
--- /dev/null
+++ b/TheRepo/TheLabs/MyVector.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+
+public class MyVector
+{
+ public float X { get; private set; }
+ public float Y { get; private set; }
+ public float Z { get; private set; }
+ // this W component has only been included to make it easier to do Matrix multiplications
+ public float W { get; private set; }
+ public MyVector(float pX, float pY, float pZ, float pW = 1)
+ {
+
+ }
+
+ public MyVector Add(MyVector pVector)
+ {
+ return null;
+ }
+ public MyVector Subtract(MyVector pVector)
+ {
+ return null;
+ }
+ public MyVector Multiply(float pScalar)
+ {
+ return null;
+ }
+ public MyVector Divide(float pScalar)
+ {
+ return null;
+ }
+ public float Magnitude()
+ {
+ return -1;
+ }
+ public MyVector Normalise()
+ {
+ return null;
+ }
+ public float DotProduct(MyVector pVector)
+ {
+ return -1;
+ }
+ public MyVector Interpolate(MyVector pVector, float pInterpolation)
+ {
+ return null;
+ }
+ public float AngleBetween(MyVector pVector)
+ {
+ return -1;
+ }
+ public MyVector CrossProduct(MyVector pVector)
+ {
+ return null;
+ }
+
+}
diff --git a/TheRepo/TheLabs/Program.cs b/TheRepo/TheLabs/Program.cs
new file mode 100644
index 0000000..0c4e298
--- /dev/null
+++ b/TheRepo/TheLabs/Program.cs
@@ -0,0 +1,24 @@
+using OpenTK.Mathematics;
+using OpenTK.Windowing.Desktop;
+using System;
+
+namespace TheLabs
+{
+ public static class Program
+ {
+ public static void Main()
+ {
+ var nativeWindowSettings = new NativeWindowSettings
+ {
+ Size = new Vector2i(800, 600),
+ Title = "My OpenTK Example Program"
+ };
+
+ using (var window = new MyExampleWindow(GameWindowSettings.Default,
+ nativeWindowSettings))
+ {
+ window.Run();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TheRepo/TheLabs/Shaders/shader.frag b/TheRepo/TheLabs/Shaders/shader.frag
new file mode 100644
index 0000000..db749de
--- /dev/null
+++ b/TheRepo/TheLabs/Shaders/shader.frag
@@ -0,0 +1,8 @@
+#version 330
+
+out vec4 outputColor;
+
+void main()
+{
+ outputColor = vec4(1.0, 1.0, 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/TheRepo/TheLabs/Shaders/shader.vert b/TheRepo/TheLabs/Shaders/shader.vert
new file mode 100644
index 0000000..e9e0e7b
--- /dev/null
+++ b/TheRepo/TheLabs/Shaders/shader.vert
@@ -0,0 +1,41 @@
+// 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);
+}
\ No newline at end of file
diff --git a/TheRepo/TheLabs/TheLabs.csproj b/TheRepo/TheLabs/TheLabs.csproj
new file mode 100644
index 0000000..c30b43d
--- /dev/null
+++ b/TheRepo/TheLabs/TheLabs.csproj
@@ -0,0 +1,31 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
diff --git a/TheRepo/TheRepo.sln b/TheRepo/TheRepo.sln
new file mode 100644
index 0000000..5511f00
--- /dev/null
+++ b/TheRepo/TheRepo.sln
@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34302.85
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TheLabs", "TheLabs\TheLabs.csproj", "{4761B222-F037-4EE0-815E-0264545C5393}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VectorAndMatrixTests", "VectorAndMatrixTests\VectorAndMatrixTests.csproj", "{D138F1AF-D5B0-442A-BECB-93DB7D6BD4C9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoWhateverYouWant", "DoWhateverYouWant\DoWhateverYouWant.csproj", "{E59FCA07-9BE4-096B-6CD4-DAC39A9BC7B3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4761B222-F037-4EE0-815E-0264545C5393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4761B222-F037-4EE0-815E-0264545C5393}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4761B222-F037-4EE0-815E-0264545C5393}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4761B222-F037-4EE0-815E-0264545C5393}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D138F1AF-D5B0-442A-BECB-93DB7D6BD4C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D138F1AF-D5B0-442A-BECB-93DB7D6BD4C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D138F1AF-D5B0-442A-BECB-93DB7D6BD4C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D138F1AF-D5B0-442A-BECB-93DB7D6BD4C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E59FCA07-9BE4-096B-6CD4-DAC39A9BC7B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E59FCA07-9BE4-096B-6CD4-DAC39A9BC7B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E59FCA07-9BE4-096B-6CD4-DAC39A9BC7B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E59FCA07-9BE4-096B-6CD4-DAC39A9BC7B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {AC8A53CE-EB86-4B6B-B38B-00D843404282}
+ EndGlobalSection
+EndGlobal
diff --git a/TheRepo/VectorAndMatrixTests/MatrixTests.cs b/TheRepo/VectorAndMatrixTests/MatrixTests.cs
new file mode 100644
index 0000000..d5879be
--- /dev/null
+++ b/TheRepo/VectorAndMatrixTests/MatrixTests.cs
@@ -0,0 +1,400 @@
+using OpenTK.Mathematics;
+
+namespace VectorAndMatrixTests
+{
+ [TestClass]
+ public class MatrixTests
+ {
+ [TestMethod]
+ public void Constructor()
+ {
+ // Use the Assert class to test conditions
+ MyMatrix myMatrix = new MyMatrix(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ Matrix4 matrix4 = new Matrix4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateIdentity()
+ {
+ // Use the Assert class to test conditions
+ MyMatrix myMatrix = MyMatrix.CreateIdentity();
+ Matrix4 matrix4 = Matrix4.Identity;
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateTranslation()
+ {
+ // Use the Assert class to test conditions
+ MyVector translationVector = new MyVector(30, 40, 50);
+ MyMatrix myMatrix = MyMatrix.CreateTranslation(translationVector);
+ Matrix4 matrix4 = Matrix4.CreateTranslation(translationVector.X, translationVector.Y, translationVector.Z);
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateScale()
+ {
+ // Use the Assert class to test conditions
+ MyVector scaleVector = new MyVector(30, 40, 50);
+ MyMatrix myMatrix = MyMatrix.CreateScale(scaleVector);
+ Matrix4 matrix4 = Matrix4.CreateScale(scaleVector.X, scaleVector.Y, scaleVector.Z);
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateRotationX()
+ {
+ // Use the Assert class to test conditions
+ float angle = MathF.PI/4;
+ float cosAngle = MathF.Cos(angle);
+ float sinAngle = MathF.Sin(angle);
+ MyMatrix myMatrix = MyMatrix.CreateRotationX(angle);
+ Matrix4 matrix4 = Matrix4.CreateRotationX(angle);
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateRotationY()
+ {
+ // Use the Assert class to test conditions
+ float angle = MathF.PI/4;
+ float cosAngle = MathF.Cos(angle);
+ float sinAngle = MathF.Sin(angle);
+ MyMatrix myMatrix = MyMatrix.CreateRotationY(angle);
+ Matrix4 matrix4 = Matrix4.CreateRotationY(angle);
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void CreateRotationZ()
+ {
+ // Use the Assert class to test conditions
+ float angle = MathF.PI / 4;
+ float cosAngle = MathF.Cos(angle);
+ float sinAngle = MathF.Sin(angle);
+ MyMatrix myMatrix = MyMatrix.CreateRotationZ(angle);
+ Matrix4 matrix4 = Matrix4.CreateRotationZ(angle);
+ Assert.AreEqual(matrix4.M11, myMatrix.GetElement(0, 0));
+ Assert.AreEqual(matrix4.M12, myMatrix.GetElement(0, 1));
+ Assert.AreEqual(matrix4.M13, myMatrix.GetElement(0, 2));
+ Assert.AreEqual(matrix4.M14, myMatrix.GetElement(0, 3));
+ Assert.AreEqual(matrix4.M21, myMatrix.GetElement(1, 0));
+ Assert.AreEqual(matrix4.M22, myMatrix.GetElement(1, 1));
+ Assert.AreEqual(matrix4.M23, myMatrix.GetElement(1, 2));
+ Assert.AreEqual(matrix4.M24, myMatrix.GetElement(1, 3));
+ Assert.AreEqual(matrix4.M31, myMatrix.GetElement(2, 0));
+ Assert.AreEqual(matrix4.M32, myMatrix.GetElement(2, 1));
+ Assert.AreEqual(matrix4.M33, myMatrix.GetElement(2, 2));
+ Assert.AreEqual(matrix4.M34, myMatrix.GetElement(2, 3));
+ Assert.AreEqual(matrix4.M41, myMatrix.GetElement(3, 0));
+ Assert.AreEqual(matrix4.M42, myMatrix.GetElement(3, 1));
+ Assert.AreEqual(matrix4.M43, myMatrix.GetElement(3, 2));
+ Assert.AreEqual(matrix4.M44, myMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void MultiplyVector()
+ {
+ // Use the Assert class to test conditions
+ MyVector myVector = new MyVector(30, 40, 0);
+ MyVector translationVector = new MyVector(10, 20, 30);
+ MyMatrix translationMatrix = MyMatrix.CreateTranslation(translationVector);
+ MyVector translatedVector = translationMatrix.Multiply(myVector);
+
+ Matrix4 translationMatrix4 = Matrix4.CreateTranslation(translationVector.X, translationVector.Y, translationVector.Z);
+ Vector4 vector4 = new Vector4(myVector.X, myVector.Y, myVector.Z, myVector.W);
+ Vector4 translatedVector4 = translationMatrix4 * vector4;
+
+ Assert.AreEqual(translatedVector4.X, translatedVector.X);
+ Assert.AreEqual(translatedVector4.Y, translatedVector.Y);
+ Assert.AreEqual(translatedVector4.Z, translatedVector.Z);
+ Assert.AreEqual(translatedVector4.W, translatedVector.W);
+
+ MyVector scaleVector = new MyVector(2, 2, 2);
+ MyMatrix scaleMatrix = MyMatrix.CreateScale(scaleVector);
+ MyVector scaledVector = scaleMatrix.Multiply(myVector);
+
+ Matrix4 scaleMatrix4 = Matrix4.CreateScale(scaleVector.X, scaleVector.Y, scaleVector.Z);
+ vector4 = new Vector4(myVector.X, myVector.Y, myVector.Z, myVector.W);
+ Vector4 scaledVector4 = scaleMatrix4 * vector4;
+
+ Assert.AreEqual(scaledVector4.X, scaledVector.X);
+ Assert.AreEqual(scaledVector4.Y, scaledVector.Y);
+ Assert.AreEqual(scaledVector4.Z, scaledVector.Z);
+ Assert.AreEqual(scaledVector4.W, scaledVector.W);
+
+ float angle = MathF.PI / 4;
+ MyMatrix rotationMatrix = MyMatrix.CreateRotationZ(angle);
+ MyVector rotatedVector = rotationMatrix.Multiply(myVector);
+
+ Matrix4 rotationMatrix4 = Matrix4.CreateRotationZ(angle);
+ vector4 = new Vector4(myVector.X, myVector.Y, myVector.Z, myVector.W);
+ Vector4 rotatedVector4 = rotationMatrix4 * vector4;
+
+ Assert.AreEqual(rotatedVector4.X, rotatedVector.X);
+ Assert.AreEqual(rotatedVector4.Y, rotatedVector.Y);
+ Assert.AreEqual(rotatedVector4.Z, rotatedVector.Z);
+ Assert.AreEqual(rotatedVector4.W, rotatedVector.W);
+ }
+
+ [TestMethod]
+ public void Multiply()
+ {
+ MyVector myVector = new MyVector(30, 40, 0);
+ MyVector translationVector = new MyVector(10, 20, 30);
+ MyMatrix translationMatrix = MyMatrix.CreateTranslation(translationVector);
+ float angle = MathF.PI / 4;
+ MyMatrix rotationMatrix = MyMatrix.CreateRotationZ(angle);
+ MyVector scaleVector = new MyVector(2, 2, 2);
+ MyMatrix scaleMatrix = MyMatrix.CreateScale(scaleVector);
+
+ Matrix4 translationMatrix4 = Matrix4.CreateTranslation(translationVector.X, translationVector.Y, translationVector.Z);
+ Matrix4 rotationMatrix4 = Matrix4.CreateRotationZ(angle);
+ Matrix4 scaleMatrix4 = Matrix4.CreateScale(scaleVector.X, scaleVector.Y, scaleVector.Z);
+
+ MyMatrix scaleXTranslationMatrix = scaleMatrix.Multiply(translationMatrix);
+
+ Matrix4 scaleXTranslationMatrix4 = scaleMatrix4 * translationMatrix4;
+
+ Assert.AreEqual(scaleXTranslationMatrix4.M11, scaleXTranslationMatrix.GetElement(0, 0));
+ Assert.AreEqual(scaleXTranslationMatrix4.M12, scaleXTranslationMatrix.GetElement(0, 1));
+ Assert.AreEqual(scaleXTranslationMatrix4.M13, scaleXTranslationMatrix.GetElement(0, 2));
+ Assert.AreEqual(scaleXTranslationMatrix4.M14, scaleXTranslationMatrix.GetElement(0, 3));
+ Assert.AreEqual(scaleXTranslationMatrix4.M21, scaleXTranslationMatrix.GetElement(1, 0));
+ Assert.AreEqual(scaleXTranslationMatrix4.M22, scaleXTranslationMatrix.GetElement(1, 1));
+ Assert.AreEqual(scaleXTranslationMatrix4.M23, scaleXTranslationMatrix.GetElement(1, 2));
+ Assert.AreEqual(scaleXTranslationMatrix4.M24, scaleXTranslationMatrix.GetElement(1, 3));
+ Assert.AreEqual(scaleXTranslationMatrix4.M31, scaleXTranslationMatrix.GetElement(2, 0));
+ Assert.AreEqual(scaleXTranslationMatrix4.M32, scaleXTranslationMatrix.GetElement(2, 1));
+ Assert.AreEqual(scaleXTranslationMatrix4.M33, scaleXTranslationMatrix.GetElement(2, 2));
+ Assert.AreEqual(scaleXTranslationMatrix4.M34, scaleXTranslationMatrix.GetElement(2, 3));
+ Assert.AreEqual(scaleXTranslationMatrix4.M41, scaleXTranslationMatrix.GetElement(3, 0));
+ Assert.AreEqual(scaleXTranslationMatrix4.M42, scaleXTranslationMatrix.GetElement(3, 1));
+ Assert.AreEqual(scaleXTranslationMatrix4.M43, scaleXTranslationMatrix.GetElement(3, 2));
+ Assert.AreEqual(scaleXTranslationMatrix4.M44, scaleXTranslationMatrix.GetElement(3, 3));
+
+ MyMatrix translationXScaleMatrix = translationMatrix.Multiply(scaleMatrix);
+
+ Matrix4 translationXScaleMatrix4 = translationMatrix4 * scaleMatrix4;
+
+ Assert.AreEqual(translationXScaleMatrix4.M11, translationXScaleMatrix.GetElement(0, 0));
+ Assert.AreEqual(translationXScaleMatrix4.M12, translationXScaleMatrix.GetElement(0, 1));
+ Assert.AreEqual(translationXScaleMatrix4.M13, translationXScaleMatrix.GetElement(0, 2));
+ Assert.AreEqual(translationXScaleMatrix4.M14, translationXScaleMatrix.GetElement(0, 3));
+ Assert.AreEqual(translationXScaleMatrix4.M21, translationXScaleMatrix.GetElement(1, 0));
+ Assert.AreEqual(translationXScaleMatrix4.M22, translationXScaleMatrix.GetElement(1, 1));
+ Assert.AreEqual(translationXScaleMatrix4.M23, translationXScaleMatrix.GetElement(1, 2));
+ Assert.AreEqual(translationXScaleMatrix4.M24, translationXScaleMatrix.GetElement(1, 3));
+ Assert.AreEqual(translationXScaleMatrix4.M31, translationXScaleMatrix.GetElement(2, 0));
+ Assert.AreEqual(translationXScaleMatrix4.M32, translationXScaleMatrix.GetElement(2, 1));
+ Assert.AreEqual(translationXScaleMatrix4.M33, translationXScaleMatrix.GetElement(2, 2));
+ Assert.AreEqual(translationXScaleMatrix4.M34, translationXScaleMatrix.GetElement(2, 3));
+ Assert.AreEqual(translationXScaleMatrix4.M41, translationXScaleMatrix.GetElement(3, 0));
+ Assert.AreEqual(translationXScaleMatrix4.M42, translationXScaleMatrix.GetElement(3, 1));
+ Assert.AreEqual(translationXScaleMatrix4.M43, translationXScaleMatrix.GetElement(3, 2));
+ Assert.AreEqual(translationXScaleMatrix4.M44, translationXScaleMatrix.GetElement(3, 3));
+
+ MyMatrix chainedMatrix = translationMatrix.Multiply(scaleMatrix).Multiply(rotationMatrix);
+ Matrix4 chainedMatrix4 = translationMatrix4 * scaleMatrix4 * rotationMatrix4;
+
+ float cosAngle = MathF.Cos(angle);
+ float sinAngle = MathF.Sin(angle);
+
+ Assert.AreEqual(chainedMatrix4.M11, chainedMatrix.GetElement(0, 0));
+ Assert.AreEqual(chainedMatrix4.M12, chainedMatrix.GetElement(0, 1));
+ Assert.AreEqual(chainedMatrix4.M13, chainedMatrix.GetElement(0, 2));
+ Assert.AreEqual(chainedMatrix4.M14, chainedMatrix.GetElement(0, 3));
+ Assert.AreEqual(chainedMatrix4.M21, chainedMatrix.GetElement(1, 0));
+ Assert.AreEqual(chainedMatrix4.M22, chainedMatrix.GetElement(1, 1));
+ Assert.AreEqual(chainedMatrix4.M23, chainedMatrix.GetElement(1, 2));
+ Assert.AreEqual(chainedMatrix4.M24, chainedMatrix.GetElement(1, 3));
+ Assert.AreEqual(chainedMatrix4.M31, chainedMatrix.GetElement(2, 0));
+ Assert.AreEqual(chainedMatrix4.M32, chainedMatrix.GetElement(2, 1));
+ Assert.AreEqual(chainedMatrix4.M33, chainedMatrix.GetElement(2, 2));
+ Assert.AreEqual(chainedMatrix4.M34, chainedMatrix.GetElement(2, 3));
+ Assert.AreEqual(chainedMatrix4.M41, chainedMatrix.GetElement(3, 0));
+ Assert.AreEqual(chainedMatrix4.M42, chainedMatrix.GetElement(3, 1));
+ Assert.AreEqual(chainedMatrix4.M43, chainedMatrix.GetElement(3, 2));
+ Assert.AreEqual(chainedMatrix4.M44, chainedMatrix.GetElement(3, 3));
+
+ }
+
+ [TestMethod]
+ public void Inverse()
+ {
+ MyVector translationVector = new MyVector(20, 10, 5);
+ MyMatrix translationMatrix = MyMatrix.CreateTranslation(translationVector);
+ MyVector scaleVector = new MyVector(2, 2, 2);
+ MyMatrix scaleMatrix = MyMatrix.CreateScale(scaleVector);
+ float angle = MathF.PI / 2;
+ MyMatrix rotationZMatrix = MyMatrix.CreateRotationZ(angle);
+ MyMatrix multipliedMatrix = translationMatrix.Multiply(scaleMatrix).Multiply(rotationZMatrix);
+ MyMatrix inverseMatrix = multipliedMatrix.Inverse();
+ MyMatrix identityMatrix = multipliedMatrix.Multiply(inverseMatrix);
+ Assert.AreEqual(1, identityMatrix.GetElement(0, 0), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(0, 1), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(0, 2), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(0, 3), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(1, 0), 0.001);
+ Assert.AreEqual(1, identityMatrix.GetElement(1, 1), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(1, 2), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(1, 3), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(2, 0), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(2, 1), 0.001);
+ Assert.AreEqual(1, identityMatrix.GetElement(2, 2), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(2, 3), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(3, 0), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(3, 1), 0.001);
+ Assert.AreEqual(0, identityMatrix.GetElement(3, 2), 0.001);
+ Assert.AreEqual(1, identityMatrix.GetElement(3, 3), 0.001);
+
+ Matrix4 translationMatrix4 = Matrix4.CreateTranslation(translationVector.X, translationVector.Y, translationVector.Z);
+ Matrix4 scaleMatrix4 = Matrix4.CreateScale(scaleVector.X, scaleVector.Y, scaleVector.Z);
+ Matrix4 rotationZMatrix4 = Matrix4.CreateRotationZ(angle);
+ Matrix4 multipliedMatrix4 = translationMatrix4 * scaleMatrix4 * rotationZMatrix4;
+ Matrix4 inverseMatrix4 = Matrix4.Invert(multipliedMatrix4);
+ Assert.AreEqual(inverseMatrix4.M11, inverseMatrix.GetElement(0, 0));
+ Assert.AreEqual(inverseMatrix4.M12, inverseMatrix.GetElement(0, 1));
+ Assert.AreEqual(inverseMatrix4.M13, inverseMatrix.GetElement(0, 2));
+ Assert.AreEqual(inverseMatrix4.M14, inverseMatrix.GetElement(0, 3));
+ Assert.AreEqual(inverseMatrix4.M21, inverseMatrix.GetElement(1, 0));
+ Assert.AreEqual(inverseMatrix4.M22, inverseMatrix.GetElement(1, 1));
+ Assert.AreEqual(inverseMatrix4.M23, inverseMatrix.GetElement(1, 2));
+ Assert.AreEqual(inverseMatrix4.M24, inverseMatrix.GetElement(1, 3));
+ Assert.AreEqual(inverseMatrix4.M31, inverseMatrix.GetElement(2, 0));
+ Assert.AreEqual(inverseMatrix4.M32, inverseMatrix.GetElement(2, 1));
+ Assert.AreEqual(inverseMatrix4.M33, inverseMatrix.GetElement(2, 2));
+ Assert.AreEqual(inverseMatrix4.M34, inverseMatrix.GetElement(2, 3));
+ Assert.AreEqual(inverseMatrix4.M41, inverseMatrix.GetElement(3, 0));
+ Assert.AreEqual(inverseMatrix4.M42, inverseMatrix.GetElement(3, 1));
+ Assert.AreEqual(inverseMatrix4.M43, inverseMatrix.GetElement(3, 2));
+ Assert.AreEqual(inverseMatrix4.M44, inverseMatrix.GetElement(3, 3));
+ }
+ [TestMethod]
+ public void ToMatrix4()
+ {
+ MyMatrix myMatrix = new MyMatrix(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+ Matrix4 matrix4 = new Matrix4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+
+ Matrix4 matrix4Convert = myMatrix.ToMatrix4();
+
+ Assert.AreEqual(matrix4.M11, matrix4Convert.M11);
+ Assert.AreEqual(matrix4.M12, matrix4Convert.M12);
+ Assert.AreEqual(matrix4.M13, matrix4Convert.M13);
+ Assert.AreEqual(matrix4.M14, matrix4Convert.M14);
+ Assert.AreEqual(matrix4.M21, matrix4Convert.M21);
+ Assert.AreEqual(matrix4.M22, matrix4Convert.M22);
+ Assert.AreEqual(matrix4.M23, matrix4Convert.M23);
+ Assert.AreEqual(matrix4.M24, matrix4Convert.M24);
+ Assert.AreEqual(matrix4.M31, matrix4Convert.M31);
+ Assert.AreEqual(matrix4.M32, matrix4Convert.M32);
+ Assert.AreEqual(matrix4.M33, matrix4Convert.M33);
+ Assert.AreEqual(matrix4.M34, matrix4Convert.M34);
+ Assert.AreEqual(matrix4.M41, matrix4Convert.M41);
+ Assert.AreEqual(matrix4.M42, matrix4Convert.M42);
+ Assert.AreEqual(matrix4.M43, matrix4Convert.M43);
+ Assert.AreEqual(matrix4.M44, matrix4Convert.M44);
+
+ }
+ }
+}
diff --git a/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj b/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj
new file mode 100644
index 0000000..c13b5c4
--- /dev/null
+++ b/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TheRepo/VectorAndMatrixTests/VectorTests.cs b/TheRepo/VectorAndMatrixTests/VectorTests.cs
new file mode 100644
index 0000000..610a95d
--- /dev/null
+++ b/TheRepo/VectorAndMatrixTests/VectorTests.cs
@@ -0,0 +1,218 @@
+using OpenTK.Mathematics;
+
+namespace VectorAndMatrixTests
+{
+ [TestClass]
+ public class VectorTests
+ {
+ [TestMethod]
+ public void Constructor()
+ {
+ // Use the Assert class to test conditions
+ MyVector myVector = new MyVector(30, 40, 0);
+
+ Vector3 Vector3 = new Vector3(30, 40, 0);
+
+ Assert.AreEqual(Vector3.X, myVector.X);
+ Assert.AreEqual(Vector3.Y, myVector.Y);
+ Assert.AreEqual(Vector3.Z, myVector.Z);
+
+
+
+
+ }
+ [TestMethod]
+ public void Add()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector secondVector = new MyVector(20, 30, 0);
+ MyVector thirdVector = firstVector.Add(secondVector);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = new Vector3(20, 30, 0);
+ Vector3 thirdVector3 = firstVector3 + secondVector3;
+
+
+ Assert.AreEqual(thirdVector3.X, thirdVector.X);
+ Assert.AreEqual(thirdVector3.Y, thirdVector.Y);
+ Assert.AreEqual(thirdVector3.Z, thirdVector.Z);
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+ }
+
+ [TestMethod]
+ public void Subtract()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector secondVector = new MyVector(5, 10, 0);
+ MyVector thirdVector = firstVector.Subtract(secondVector);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = new Vector3(5, 10, 0);
+ Vector3 thirdVector3 = firstVector3 - secondVector3;
+
+ Assert.AreEqual(thirdVector3.X, thirdVector.X);
+ Assert.AreEqual(thirdVector3.Y, thirdVector.Y);
+ Assert.AreEqual(thirdVector3.Z, thirdVector.Z);
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+ }
+
+ [TestMethod]
+ public void Multiply()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ float scalar = 10;
+ MyVector secondVector = firstVector.Multiply(scalar);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = firstVector3 * scalar;
+
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+ }
+ [TestMethod]
+ public void Divide()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ float scalar = 10;
+ MyVector secondVector = firstVector.Divide(scalar);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = firstVector3 / scalar;
+
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+ }
+ [TestMethod]
+ public void Magnitude()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ float magnitude = firstVector.Magnitude();
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ float magnitude4 = firstVector3.Length;
+
+ Assert.AreEqual(magnitude4, magnitude);
+ }
+
+ [TestMethod]
+ public void Normalise()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector normalisedVector = firstVector.Normalise();
+ float magnitude = normalisedVector.Magnitude();
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 normalisedVector3 = Vector3.Normalize(firstVector3);
+ float magnitude4 = normalisedVector3.Length;
+
+ Assert.AreEqual(normalisedVector3.X, normalisedVector.X, 0.001);
+ Assert.AreEqual(normalisedVector3.Y, normalisedVector.Y, 0.001);
+ Assert.AreEqual(normalisedVector3.Z, normalisedVector.Z, 0.001);
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+
+ }
+
+ [TestMethod]
+ public void DotProduct()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, -10);
+ MyVector secondVector = new MyVector(40, -20, 40);
+ float dotProduct = firstVector.DotProduct(secondVector);
+
+ Vector3 firstVector3 = new Vector3(30, 40, -10);
+ Vector3 secondVector3 = new Vector3(40, -20, 40);
+ float dotProduct4 = Vector3.Dot(firstVector3, secondVector3);
+ Assert.AreEqual(dotProduct4, dotProduct);
+
+ }
+
+ [TestMethod]
+ public void Interpolate()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector secondVector = new MyVector(60, 80, 0);
+ float interpolation = 0.25f;
+ MyVector interpolatedVector = firstVector.Interpolate(secondVector, interpolation);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = new Vector3(60, 80, 0);
+ Vector3 interpolatedVector3 = Vector3.Lerp(firstVector3, secondVector3, interpolation);
+
+ Assert.AreEqual(interpolatedVector3.X, interpolatedVector.X);
+ Assert.AreEqual(interpolatedVector3.Y, interpolatedVector.Y);
+ Assert.AreEqual(interpolatedVector3.Z, interpolatedVector.Z);
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+
+ }
+ [TestMethod]
+ public void AngleBetween()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector secondVector = new MyVector(-40, 30, 0);
+ float angleBetween = firstVector.AngleBetween(secondVector);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = new Vector3(-40, 30, 0);
+ float angleBetween3 = Vector3.CalculateAngle(firstVector3, secondVector3);
+
+ Assert.AreEqual(angleBetween3, angleBetween, 0.001);
+ }
+ [TestMethod]
+ public void CrossProduct()
+ {
+ // Use the Assert class to test conditions
+ MyVector firstVector = new MyVector(30, 40, 0);
+ MyVector secondVector = new MyVector(-40, 30, 0);
+ MyVector crossProduct = firstVector.CrossProduct(secondVector);
+
+ Vector3 firstVector3 = new Vector3(30, 40, 0);
+ Vector3 secondVector3 = new Vector3(-40, 30, 0);
+ Vector3 crossProduct3 = Vector3.Cross(firstVector3, secondVector3);
+
+ Assert.AreEqual(crossProduct3.X, crossProduct.X);
+ Assert.AreEqual(crossProduct3.Y, crossProduct.Y);
+ Assert.AreEqual(crossProduct3.Z, crossProduct.Z);
+ Assert.AreEqual(firstVector3.X, firstVector.X);
+ Assert.AreEqual(firstVector3.Y, firstVector.Y);
+ Assert.AreEqual(firstVector3.Z, firstVector.Z);
+ Assert.AreEqual(secondVector3.X, secondVector.X);
+ Assert.AreEqual(secondVector3.Y, secondVector.Y);
+ Assert.AreEqual(secondVector3.Z, secondVector.Z);
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/images/busses.png b/images/busses.png
new file mode 100644
index 0000000000000000000000000000000000000000..057ed65602dca9061cee501cb56793c549c58c9f
GIT binary patch
literal 12096
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
B8BqWL
literal 0
HcmV?d00001