-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve utils for mesh generation and processing #179
Comments
Since .NET 6 we have the static class
Here is an example: using System.Numerics;
using System.Runtime.InteropServices;
using Raylib_cs;
Raylib.InitWindow(800, 450, "Test");
var mesh = new Mesh() { triangleCount = 1, vertexCount = 3 };
Span<float> vertices, normals, texcoords;
unsafe
{
mesh.vertices = (float*)NativeMemory.AllocZeroed(9, sizeof(float));
mesh.normals = (float*)NativeMemory.AllocZeroed(9, sizeof(float));
mesh.texcoords = (float*)NativeMemory.AllocZeroed(6, sizeof(float));
vertices = new Span<float>(mesh.vertices, 9);
normals = new Span<float>(mesh.normals, 9);
texcoords = new Span<float>(mesh.texcoords, 6);
}
vertices[3] = 1;
vertices[5] = 2;
vertices[6] = 2;
normals[1] = 1;
normals[4] = 1;
normals[7] = 1;
texcoords[2] = .5f;
texcoords[3] = 1;
texcoords[4] = 1;
Raylib.UploadMesh(ref mesh, false);
var model = Raylib.LoadModelFromMesh(mesh);
var camera = new Camera3D() { position = new(5, 5, 5), up = new() { Y = 1 }, fovy = 45 };
while (!Raylib.WindowShouldClose())
{
Raylib.UpdateCamera(ref camera, CameraMode.CAMERA_ORBITAL);
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.WHITE);
Raylib.BeginMode3D(camera);
Raylib.DrawModel(model, Vector3.Zero, 1, Color.RED);
Raylib.DrawGrid(10, 1);
Raylib.EndMode3D();
Raylib.EndDrawing();
}
Raylib.UnloadModel(model);
Raylib.CloseWindow(); We don't need to call |
Taking @JupiterRider's idea you could add a constructor that looks something like this: /// <summary>Generate mesh</summary>
public Mesh(int triangleCount, int vertexCount)
{
this.triangleCount = triangleCount;
this.vertexCount = vertexCount;
vertices = (float*)NativeMemory.AllocZeroed((nuint)(3 * this.vertexCount), sizeof(float));
} Also add extention method's to utils: /// <summary>Access mesh triangles</summary>
public static Span<T> TrianglesAs<T>(this Mesh mesh) where T : unmanaged
{
return new(mesh.vertices, 3 * mesh.vertexCount * sizeof(float) / sizeof(T));
} Utilizing these would look something like this: using Raylib_cs;
using System.Numerics;
Raylib.InitWindow(1280, 960, "Triangle");
Raylib.SetTargetFPS(60);
Camera3D camera = new(Vector3.One, Vector3.Zero, Vector3.UnitY, 90f, CameraProjection.CAMERA_PERSPECTIVE);
Mesh mesh = new(1, 3);
Span<Vector3> vertices = mesh.TrianglesAs<Vector3>();
vertices[0] = Vector3.UnitZ;
vertices[1] = Vector3.UnitX;
vertices[2] = Vector3.Zero;
Raylib.UploadMesh(ref mesh, false);
Model model = Raylib.LoadModelFromMesh(mesh);
while (!Raylib.WindowShouldClose())
{
Raylib.UpdateCamera(ref camera, CameraMode.CAMERA_ORBITAL);
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.BLACK);
Raylib.BeginMode3D(camera);
Raylib.DrawModel(model, Vector3.Zero, 1f, Color.PINK);
Raylib.DrawGrid(2, 1);
Raylib.EndMode3D();
Raylib.EndDrawing();
}
Raylib.UnloadModel(model);
Raylib.CloseWindow(); |
@JupiterRider Unsure if it is better to use MemAlloc, MemFree via Raylib instead. Raylib can be recompiled to change how allocation works internally so using the built in versions can make sure alloc/free calls work with each other correctly. @n77y Interesting idea! How could this be applied to other mesh fields? |
@chrisdill Doesn't matter. They both use calloc in stdlib.h anyways: https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeMemory.Unix.cs,149 I don't think its a good Idea for maintainability compiling our own version of raylib or writing to many layers of abstraction. Raylib-cs should better be a binding only. For everything beyond we could create an additional project called Raylib-cs.Extras or something. |
@chrisdill, @JupiterRider, would it be possible and sensible to add an syntax suger allocator like this? /// <summary>C++ style memory allocator</summary>
public static T* New<T>(int count) where T : unmanaged
{
return (T*)MemAlloc(count * sizeof(T));
} or this: /// <summary>C++ style memory allocator</summary>
public static T* New<T>(int count) where T : unmanaged
{
return (T*)NativeMemory.AllocZeroed((nuint)count, (nuint)sizeof(T));
} The method name can be changed if its not disirable. This could help with changing: mesh.vertices = (float*)NativeMemory.AllocZeroed((nuint)(3 * mesh.vertexCount), sizeof(float)); To instead look like this: mesh.vertices = New<float>(3 * mesh.vertexCount); |
We could simplify the constructor to: public Mesh(int triangleCount, int vertexCount)
{
this.triangleCount = triangleCount;
this.vertexCount = vertexCount;
} And have all of our allocations be in seperate extention methods (utils) so we can pick and choose which ones we are going to use when generating custom meshes: /// <summary>Allocate mesh vertices</summary>
public static void AllocVertices(ref this Mesh mesh)
{
mesh.vertices = New<float>(3 * mesh.vertexCount);
}
/// <summary>Allocate mesh texcoords</summary>
public static void AllocTexcoords(ref this Mesh mesh)
{
mesh.texcoords = New<float>(2 * mesh.vertexCount);
}
/// <summary>Allocate mesh colors</summary>
public static void AllocColors(ref this Mesh mesh)
{
mesh.colors = New<byte>(4 * mesh.vertexCount);
}
/// <summary>Allocate mesh indices</summary>
public static void AllocIndices(ref this Mesh mesh)
{
mesh.indices = New<ushort>(3 * mesh.triangleCount);
} All of the accessors can be more utils: /// <summary>Access mesh vertices</summary>
public static Span<T> VerticesAs<T>(this Mesh mesh) where T : unmanaged
{
return new(mesh.vertices, 3 * mesh.vertexCount * sizeof(float) / sizeof(T));
}
/// <summary>Access mesh texcoords</summary>
public static Span<T> TexcoordsAs<T>(this Mesh mesh) where T : unmanaged
{
return new(mesh.texcoords, 2 * mesh.vertexCount * sizeof(float) / sizeof(T));
}
/// <summary>Access mesh colors</summary>
public static Span<T> ColorsAs<T>(this Mesh mesh) where T : unmanaged
{
return new(mesh.colors, 4 * mesh.vertexCount * sizeof(byte) / sizeof(T));
}
/// <summary>Access mesh indices</summary>
public static Span<T> IndicesAs<T>(this Mesh mesh) where T : unmanaged
{
return new(mesh.indices, 3 * mesh.triangleCount * sizeof(ushort) / sizeof(T));
} Example of usage: using Raylib_cs;
using System.Numerics;
Raylib.InitWindow(1280, 960, "Hello World!");
Raylib.SetTargetFPS(60);
Camera3D camera = new(Vector3.One * 1.5f, Vector3.Zero, Vector3.UnitY, 60f, CameraProjection.CAMERA_PERSPECTIVE);
Mesh tetrahedron = new(4, 4);
tetrahedron.AllocVertices();
tetrahedron.AllocTexcoords();
tetrahedron.AllocColors();
tetrahedron.AllocIndices();
Span<Vector3> vertices = tetrahedron.VerticesAs<Vector3>();
Span<Vector2> texcoords = tetrahedron.TexcoordsAs<Vector2>();
Span<Color> colors = tetrahedron.ColorsAs<Color>();
Span<ushort> indices = tetrahedron.IndicesAs<ushort>();
// Coordinates for a regular tetrahedron (wikipedia)
vertices[0] = new(float.Sqrt(8f / 9f), 0f, -1f / 3f);
vertices[1] = new(-float.Sqrt(2f / 9f), float.Sqrt(2f / 3f), -1f / 3f);
vertices[2] = new(-float.Sqrt(2f / 9f), -float.Sqrt(2f / 3f), -1f / 3f);
vertices[3] = Vector3.UnitZ;
texcoords[0] = Vector2.Zero;
texcoords[1] = Vector2.UnitX;
texcoords[2] = Vector2.UnitY;
texcoords[3] = Vector2.One;
colors[0] = Color.PINK;
colors[1] = Color.LIME;
colors[2] = Color.SKYBLUE;
colors[3] = Color.VIOLET;
indices[0] = 2;
indices[1] = 1;
indices[2] = 0;
indices[3] = 1;
indices[4] = 3;
indices[5] = 0;
indices[6] = 2;
indices[7] = 3;
indices[8] = 1;
indices[9] = 0;
indices[10] = 3;
indices[11] = 2;
float rotationAngle = 0f;
Raylib.UploadMesh(ref tetrahedron, false);
Model model = Raylib.LoadModelFromMesh(tetrahedron);
Image image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f);
Raylib.ImageBlurGaussian(ref image, 2);
Raylib.ImageColorBrightness(ref image, 100);
Raylib.ImageDither(ref image, 4, 4, 4, 4);
Texture2D texture = Raylib.LoadTextureFromImage(image);
Raylib.UnloadImage(image);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.MATERIAL_MAP_DIFFUSE, ref texture);
while (!Raylib.WindowShouldClose())
{
Raylib.UpdateCamera(ref camera, CameraMode.CAMERA_ORBITAL);
rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f);
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.BLACK);
Raylib.BeginMode3D(camera);
Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.WHITE);
Raylib.EndMode3D();
Raylib.EndDrawing();
}
Raylib.UnloadModel(model);
Raylib.UnloadTexture(texture);
Raylib.CloseWindow(); |
Nice work @n77y! U can also apply an example for that here: Side notice: |
@n77y Trying out the idea further using your example and I changed my mind on it needing to be separate from the Mesh struct. Updated the pr with a few more suggestions. Once done I will merge it in and add your example for it. @n77y @JupiterRider Also note that examples have been merged back into the main repo so further development on examples will be done here. |
@JupiterRider @n77y The MeshDemo example has now been added and the utils seem to be working as expected. Plan to experiment more with this and maybe apply the same approach to other resource types in the future. Thanks for all the help on this issue! |
New issue continuing #35. I want it to be easier to build and process meshes as that is one of the few areas that is a pain to manage with unsafe code even if you are careful.
Initially this starts with adding span wrappers/checks to help change the existing mesh but I would like to extend that to make the initial mesh creation/building process easier.
The text was updated successfully, but these errors were encountered: