using Editor.MeshEditor; using NativeEngine; using NativeMapDoc; using System; using System.ComponentModel.DataAnnotations; using System.Runtime.InteropServices; namespace Editor.MapDoc; internal struct TransformOperationScope : IDisposable { private CMapMesh mapMesh; public TransformOperationScope( CMapMesh mesh, TransformOperationMode mode, TransformFlags flags ) { mapMesh = mesh; mesh.BeginTransformOperation( mode, flags ); } public void Dispose() { mapMesh.EndTransformOperation(); } } /// /// MapMesh is the Hammer map node which represents editable mesh geometry in a Hammer map. /// This is the map node that is created when using the hammer geometry editing tools. /// [Display( Name = "Mesh" ), Icon( "foundation" )] public class MapMesh : MapNode { internal CMapMesh meshNative; internal MapMesh( HandleCreationData _ ) { } public MapMesh( MapDocument mapDocument = null ) { ThreadSafe.AssertIsMainThread(); // Default to the active map document if none specificed mapDocument ??= MapEditor.Hammer.ActiveMap; Assert.IsValid( mapDocument ); using ( var h = IHandle.MakeNextHandle( this ) ) { mapDocument.native.CreateEmptyMesh( true ); // meh } } internal override void OnNativeInit( CMapNode ptr ) { base.OnNativeInit( ptr ); meshNative = (CMapMesh)ptr; } internal override void OnNativeDestroy() { base.OnNativeDestroy(); meshNative = default; } /// /// Assigns the specified material to the entire mesh /// public void SetMaterial( Material material ) { ArgumentNullException.ThrowIfNull( material ); meshNative.AssignMaterialToMesh( material.Name ); } /// /// Constructs the mesh from the given builder. /// public unsafe void ConstructFromPolygons( PrimitiveBuilder.PolygonMesh mesh ) { ArgumentNullException.ThrowIfNull( mesh ); // Construct data streams from our lists var vertexPositions = mesh.Vertices.Select( v => v ).ToList(); var vertexTexCoords = mesh.Vertices.Select( v => Vector2.Zero ).ToList(); var faceIndices = new List( mesh.Faces.Sum( f => f.Indices.Count ) ); // Array of indices specifying which vertices are used by each face var faceVertexCounts = new List( mesh.Faces.Count ); // Number of vertices used by each face var faceMaterials = new List( mesh.Faces.Count ); foreach ( var face in mesh.Faces ) { faceIndices.AddRange( face.Indices ); faceVertexCounts.Add( face.Indices.Count ); var material = (!string.IsNullOrEmpty( face.Material ) ? Material.Load( face.Material ) : null) ?? MapEditor.Hammer.CurrentMaterial ?? Material.Load( "materials/dev/reflectivity_30.vmat" ); // didn't specify a material? assign hammer current material faceMaterials.Add( material.native ); } fixed ( Vector3* vertexPositionPtr = CollectionsMarshal.AsSpan( vertexPositions ) ) fixed ( int* faceIndicesPtr = CollectionsMarshal.AsSpan( faceIndices ) ) fixed ( Vector2* vertexTexCoordsPtr = CollectionsMarshal.AsSpan( vertexTexCoords ) ) fixed ( int* faceVertexCountsPtr = CollectionsMarshal.AsSpan( faceVertexCounts ) ) fixed ( IntPtr* faceMaterialsPtr = CollectionsMarshal.AsSpan( faceMaterials ) ) { meshNative.ConstructFromData( mesh.Vertices.Count, (IntPtr)vertexPositionPtr, (IntPtr)vertexTexCoordsPtr, faceIndices.Count, (IntPtr)faceIndicesPtr, mesh.Faces.Count, (IntPtr)faceVertexCountsPtr, (IntPtr)faceMaterialsPtr, true, 0.001f ); } } /// /// Get all material assets used on this mesh /// public IEnumerable GetFaceMaterialAssets() { var strs = CUtlVectorString.Create( 16, 16 ); meshNative.GetFaceMaterials( strs ); List assets = new(); for ( int i = 0; i < strs.Count(); i++ ) { var asset = AssetSystem.FindByPath( strs.Element( i ) ); if ( asset == null ) continue; assets.Add( asset ); } strs.DeleteThis(); return assets.Distinct(); } internal IDisposable TransformOperation( TransformOperationMode mode, TransformFlags flags ) => new TransformOperationScope( meshNative, mode, flags ); internal void Transform( Matrix matrix, TransformFlags flags ) => meshNative.Transform( matrix, flags ); }