Editor/Builder/PolyMeshX.cs
using BspImport;
using HalfEdgeMesh;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace BspImport.Builder;
public static class PolyMeshX
{
private static void AddMeshFaceInternal( this PolygonMesh mesh, ImportContext context, Face face )
{
MapGeometry geo;
if ( !context.HasCompleteGeometry( out geo ) )
{
return;
}
var texInfo = face.TexInfo;
// only construct valid primitives, 2 edges needed for a triangle
if ( face.EdgeCount < 2 )
return;
// validate surface edge range
if ( face.FirstEdge < 0 || face.FirstEdge >= geo.SurfaceEdgesCount || face.FirstEdge + face.EdgeCount > geo.SurfaceEdgesCount )
return;
string? materialName = null;
Vector3 reflectivity = Vector3.One;
bool isWater = false;
// check for valid texinfo and fetch material name
if ( context.TexInfo is not null && texInfo >= 0 && texInfo < context.TexInfo.Length )
{
materialName = face.GetMaterialName( context );
reflectivity = face.GetReflectivity( context );
var surfaceFlags = face.GetSurfaceFlags( context );
if ( (surfaceFlags & SurfaceFlags.Warp) != 0 )
{
isWater = true;
}
}
if ( string.IsNullOrEmpty( materialName ) )
return;
// simple patch
materialName = materialName.Replace( "toolsskybox2d", "toolsskybox" );
// cull skybox faces entirely
if ( context.BuildSettings.CullSkybox && materialName.Contains( "toolsskybox" ) )
return;
var verts = new List<Vector3>();
var uvs = new List<Vector2>();
// get verts from surf edges -> edges -> vertices
for ( int i = 0; i < face.EdgeCount; i++ )
{
int surfEdgeIdx = face.FirstEdge + i;
if ( !geo.TryGetSurfaceEdge( surfEdgeIdx, out var edge ) )
return;
// edge sign affects winding order, indexing back to front or vice versa on the edge vertices
int edgeIndex = edge >= 0 ? edge : -edge;
if ( !geo.TryGetEdgeIndices( edgeIndex, out var edgeIndices ) )
return;
var indices = edgeIndices.Indices;
if ( indices is null || indices.Length < 2 )
return;
int vertIdx = edge >= 0 ? indices[0] : indices[1];
if ( !geo.TryGetVertex( vertIdx, out var vertex ) )
return;
verts.Add( vertex );
uvs.Add( MapBuilder.GetTexCoords( context, texInfo, vertex ) );
}
verts.Reverse();
uvs.Reverse();
// construct mesh vertex from vert pos and calculated uv
var hVertices = mesh.AddVertices( verts.ToArray() );
var hFace = mesh.AddFace( hVertices );
if ( context.BuildSettings.LoadMaterials )
{
var material = Material.Load( $"materials/{materialName}.vmat" );
mesh.SetFaceMaterial( hFace, material );
}
else
{
var materialFallback = $"bsp_vertex_color";
if ( isWater )
{
materialFallback = "bsp_water";
}
var material = Material.Load( $"materials/{materialFallback}.vmat" );
mesh.SetFaceMaterial( hFace, material );
foreach ( var edge in mesh.HalfEdgeHandles )
{
Color col = new Color( reflectivity.x, reflectivity.y, reflectivity.z );
mesh.SetVertexColor( edge, col.ToColor32( true ) );
}
}
// uv fix for tools materials
if ( materialName.Contains( "tools" ) )
mesh.TextureAlignToGrid( Transform.Zero );
else
mesh.SetFaceTextureCoords( hFace, uvs.ToArray() );
}
public static void AddMeshFace( this PolygonMesh mesh, ImportContext context, ushort faceIndex )
{
if ( !context.HasCompleteGeometry( out var geo ) )
return;
if ( faceIndex < 0 || faceIndex >= geo.FacesCount )
return;
if ( !geo.TryGetFace( faceIndex, out var face ) )
return;
mesh.AddMeshFaceInternal( context, face );
}
}