Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ internal sealed class DecompilerOptionsBinder : BinderBase<DecompilerOptions>

private readonly Option<bool> _mergeBrushesOption;

private readonly Option<bool> _skipSolidSkyLeafsOption;

private readonly Option<bool> _includeLiquidsOption;

private readonly Option<BrushOptimization> _brushOptimizationOption;
Expand All @@ -21,13 +23,15 @@ internal sealed class DecompilerOptionsBinder : BinderBase<DecompilerOptions>
public DecompilerOptionsBinder(Option<bool> applyNullToGeneratedFacesOption,
Option<bool> alwaysGenerateOriginBrushesOption,
Option<bool> mergeBrushesOption,
Option<bool> skipSolidSkyLeafsOption,
Option<bool> includeLiquidsOption,
Option<BrushOptimization> brushOptimizationOption,
Option<List<string>> triggerEntityClassNameWildcardsOption)
{
_applyNullToGeneratedFacesOption = applyNullToGeneratedFacesOption;
_alwaysGenerateOriginBrushesOption = alwaysGenerateOriginBrushesOption;
_mergeBrushesOption = mergeBrushesOption;
_skipSolidSkyLeafsOption = skipSolidSkyLeafsOption;
_includeLiquidsOption = includeLiquidsOption;
_brushOptimizationOption = brushOptimizationOption;
_triggerEntityClassNameWildcardsOption = triggerEntityClassNameWildcardsOption;
Expand All @@ -38,6 +42,7 @@ protected override DecompilerOptions GetBoundValue(BindingContext bindingContext
var applyNullToGeneratedFaces = bindingContext.ParseResult.GetValueForOption(_applyNullToGeneratedFacesOption);
var alwaysGenerateOriginBrushes = bindingContext.ParseResult.GetValueForOption(_alwaysGenerateOriginBrushesOption);
var mergeBrushes = bindingContext.ParseResult.GetValueForOption(_mergeBrushesOption);
var skipSolidSkyLeafs = bindingContext.ParseResult.GetValueForOption(_skipSolidSkyLeafsOption);
var includeLiquids = bindingContext.ParseResult.GetValueForOption(_includeLiquidsOption);
var brushOptimization = bindingContext.ParseResult.GetValueForOption(_brushOptimizationOption);
var triggerEntityClassNameWildcards = bindingContext.ParseResult.GetValueForOption(_triggerEntityClassNameWildcardsOption);
Expand All @@ -47,6 +52,7 @@ protected override DecompilerOptions GetBoundValue(BindingContext bindingContext
ApplyNullToGeneratedFaces = applyNullToGeneratedFaces,
AlwaysGenerateOriginBrushes = alwaysGenerateOriginBrushes,
MergeBrushes = mergeBrushes,
SkipSolidSkyLeafs = skipSolidSkyLeafs,
IncludeLiquids = includeLiquids,
BrushOptimization = brushOptimization,
TriggerEntityWildcards = triggerEntityClassNameWildcards?.ToImmutableList() ?? ImmutableList<string>.Empty,
Expand Down
6 changes: 6 additions & 0 deletions HalfLife.UnifiedSdk.MapDecompiler.CmdLine/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ static async Task<int> Main(string[] args)
getDefaultValue: () => true,
description: "Whether to merge brushes");

var skipSolidSkyLeafsOption = new Option<bool>("--skip-outside",
getDefaultValue: () => false,
description: "Skip SKY/SOLID leafs");

var includeLiquidsOption = new Option<bool>("--include-liquids",
getDefaultValue: () => true,
description: "Whether to include brushes with liquid content types");
Expand All @@ -44,6 +48,7 @@ static async Task<int> Main(string[] args)
DecompilerOptionsBinder decompilerOptionsBinder = new(applyNullToGeneratedFacesOption,
alwaysGenerateOriginBrushesOption,
mergeBrushesOption,
skipSolidSkyLeafsOption,
includeLiquidsOption,
brushOptimizationOption,
triggerEntityClassNameWildcardsOption);
Expand All @@ -57,6 +62,7 @@ static async Task<int> Main(string[] args)
applyNullToGeneratedFacesOption,
alwaysGenerateOriginBrushesOption,
mergeBrushesOption,
skipSolidSkyLeafsOption,
includeLiquidsOption,
brushOptimizationOption,
triggerEntityClassNameWildcardsOption
Expand Down
9 changes: 9 additions & 0 deletions HalfLife.UnifiedSdk.MapDecompiler.GUI/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ public bool MergeBrushes
set => this.RaiseAndSetIfChanged(ref _mergeBrushes, value);
}

private bool _skipSolidSkyLeafs = false;

[DataMember]
public bool SkipSolidSkyLeafs
{
get => _skipSolidSkyLeafs;
set => this.RaiseAndSetIfChanged(ref _skipSolidSkyLeafs, value);
}

private bool _includeLiquids = true;

[DataMember]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public DecompilerOptions ToOptions()
ApplyNullToGeneratedFaces = Settings.ApplyNullToGeneratedFaces,
AlwaysGenerateOriginBrushes = Settings.AlwaysGenerateOriginBrushes,
MergeBrushes = Settings.MergeBrushes,
SkipSolidSkyLeafs = Settings.SkipSolidSkyLeafs,
IncludeLiquids = Settings.IncludeLiquids,
BrushOptimization = Settings.BrushOptimization,
TriggerEntityWildcards = Settings.TriggerEntityWildcards.ToImmutableList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,21 @@
</TabItem>

<TabItem Header="Face-To-Brush Decompiler">
<Grid ColumnDefinitions="Auto">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="DecompilerTabItemHeight"/>
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="DecompilerTabItemHeight"/>
</Grid.RowDefinitions>

<Label Content="No Additional Options"/>
</Grid>
<Grid ColumnDefinitions="Auto, Auto, Auto, Auto"
RowDefinitions="Auto, Auto, Auto, *">
<Label Grid.Column="0" Grid.Row="0" Content="General:"/>

<CheckBox Grid.Column="0" Grid.Row="1"
Content="Ignore SKY/SOLID faces(outside)"
IsChecked="{Binding Settings.SkipSolidSkyLeafs}"/>

</Grid>
</Grid>
</TabItem>
</TabControl>
</Grid>
Expand Down
2 changes: 2 additions & 0 deletions HalfLife.UnifiedSdk.MapDecompiler/DecompilerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public sealed class DecompilerOptions

public bool MergeBrushes { get; init; }

public bool SkipSolidSkyLeafs { get; init; }

public bool IncludeLiquids { get; init; }

public BrushOptimization BrushOptimization { get; init; } = BrushOptimization.BestTextureMatch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ internal sealed class FaceToBrushDecompiler
private readonly Edges _bspEdges;
private readonly List<Vector3> _bspVertices;
private readonly Models _bspModels;
private readonly Leaves _bspLeaves;
private readonly LeafFaces _bspMarksurfs;

private FaceToBrushDecompiler(ILogger logger, BspFile bspFile, DecompilerOptions options)
{
Expand All @@ -42,6 +44,8 @@ private FaceToBrushDecompiler(ILogger logger, BspFile bspFile, DecompilerOptions
_bspEdges = _bspFile.Edges;
_bspVertices = _bspFile.Vertices.Select(v => v.ToDouble()).ToList();
_bspModels = _bspFile.Models;
_bspLeaves = _bspFile.Leaves;
_bspMarksurfs = _bspFile.LeafFaces;
}

public static MapFile Decompile(ILogger logger, BspFile bspFile, DecompilerOptions options, CancellationToken cancellationToken)
Expand Down Expand Up @@ -80,6 +84,11 @@ private MapFile DecompileCore(CancellationToken cancellationToken)

entities.AddRange(mapFile.Worldspawn.Children.Cast<MapEntity>());

if (_options.SkipSolidSkyLeafs)
{
_logger.Information("Skip CONTENTS_SKY CONTENTS_SOLID faces enabled.");
}

foreach (var e in entities.Select((e, i) => new { Entity = e, Index = i }))
{
cancellationToken.ThrowIfCancellationRequested();
Expand Down Expand Up @@ -157,26 +166,67 @@ private BspSide FaceToSide(BspFace face)

private void RemoveBadSides(int modelNumber, List<BspSide> sides)
{
int absoluteIndex = 0;
List<int> skip_solid_sides = new List<int>();
List<int> skip_sky_sides = new List<int>();

for (int i = 0; i < sides.Count;)
int skipped_solid_sides = 0;
int skipped_sky_sides = 0;

if (_options.SkipSolidSkyLeafs)
{
foreach (var leaf in _bspLeaves)
{
if (leaf.Contents == Sledge.Formats.Bsp.Objects.Contents.Sky)
{
for (int i = leaf.FirstLeafFace; i < leaf.FirstLeafFace + leaf.NumLeafFaces; i++)
{
skip_sky_sides.Add(_bspMarksurfs[i]);
}
}
else if (leaf.Contents == Sledge.Formats.Bsp.Objects.Contents.Solid)
{
for (int i = leaf.FirstLeafFace; i < leaf.FirstLeafFace + leaf.NumLeafFaces; i++)
{
skip_solid_sides.Add(_bspMarksurfs[i]);
}
}
}
}

for (int i = sides.Count - 1; i >= 0; i--)
{
var side = sides[i];

if (side.Winding.Points.Count < 3)
if (skip_solid_sides.IndexOf(i) >= 0)
{
_logger.Warning("Skipping model {ModelNumber} face {Index}: face has only collinear points",
modelNumber, absoluteIndex);

_logger.Verbose("Skipping model {ModelNumber} face {Index}: CONTENTS_SOLID",
modelNumber, i);
sides.RemoveAt(i);
skipped_solid_sides++;
}
else
else if (skip_sky_sides.IndexOf(i) >= 0)
{
++i;
_logger.Verbose("Skipping model {ModelNumber} face {Index}: CONTENTS_SKY",
modelNumber, i);

sides.RemoveAt(i);
skipped_sky_sides++;
}
else if (side.Winding.Points.Count < 3)
{
_logger.Verbose("Skipping model {ModelNumber} face {Index}: face has only collinear points",
modelNumber, i);

++absoluteIndex;
sides.RemoveAt(i);
}
}

if (skipped_sky_sides > 0)
_logger.Information("Model {ModelNumber}: skipped {Count} SKY faces",
modelNumber, skipped_sky_sides);
if (skipped_solid_sides > 0)
_logger.Information("Model {ModelNumber}: skipped {Count} SOLID faces",
modelNumber, skipped_solid_sides);
}

private List<BspSide> MergeSides(int modelNumber, List<BspSide> sides)
Expand Down