Pixel-Composer/scripts/BBMOD_MeshBuilder/BBMOD_MeshBuilder.gml
2023-04-23 16:47:33 +02:00

283 lines
7.4 KiB
Text

/// @func BBMOD_MeshBuilder([_primitiveType])
///
/// @extends BBMOD_Class
///
/// @desc Allows you to build meshes through code.
///
/// @param {Constant.PrimitiveType} [_primitiveType] The primitive type of built
/// meshes. Defaults to `pr_trianglelist`.
///
/// @example
/// Following code shows how you can create a plane mesh using the mesh builder.
/// ```gml
/// var _meshBuilder = new BBMOD_MeshBuilder();
/// var _vertexFormat = new BBMOD_VertexFormat(true);
/// var _v1 = new BBMOD_Vertex(_vertexFormat);
/// _v1.Position = new BBMOD_Vec3(0.0, 0.0, 0.0);
/// var _v1Ind = _meshBuilder.add_vertex(v1);
/// var _v2 = new BBMOD_Vertex(_vertexFormat);
/// _v2.Position = new BBMOD_Vec3(1.0, 0.0, 0.0);
/// var _v2Ind = _meshBuilder.add_vertex(v2);
/// var _v3 = new BBMOD_Vertex(_vertexFormat);
/// _v3.Position = new BBMOD_Vec3(1.0, 1.0, 0.0);
/// var _v3Ind = _meshBuilder.add_vertex(v3);
/// var _v4 = new BBMOD_Vertex(_vertexFormat);
/// _v4.Position = new BBMOD_Vec3(0.0, 1.0, 0.0);
/// var _v4Ind = _meshBuilder.add_vertex(v4);
/// _meshBuilder.add_face(_v1, _v2, _v4);
/// _meshBuilder.add_face(_v2, _v3, _v4);
/// var _mesh = _meshBuilder.build();
/// _meshBuilder = _meshBuilder.destroy();
/// ```
///
/// @see BBMOD_Mesh
/// @see BBMOD_Vertex
/// @see BBMOD_VertexFormat
function BBMOD_MeshBuilder(_primitiveType=pr_trianglelist)
: BBMOD_Class() constructor
{
BBMOD_CLASS_GENERATED_BODY;
static Class_destroy = destroy;
/// @var {Constant.PrimitiveType} The primitive type of built meshes.
/// @readonly
PrimitiveType = _primitiveType;
/// @var {Id.DsList<Struct.BBMOD_Vertex>} List of mesh vertices.
/// @readonly
Vertices = ds_list_create();
/// @var {Id.DsList<Real>} List of vertex indices that make up a face. First
/// three indices are the first face, next three indices are the second face
/// etc.
/// @readonly
Faces = ds_list_create();
/// @func add_vertex(_vertex)
///
/// @desc Adds a vertex to the mesh.
///
/// @param {Struct.BBMOD_Vertex} _vertex The vertex to add.
///
/// @return {Real} Returns the index of the vertex.
///
/// @see BBMOD_Vertex
static add_vertex = function (_vertex) {
gml_pragma("forceinline");
var _ind = ds_list_size(Vertices);
ds_list_add(Vertices, _vertex);
return _ind;
};
/// @func add_face(_index...)
///
/// @desc Adds a face to the mesh.
///
/// @param {Real} _index The index of the first vertex of the face.
///
/// @return {Real} Returns the index within the list of faces where
/// the first vertex was stored.
///
/// @see BBMOD_MeshBuilder.Faces
static add_face = function (_index) {
gml_pragma("forceinline");
var _ind = ds_list_size(Faces);
var i = 0;
repeat (argument_count)
{
ds_list_add(Faces, argument[i++]);
}
return _ind;
};
/// @func make_tangents()
///
/// @desc Makes tangent and bitangent vectors for added vertices.
///
/// @return {Struct.BBMOD_MeshBuilder} Returns `self`.
///
/// @throws {BBMOD_Exception} If an error occurs during the process.
///
/// @note This works only for the `pr_trianglelist` primitive type!
///
/// @source https://gamedev.stackexchange.com/a/68617
static make_tangents = function () {
if (PrimitiveType != pr_trianglelist)
{
throw new BBMOD_Exception(
"Cannot build tangents and bitangents for meshes with primitive"
+ " type other than pr_trianglelist!");
}
var _faceCount = ds_list_size(Faces);
var _vertexCount = ds_list_size(Vertices);
var _tan2 = array_create(_vertexCount);
for (var i = 0; i < _vertexCount; ++i)
{
Vertices[| i].TangentW = new BBMOD_Vec4(0.0);
_tan2[@ i] = new BBMOD_Vec3(0.0);
}
for (var i = 0; i < _faceCount; i += 3)
{
var _i1 = Faces[| i];
var _i2 = Faces[| i + 1];
var _i3 = Faces[| i + 2];
var _f1 = Vertices[| _i1];
var _f2 = Vertices[| _i2];
var _f3 = Vertices[| _i3];
// Check if their vertex formats have the required data
if (!(_f1.VertexFormat.Vertices && _f1.VertexFormat.Normals && _f1.VertexFormat.TextureCoords
&& _f2.VertexFormat.Vertices && _f2.VertexFormat.Normals && _f2.VertexFormat.TextureCoords
&& _f3.VertexFormat.Vertices && _f3.VertexFormat.Normals && _f3.VertexFormat.TextureCoords))
{
throw new BBMOD_Exception(
"Vertices, normals and texture coords are required to build tangents!");
}
var _v1 = _f1.Position;
var _v2 = _f2.Position;
var _v3 = _f3.Position;
var _w1 = _f1.TextureCoord;
var _w2 = _f2.TextureCoord;
var _w3 = _f3.TextureCoord;
var _x1 = _v2.X - _v1.X;
var _x2 = _v3.X - _v1.X;
var _y1 = _v2.Y - _v1.Y;
var _y2 = _v3.Y - _v1.Y;
var _z1 = _v2.Z - _v1.Z;
var _z2 = _v3.Z - _v1.Z;
var _s1 = _w2.X - _w1.X;
var _s2 = _w3.X - _w1.X;
var _t1 = _w2.Y - _w1.Y;
var _t2 = _w3.Y - _w1.Y;
if (_s1 * _t2 == _t1 * _s2)
{
_s1 = 1.0;
_t1 = 0.0;
_s2 = 0.0;
_t2 = 1.0;
}
var _r = 1.0 / ((_s1 * _t2) - (_s2 * _t1));
var _temp;
var _sdirX = ((_t2 * _x1) - (_t1 * _x2)) * _r;
var _sdirY = ((_t2 * _y1) - (_t1 * _y2)) * _r;
var _sdirZ = ((_t2 * _z1) - (_t1 * _z2)) * _r;
_temp = _f1.TangentW;
_temp.X += _sdirX;
_temp.Y += _sdirY;
_temp.Z += _sdirZ;
_temp = _f2.TangentW;
_temp.X += _sdirX;
_temp.Y += _sdirY;
_temp.Z += _sdirZ;
_temp = _f3.TangentW;
_temp.X += _sdirX;
_temp.Y += _sdirY;
_temp.Z += _sdirZ;
var _tdirX = ((_s1 * _x2) - (_s2 * _x1)) * _r;
var _tdirY = ((_s1 * _y2) - (_s2 * _y1)) * _r;
var _tdirZ = ((_s1 * _z2) - (_s2 * _z1)) * _r;
_temp = _tan2[_i1];
_temp.X += _tdirX;
_temp.Y += _tdirY;
_temp.Z += _tdirZ;
_temp = _tan2[_i2];
_temp.X += _tdirX;
_temp.Y += _tdirY;
_temp.Z += _tdirZ;
_temp = _tan2[_i3];
_temp.X += _tdirX;
_temp.Y += _tdirY;
_temp.Z += _tdirZ;
}
for (var i = 0; i < _vertexCount; ++i)
{
var _v = Vertices[| i];
var _n = _v.Normal;
var _t = new BBMOD_Vec3(
_v.TangentW.X,
_v.TangentW.Y,
_v.TangentW.Z
);
// Gram-Schmidt orthogonalize
var _tNew = _t.Sub(_n.Scale(_n.Dot(_t))).Normalize().Copy(_v.TangentW);
// Calculate handedness
var _dot = _n.Cross(_tNew).Dot(_tan2[i]);
_v.TangentW.W = (_dot < 0.0) ? -1.0 : 1.0;
}
return self;
};
/// @func build([_vertexFormat])
///
/// @desc Builds a mesh from the added vertices and faces.
///
/// @param {Struct.BBMOD_VertexFormat} [_vertexFormat] The vertex format of
/// the mesh. This must be compatible with the format of the added vertices.
/// If `undefined`, then the format of the first added vertex is used.
///
/// @return {Struct.BBMOD_Mesh} The created mesh.
///
/// @throws {BBMOD_Exception} If an error occurs during the mesh building
/// process.
///
/// @see BBMOD_Mesh
/// @see BBMOD_VertexFormat
static build = function (_vertexFormat=undefined) {
_vertexFormat ??= Vertices[| 0].VertexFormat;
var _vbuffer = vertex_create_buffer();
var _faceCount = ds_list_size(Faces);
vertex_begin(_vbuffer, _vertexFormat.Raw);
for (var i = 0; i < _faceCount; ++i)
{
var _ind = Faces[| i];
var _vertex = Vertices[| _ind];
try
{
_vertex.to_vertex_buffer(_vbuffer, _vertexFormat);
}
catch (_err)
{
vertex_delete_buffer(_vbuffer);
throw _err;
}
}
vertex_end(_vbuffer);
var _mesh = new BBMOD_Mesh(_vertexFormat);
_mesh.VertexBuffer = _vbuffer;
_mesh.PrimitiveType = PrimitiveType;
return _mesh;
};
static destroy = function () {
Class_destroy();
ds_list_destroy(Vertices);
ds_list_destroy(Faces);
return undefined;
};
}