mirror of
https://github.com/UOH-CS-Level5/551455-graphics-programming-2526-the-repo-Zyb3rWolfi.git
synced 2025-11-29 00:43:08 +00:00
Compare commits
No commits in common. "87804fc932f1fe87fa1f9bd6de2573c2806c043a" and "5dd7aef809e27718c350952720413793276bd0e1" have entirely different histories.
87804fc932
...
5dd7aef809
@ -3,7 +3,6 @@
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders>
|
||||
<Path>../../551455-graphics-programming-2526-the-repo-Zyb3rWolfi</Path>
|
||||
<Path>TheLabs/Textures</Path>
|
||||
</attachedFolders>
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK" Version="4.8.2" />
|
||||
<PackageReference Include="StbImageSharp" Version="2.30.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
namespace TheLabs;
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using StbImageSharp;
|
||||
|
||||
public class Mesh : IDisposable
|
||||
{
|
||||
private readonly int _vao;
|
||||
private readonly int _vbo;
|
||||
private readonly int _ebo;
|
||||
private readonly int _vertexCount;
|
||||
private readonly bool _useIndices;
|
||||
|
||||
// A flexible way to define vertex layout
|
||||
// (Could be an enum or a struct)
|
||||
public enum VertexLayout { PosColor, PosTex }
|
||||
|
||||
public Mesh(float[] vertices, uint[] indices, VertexLayout layout)
|
||||
{
|
||||
_vertexCount = indices.Length;
|
||||
_useIndices = true;
|
||||
|
||||
_vao = GL.GenVertexArray();
|
||||
GL.BindVertexArray(_vao);
|
||||
|
||||
_vbo = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
_ebo = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// --- Set up Vertex Attributes based on layout ---
|
||||
if (layout == VertexLayout.PosColor)
|
||||
{
|
||||
// layout(location = 0) in shader: vec3 position
|
||||
var stride = 7 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
// layout(location = 1) in shader: vec4 color
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
}
|
||||
else if (layout == VertexLayout.PosTex)
|
||||
{
|
||||
// layout(location = 0) in shader: vec3 position
|
||||
var stride = 5 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0 );
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
// layout(location = 1) in shader: vec2 texCoord
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
}
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
// Constructor for non-indexed drawing (like your Circle)
|
||||
public Mesh(float[] vertices, VertexLayout layout)
|
||||
{
|
||||
// Calculate vertex count based on stride
|
||||
if (layout == VertexLayout.PosColor)
|
||||
_vertexCount = vertices.Length / 7;
|
||||
else // PosTex
|
||||
_vertexCount = vertices.Length / 5;
|
||||
|
||||
_useIndices = false;
|
||||
_ebo = 0; // No EBO
|
||||
|
||||
_vao = GL.GenVertexArray();
|
||||
GL.BindVertexArray(_vao);
|
||||
|
||||
_vbo = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// --- Set up Vertex Attributes based on layout ---
|
||||
if (layout == VertexLayout.PosColor)
|
||||
{
|
||||
// layout(location = 0) in shader: vec3 position
|
||||
var stride = 7 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
// layout(location = 1) in shader: vec4 color
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
}
|
||||
else if (layout == VertexLayout.PosTex)
|
||||
{
|
||||
// layout(location = 0) in shader: vec3 position
|
||||
var stride = 5 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
// layout(location = 1) in shader: vec2 texCoord
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
}
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
// Draw the mesh. Notice it doesn't know about shaders or matrices!
|
||||
public void Draw()
|
||||
{
|
||||
GL.BindVertexArray(_vao);
|
||||
|
||||
if (_useIndices)
|
||||
{
|
||||
GL.DrawElements(PrimitiveType.Triangles, _vertexCount, DrawElementsType.UnsignedInt, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For your Circle (which used TriangleFan)
|
||||
GL.DrawArrays(PrimitiveType.TriangleFan, 0, _vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteBuffer(_vbo);
|
||||
GL.DeleteBuffer(_ebo);
|
||||
GL.DeleteVertexArray(_vao);
|
||||
}
|
||||
}
|
||||
@ -3,8 +3,6 @@ using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Mathematics;
|
||||
using TheLabs.Shapes;
|
||||
|
||||
namespace TheLabs
|
||||
{
|
||||
@ -15,35 +13,70 @@ namespace TheLabs
|
||||
// Negative X coordinates move to the left, positive X move to the right.
|
||||
// Negative Y coordinates move to the bottom, positive Y move to the top.
|
||||
// OpenGL only supports rendering in 3D, so to create a flat triangle, the Z coordinate will be kept as 0.
|
||||
private readonly float[] _vertices =
|
||||
{
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex
|
||||
0.0f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-right vertex
|
||||
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f // Top vertex
|
||||
};
|
||||
|
||||
private readonly float[] _quadvertices =
|
||||
{
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex
|
||||
0.0f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-right vertex
|
||||
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top vertex
|
||||
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left vertex
|
||||
-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-right vertex
|
||||
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f // Top vertex
|
||||
};
|
||||
|
||||
private readonly float[] _cubevertices =
|
||||
{
|
||||
// Positions
|
||||
// Front face
|
||||
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, // Top-left
|
||||
|
||||
// Back face
|
||||
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
|
||||
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 1.0f, // Top-left
|
||||
};
|
||||
|
||||
private readonly uint[] _cubeIndices =
|
||||
{
|
||||
// Front face
|
||||
0, 1, 2, 2, 3, 0,
|
||||
// Back face
|
||||
4, 5, 6, 6, 7, 4,
|
||||
// Left face
|
||||
4, 0, 3, 3, 7, 4,
|
||||
// Right face
|
||||
1, 5, 6, 6, 2, 1,
|
||||
// Top face
|
||||
3, 2, 6, 6, 7, 3,
|
||||
// Bottom face
|
||||
4, 5, 1, 1, 0, 4
|
||||
};
|
||||
|
||||
// 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 _vao, _vbo, _ebo;
|
||||
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;
|
||||
|
||||
// - Resources that only need to be created once
|
||||
private Mesh _cubeMesh;
|
||||
private Texture _cubeTexture;
|
||||
private Texture _cubeTexture2;
|
||||
private float _rotation;
|
||||
|
||||
// -- Scene Objects
|
||||
private RenderObject _cubeObject;
|
||||
private RenderObject _cubeObject2;
|
||||
|
||||
private Vector3 _cameraPosition = new Vector3(0.0f, 0.0f, -3.0f);
|
||||
private Vector3 _cameraFront = new Vector3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
private Vector3 _cameraUp = Vector3.UnitY;
|
||||
|
||||
private float _yaw = -90.0f;
|
||||
private float _pitch = 0.0f;
|
||||
|
||||
private float _cameraSpeed = 2.5f;
|
||||
private float _rotationSpeed = 50.0f;
|
||||
|
||||
private float _mouseSensitivity = 0.1f;
|
||||
|
||||
// This prevents a large camera jump when the window first gets focus
|
||||
private bool _firstMove = true;
|
||||
|
||||
public MyExampleWindow(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
|
||||
: base(gameWindowSettings, nativeWindowSettings)
|
||||
{
|
||||
@ -52,129 +85,142 @@ namespace TheLabs
|
||||
// Now, we start initializing OpenGL.
|
||||
protected override void OnLoad()
|
||||
{
|
||||
// This is called when the window is created and is where we can set up OpenGL resources.
|
||||
base.OnLoad();
|
||||
//GL.Disable(EnableCap.CullFace);
|
||||
|
||||
//GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
|
||||
// Set The background color to a nice blue.
|
||||
// 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);
|
||||
GL.Enable(EnableCap.DepthTest);
|
||||
CursorState = CursorState.Grabbed;
|
||||
|
||||
// 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, _cubevertices.Length * sizeof(float), _cubevertices, 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.
|
||||
// --- ELEMENT BUFFER (for indices) ---
|
||||
_ebo = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, _cubeIndices.Length * sizeof(uint), _cubeIndices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// 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, 7 * sizeof(float), 0);
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 7 * sizeof(float), 3 * sizeof(float));
|
||||
|
||||
// Enable variable 0 in the shader.
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
// 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");
|
||||
_cubeMesh = ShapeFactory.CreateTexturedCube();
|
||||
_cubeTexture = new Texture("Textures/stone.jpg");
|
||||
_cubeTexture2 = new Texture("Textures/placeholder.png");
|
||||
_cubeObject = new RenderObject(_cubeMesh, _shader, _cubeTexture);
|
||||
_cubeObject2 = new RenderObject(_cubeMesh, _shader, _cubeTexture2);
|
||||
_cubeObject.Position = new Vector3(-1.5f, 0.0f, 0.0f);
|
||||
_cubeObject2.Position = new Vector3(1.5f, 0.0f, 0.0f);
|
||||
_cubeObject.Scale = new Vector3(0.5f, 0.5f, 0.5f);
|
||||
_cubeObject2.Scale = new Vector3(0.5f, 0.5f, 0.5f);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// This is called once per frame and is where all the rendering code goes.
|
||||
base.OnRenderFrame(e);
|
||||
// Clear the color buffer and the depth buffer
|
||||
GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit);
|
||||
_cubeObject.Rotation = Quaternion.FromEulerAngles(_rotation * 0.5f, _rotation, 0); //
|
||||
_cubeObject2.Rotation = Quaternion.FromEulerAngles(-_rotation * 0.5f, -_rotation, 0);
|
||||
|
||||
// --- Set up Camera ---
|
||||
Matrix4 view = Matrix4.LookAt(_cameraPosition, _cameraPosition + _cameraFront, _cameraUp);
|
||||
float aspectRatio = (float)Size.X / Size.Y;
|
||||
// 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 | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
// 2. Add a safety check for divide-by-zero
|
||||
if (Size.Y == 0 || Size.X == 0)
|
||||
{
|
||||
aspectRatio = 1.0f; // Default to 1:1 if window is minimized
|
||||
}
|
||||
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(
|
||||
MathHelper.DegreesToRadians(45f),
|
||||
aspectRatio,
|
||||
0.1f,
|
||||
100.0f
|
||||
);
|
||||
// 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.
|
||||
|
||||
_cubeObject.Draw(view, projection);
|
||||
_cubeObject2.Draw(view, projection);
|
||||
// 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.DrawElements(PrimitiveType.Triangles, _cubeIndices.Length, DrawElementsType.UnsignedInt, 0); // 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);
|
||||
_rotation += (float)e.Time;
|
||||
|
||||
var input = KeyboardState;
|
||||
|
||||
if (input.IsKeyDown(Keys.Escape))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
if (!IsFocused)
|
||||
{
|
||||
_firstMove = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var mouuse = MouseState;
|
||||
|
||||
if (_firstMove)
|
||||
{
|
||||
mouuse = MouseState;
|
||||
_firstMove = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the mouse's movement delta
|
||||
float deltaX = mouuse.Delta.X;
|
||||
float deltaY = mouuse.Delta.Y;
|
||||
|
||||
// Apply sensitivity
|
||||
_yaw += deltaX * _mouseSensitivity;
|
||||
_pitch -= deltaY * _mouseSensitivity; // Y-axis is inverted
|
||||
}
|
||||
|
||||
|
||||
// --- Rotation (Arrow Keys) ---
|
||||
if (input.IsKeyDown(Keys.Left))
|
||||
{
|
||||
_yaw -= _rotationSpeed * (float)e.Time;
|
||||
}
|
||||
if (input.IsKeyDown(Keys.Right))
|
||||
{
|
||||
_yaw += _rotationSpeed * (float)e.Time;
|
||||
}
|
||||
if (input.IsKeyDown(Keys.Up))
|
||||
{
|
||||
_pitch += _rotationSpeed * (float)e.Time;
|
||||
}
|
||||
if (input.IsKeyDown(Keys.Down))
|
||||
{
|
||||
_pitch -= _rotationSpeed * (float)e.Time;
|
||||
}
|
||||
_pitch = MathHelper.Clamp(_pitch, -89.0f, 89.0f);
|
||||
Vector3 front;
|
||||
front.X = MathF.Cos(MathHelper.DegreesToRadians(_yaw)) * MathF.Cos(MathHelper.DegreesToRadians(_pitch));
|
||||
front.Y = MathF.Sin(MathHelper.DegreesToRadians(_pitch));
|
||||
front.Z = MathF.Sin(MathHelper.DegreesToRadians(_yaw)) * MathF.Cos(MathHelper.DegreesToRadians(_pitch));
|
||||
_cameraFront = Vector3.Normalize(front);
|
||||
var _cameraRight = Vector3.Normalize(Vector3.Cross(_cameraFront, _cameraUp));
|
||||
if (input.IsKeyDown(Keys.W)) _cameraPosition += _cameraFront * _cameraSpeed * (float)e.Time;
|
||||
if (input.IsKeyDown(Keys.S)) _cameraPosition -= _cameraFront * _cameraSpeed * (float)e.Time;
|
||||
if (input.IsKeyDown(Keys.A)) _cameraPosition -= _cameraRight * _cameraSpeed * (float)e.Time;
|
||||
if (input.IsKeyDown(Keys.D)) _cameraPosition += _cameraRight * _cameraSpeed * (float)e.Time;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override void OnResize(ResizeEventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
@ -204,6 +250,10 @@ namespace TheLabs
|
||||
GL.BindVertexArray(0);
|
||||
GL.UseProgram(0);
|
||||
|
||||
// Delete all the resources.
|
||||
GL.DeleteBuffer(_vertexBufferObject);
|
||||
GL.DeleteVertexArray(_vertexArrayObject);
|
||||
|
||||
GL.DeleteProgram(_shader.Handle);
|
||||
|
||||
base.OnUnload();
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace TheLabs;
|
||||
|
||||
public class RenderObject
|
||||
{
|
||||
public Mesh Mesh;
|
||||
public Shader Shader;
|
||||
public Texture Texture;
|
||||
|
||||
public Vector3 Position = Vector3.Zero;
|
||||
public Quaternion Rotation = Quaternion.Identity;
|
||||
public Vector3 Scale = Vector3.One;
|
||||
|
||||
public RenderObject(Mesh mesh, Shader shader, Texture texture)
|
||||
{
|
||||
Mesh = mesh;
|
||||
Shader = shader;
|
||||
Texture = texture;
|
||||
}
|
||||
|
||||
public void Draw(Matrix4 view, Matrix4 projection)
|
||||
{
|
||||
// 1. Activate Shader
|
||||
Shader.Use();
|
||||
Shader.SetInt("uTexture", 0);
|
||||
// 2. Bind Texture
|
||||
Texture.Use(); // Assumes you have a Texture class
|
||||
// 3. Calculate Model Matrix
|
||||
Matrix4 model = Matrix4.CreateScale(Scale) * Matrix4.CreateFromQuaternion(Rotation) * Matrix4.CreateTranslation(Position);
|
||||
int modelLoc = GL.GetUniformLocation(Shader.Handle, "model");
|
||||
GL.UniformMatrix4(modelLoc, false, ref model); // Try TRUE here
|
||||
|
||||
int viewLoc = GL.GetUniformLocation(Shader.Handle, "view");
|
||||
GL.UniformMatrix4(viewLoc, false, ref view); // Try TRUE here
|
||||
|
||||
int projLoc = GL.GetUniformLocation(Shader.Handle, "projection");
|
||||
GL.UniformMatrix4(projLoc, false, ref projection); // Try TRUE here
|
||||
|
||||
// 5. Tell the Mesh to draw itself
|
||||
Mesh.Draw();
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,8 @@
|
||||
#version 330
|
||||
|
||||
in vec4 outColour;
|
||||
out vec4 outputColor;
|
||||
in vec2 TexCoord;
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
in vec4 vertexColor;
|
||||
out vec4 FragColor;
|
||||
void main()
|
||||
{
|
||||
outputColor = texture(uTexture, TexCoord);
|
||||
FragColor = vertexColor;
|
||||
}
|
||||
@ -25,14 +25,16 @@
|
||||
// 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;
|
||||
layout(location = 1)in vec2 aTexCoord;
|
||||
out vec4 outColour;
|
||||
|
||||
layout (location = 0) in vec3 aPosition;
|
||||
layout (location = 1) in vec4 aColor;
|
||||
|
||||
out vec4 vertexColor;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
out vec2 TexCoord;
|
||||
|
||||
// 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.
|
||||
@ -41,8 +43,8 @@ out vec2 TexCoord;
|
||||
// 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)
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * model * vec4(aPosition, 1.0);
|
||||
TexCoord = aTexCoord;
|
||||
vertexColor = aColor;
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
namespace TheLabs;
|
||||
|
||||
public static class ShapeFactory
|
||||
{
|
||||
public static Mesh CreateTexturedCube()
|
||||
{
|
||||
float[] vertices = {
|
||||
// Front face (z = +0.5)
|
||||
-0.5f, -0.5f, 0.5f, 0f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 1f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 0f, 1f, // Top-left
|
||||
|
||||
// Back face (z = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, -0.5f, 0f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 1f, 1f, // Top-left
|
||||
|
||||
// Left face (x = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 0f, 0f, // Bottom-left
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, // Bottom-right
|
||||
-0.5f, 0.5f, 0.5f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 0f, 1f, // Top-left
|
||||
// Right face (x = +0.5)
|
||||
0.5f, -0.5f, -0.5f, 1f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 0f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 0f, 1f, // Top-right
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, // Top-left
|
||||
|
||||
// Top face (y = +0.5)
|
||||
-0.5f, 0.5f, -0.5f, 0f, 1f, // Bottom-left
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 1f, 0f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 0f, 0f, // Top-left
|
||||
|
||||
// Bottom face (y = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 1f, // Bottom-right
|
||||
0.5f, -0.5f, 0.5f, 0f, 0f, // Top-right
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, // Top-left
|
||||
|
||||
};
|
||||
|
||||
uint[] indices = {
|
||||
// Front face
|
||||
0, 2, 1,
|
||||
0, 2, 3,
|
||||
// Back face
|
||||
4, 5, 6,
|
||||
4, 6, 7,
|
||||
// Left face
|
||||
8, 9, 10,
|
||||
8, 10, 11,
|
||||
// Right face
|
||||
12, 14, 13,
|
||||
12, 14, 15,
|
||||
// Top face
|
||||
16, 17, 18,
|
||||
16, 18, 19,
|
||||
// Bottom face
|
||||
20, 22, 21,
|
||||
20, 22, 23
|
||||
};
|
||||
|
||||
return new Mesh(vertices, indices, Mesh.VertexLayout.PosTex);
|
||||
}
|
||||
|
||||
public static Mesh CreateColorCube()
|
||||
{
|
||||
float[] vertices = {
|
||||
// Front face (z = +0.5)
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, 0f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 1f, 0f, 0f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 0f, 0f, 0f, 1f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 1f, 0f, 0f, 1f, // Top-left
|
||||
|
||||
// Back face (z = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 0f, 1f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 1f, 1f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 1f // Top-left
|
||||
};
|
||||
|
||||
uint[] indices = {
|
||||
// Front face
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
|
||||
// Back face
|
||||
4, 5, 6,
|
||||
6, 7, 4,
|
||||
|
||||
// Left face
|
||||
4, 0, 3,
|
||||
3, 7, 4,
|
||||
|
||||
// Right face
|
||||
1, 5, 6,
|
||||
6, 2, 1,
|
||||
|
||||
// Top face
|
||||
3, 2, 6,
|
||||
6, 7, 3,
|
||||
|
||||
// Bottom face
|
||||
4, 5, 1,
|
||||
1, 0, 4
|
||||
};
|
||||
|
||||
return new Mesh(vertices, indices, Mesh.VertexLayout.PosColor);
|
||||
}
|
||||
|
||||
// You'd also add CreateCircle, CreateCylinder, etc.
|
||||
// Your Cylinder should be ONE mesh, not 3 draw calls.
|
||||
// Generate the top, bottom, and side vertices into one
|
||||
// big array and use one EBO for all of it.
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace TheLabs.Shapes;
|
||||
|
||||
public class Circle
|
||||
{
|
||||
// Vertex Array Object, Vertex Buffer Object, Element Buffer Object
|
||||
private int _vao;
|
||||
private int _vbo;
|
||||
|
||||
private readonly float[] _vertices =
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
public Circle(float z = 0f, int segments = 100, Vector4? color = null)
|
||||
{
|
||||
Vector4 col = color ?? new Vector4(0f, 0f, 1f, 1f);
|
||||
for (int i = 0; i <= segments; i++)
|
||||
{
|
||||
|
||||
float angle = i * 2.0f * MathF.PI / segments;
|
||||
float x = 0.5f * MathF.Cos(angle);
|
||||
float y = 0.5f * MathF.Sin(angle);
|
||||
|
||||
Array.Resize(ref _vertices, _vertices.Length + 7);
|
||||
_vertices[^7] = x;
|
||||
_vertices[^6] = y;
|
||||
_vertices[^5] = z;
|
||||
_vertices[^4] = col.X;
|
||||
_vertices[^3] = col.Y;
|
||||
_vertices[^2] = col.Z;
|
||||
_vertices[^1] = col.W;
|
||||
|
||||
}
|
||||
// The Vertex Array Object
|
||||
// This stores the confifuration of vertex atributes
|
||||
_vao = GL.GenVertexArray();
|
||||
// The Vertex Buffer Object
|
||||
// This stores the actual vertex data e.g. positions, colors, normals, texture coordinates
|
||||
_vbo = GL.GenBuffer();
|
||||
|
||||
// We bind the VAO
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); // Specifying the type of buffer
|
||||
// Uploading the vertex data to the GPU
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// We tell opengl how to interpret the vertex data.
|
||||
// The Location 0 corresponds to the layout(location = 0) in the vertex shader
|
||||
// How many components (x, y, z) -> 3
|
||||
// Data type -> float
|
||||
// Not normalized
|
||||
// The Stride -> The total size of a vertex (in bytes)
|
||||
// The offset -> The position data starts at the beginning of the vertex data so 0
|
||||
var stride = 7 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
public float[] GetVertices()
|
||||
{
|
||||
return _vertices;
|
||||
}
|
||||
public void Draw(Shader shader, Matrix4 view, Matrix4 projection, Matrix4 model)
|
||||
{
|
||||
//shader.SetMatrix4("model", matrix4);
|
||||
int modelLoc = GL.GetUniformLocation(shader.Handle, "model");
|
||||
int viewLoc = GL.GetUniformLocation(shader.Handle, "view");
|
||||
int projLoc = GL.GetUniformLocation(shader.Handle, "projection");
|
||||
|
||||
// Send the matrices to the shader
|
||||
GL.UniformMatrix4(modelLoc, false, ref model);
|
||||
GL.UniformMatrix4(viewLoc, false, ref view);
|
||||
GL.UniformMatrix4(projLoc, false, ref projection);
|
||||
|
||||
shader.Use();
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.DrawArrays(PrimitiveType.TriangleFan, 0, _vertices.Length / 7);
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteBuffer(_vbo);
|
||||
GL.DeleteVertexArray(_vao);
|
||||
}
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
|
||||
namespace TheLabs.Shapes;
|
||||
|
||||
public class Cube
|
||||
{
|
||||
// Vertex Array Object, Vertex Buffer Object, Element Buffer Object
|
||||
private int _vao;
|
||||
private int _vbo;
|
||||
private int _ebo;
|
||||
private int _vertexCount;
|
||||
public Vector3 Position = Vector3.Zero;
|
||||
|
||||
private readonly float[] _cubevertices =
|
||||
{
|
||||
// Position // Color (RGBA)
|
||||
// Front face (z = +0.5)
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, 0f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 1f, 0f, 0f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 0f, 0f, 0f, 1f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 1f, 0f, 0f, 1f, // Top-left
|
||||
|
||||
// Back face (z = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 0f, 1f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 1f, 1f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 1f // Top-left
|
||||
};
|
||||
private readonly uint[] _cubeIndices =
|
||||
{
|
||||
// Front face
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
|
||||
// Back face
|
||||
4, 5, 6,
|
||||
6, 7, 4,
|
||||
|
||||
// Left face
|
||||
4, 0, 3,
|
||||
3, 7, 4,
|
||||
|
||||
// Right face
|
||||
1, 5, 6,
|
||||
6, 2, 1,
|
||||
|
||||
// Top face
|
||||
3, 2, 6,
|
||||
6, 7, 3,
|
||||
|
||||
// Bottom face
|
||||
4, 5, 1,
|
||||
1, 0, 4
|
||||
};
|
||||
|
||||
public Cube()
|
||||
{
|
||||
// The number of vertices to draw
|
||||
_vertexCount = _cubeIndices.Length;
|
||||
// The Vertex Array Object
|
||||
// This stores the confifuration of vertex atributes
|
||||
_vao = GL.GenVertexArray();
|
||||
// The Vertex Buffer Object
|
||||
// This stores the actual vertex data e.g. positions, colors, normals, texture coordinates
|
||||
_vbo = GL.GenBuffer();
|
||||
// The Element Buffer Object, stores the indices for indexed drawing
|
||||
_ebo = GL.GenBuffer();
|
||||
|
||||
// We bind the VAO
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); // Specifying the type of buffer
|
||||
// Uploading the vertex data to the GPU
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, _cubevertices.Length * sizeof(float), _cubevertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// Now we set up the EBO
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);
|
||||
// Uploading the index data to the GPU
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, _cubeIndices.Length * sizeof(uint), _cubeIndices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// We tell opengl how to interpret the vertex data.
|
||||
// The Location 0 corresponds to the layout(location = 0) in the vertex shader
|
||||
// How many components (x, y, z) -> 3
|
||||
// Data type -> float
|
||||
// Not normalized
|
||||
// The Stride -> The total size of a vertex (in bytes)
|
||||
// The offset -> The position data starts at the beginning of the vertex data so 0
|
||||
var stride = 7 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
public void Draw(Shader shader, Matrix4 view, Matrix4 projection, float rotation)
|
||||
{
|
||||
// Create the model matrix with rotation and translation
|
||||
Matrix4 model = Matrix4.CreateRotationY(rotation) * Matrix4.CreateRotationX(rotation * 0.5f) * Matrix4.CreateTranslation(Position);
|
||||
|
||||
// Set the matrices in the shader
|
||||
int modelLoc = GL.GetUniformLocation(shader.Handle, "model");
|
||||
int viewLoc = GL.GetUniformLocation(shader.Handle, "view");
|
||||
int projLoc = GL.GetUniformLocation(shader.Handle, "projection");
|
||||
|
||||
// Send the matrices to the shader
|
||||
GL.UniformMatrix4(modelLoc, false, ref model);
|
||||
GL.UniformMatrix4(viewLoc, false, ref view);
|
||||
GL.UniformMatrix4(projLoc, false, ref projection);
|
||||
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.DrawElements(PrimitiveType.Triangles, _cubeIndices.Length, DrawElementsType.UnsignedInt, 0);
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteBuffer(_vbo);
|
||||
GL.DeleteBuffer(_ebo);
|
||||
GL.DeleteVertexArray(_vao);
|
||||
}
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
|
||||
namespace TheLabs.Shapes;
|
||||
|
||||
public class Cylinder
|
||||
{
|
||||
// Vertex Array Object, Vertex Buffer Object, Element Buffer Object
|
||||
private int _vao;
|
||||
private int _vbo;
|
||||
private int _ebo;
|
||||
private int _vertexCount;
|
||||
private Circle _topCircle;
|
||||
private Circle _bottomCircle;
|
||||
private float _radius = 0.5f;
|
||||
private float _height = 1.0f;
|
||||
|
||||
private float[] _vertices =
|
||||
{
|
||||
};
|
||||
|
||||
private uint[] _indices =
|
||||
{
|
||||
|
||||
};
|
||||
public Cylinder()
|
||||
{
|
||||
// The number of vertices to draw
|
||||
_vertexCount = _indices.Length;
|
||||
_topCircle = new Circle(0.5f, 10, new Vector4(1f, 1f, 0f, 1f));
|
||||
_bottomCircle = new Circle(-0.5f, 10, new Vector4(1f, 0f, 0f, 1f));
|
||||
GenerateCylinder(10);
|
||||
|
||||
}
|
||||
|
||||
private void GenerateCylinder(int segments)
|
||||
{
|
||||
var vertices = new List<float>();
|
||||
var indices = new List<uint>();
|
||||
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
float angle1 = 2.0f * MathF.PI * i / segments;
|
||||
float angle2 = 2.0f * MathF.PI * ((i + 1) % segments) / segments;
|
||||
|
||||
float x1 = _radius * MathF.Cos(angle1);
|
||||
float y1 = _radius * MathF.Sin(angle1);
|
||||
float x2 = _radius * MathF.Cos(angle2);
|
||||
float y2 = _radius * MathF.Sin(angle2);
|
||||
|
||||
float zTop = _height / 2f;
|
||||
float zBottom = -_height / 2f;
|
||||
|
||||
Vector4 sideColor = new Vector4(0f, 1f, 1f, 1f); // side color
|
||||
|
||||
// Add vertices: top1, bottom1, top2, bottom2
|
||||
int top1Index = vertices.Count / 7;
|
||||
vertices.AddRange(new float[] { x1, y1, zTop, sideColor.X, sideColor.Y, sideColor.Z, sideColor.W });
|
||||
|
||||
int bottom1Index = vertices.Count / 7;
|
||||
vertices.AddRange(new float[] { x1, y1, zBottom, sideColor.X, sideColor.Y, sideColor.Z, sideColor.W });
|
||||
|
||||
int top2Index = vertices.Count / 7;
|
||||
vertices.AddRange(new float[] { x2, y2, zTop, sideColor.X, sideColor.Y, sideColor.Z, sideColor.W });
|
||||
|
||||
int bottom2Index = vertices.Count / 7;
|
||||
vertices.AddRange(new float[] { x2, y2, zBottom, sideColor.X, sideColor.Y, sideColor.Z, sideColor.W });
|
||||
|
||||
// First triangle
|
||||
indices.Add((uint)top1Index);
|
||||
indices.Add((uint)bottom1Index);
|
||||
indices.Add((uint)top2Index);
|
||||
|
||||
// Second triangle
|
||||
indices.Add((uint)top2Index);
|
||||
indices.Add((uint)bottom1Index);
|
||||
indices.Add((uint)bottom2Index);
|
||||
}
|
||||
|
||||
_vertices = vertices.ToArray();
|
||||
_indices = indices.ToArray();
|
||||
_vertexCount = indices.Count;
|
||||
|
||||
// OpenGL setup
|
||||
_vao = GL.GenVertexArray();
|
||||
_vbo = GL.GenBuffer();
|
||||
_ebo = GL.GenBuffer();
|
||||
|
||||
GL.BindVertexArray(_vao);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * sizeof(float), vertices.ToArray(), BufferUsageHint.StaticDraw);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Count * sizeof(uint), indices.ToArray(), BufferUsageHint.StaticDraw);
|
||||
|
||||
var stride = 7 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
public void Draw(Shader shader, Matrix4 model, Matrix4 view, Matrix4 projection)
|
||||
{
|
||||
shader.SetMatrix4("model", model);
|
||||
// Set the matrices in the shader
|
||||
int modelLoc = GL.GetUniformLocation(shader.Handle, "model");
|
||||
int viewLoc = GL.GetUniformLocation(shader.Handle, "view");
|
||||
int projLoc = GL.GetUniformLocation(shader.Handle, "projection");
|
||||
|
||||
// Send the matrices to the shader
|
||||
GL.UniformMatrix4(modelLoc, false, ref model);
|
||||
GL.UniformMatrix4(viewLoc, false, ref view);
|
||||
GL.UniformMatrix4(projLoc, false, ref projection);
|
||||
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0);
|
||||
_topCircle.Draw(shader, view, projection, model);
|
||||
_bottomCircle.Draw(shader, view, projection, model);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteBuffer(_vbo);
|
||||
GL.DeleteBuffer(_ebo);
|
||||
GL.DeleteVertexArray(_vao);
|
||||
}
|
||||
}
|
||||
@ -1,163 +0,0 @@
|
||||
using LearnOpenTK.Common;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using StbImageSharp;
|
||||
|
||||
namespace TheLabs.Shapes;
|
||||
|
||||
public class TexturedCube
|
||||
{
|
||||
// Vertex Array Object, Vertex Buffer Object, Element Buffer Object
|
||||
private int _vao;
|
||||
private int _vbo;
|
||||
private int _ebo;
|
||||
private int _tb;
|
||||
private int _vertexCount;
|
||||
public Vector3 Position = Vector3.Zero;
|
||||
|
||||
private readonly float[] _cubevertices =
|
||||
{
|
||||
// Position // Color (RGBA)
|
||||
// Front face (z = +0.5)
|
||||
-0.5f, -0.5f, 0.5f, 0f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 1f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 0f, 1f, // Top-left
|
||||
|
||||
// Back face (z = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, -0.5f, 0f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 1f, 1f, // Top-left
|
||||
|
||||
// Left face (x = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 0f, 0f, // Bottom-left
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, // Bottom-right
|
||||
-0.5f, 0.5f, 0.5f, 1f, 1f, // Top-right
|
||||
-0.5f, 0.5f, -0.5f, 0f, 1f, // Top-left
|
||||
// Right face (x = +0.5)
|
||||
0.5f, -0.5f, -0.5f, 1f, 0f, // Bottom-left
|
||||
0.5f, -0.5f, 0.5f, 0f, 0f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 0f, 1f, // Top-right
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, // Top-left
|
||||
|
||||
// Top face (y = +0.5)
|
||||
-0.5f, 0.5f, -0.5f, 0f, 1f, // Bottom-left
|
||||
0.5f, 0.5f, -0.5f, 1f, 1f, // Bottom-right
|
||||
0.5f, 0.5f, 0.5f, 1f, 0f, // Top-right
|
||||
-0.5f, 0.5f, 0.5f, 0f, 0f, // Top-left
|
||||
|
||||
// Bottom face (y = -0.5)
|
||||
-0.5f, -0.5f, -0.5f, 1f, 1f, // Bottom-left
|
||||
0.5f, -0.5f, -0.5f, 0f, 1f, // Bottom-right
|
||||
0.5f, -0.5f, 0.5f, 0f, 0f, // Top-right
|
||||
-0.5f, -0.5f, 0.5f, 1f, 0f, // Top-left
|
||||
|
||||
|
||||
|
||||
};
|
||||
private readonly uint[] _cubeIndices =
|
||||
{
|
||||
// Front face
|
||||
0, 2, 1,
|
||||
0, 2, 3,
|
||||
// Back face
|
||||
4, 5, 6,
|
||||
4, 6, 7,
|
||||
// Left face
|
||||
8, 9, 10,
|
||||
8, 10, 11,
|
||||
// Right face
|
||||
12, 14, 13,
|
||||
12, 14, 15,
|
||||
// Top face
|
||||
16, 17, 18,
|
||||
16, 18, 19,
|
||||
// Bottom face
|
||||
20, 22, 21,
|
||||
20, 22, 23
|
||||
|
||||
};
|
||||
|
||||
public TexturedCube()
|
||||
{
|
||||
_tb = GL.GenTexture();
|
||||
GL.BindTexture(TextureTarget.Texture2D, _tb);
|
||||
StbImage.stbi_set_flip_vertically_on_load(1);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
// Load the image
|
||||
ImageResult image = ImageResult.FromStream(File.OpenRead("Textures/placeholder.png"), ColorComponents.RedGreenBlueAlpha);
|
||||
GL.TexImage2D(TextureTarget.Texture2D,0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
|
||||
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
||||
// The number of vertices to draw
|
||||
_vertexCount = _cubeIndices.Length;
|
||||
// The Vertex Array Object
|
||||
// This stores the confifuration of vertex atributes
|
||||
_vao = GL.GenVertexArray();
|
||||
// The Vertex Buffer Object
|
||||
// This stores the actual vertex data e.g. positions, colors, normals, texture coordinates
|
||||
_vbo = GL.GenBuffer();
|
||||
// The Element Buffer Object, stores the indices for indexed drawing
|
||||
_ebo = GL.GenBuffer();
|
||||
|
||||
// We bind the VAO
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); // Specifying the type of buffer
|
||||
// Uploading the vertex data to the GPU
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, _cubevertices.Length * sizeof(float), _cubevertices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// Now we set up the EBO
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo);
|
||||
// Uploading the index data to the GPU
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, _cubeIndices.Length * sizeof(uint), _cubeIndices, BufferUsageHint.StaticDraw);
|
||||
|
||||
// We tell opengl how to interpret the vertex data.
|
||||
// The Location 0 corresponds to the layout(location = 0) in the vertex shader
|
||||
// How many components (x, y, z) -> 3
|
||||
// Data type -> float
|
||||
// Not normalized
|
||||
// The Stride -> The total size of a vertex (in bytes)
|
||||
// The offset -> The position data starts at the beginning of the vertex data so 0
|
||||
var stride = 5 * sizeof(float);
|
||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
GL.BindVertexArray(0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void Draw(Shader shader, Matrix4 view, Matrix4 projection, float rotation)
|
||||
{
|
||||
// Create the model matrix with rotation and translation
|
||||
Matrix4 model = Matrix4.CreateRotationY(rotation) * Matrix4.CreateRotationX(rotation * 0.5f) * Matrix4.CreateTranslation(Position);
|
||||
|
||||
shader.Use();
|
||||
GL.Uniform1(GL.GetUniformLocation(shader.Handle, "uTexture"), 0);
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(shader.Handle, "model"), false, ref model);
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(shader.Handle, "view"), false, ref view);
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(shader.Handle, "projection"), false, ref projection);
|
||||
int texLoc = GL.GetUniformLocation(shader.Handle, "uTexture");
|
||||
GL.Uniform1(texLoc, 0);
|
||||
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, _tb);
|
||||
|
||||
GL.BindVertexArray(_vao);
|
||||
GL.DrawElements(PrimitiveType.Triangles, _vertexCount, DrawElementsType.UnsignedInt, 0);
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteBuffer(_vbo);
|
||||
GL.DeleteBuffer(_ebo);
|
||||
GL.DeleteVertexArray(_vao);
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
namespace TheLabs;
|
||||
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using StbImageSharp;
|
||||
using System.IO;
|
||||
|
||||
public class Texture : IDisposable
|
||||
{
|
||||
public readonly int Handle;
|
||||
|
||||
public Texture(string path)
|
||||
{
|
||||
// Generate the handle
|
||||
Handle = GL.GenTexture();
|
||||
|
||||
// Bind the texture so we can configure it
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||
|
||||
// --- Set texture parameters ---
|
||||
// Repeat the texture if UVs go outside [0, 1]
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||
|
||||
// Set filter for shrinking (mipmap) and stretching (linear)
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
|
||||
// --- Load and upload the image data ---
|
||||
StbImage.stbi_set_flip_vertically_on_load(1);
|
||||
|
||||
// Load the image from disk
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
ImageResult image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
|
||||
|
||||
// Upload data to the GPU
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
|
||||
image.Width, image.Height, 0,
|
||||
PixelFormat.Rgba, PixelType.UnsignedByte, image.Data);
|
||||
}
|
||||
|
||||
// Generate mipmaps for better quality shrinking
|
||||
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
||||
|
||||
// Unbind the texture
|
||||
GL.BindTexture(TextureTarget.Texture2D, 0);
|
||||
}
|
||||
|
||||
// A simple method to bind the texture to a specific unit
|
||||
public void Use(TextureUnit unit = TextureUnit.Texture0)
|
||||
{
|
||||
GL.ActiveTexture(unit);
|
||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteTexture(Handle);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB |
@ -9,7 +9,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK" Version="4.8.2" />
|
||||
<PackageReference Include="StbImageSharp" Version="2.30.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="OpenTK" Version="4.8.2" />
|
||||
<PackageReference Include="StbImageSharp" Version="2.30.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user