Updated Sphere generation
Some checks failed
Gitea Actions Demo / Scan the project (push) Failing after 7s

This commit is contained in:
Daniel 2024-01-02 14:17:50 +01:00
parent cf6fb35eb2
commit 8470304859
9 changed files with 126 additions and 181 deletions

View File

@ -1 +0,0 @@
global using Xunit;

View File

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nebulix\Nebulix.csproj" />
</ItemGroup>
</Project>

View File

@ -1,12 +0,0 @@
using Nebulix.InputSystem;
namespace Nebulix.Test
{
public class TestInputSystem
{
[Fact]
public void IfSingleKeyIsPressed_InputIsKeyPressedShouldReturnTrue()
{
}
}
}

View File

@ -0,0 +1,36 @@
using System.Numerics;
using Silk.NET.Maths;
namespace Nebulix
{
public static class ArrayExtensions
{
/// <summary>
/// Copies the given arrays to the start of this array. The order of <paramref name="arrays"/> is preserved
/// </summary>
/// <param name="arrays">The arrays copied into this array</param>
public static void CopyFrom(this Array array, params Array[] arrays)
{
int lastIndex = 0;
for (int i = 0; i < arrays.Length; i++)
{
arrays[i].CopyTo(array, lastIndex);
lastIndex += arrays[i].Length;
}
}
public static T[] ExtractComponents<T>(this Vector3D<T>[] array) where T : unmanaged, IFormattable, IEquatable<T>, IComparable<T>
{
T[] result = new T[array.Length * 3]; // * 3 cause a Vector3D has 3 components
int resultIdx = 0;
for (int i = 0; i < array.Length; i++)
{
result[resultIdx] = array[i].X;
result[resultIdx + 1] = array[i].Y;
result[resultIdx + 2] = array[i].Z;
}
return result;
}
}
}

View File

@ -5,19 +5,29 @@ namespace Nebulix.Rendering
public sealed class Mesh public sealed class Mesh
{ {
public float[] Vertices { get => vertices; set { vertices = value; regenerate = true; } } public float[] Vertices { get => vertices; set { vertices = value; regenerate = true; } }
public float[] Indices { get => indices; set { indices = value; regenerate = true; } } public nuint[] Indices { get => indices; set { indices = value; regenerate = true; } }
public float[] Normals { get => normals; }
private uint vao = 0, vbo = 0, ebo = 0; private uint vao = 0, vbo = 0, ebo = 0;
private bool regenerate = true; private bool regenerate = true;
private float[] vertices = []; private float[] vertices = [];
private float[] indices = []; private nuint[] indices = [];
private float[] normals = []; private float[] normals = [];
public void Clear()
{
vertices = [];
indices = [];
}
// getting called by "Engine" which currently is in other assembly, meaning I probably need to make this public public void CalculateNormals()
internal void Use(GL gl) {
throw new NotImplementedException();
}
// getting called by "Engine" which currently is in another assembly, meaning I probably need to make this public
// needs to be change for the real engine
public void Use(GL gl)
{ {
if (regenerate) Generate(gl); if (regenerate) Generate(gl);
@ -44,7 +54,7 @@ namespace Nebulix.Rendering
gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo);
gl.BufferData(BufferTargetARB.ArrayBuffer, (nuint)(data.Length * sizeof(float)), data, BufferUsageARB.StaticDraw); gl.BufferData(BufferTargetARB.ArrayBuffer, (nuint)(data.Length * sizeof(float)), data, BufferUsageARB.StaticDraw);
ReadOnlySpan<float> indicesData = new(indices); ReadOnlySpan<nuint> indicesData = new(indices);
gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, ebo); gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, ebo);
gl.BufferData(BufferTargetARB.ElementArrayBuffer, (nuint)(indicesData.Length * sizeof(uint)), indicesData, BufferUsageARB.StaticDraw); gl.BufferData(BufferTargetARB.ElementArrayBuffer, (nuint)(indicesData.Length * sizeof(uint)), indicesData, BufferUsageARB.StaticDraw);

View File

@ -1,19 +0,0 @@
namespace Engine_silk.NET
{
public static class ArrayExtensions
{
/// <summary>
/// Copies the given arrays to the start of this array. The order of <paramref name="arrays"/> is preserved
/// </summary>
/// <param name="arrays">The arrays copied into this array</param>
public static void CopyFrom(this Array array, params Array[] arrays)
{
int lastIndex = 0;
for (int i = 0; i < arrays.Length; i++)
{
arrays[i].CopyTo(array, lastIndex);
lastIndex += arrays[i].Length;
}
}
}
}

View File

@ -7,11 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine_silk.NET", "Engine_s
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebulix", "..\Nebulix\Nebulix.csproj", "{0DEED1E3-932E-47C3-AF2D-FCA74000908E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebulix", "..\Nebulix\Nebulix.csproj", "{0DEED1E3-932E-47C3-AF2D-FCA74000908E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nebulix.Test", "..\Nebulix.Test\Nebulix.Test.csproj", "{60B8C905-7AC4-45EE-BAB1-26D3F826124B}"
ProjectSection(ProjectDependencies) = postProject
{0DEED1E3-932E-47C3-AF2D-FCA74000908E} = {0DEED1E3-932E-47C3-AF2D-FCA74000908E}
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -26,10 +21,6 @@ Global
{0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Debug|Any CPU.Build.0 = Debug|Any CPU {0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Release|Any CPU.Build.0 = Release|Any CPU {0DEED1E3-932E-47C3-AF2D-FCA74000908E}.Release|Any CPU.Build.0 = Release|Any CPU
{60B8C905-7AC4-45EE-BAB1-26D3F826124B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60B8C905-7AC4-45EE-BAB1-26D3F826124B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B8C905-7AC4-45EE-BAB1-26D3F826124B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60B8C905-7AC4-45EE-BAB1-26D3F826124B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -31,7 +31,6 @@ public static class Program
private static uint _vao, _vbo, _ebo; private static uint _vao, _vbo, _ebo;
public static void Main(string[] args) public static void Main(string[] args)
{ {
WindowOptions options = WindowOptions.Default with WindowOptions options = WindowOptions.Default with
@ -158,9 +157,6 @@ public static class Program
_gl.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), (void*)(6 * sizeof(float))); _gl.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), (void*)(6 * sizeof(float)));
_texture = new Texture2D(_gl, "images/container.png", ImageFormat.RGBA); _texture = new Texture2D(_gl, "images/container.png", ImageFormat.RGBA);
Sphere s = new(_gl, 1, 10);
s.GetVertices();
} }
private static void OnUpdate(double deltaTime) private static void OnUpdate(double deltaTime)

View File

@ -1,123 +1,96 @@
using Nebulix.Rendering; using Nebulix;
using Nebulix.Rendering;
using Silk.NET.Maths; using Silk.NET.Maths;
using Silk.NET.OpenGL; using Silk.NET.OpenGL;
namespace Engine_silk.NET namespace Engine_silk.NET
{ {
// TODO: Think about just creating a cube with a certain resolution and then use this cube to generate the Sphere.
// also maybe make an ISphere and call this class "CubeSphere"? // also maybe make an ISphere and call this class "CubeSphere"?
public class Sphere(GL gl, float radius, uint resolution) public class Sphere(uint resolution)
{ {
private readonly Face[] sphereFaces = new Face[6];
public void CreateSphere() public void CreateSphere()
{ {
uint _vao, _vbo, _ebo; Vector3D<float>[] directions =
_vao = gl.CreateVertexArray();
gl.BindVertexArray(_vao);
ReadOnlySpan<float> verticesData = GetVertices();
_vbo = gl.GenBuffer();
gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
gl.BufferData(BufferTargetARB.ArrayBuffer, (nuint)(verticesData.Length * sizeof(float)), verticesData, BufferUsageARB.StaticDraw);
}
public void RenderSphere()
{ {
Vector3D<float>.UnitZ, -Vector3D<float>.UnitZ,
Vector3D<float>.UnitY, -Vector3D<float>.UnitY,
Vector3D<float>.UnitX, -Vector3D<float>.UnitX
};
} for (int i = 0; i < sphereFaces.Length; i++)
// TODO: make this private after testing
public ReadOnlySpan<float> GetVertices()
{ {
var front = GetSide(Vector3D<float>.UnitZ).Item1; // TODO: Refactor so that a face gets a mesh it can only change the contents of (should improve performance)
var back = GetSide(-Vector3D<float>.UnitZ).Item1; sphereFaces[i] = new Face(directions[i], resolution);
var up = GetSide(Vector3D<float>.UnitY).Item1;
var down = GetSide(-Vector3D<float>.UnitY).Item1;
var left = GetSide(Vector3D<float>.UnitX).Item1;
var right = GetSide(-Vector3D<float>.UnitX).Item1;
var finalArray = new float[front.Length + back.Length + up.Length + down.Length + left.Length + right.Length];
finalArray.CopyFrom(front, back, up, down, left, right);
return new ReadOnlySpan<float>(finalArray);
}
[Obsolete("Use Sphere.GetFace(Vector3D<float>) instead")]
private (float[], float[]) GetSide(Vector3D<float> localUp)
{
float stepSize = 2f / resolution;
List<float> vertices = new((int)(3 * resolution * resolution * 2)); // resolution * resolution == number of rows/columns; 3 * ... == each vertex has 3 positions; 2 * ... == vertex also needs normal
Vector3D<float> position = localUp;
for (uint y = 0; y <= resolution; y++)
{
for (uint x = 0; x <= resolution; x++)
{
uint i = x + y * resolution;
Vector2D<float> percent = new Vector2D<float>(x, y) / (resolution - 1);
//Vector3D<float> pointOnUnitCube = localUp + (percent.X - 0.5f) * stepSize *
if (localUp.X != 0)
{
position.Y = x * stepSize - 1;
position.Z = y * stepSize - 1;
}
else if (localUp.Y != 0)
{
position.X = x * stepSize - 1;
position.Z = y * stepSize - 1;
}
else if (localUp.Z != 0)
{
position.X = x * stepSize - 1;
position.Y = y * stepSize - 1;
}
vertices.AddRange([position.X, position.Y, position.Z, localUp.X, localUp.Y, localUp.Z]);
} }
} }
List<uint> indices = new((int)(resolution * resolution * 6)); // ... * 6 == one quad has 6 indices and we have resolution * resolution quads public void RenderSphere(GL gl) // Will not be needed
Console.WriteLine($"Indices size before Loop: {indices.Capacity}");
uint vi = 0; // vertexIndex
for (uint i = 0; i < vertices.Count; i += 6)
{ {
if ((vi + 1) % resolution == 0) // Use default shader etc. to render the sphere
for (int i = 0; i < 6; i++)
{ {
vi++; Mesh m = sphereFaces[i].GetMesh();
continue; m.Use(gl);
} }
indices.AddRange([vi, vi+1, vi+1+resolution,
vi, vi+1+resolution, vi+resolution]);
vi++;
}
Console.WriteLine($"Indices size after Loop: {indices.Count}");
return (vertices.ToArray(), []);
}
// https://youtu.be/QN39W020LqU?si=a66D1Lnic1vNaC6l&t=89
// https://github.com/SebLague/Procedural-Planets/blob/master/Procedural%20Planet%20E01/Assets/TerrainFace.cs
/// <param name="localUp">The unit vector in which the side should point. E.g. Vector.UnitX</param>
public Mesh GetFace(Vector3D<float> localUp)
{
// this all probably needs to be rewritten to use the "Face" record below and split the stuff better
return new Mesh();
} }
} }
internal record struct Face internal record struct Face
{ {
public Face() private readonly Vector3D<float> _localX;
{ private readonly Vector3D<float> _localY;
private readonly Vector3D<float> _localUp;
private readonly uint _resolution;
public Face(Vector3D<float> localUp, uint resolution)
{
_localX = new(localUp.Y, localUp.Z, localUp.X);
_localY = Vector3D.Cross(localUp, _localX);
_localUp = localUp;
_resolution = resolution;
}
internal Mesh GetMesh()
{
var vertices = new Vector3D<float>[_resolution * _resolution];
// _resolution - 1 because the vertices index starts at 0
// * 6 because each triangle needs 3 points and each small quad has 2 triangles 3*2 = 6
nuint[] indices = new nuint[(_resolution - 1) * (_resolution - 1) * 6];
int triangleIndex = 0;
uint i;
for (uint y = 0; y < _resolution; y++)
{
for (uint x = 0; x < _resolution; x++)
{
i = x + y * _resolution;
Vector2D<float> percent = new Vector2D<float>(x, y) / (_resolution - 1);
// place vertex on correct position of the plane to easily calculate indices
Vector3D<float> vertexPosition = _localUp + (percent.X - 0.5f) * 2 * _localX + (percent.Y - 0.5f) * 2 * _localY;
vertices[i] = Vector3D.Normalize(vertexPosition); // normalise vertex position to get it to be "on the sphere" and not "on the plane"
if (x != _resolution - 1 && y != _resolution - 1) // we didn't reach the bottom right point yet
{
indices[triangleIndex] = i;
indices[triangleIndex + 1] = i + _resolution + 1;
indices[triangleIndex + 2] = i + _resolution;
indices[triangleIndex + 3] = i;
indices[triangleIndex + 4] = i + 1;
indices[triangleIndex + 5] = i + _resolution + 1;
triangleIndex += 6; // doing it this way is faster than doing indices[triangleIndex++] (takes about double the time)
}
}
}
Mesh m = new Mesh();
m.Vertices = vertices.ExtractComponents();
m.Indices = indices;
return m;
} }
} }
} }