patterncsharpMinor
Voxel Islands - Dynamic mesh generation
Viewed 0 times
meshgenerationvoxeldynamicislands
Problem
While I can often understand the performance reasons behind using fixed-size arrays to store vertex, triangle and UV data, I've often found it annoying because it makes dynamically generating meshes somewhat difficult. In order to fix this, I've built a small helper class to make the process much easier. It currently only supports vertex, triangle and UV generation, but that's really all I need at the moment.
```
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace VoxelIslands.Engine.Utilities
{
///
/// This class is simply a wrapper used to dynamically generate mesh data for
/// things like voxels and chunks. In the end, it will be converted to a normal
/// Unity3D mesh.
///
public class DynamicMesh
{
private List Vertices { get; set; }
private List Triangles { get; set; }
private List UVs { get; set; }
private List ColliderVertices { get; set; }
private List ColliderTriangles { get; set; }
private Vector3 MeshOffset { get; set; }
private bool _GenerateColliderData { get; set; }
///
/// Constructor for the DynamicMesh class.
///
/// The offset of the mesh and it's vertices.
/// Whether or not to generate collider data.
public DynamicMesh(Vector3 meshOffset, bool generateColliderData = true)
{
this.Vertices = new List() { };
this.Triangles = new List() { };
this.UVs = new List() { };
this.ColliderVertices = new List() { };
this.ColliderTriangles = new List() { };
this.MeshOffset = meshOffset;
this._GenerateColliderData = generateColliderData;
}
///
/// This function creates a new Unity3D mesh from the collider data contained in the vertex,
/// and triangle data. The mesh returned by this function is intended to be used for collisions
/// only.
///
```
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace VoxelIslands.Engine.Utilities
{
///
/// This class is simply a wrapper used to dynamically generate mesh data for
/// things like voxels and chunks. In the end, it will be converted to a normal
/// Unity3D mesh.
///
public class DynamicMesh
{
private List Vertices { get; set; }
private List Triangles { get; set; }
private List UVs { get; set; }
private List ColliderVertices { get; set; }
private List ColliderTriangles { get; set; }
private Vector3 MeshOffset { get; set; }
private bool _GenerateColliderData { get; set; }
///
/// Constructor for the DynamicMesh class.
///
/// The offset of the mesh and it's vertices.
/// Whether or not to generate collider data.
public DynamicMesh(Vector3 meshOffset, bool generateColliderData = true)
{
this.Vertices = new List() { };
this.Triangles = new List() { };
this.UVs = new List() { };
this.ColliderVertices = new List() { };
this.ColliderTriangles = new List() { };
this.MeshOffset = meshOffset;
this._GenerateColliderData = generateColliderData;
}
///
/// This function creates a new Unity3D mesh from the collider data contained in the vertex,
/// and triangle data. The mesh returned by this function is intended to be used for collisions
/// only.
///
Solution
I don't see much refactoring there, mostly just a matter of taste.
There are a lot of
I prefer inversing the condition in the
One of the properies begins with an
As you don't expose any of them publicly I'd turn them to regular fields.
You don't need to use
It might be a good idea to prefix boolean variables with is or can if it makes sense.
You don't need to always explitly specify the type. If you define a new variable inside a method etc. you can use the
Example:
There are a lot of
thisis - you can do without them, most of the time they are only code bloat.I prefer inversing the condition in the
AddQuad method to reduce nesting and remove the last elseif(this.Vertices.Count < 4)
{
throw...
}One of the properies begins with an
_ underscore, is there a reason for that?As you don't expose any of them publicly I'd turn them to regular fields.
You don't need to use
{} for creating empty lists this new List() { } is the same as new List(). You can also initialize them together with the declaration.It might be a good idea to prefix boolean variables with is or can if it makes sense.
You don't need to always explitly specify the type. If you define a new variable inside a method etc. you can use the
var keyword to not repeat yourself.Example:
public class DynamicMesh
{
private List _vertices = new List();
private List _triangles = new List();
private List _uvs = new List();
private List _colliderVertices = new List();
private List _colliderTriangles = new List();
private Vector3 _meshOffset;
private bool _canGnerateColliderData;
///
/// Constructor for the DynamicMesh class.
///
/// The offset of the mesh and it's vertices.
/// Whether or not to generate collider data.
public DynamicMesh(Vector3 meshOffset, bool canGenerateColliderData = true)
{
_meshOffset = meshOffset;
_canGnerateColliderData = canGenerateColliderData;
}
///
/// This function creates a new Unity3D mesh from the collider data contained in the vertex,
/// and triangle data. The mesh returned by this function is intended to be used for collisions
/// only.
///
/// A newly created collider mesh.
public Mesh CreateColliderMesh()
{
// using object initializer
var colliderMesh = new Mesh
{
vertices = _colliderVertices.ToArray(),
triangles = _colliderTriangles.ToArray(),
};
colliderMesh.RecalculateNormals();
return colliderMesh;
}
///
/// This function creates a new Unity3D mesh from the data contained in the vertex, triangle
/// and UV data. The mesh returned by this function is intended to be used for rendering only.
///
/// A newly created rendering mesh.
public Mesh CreateRenderingMesh()
{
var renderingMesh = new Mesh
{
vertices = _vertices.ToArray(),
triangles = _triangles.ToArray(),
uv = _uvs.ToArray(),
};
renderingMesh.RecalculateNormals();
return renderingMesh;
}
///
/// Add a new UV coordinate to the UV data.
///
///
public void AddUVs(Vector2[] uvCoordinates)
{
_uvs.AddRange(uvCoordinates);
}
///
/// Add two trianges to form a quad. This is based of the most recent vertex
/// data and will not work if your vertex data is empty. Collision data is generated
/// only if collision mesh generation is enabled. There is however an optional
/// parameter that allows for you to enable or disable collider generation on the fly.
///
/// Whether or not to generate collider data.
public void AddQuad(bool canGenerateColliderData = true)
{
if (_vertices.Count
/// Add a vertex to the vertex data list. A collider vertex is generated only
/// if collision mesh generation is enabled. There is however an optional
/// parameter that allows for you to enable or disable collider generation on the fly.
///
/// The position of the vertex.
/// The offset of the vertex.
/// Whether or not to generate collider data.
public void AddVertex(Vector3 vertexPosition, Vector3 vertexOffset, bool canGenerateColliderData = true)
{
// perform calculation only once, then add its result whereever needed
var vertexWithOffset = (vertexPosition - _meshOffset) + vertexOffset;
_vertices.Add(vertexWithOffset);
if (_canGnerateColliderData && canGenerateColliderData)
{
_colliderVertices.Add(vertexWithOffset);
}
}
}Code Snippets
if(this.Vertices.Count < 4)
{
throw...
}public class DynamicMesh
{
private List<Vector3> _vertices = new List<Vector3>();
private List<int> _triangles = new List<int>();
private List<Vector2> _uvs = new List<Vector2>();
private List<Vector3> _colliderVertices = new List<Vector3>();
private List<int> _colliderTriangles = new List<int>();
private Vector3 _meshOffset;
private bool _canGnerateColliderData;
/// <summary>
/// Constructor for the DynamicMesh class.
/// </summary>
/// <param name="meshOffset">The offset of the mesh and it's vertices.</param>
/// <param name="generateColliderData">Whether or not to generate collider data.</param>
public DynamicMesh(Vector3 meshOffset, bool canGenerateColliderData = true)
{
_meshOffset = meshOffset;
_canGnerateColliderData = canGenerateColliderData;
}
/// <summary>
/// This function creates a new Unity3D mesh from the collider data contained in the vertex,
/// and triangle data. The mesh returned by this function is intended to be used for collisions
/// only.
/// </summary>
/// <returns>A newly created collider mesh.</returns>
public Mesh CreateColliderMesh()
{
// using object initializer
var colliderMesh = new Mesh
{
vertices = _colliderVertices.ToArray(),
triangles = _colliderTriangles.ToArray(),
};
colliderMesh.RecalculateNormals();
return colliderMesh;
}
/// <summary>
/// This function creates a new Unity3D mesh from the data contained in the vertex, triangle
/// and UV data. The mesh returned by this function is intended to be used for rendering only.
/// </summary>
/// <returns>A newly created rendering mesh.</returns>
public Mesh CreateRenderingMesh()
{
var renderingMesh = new Mesh
{
vertices = _vertices.ToArray(),
triangles = _triangles.ToArray(),
uv = _uvs.ToArray(),
};
renderingMesh.RecalculateNormals();
return renderingMesh;
}
/// <summary>
/// Add a new UV coordinate to the UV data.
/// </summary>
/// <param name="uvCoordinate"></param>
public void AddUVs(Vector2[] uvCoordinates)
{
_uvs.AddRange(uvCoordinates);
}
/// <summary>
/// Add two trianges to form a quad. This is based of the most recent vertex
/// data and will not work if your vertex data is empty. Collision data is generated
/// only if collision mesh generation is enabled. There is however an optional
/// parameter that allows for you to enable or disable collider generation on the fly.
/// </summary>
/// <param name="generateColliderData">Whether or not to generate collider data.</param>
public void AddQuad(bool canGenerateColliderData = true)
{
if (_vertices.Count < 4)
{
throw new System.Exception("Rendering vertex data must contain enough vertices to generate a quad.");
Context
StackExchange Code Review Q#128634, answer score: 4
Revisions (0)
No revisions yet.