diff --git a/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj b/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj index b218602..6b70cc0 100644 --- a/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj +++ b/TheRepo/DoWhateverYouWant/DoWhateverYouWant.csproj @@ -8,6 +8,7 @@ + diff --git a/TheRepo/TheLabs/ModelLoader.cs b/TheRepo/TheLabs/ModelLoader.cs new file mode 100644 index 0000000..9e1faab --- /dev/null +++ b/TheRepo/TheLabs/ModelLoader.cs @@ -0,0 +1,77 @@ +namespace TheLabs; +using System; +using System.Collections.Generic; +using System.IO; +using Assimp; // The library we just installed +using OpenTK.Mathematics; + +public static class ModelLoader +{ + public static Mesh LoadMesh(string path) + { + var importer = new AssimpContext(); + + var scene = importer.ImportFile(path, PostProcessSteps.Triangulate | PostProcessSteps.JoinIdenticalVertices); + + if (scene == null || scene.SceneFlags.HasFlag(SceneFlags.Incomplete) || scene.RootNode == null) + { + throw new Exception("Error loading model: " + path); + } + + // --- NEW LOGIC: Combine all meshes into one --- + List allVertices = new List(); + List allIndices = new List(); + + // We need to track how many vertices we've added so far + // so the indices point to the correct new location + uint indexOffset = 0; + + // Loop through EVERY mesh found in the file (Legs, Torso, Head, etc.) + foreach (var mesh in scene.Meshes) + { + // 1. Process Vertices for this specific part + for (int i = 0; i < mesh.VertexCount; i++) + { + // Position (x, y, z) + var pos = mesh.Vertices[i]; + allVertices.Add(pos.X); + allVertices.Add(pos.Y); + allVertices.Add(pos.Z); + + // Texture Coordinates (u, v) + if (mesh.HasTextureCoords(0)) + { + var uv = mesh.TextureCoordinateChannels[0][i]; + allVertices.Add(uv.X); + allVertices.Add(uv.Y); + } + else + { + allVertices.Add(0.0f); + allVertices.Add(0.0f); + } + } + + // 2. Process Indices for this specific part + for (int i = 0; i < mesh.FaceCount; i++) + { + var face = mesh.Faces[i]; + for (int j = 0; j < face.IndexCount; j++) + { + // CRITICAL: We add the offset to the index! + // If the leg had 100 vertices, the first vertex of the torso + // needs to be index 100, not 0. + allIndices.Add((uint)(face.Indices[j] + indexOffset)); + } + } + + // Increase the offset by the number of vertices we just added + indexOffset += (uint)mesh.VertexCount; + } + + // Return one giant Mesh containing the whole character + return new Mesh(allVertices.ToArray(), allIndices.ToArray(), Mesh.VertexLayout.PosTex); + } + + +} \ No newline at end of file diff --git a/TheRepo/TheLabs/MyExampleWindow.cs b/TheRepo/TheLabs/MyExampleWindow.cs index ff4a03d..a81e44a 100644 --- a/TheRepo/TheLabs/MyExampleWindow.cs +++ b/TheRepo/TheLabs/MyExampleWindow.cs @@ -1,4 +1,5 @@ -using OpenTK.Graphics.OpenGL4; +using Assimp; +using OpenTK.Graphics.OpenGL4; using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.Desktop; @@ -15,23 +16,21 @@ 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 SceneNode _rootNode; + private SceneNode _characterNode; + private SceneNode _buildingNode; private Shader _shader; private Camera _camera; // - Resources that only need to be created once - private Mesh _cubeMesh; - private Mesh _circleMesh; - private Mesh _cylinderMesh; - private Texture _cubeTexture; - private Texture _cubeTexture2; - private float _rotation; + private Mesh _exampleObject; + private Mesh _buildingObject; + private Texture _texture; + private RenderObject _render; + private RenderObject _buildingRender; + float _rotation = 0.0f; // -- Scene Objects - private RenderObject _cubeObject; - private RenderObject _cubeObject2; - private RenderObject _circleObject; - private RenderObject _cylinderObject; - private Bus _bus; private Vector3 _cameraPosition = new Vector3(0.0f, 0.0f, -3.0f); private Vector3 _cameraFront = new Vector3(0.0f, 0.0f, -1.0f); @@ -62,21 +61,32 @@ namespace TheLabs //GL.Disable(EnableCap.CullFace); //GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); - // Set The background color to a nice blue. GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f); GL.Enable(EnableCap.DepthTest); _camera = new Camera(_cameraPosition, _cameraFront, _cameraUp); + _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); + _texture = new Texture("Textures/texture-a.png"); + + _rootNode = new SceneNode(); + _exampleObject = ModelLoader.LoadMesh("Objects/cat.obj"); + _buildingObject = ModelLoader.LoadMesh("Objects/building1.obj"); + _render = new RenderObject(_exampleObject, _shader, _texture); + _buildingRender = new RenderObject(_buildingObject, _shader, _texture); + + _characterNode = new SceneNode(_render); + _characterNode.Position = new Vector3(0.0f, -1.0f, 0.0f); + _rootNode.AddChild(_characterNode); + + _buildingNode = new SceneNode(_buildingRender); + _buildingNode.Position = new Vector3(2.0f, -1.0f, 0.0f); + _rootNode.AddChild(_buildingNode); + + + CursorState = CursorState.Grabbed; - - _shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag"); - _cubeTexture = new Texture("Textures/placeholder.png"); - _cubeTexture2 = new Texture("Textures/placeholder.png"); - - _bus = new Bus(_cubeTexture, _shader); - - - + + } @@ -105,9 +115,8 @@ namespace TheLabs 0.1f, 100.0f ); - // --- Draw Scene Objects --- - _bus.DrawBus(view, projection); + _rootNode.Draw(view, projection, Matrix4.Identity); SwapBuffers(); } diff --git a/TheRepo/TheLabs/RenderObject.cs b/TheRepo/TheLabs/RenderObject.cs index 21ae1fc..05fa7fb 100644 --- a/TheRepo/TheLabs/RenderObject.cs +++ b/TheRepo/TheLabs/RenderObject.cs @@ -28,7 +28,7 @@ public class RenderObject Shader.Use(); Shader.SetInt("uTexture", 0); // 2. Bind Texture - Texture.Use(); // Assumes you have a Texture class + Texture.Use(); // 3. Calculate Model Matrix Matrix4 model = Matrix4.CreateScale(Scale) * Matrix4.CreateFromQuaternion(Rotation) * Matrix4.CreateTranslation(Position); int modelLoc = GL.GetUniformLocation(Shader.Handle, "model"); diff --git a/TheRepo/TheLabs/SceneNode.cs b/TheRepo/TheLabs/SceneNode.cs new file mode 100644 index 0000000..3de370f --- /dev/null +++ b/TheRepo/TheLabs/SceneNode.cs @@ -0,0 +1,45 @@ +using OpenTK.Mathematics; +using System.Collections.Generic; + +namespace TheLabs; + +public class SceneNode +{ + public RenderObject? RenderObject; + + public List Children = new List(); + + public Vector3 Position = Vector3.Zero; + public Quaternion Rotation = Quaternion.Identity; + public Vector3 Scale = Vector3.One; + + public SceneNode(RenderObject? renderObject = null) + { + RenderObject = renderObject; + } + + public void AddChild(SceneNode child) + { + Children.Add(child); + } + + public void Draw(Matrix4 view, Matrix4 projection, Matrix4 parentTransform) + { + Matrix4 localTransform = Matrix4.CreateScale(Scale) * Matrix4.CreateFromQuaternion(Rotation) * Matrix4.CreateTranslation(Position); + Matrix4 globalTransform = localTransform * parentTransform; + + if (RenderObject != null) + { + RenderObject.Position = Vector3.Zero; + RenderObject.Rotation = Quaternion.Identity; + RenderObject.Scale = Vector3.One; + + RenderObject.Draw(view, projection); + } + + foreach (var child in Children) + { + child.Draw(view, projection, globalTransform); + } + } +} \ No newline at end of file diff --git a/TheRepo/TheLabs/Shapes/Bus.cs b/TheRepo/TheLabs/Shapes/Bus.cs index 9cc40f5..1e35e71 100644 --- a/TheRepo/TheLabs/Shapes/Bus.cs +++ b/TheRepo/TheLabs/Shapes/Bus.cs @@ -5,85 +5,70 @@ namespace TheLabs.Shapes; public class Bus { + public SceneNode MainNode; + private Mesh _wheelMesh; - private RenderObject _wheelObject; + private Mesh _bodyMesh; + private Mesh _roofMesh; - private Texture _wheelTexture; - private Shader _wheelShader; - private Texture _bodyTexture; - public Bus(Texture texture, Shader shader) + public Bus(Texture wheelTexture,Texture bodyTexture, Shader shader) { - _wheelTexture = new Texture("Textures/rubber.jpg"); - _wheelShader = shader; - - _bodyTexture = new Texture("Textures/rust.png"); - } - - public void DrawWheel(Matrix4 view, Matrix4 projection, Vector3 position) - { - Matrix4 localTransform = Matrix4.Identity; - //Matrix4 comboTransform = localTransform * _wheelObject.Transform; - _wheelMesh = ShapeFactory.CreateTexturedCylinder(); - _wheelObject = new RenderObject(_wheelMesh, _wheelShader, _wheelTexture); + _bodyMesh = ShapeFactory.CreateTexturedCube(); + _roofMesh = ShapeFactory.CreateTexturedCylinder(); - // Front Left Wheel - _wheelObject.Position = new Vector3(position); - _wheelObject.Scale = new Vector3(0.3f, 0.1f, 0.2f); - _wheelObject.Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, MathHelper.DegreesToRadians(90.0f)); + MainNode = new SceneNode(); - _wheelObject.Draw(view, projection); + // Building the body + var bodyObj = new RenderObject(_bodyMesh, shader, bodyTexture); + var bodyNode = new SceneNode(bodyObj); + bodyNode.Scale = new Vector3(1.5f, 0.5f, 0.5f); + bodyNode.Scale = new Vector3(0.0f, 0.0f, 0.0f); + MainNode.AddChild(bodyNode); - } - - public void drawBody(Matrix4 view, Matrix4 projection) - { - Mesh bodyMesh = ShapeFactory.CreateTexturedCube(); - RenderObject bodyObject = new RenderObject(bodyMesh, _wheelShader, _bodyTexture); + // Building the body + var roofObj = new RenderObject(_roofMesh, shader, bodyTexture); + var roofNode = new SceneNode(roofObj); + roofNode.Position = new Vector3(0.0f, 0.25f, 0.0f); + roofNode.Scale = new Vector3(0.5f, 1.5f, .25f); + roofNode.Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(90.0f)); + roofNode.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitX, MathHelper.DegreesToRadians(90.0f)); + MainNode.AddChild(roofNode); - bodyObject.Position = new Vector3(0.0f, 0.0f, 0.0f); - bodyObject.Scale = new Vector3(1.5f, 0.5f, 0.5f); + // Building the wheels - bodyObject.Draw(view, projection); - } - - private void drawRoof(Matrix4 view, Matrix4 projection) - { - Mesh roofMesh = ShapeFactory.CreateTexturedCylinder(); - RenderObject roofObject = new RenderObject(roofMesh, _wheelShader, _bodyTexture); - - roofObject.Position = new Vector3(0.0f, 0.25f, 0.0f); - roofObject.Scale = new Vector3(0.5f, 1.5f, .25f); - - roofObject.Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(90.0f)); - roofObject.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitX, MathHelper.DegreesToRadians(90.0f)); - - - - roofObject.Draw(view, projection); - } - - public void DrawBus(Matrix4 view, Matrix4 projection) - { - // Drawing 4 wheels float xOffset = 0.4f; // Reduced to bring wheels closer to the body float zOffset = 0.28f; // Reduced to bring front and back wheels closer float yOffset = -0.3f; // Adjusted to ensure wheels touch the body - - // Drawing 4 wheels - for (int i = 0; i < 4; i++) - { - float xPosition = (i % 2 == 0) ? -xOffset : xOffset; // Left or Right - float zPosition = (i < 2) ? zOffset : -zOffset; // Front or Back - DrawWheel(view, projection, new Vector3(xPosition, yOffset, zPosition)); - } + Vector3[] wheelPositions = + { + new Vector3(-xOffset, yOffset, zOffset), // Front Left + new Vector3(xOffset, yOffset, zOffset), // Front Right + new Vector3(-xOffset, yOffset, -zOffset), // Back Left + new Vector3(xOffset, yOffset, -zOffset) // Back Right + }; - // Drawing the body - drawBody(view, projection); - // Drawing the roof - drawRoof(view, projection); + foreach (var pos in wheelPositions) + { + var wheelObj = new RenderObject(_wheelMesh, shader, wheelTexture); + var wheelNode = new SceneNode(wheelObj); + + wheelNode.Position = pos; // Relative to the MainNode! + wheelNode.Scale = new Vector3(0.3f, 0.1f, 0.2f); + // Rotate cylinder to act like a wheel + wheelNode.Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, MathHelper.DegreesToRadians(90.0f)); + + MainNode.AddChild(wheelNode); + } + + } + + + public void Draw(Matrix4 view, Matrix4 projection) + { + MainNode.Draw(view, projection, Matrix4.Identity); } diff --git a/TheRepo/TheLabs/Texture.cs b/TheRepo/TheLabs/Texture.cs index bd171ff..0c64ac3 100644 --- a/TheRepo/TheLabs/Texture.cs +++ b/TheRepo/TheLabs/Texture.cs @@ -18,7 +18,6 @@ public class Texture : IDisposable 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); diff --git a/TheRepo/TheLabs/TheLabs.csproj b/TheRepo/TheLabs/TheLabs.csproj index b218602..6b70cc0 100644 --- a/TheRepo/TheLabs/TheLabs.csproj +++ b/TheRepo/TheLabs/TheLabs.csproj @@ -8,6 +8,7 @@ + diff --git a/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj b/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj index 73786e6..7e03be5 100644 --- a/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj +++ b/TheRepo/VectorAndMatrixTests/VectorAndMatrixTests.csproj @@ -10,6 +10,7 @@ +