added code to generate a mesh to render; rendering not yet fully implemented
This commit is contained in:
@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp.Core", "EngineS
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp", "..\EngineSharp\EngineSharp.csproj", "{1D984FEE-1A61-4E35-9D00-0264D92A31F4}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp", "..\EngineSharp\EngineSharp.csproj", "{1D984FEE-1A61-4E35-9D00-0264D92A31F4}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineSharp.Extensions", "..\EngineSharp.Extensions\EngineSharp.Extensions.csproj", "{61306157-AA01-4899-8F80-50D6E76FEBB0}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -18,5 +20,9 @@ Global
|
|||||||
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1D984FEE-1A61-4E35-9D00-0264D92A31F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{61306157-AA01-4899-8F80-50D6E76FEBB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{61306157-AA01-4899-8F80-50D6E76FEBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{61306157-AA01-4899-8F80-50D6E76FEBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{61306157-AA01-4899-8F80-50D6E76FEBB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
|||||||
@ -4,9 +4,9 @@ namespace EngineSharp.Core.Rendering;
|
|||||||
|
|
||||||
public class Mesh
|
public class Mesh
|
||||||
{
|
{
|
||||||
public Vector3D<float>[] Vertices { get; set; }
|
public Vector3D<float>[] Vertices { get; set; } = [];
|
||||||
public Vector3D<float>[] Normals { get; set; }
|
public Vector3D<float>[] Normals { get; set; } = [];
|
||||||
public uint[] Indices { get; set; }
|
public uint[] Indices { get; set; } = [];
|
||||||
|
|
||||||
// TODO: Add uv textures but make them nullable; MeshRenderer should then only send uv data to shader if specified
|
// TODO: Add uv textures but make them nullable; MeshRenderer should then only send uv data to shader if specified
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ namespace EngineSharp.Core.Rendering;
|
|||||||
|
|
||||||
public class MeshRenderer : RenderComponent
|
public class MeshRenderer : RenderComponent
|
||||||
{
|
{
|
||||||
public Mesh Mesh { get; set; }
|
public required Mesh Mesh { get; set; }
|
||||||
|
|
||||||
private uint vao, vbo, ebo;
|
private uint vao, vbo, ebo;
|
||||||
|
|
||||||
|
|||||||
13
src/EngineSharp.Extensions/EngineSharp.Extensions.csproj
Normal file
13
src/EngineSharp.Extensions/EngineSharp.Extensions.csproj
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Silk.NET.Maths" Version="2.22.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
55
src/EngineSharp.Extensions/VectorExtensions.cs
Normal file
55
src/EngineSharp.Extensions/VectorExtensions.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using Silk.NET.Maths;
|
||||||
|
|
||||||
|
namespace EngineSharp.Extensions;
|
||||||
|
|
||||||
|
public static class VectorExtensions
|
||||||
|
{
|
||||||
|
extension<T>(Vector3D<T>)
|
||||||
|
where T : unmanaged, IFormattable, IEquatable<T>, IComparable<T>
|
||||||
|
{
|
||||||
|
public static Vector3D<float> Slerp(Vector3D<float> start, Vector3D<float> end, float percent)
|
||||||
|
{
|
||||||
|
// the cosine of the angle between 2 vectors.
|
||||||
|
var dot = Vector3D.Dot(start, end);
|
||||||
|
|
||||||
|
// Clamp it to be in the range of Acos()
|
||||||
|
// This may be unnecessary, but floating point precision can be a fickle mistress.
|
||||||
|
Math.Clamp(dot, -1.0f, 1.0f);
|
||||||
|
|
||||||
|
// Acos(dot) returns the angle between start and end,
|
||||||
|
// And multiplying that by percent returns the angle between start and the final result.
|
||||||
|
var theta = (float)Math.Acos(dot) * percent;
|
||||||
|
var relativeVec = end - start * dot;
|
||||||
|
relativeVec = Vector3D.Normalize(relativeVec);
|
||||||
|
|
||||||
|
// Orthonormal basis
|
||||||
|
// The final result.
|
||||||
|
return ((start * (float)Math.Cos(theta)) + (relativeVec * (float)Math.Sin(theta)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(Vector3D<float> vector)
|
||||||
|
// where T : unmanaged, IFormattable, IEquatable<T>, IComparable<T>
|
||||||
|
{
|
||||||
|
public Vector3D<float> Slerp(Vector3D<float> end, float percent)
|
||||||
|
{
|
||||||
|
// the cosine of the angle between 2 vectors.
|
||||||
|
var dot = Vector3D.Dot(vector, end);
|
||||||
|
|
||||||
|
// Clamp it to be in the range of Acos()
|
||||||
|
// This may be unnecessary, but floating point precision can be a fickle mistress.
|
||||||
|
// Math.Clamp(dot, -1.0f, 1.0f);
|
||||||
|
|
||||||
|
|
||||||
|
// Acos(dot) returns the angle between start and end,
|
||||||
|
// And multiplying that by percent returns the angle between start and the final result.
|
||||||
|
var theta = Math.Acos(dot) * percent;
|
||||||
|
var relativeVec = end - vector * dot;
|
||||||
|
relativeVec = Vector3D.Normalize(relativeVec);
|
||||||
|
|
||||||
|
// Orthonormal basis
|
||||||
|
// The final result.
|
||||||
|
return ((vector * (float)Math.Cos(theta)) + (relativeVec * (float)Math.Sin(theta)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,16 +2,18 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\EngineSharp.Core\EngineSharp.Core\EngineSharp.Core.csproj" />
|
<ProjectReference Include="..\EngineSharp.Core\EngineSharp.Core\EngineSharp.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\EngineSharp.Extensions\EngineSharp.Extensions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Silk.NET.Maths" Version="2.22.0" />
|
||||||
<PackageReference Include="Silk.NET.Windowing" Version="2.22.0" />
|
<PackageReference Include="Silk.NET.Windowing" Version="2.22.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
182
src/EngineSharp/IcoSphere.cs
Normal file
182
src/EngineSharp/IcoSphere.cs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
using EngineSharp.Core.Rendering;
|
||||||
|
using EngineSharp.Extensions;
|
||||||
|
using Silk.NET.Maths;
|
||||||
|
|
||||||
|
namespace Engine_silk.NET;
|
||||||
|
|
||||||
|
public class IcoSphere
|
||||||
|
{
|
||||||
|
private readonly IcoSphereGenerator _generator = new();
|
||||||
|
|
||||||
|
public required int Resolution
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), value,
|
||||||
|
"Resolution must be greater than 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mesh CreateSphere()
|
||||||
|
{
|
||||||
|
var mesh = new Mesh();
|
||||||
|
_generator.Generate(Resolution);
|
||||||
|
|
||||||
|
mesh.Vertices = _generator.Vertices;
|
||||||
|
mesh.Indices = Array.ConvertAll(_generator.Triangles, x => (uint)x);
|
||||||
|
mesh.CalculateNormals();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thank you Sebastian Lague
|
||||||
|
public class IcoSphereGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
public Vector3D<float>[] Vertices => vertices?.Items ?? [];
|
||||||
|
public int[] Triangles => triangles?.Items ?? [];
|
||||||
|
|
||||||
|
// Internal:
|
||||||
|
FixedSizeList<Vector3D<float>>? vertices;
|
||||||
|
FixedSizeList<int>? triangles;
|
||||||
|
int numDivisions;
|
||||||
|
int numVertsPerFace;
|
||||||
|
|
||||||
|
// Indices of the vertex pairs that make up each of the initial 12 edges
|
||||||
|
static readonly int[] VertexPairs = [0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 2, 3, 3, 4, 4, 1, 5, 1, 5, 2, 5, 3, 5, 4];
|
||||||
|
// Indices of the edge triplets that make up the initial 8 faces
|
||||||
|
static readonly int[] EdgeTriplets = [0, 1, 4, 1, 2, 5, 2, 3, 6, 3, 0, 7, 8, 9, 4, 9, 10, 5, 10, 11, 6, 11, 8, 7];
|
||||||
|
// The six initial vertices up left back right forward down
|
||||||
|
static readonly Vector3D<float>[] BaseVertices = [Vector3D<float>.UnitY, -Vector3D<float>.UnitX, -Vector3D<float>.UnitZ, Vector3D<float>.UnitX, Vector3D<float>.UnitZ, -Vector3D<float>.UnitY];
|
||||||
|
|
||||||
|
public void Generate (int resolution) {
|
||||||
|
numDivisions = Math.Max (0, resolution);
|
||||||
|
numVertsPerFace = ((numDivisions + 3) * (numDivisions + 3) - (numDivisions + 3)) / 2;
|
||||||
|
int numVerts = numVertsPerFace * 8 - (numDivisions + 2) * 12 + 6;
|
||||||
|
int numTrisPerFace = (numDivisions + 1) * (numDivisions + 1);
|
||||||
|
|
||||||
|
vertices = new FixedSizeList<Vector3D<float>> (numVerts);
|
||||||
|
triangles = new FixedSizeList<int> (numTrisPerFace * 8 * 3);
|
||||||
|
|
||||||
|
vertices.AddRange (BaseVertices);
|
||||||
|
|
||||||
|
// Create 12 edges, with n vertices added along them (n = numDivisions)
|
||||||
|
var edges = new Edge[12];
|
||||||
|
for (var i = 0; i < VertexPairs.Length; i += 2) {
|
||||||
|
var startVertex = vertices.Items[VertexPairs[i]];
|
||||||
|
var endVertex = vertices.Items[VertexPairs[i + 1]];
|
||||||
|
|
||||||
|
var edgeVertexIndices = new int[numDivisions + 2];
|
||||||
|
edgeVertexIndices[0] = VertexPairs[i];
|
||||||
|
|
||||||
|
// Add vertices along edge
|
||||||
|
for (var divisionIndex = 0; divisionIndex < numDivisions; divisionIndex++) {
|
||||||
|
var t = (divisionIndex + 1f) / (numDivisions + 1f);
|
||||||
|
edgeVertexIndices[divisionIndex + 1] = vertices.NextIndex;
|
||||||
|
vertices.Add (startVertex.Slerp(endVertex, t));
|
||||||
|
}
|
||||||
|
edgeVertexIndices[numDivisions + 1] = VertexPairs[i + 1];
|
||||||
|
var edgeIndex = i / 2;
|
||||||
|
edges[edgeIndex] = new Edge (edgeVertexIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create faces
|
||||||
|
for (var i = 0; i < EdgeTriplets.Length; i += 3) {
|
||||||
|
var faceIndex = i / 3;
|
||||||
|
var reverse = faceIndex >= 4;
|
||||||
|
CreateFace (edges[EdgeTriplets[i]], edges[EdgeTriplets[i + 1]], edges[EdgeTriplets[i + 2]], reverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateFace (Edge sideA, Edge sideB, Edge bottom, bool reverse)
|
||||||
|
{
|
||||||
|
if (vertices is null) throw new ArgumentException("Vertices cannot be null", nameof(vertices));
|
||||||
|
if (triangles is null) throw new ArgumentException("triangles cannot be null", nameof(triangles));
|
||||||
|
|
||||||
|
var numPointsInEdge = sideA.VertexIndices.Length;
|
||||||
|
var vertexMap = new FixedSizeList<int> (numVertsPerFace);
|
||||||
|
vertexMap.Add (sideA.VertexIndices[0]); // top of triangle
|
||||||
|
|
||||||
|
for (var i = 1; i < numPointsInEdge - 1; i++) {
|
||||||
|
// Side A vertex
|
||||||
|
vertexMap.Add (sideA.VertexIndices[i]);
|
||||||
|
|
||||||
|
// Add vertices between sideA and sideB
|
||||||
|
var sideAVertex = vertices.Items[sideA.VertexIndices[i]];
|
||||||
|
var sideBVertex = vertices.Items[sideB.VertexIndices[i]];
|
||||||
|
var numInnerPoints = i - 1;
|
||||||
|
for (var j = 0; j < numInnerPoints; j++) {
|
||||||
|
var t = (j + 1f) / (numInnerPoints + 1f);
|
||||||
|
vertexMap.Add (vertices.NextIndex);
|
||||||
|
vertices.Add (sideAVertex.Slerp(sideBVertex, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Side B vertex
|
||||||
|
vertexMap.Add (sideB.VertexIndices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add bottom edge vertices
|
||||||
|
for (var i = 0; i < numPointsInEdge; i++) {
|
||||||
|
vertexMap.Add (bottom.VertexIndices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulate
|
||||||
|
var numRows = numDivisions + 1;
|
||||||
|
for (var row = 0; row < numRows; row++) {
|
||||||
|
// vertices down left edge follow quadratic sequence: 0, 1, 3, 6, 10, 15...
|
||||||
|
// the nth term can be calculated with: (n^2 - n)/2
|
||||||
|
var topVertex = ((row + 1) * (row + 1) - row - 1) / 2;
|
||||||
|
var bottomVertex = ((row + 2) * (row + 2) - row - 2) / 2;
|
||||||
|
|
||||||
|
var numTrianglesInRow = 1 + 2 * row;
|
||||||
|
for (var column = 0; column < numTrianglesInRow; column++) {
|
||||||
|
int v0, v1, v2;
|
||||||
|
|
||||||
|
v0 = topVertex;
|
||||||
|
if (column % 2 == 0) {
|
||||||
|
v1 = bottomVertex + 1;
|
||||||
|
v2 = bottomVertex;
|
||||||
|
topVertex++;
|
||||||
|
bottomVertex++;
|
||||||
|
} else {
|
||||||
|
v1 = bottomVertex;
|
||||||
|
v2 = topVertex - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
triangles.Add (vertexMap.Items[v0]);
|
||||||
|
triangles.Add (vertexMap.Items[(reverse) ? v2 : v1]);
|
||||||
|
triangles.Add (vertexMap.Items[(reverse) ? v1 : v2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience classes:
|
||||||
|
|
||||||
|
private record Edge(int[] VertexIndices);
|
||||||
|
|
||||||
|
private class FixedSizeList<T>(int size)
|
||||||
|
{
|
||||||
|
public readonly T[] Items = new T[size];
|
||||||
|
public int NextIndex;
|
||||||
|
|
||||||
|
public void Add (T item) {
|
||||||
|
Items[NextIndex] = item;
|
||||||
|
NextIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRange (IEnumerable<T> items) {
|
||||||
|
foreach (var item in items) {
|
||||||
|
Add (item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using EngineSharp.Core;
|
using Engine_silk.NET;
|
||||||
|
using EngineSharp.Core;
|
||||||
|
using EngineSharp.Core.Rendering;
|
||||||
using Silk.NET.Maths;
|
using Silk.NET.Maths;
|
||||||
using Silk.NET.Windowing;
|
using Silk.NET.Windowing;
|
||||||
using GraphicsAPI = EngineSharp.Core.GraphicsAPI;
|
using GraphicsAPI = EngineSharp.Core.GraphicsAPI;
|
||||||
@ -16,6 +18,21 @@ static class Program
|
|||||||
};
|
};
|
||||||
|
|
||||||
var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options);
|
var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options);
|
||||||
|
var mainScene = engine.CreateScene();
|
||||||
|
var cube = mainScene.CreateEntity("cube");
|
||||||
|
var sphereGenerator = new IcoSphere
|
||||||
|
{
|
||||||
|
Resolution = 10
|
||||||
|
};
|
||||||
|
var cubeMeshRenderer = new MeshRenderer
|
||||||
|
{
|
||||||
|
Mesh = sphereGenerator.CreateSphere(),
|
||||||
|
};
|
||||||
|
|
||||||
|
cube.AddComponent(cubeMeshRenderer);
|
||||||
|
|
||||||
|
// TODO: ensure that model matrix etc. will be set correctly on rendering, so that the icosphere can actually be rendered
|
||||||
|
|
||||||
engine.Start();
|
engine.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user