diff --git a/src/CSUtilities b/src/CSUtilities
index e10bfee..5831470 160000
--- a/src/CSUtilities
+++ b/src/CSUtilities
@@ -1 +1 @@
-Subproject commit e10bfee513462f3a92ef3e233df2bdf5a942ab8a
+Subproject commit 5831470485012c00a7ea8f42d6dca50c244b948c
diff --git a/src/MeshIO/Entities/Camera.cs b/src/MeshIO/Entities/Camera.cs
index f8da8b9..a980a45 100644
--- a/src/MeshIO/Entities/Camera.cs
+++ b/src/MeshIO/Entities/Camera.cs
@@ -16,6 +16,9 @@ public class Camera : Entity
///
public double AspectRatio { get; set; }
+ ///
+ /// Gets or sets the distance to the far clipping plane for the camera or view frustum.
+ ///
public double FarPlane { get; set; } = 1;
///
@@ -37,6 +40,9 @@ public class Camera : Entity
/// too low may result in rendering artifacts due to depth buffer precision limitations.
public double NearPlane { get; set; } = 0;
+ ///
+ /// Gets or sets the orthographic zoom level for the view.
+ ///
public XY OrthographicZoom { get; set; }
///
diff --git a/src/MeshIO/Entities/Light.cs b/src/MeshIO/Entities/Light.cs
index ff3cd61..82db81a 100644
--- a/src/MeshIO/Entities/Light.cs
+++ b/src/MeshIO/Entities/Light.cs
@@ -1,5 +1,5 @@
namespace MeshIO.Entities;
- public class Light : Entity
- {
- }
+public class Light : Entity
+{
+}
diff --git a/src/MeshIO/Entities/Primitives/Box.cs b/src/MeshIO/Entities/Primitives/Box.cs
index 618ef3a..26f673c 100644
--- a/src/MeshIO/Entities/Primitives/Box.cs
+++ b/src/MeshIO/Entities/Primitives/Box.cs
@@ -6,6 +6,9 @@
namespace MeshIO.Entities.Primitives;
+///
+/// Represents a three-dimensional box defined by its center point and dimensions along the X, Y, and Z axes.
+///
public class Box : Primitive
{
///
@@ -64,12 +67,6 @@ public Box(BoundingBox boundingBox)
}
///
- ///
- /// The current implementation returns a mesh with no shared vertices and the following layers:
- ///
- ///
- /// configured with and
- ///
public override Mesh ToMesh()
{
List vertices = new List();
@@ -86,16 +83,16 @@ public override Mesh ToMesh()
this.createFace(0, 1, 2, 1, -1, this.LengthX, this.LengthY, this.LengthZ, vertices, normals, uvs, polygons, ref currQuad);
this.createFace(0, 1, 2, -1, -1, this.LengthX, this.LengthY, 0.0 - this.LengthZ, vertices, normals, uvs, polygons, ref currQuad);
- return this.createMesh(vertices, normals, uvs, polygons.Cast().ToList());
+ return this.createMesh(vertices, normals, uvs, polygons);
}
private void createFace(
int index0, int index1, int index2,
int dir1, int dir2,
double length, double height, double width,
- List vertices,
- List normals,
- List uvs,
+ List vertices,
+ List normals,
+ List uvs,
List polygons,
ref int currQuad)
{
diff --git a/src/MeshIO/Entities/Primitives/Plane.cs b/src/MeshIO/Entities/Primitives/Plane.cs
new file mode 100644
index 0000000..b1dcbfe
--- /dev/null
+++ b/src/MeshIO/Entities/Primitives/Plane.cs
@@ -0,0 +1,126 @@
+using CSMath;
+using MeshIO.Entities.Geometries;
+using System.Collections.Generic;
+
+namespace MeshIO.Entities.Primitives;
+
+///
+/// Represents a planar primitive defined by a center point, normal vector, length, and width.
+///
+public class Plane : Primitive
+{
+ ///
+ /// Gets or sets the center point of the plane.
+ ///
+ public XYZ Center { get; set; } = XYZ.Zero;
+
+ ///
+ /// Gets or sets the length of the plane along the local Z axis.
+ ///
+ public double Length { get; set; } = 1.0;
+
+ ///
+ /// Gets or sets the number of segments along the length of the plane.
+ ///
+ public int LengthSegments { get; set; } = 1;
+
+ ///
+ /// Gets or sets the normal vector of the plane.
+ ///
+ public XYZ Normal { get; set; } = XYZ.AxisY;
+
+ ///
+ /// Gets or sets the width of the plane along the local X axis.
+ ///
+ public double Width { get; set; } = 1.0;
+
+ ///
+ /// Gets or sets the number of segments along the width of the plane.
+ ///
+ public int WidthSegments { get; set; } = 1;
+
+ ///
+ /// Initializes a new instance of the class with an empty name.
+ ///
+ public Plane() : this(string.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified name.
+ ///
+ /// The name of the plane.
+ public Plane(string name) : base(name)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified name, center, normal, length, and width.
+ ///
+ /// The name of the plane.
+ /// The center point of the plane.
+ /// The normal vector of the plane.
+ /// The length of the plane.
+ /// The width of the plane.
+ public Plane(string name, XYZ center, XYZ normal, double length, double width) : base(name)
+ {
+ Center = center;
+ Normal = normal;
+ Length = length;
+ Width = width;
+ }
+
+ ///
+ public override Mesh ToMesh()
+ {
+ List vertices = new List();
+ List normals = new List();
+ List uvs = new List();
+ List polygons = new List();
+
+ double lengthStep = Length / (double)LengthSegments;
+ double widthStep = Width / (double)WidthSegments;
+ double halfLength = Length * 0.5;
+ double halfWidth = Width * 0.5;
+
+ for (int i = 0; i <= LengthSegments; i++)
+ {
+ double z = i * lengthStep - halfLength;
+ for (int j = 0; j <= WidthSegments; j++)
+ {
+ double x = j * widthStep - halfWidth;
+ vertices.Add(new XYZ(x, 0.0, z));
+ uvs.Add(new XY(
+ (double)i / (double)LengthSegments,
+ (double)j / (double)WidthSegments));
+
+ if (i > 0 && j > 0)
+ {
+ int stride = LengthSegments + 1;
+ polygons.Add(new Quad(
+ i * stride + j - 1,
+ i * stride + j,
+ (i - 1) * stride + j,
+ (i - 1) * stride + j - 1));
+ }
+ }
+ }
+
+ if (!Normal.Normalize().IsEqual(XYZ.AxisY))
+ {
+ Quaternion rotation = Quaternion.FromRotation(XYZ.AxisY, Normal.Normalize());
+ for (int k = 0; k < vertices.Count; k++)
+ {
+ vertices[k] = rotation * vertices[k];
+ }
+
+ normals.Add(rotation * XYZ.AxisY);
+ }
+ else
+ {
+ normals.Add(XYZ.AxisY);
+ }
+
+ return this.createMesh(vertices, normals, uvs, polygons);
+ }
+}
\ No newline at end of file
diff --git a/src/MeshIO/Entities/Primitives/Primitive.cs b/src/MeshIO/Entities/Primitives/Primitive.cs
index feebec9..ebae764 100644
--- a/src/MeshIO/Entities/Primitives/Primitive.cs
+++ b/src/MeshIO/Entities/Primitives/Primitive.cs
@@ -5,34 +5,43 @@
namespace MeshIO.Entities.Primitives;
+///
+/// The base class of all primitives, which can be converted to mesh. It is also an entity, so it has a name and can be added to a scene.
+///
public abstract class Primitive : Entity
{
///
- /// The geometry is visible or not
+ /// This geometry can cast shadow or not
///
- public bool IsVisible { get; set; } = true;
+ public bool CastShadows { get; set; } = true;
///
- /// This geometry can cast shadow or not
+ /// The geometry is visible or not
///
- public bool CastShadows { get; set; } = true;
+ public bool IsVisible { get; set; } = true;
///
/// This geometry can receive shadow or not
///
public bool ReceiveShadows { get; set; } = true;
+ protected Primitive(string name) : base(name)
+ {
+ }
+
///
- /// Process this primitive into a mesh
+ /// Convert this primitive to mesh. The mesh will be added to the scene, and the primitive will be removed from the scene. So after calling this method, the primitive will not exist in the scene, but the mesh will exist in the scene. If you want to keep the primitive in the scene, you can call this method and then add the primitive back to the scene.
///
+ ///
+ /// The current implementation returns a mesh with no shared vertices and the following layers:
+ ///
+ ///
+ /// configured with and
+ ///
///
public abstract Mesh ToMesh();
- public Primitive() : this(string.Empty) { }
-
- public Primitive(string name) : base(name) { }
-
- protected Mesh createMesh(List vertices, List normals, List uvs, List polygons)
+ protected Mesh createMesh(IEnumerable vertices, IEnumerable normals, IEnumerable uvs, IEnumerable polygons)
{
Mesh mesh = new Mesh(this.Name);
@@ -53,4 +62,4 @@ protected Mesh createMesh(List vertices, List normals, List uvs, L
return mesh;
}
-}
+}
\ No newline at end of file
diff --git a/src/MeshIO/Entities/Primitives/Sphere.cs b/src/MeshIO/Entities/Primitives/Sphere.cs
new file mode 100644
index 0000000..923da72
--- /dev/null
+++ b/src/MeshIO/Entities/Primitives/Sphere.cs
@@ -0,0 +1,146 @@
+using CSMath;
+using MeshIO.Entities.Geometries;
+using System;
+using System.Collections.Generic;
+
+namespace MeshIO.Entities.Primitives;
+
+///
+/// Represents a three-dimensional sphere primitive defined by its center, radius, and segmentation parameters.
+///
+/// The Sphere class provides properties to control the sphere's tessellation, including the number of
+/// width and height segments, as well as angular ranges for partial spheres. Adjusting these parameters affects the
+/// level of detail and the portion of the sphere that is generated. The sphere is centered at the specified Center
+/// point and can be used to generate a mesh representation for rendering or geometric processing.
+public class Sphere : Primitive
+{
+ ///
+ /// Gets or sets the center point of the object in 3D space.
+ ///
+ public XYZ Center { get; set; } = XYZ.Zero;
+
+ ///
+ /// Gets or sets the number of vertical segments used to construct the geometry.
+ ///
+ /// The value must be at least 2. Increasing the number of height segments can improve the smoothness
+ /// of the geometry but may impact performance.
+ public int HeightSegments { get; set; } = 16;
+
+ ///
+ /// Gets or sets the length of the phi angle, in radians.
+ ///
+ public double PhiLength { get; set; } = MathHelper.TwoPI;
+
+ ///
+ /// Gets or sets the starting angle, in radians, for the phi coordinate.
+ ///
+ public double PhiStart { get; set; }
+
+ ///
+ /// Gets or sets the radius of the shape.
+ ///
+ public double Radius { get; set; } = 1.0;
+
+ ///
+ /// Gets or sets the central angle, in radians, that defines the arc length of the shape.
+ ///
+ /// The value typically ranges from 0 to 2π. Adjusting this property changes the portion of the full
+ /// circle represented by the shape.
+ public double ThetaLength { get; set; } = Math.PI;
+
+ ///
+ /// Gets or sets the starting angle, in radians, for the operation or calculation.
+ ///
+ public double ThetaStart { get; set; }
+
+ ///
+ /// Gets or sets the number of segments used to subdivide the width of the geometry.
+ ///
+ /// Increasing the number of width segments can improve the smoothness of the geometry at the cost of
+ /// additional processing and rendering overhead.
+ public int WidthSegments { get; set; } = 32;
+
+ ///
+ /// Initializes a new instance of the Sphere class with default values.
+ ///
+ /// This constructor creates a Sphere with an empty name. Use this overload when no initial name is
+ /// required.
+ public Sphere() : this(string.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the Sphere class with the specified name.
+ ///
+ /// The name to assign to the sphere. Cannot be null or empty.
+ public Sphere(string name) : base(name)
+ {
+ }
+
+ ///
+ public override Mesh ToMesh()
+ {
+ int index = 0;
+ int totalVertices = (WidthSegments + 1) * (HeightSegments + 1);
+ List vertices = new List();
+ List normals = new List();
+ List uvs = new List();
+ List polygons = new();
+
+ double thetaEnd = (double)(double)ThetaStart + (double)(double)ThetaLength;
+
+ var indexGrid = new int[HeightSegments + 1][];
+
+ for (int i = 0; i <= HeightSegments; i++)
+ {
+ var row = new int[WidthSegments + 1];
+ double v = (double)i / HeightSegments;
+
+ for (int j = 0; j <= WidthSegments; j++)
+ {
+ double u = (double)j / (double)WidthSegments;
+ double x = -(double)(double)Radius * Math.Cos((double)(double)PhiStart + u * (double)(double)PhiLength) * Math.Sin((double)(double)ThetaStart + v * (double)(double)ThetaLength);
+ double y = (double)(double)Radius * Math.Cos((double)(double)ThetaStart + v * (double)(double)ThetaLength);
+ double z = (double)(double)Radius * Math.Sin((double)(double)PhiStart + u * (double)(double)PhiLength) * Math.Sin((double)(double)ThetaStart + v * (double)(double)ThetaLength);
+
+ vertices.Add(new XYZ(x, y, z) + this.Center);
+ normals.Add(new XYZ(x, y, z).Normalize());
+ uvs.Add(new XY(u, 1f - v));
+ row[j] = index;
+ index++;
+ }
+
+ indexGrid[i] = row;
+ }
+
+ for (int k = 0; k < HeightSegments; k++)
+ {
+ for (int l = 0; l < WidthSegments; l++)
+ {
+ int a = indexGrid[k][l + 1];
+ int b = indexGrid[k][l];
+ int c = indexGrid[k + 1][l];
+ int d = indexGrid[k + 1][l + 1];
+
+ bool notTopPole = k != 0 || (double)(double)ThetaStart > 0f;
+ bool notBottomPole = k != HeightSegments - 1 || (double)thetaEnd < Math.PI;
+
+ if (notTopPole && notBottomPole)
+ {
+ var q = new Quad(a, b, c, d);
+ polygons.AddRange(q.ToTriangles());
+ }
+ else if (notTopPole)
+ {
+ polygons.Add(new Triangle(a, b, d));
+ }
+ else if (notBottomPole)
+ {
+ polygons.Add(new Triangle(b, c, d));
+ }
+ }
+ }
+
+ return this.createMesh(vertices, normals, uvs, polygons);
+ }
+}
\ No newline at end of file