From b4230cc2ed99db823cd6e96b7819edc4616dc376 Mon Sep 17 00:00:00 2001 From: ovska Date: Wed, 22 Oct 2025 20:43:20 +0300 Subject: [PATCH 1/5] fix: include shaders that have whiteimage or lightmap in pk3 --- Pack3r.Core/Models/Shader.cs | 7 ++++++- Pack3r.Core/Parsers/ShaderParser.cs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Pack3r.Core/Models/Shader.cs b/Pack3r.Core/Models/Shader.cs index b161045..92a8162 100644 --- a/Pack3r.Core/Models/Shader.cs +++ b/Pack3r.Core/Models/Shader.cs @@ -32,7 +32,7 @@ public sealed class Shader( public bool HasLightStyles { get; set; } /// Shader includes references to any files needed in pk3 - public bool NeededInPk3 => Resources.Count > 0 || Shaders.Count > 0 || ImplicitMapping.HasValue; + public bool NeededInPk3 => HasDollarMapping || Resources.Count > 0 || Shaders.Count > 0 || ImplicitMapping.HasValue; public string GetAbsolutePath() => Path.Combine(Source.RootPath, DestinationPath).NormalizePath(); @@ -41,6 +41,11 @@ public sealed class Shader( /// public QPath? ImplicitMapping { get; set; } + /// + /// Whether the shader has $whiteimage or $lightmap. + /// + public bool HasDollarMapping { get; set; } + public bool Equals(Shader? other) { return ReferenceEquals(this, other); diff --git a/Pack3r.Core/Parsers/ShaderParser.cs b/Pack3r.Core/Parsers/ShaderParser.cs index a5bb523..6eddab0 100644 --- a/Pack3r.Core/Parsers/ShaderParser.cs +++ b/Pack3r.Core/Parsers/ShaderParser.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Collections.Immutable; using System.Diagnostics; using System.Runtime.CompilerServices; using Pack3r.Extensions; @@ -454,6 +453,10 @@ public async IAsyncEnumerable Parse( { shader.Resources.Add(token); } + else + { + shader.HasDollarMapping = true; + } } else if (line.MatchKeyword("animMap", out token)) { From a499484c41159691e8edc9c21f6a0bebb57b824e Mon Sep 17 00:00:00 2001 From: ovska Date: Thu, 23 Oct 2025 11:25:52 +0300 Subject: [PATCH 2/5] fix: add more keywords to force shader to be included incl portal and fog parms --- Pack3r.Core/Models/Shader.cs | 6 +++--- Pack3r.Core/Parsers/ShaderParser.cs | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Pack3r.Core/Models/Shader.cs b/Pack3r.Core/Models/Shader.cs index 92a8162..6e5d3e3 100644 --- a/Pack3r.Core/Models/Shader.cs +++ b/Pack3r.Core/Models/Shader.cs @@ -32,7 +32,7 @@ public sealed class Shader( public bool HasLightStyles { get; set; } /// Shader includes references to any files needed in pk3 - public bool NeededInPk3 => HasDollarMapping || Resources.Count > 0 || Shaders.Count > 0 || ImplicitMapping.HasValue; + public bool NeededInPk3 => ForcePk3Include || Resources.Count > 0 || Shaders.Count > 0 || ImplicitMapping.HasValue; public string GetAbsolutePath() => Path.Combine(Source.RootPath, DestinationPath).NormalizePath(); @@ -42,9 +42,9 @@ public sealed class Shader( public QPath? ImplicitMapping { get; set; } /// - /// Whether the shader has $whiteimage or $lightmap. + /// Whether the shader has a $whiteimage or $lightmap stage, fogparms, or other shader directives that require it to be included. /// - public bool HasDollarMapping { get; set; } + public bool ForcePk3Include { get; set; } public bool Equals(Shader? other) { diff --git a/Pack3r.Core/Parsers/ShaderParser.cs b/Pack3r.Core/Parsers/ShaderParser.cs index 6eddab0..14b3e00 100644 --- a/Pack3r.Core/Parsers/ShaderParser.cs +++ b/Pack3r.Core/Parsers/ShaderParser.cs @@ -439,12 +439,6 @@ public async IAsyncEnumerable Parse( continue; } - // only map, animmap, clampmap and videomap are valid - if ((line.FirstChar | 0x20) is not ('m' or 'a' or 'c' or 'v')) - { - continue; - } - if (line.MatchKeyword("map", out token) || line.MatchKeyword("clampMap", out token)) { @@ -455,7 +449,7 @@ public async IAsyncEnumerable Parse( } else { - shader.HasDollarMapping = true; + shader.ForcePk3Include = true; } } else if (line.MatchKeyword("animMap", out token)) @@ -487,6 +481,10 @@ static void AddFrames(List list, ReadOnlyMemory source) { shader.Resources.Add(token); } + else if (!shader.ForcePk3Include && _forceIncludeKeywords.Contains(token)) + { + shader.ForcePk3Include = true; + } continue; } @@ -558,4 +556,15 @@ private enum State : byte /// In a stage e.g. map $lightmap Stage = 3, } + + private static readonly HashSet _forceIncludeKeywords = new(QString.Comparer) + { + "portal", + "fogvars", + "fogparms", + "skyfogvars", + "waterfogvars", + "lightgridmulamb", + "lightgridmuldir", + }; } From 1fb9c19d624e6f86a0af9784a69628ac83081048 Mon Sep 17 00:00:00 2001 From: ovska Date: Thu, 23 Oct 2025 11:38:00 +0300 Subject: [PATCH 3/5] feat: support for obj-models --- Pack3r.Console/Program.cs | 2 +- Pack3r.Core/Parsers/ObjParser.cs | 35 +++ Pack3r.Tests/ObjParserTests.cs | 405 +++++++++++++++++++++++++++++++ 3 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 Pack3r.Core/Parsers/ObjParser.cs create mode 100644 Pack3r.Tests/ObjParserTests.cs diff --git a/Pack3r.Console/Program.cs b/Pack3r.Console/Program.cs index f571031..fd9c7ad 100644 --- a/Pack3r.Console/Program.cs +++ b/Pack3r.Console/Program.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Text; using DotMake.CommandLine; using Pack3r.Extensions; @@ -27,6 +26,7 @@ internal static IConfiguration SetupDI() .Bind(1).To() .Bind(2).To() .Bind(3).To() + .Bind(4).To() .Bind().To() .Bind().To() .Bind().To() diff --git a/Pack3r.Core/Parsers/ObjParser.cs b/Pack3r.Core/Parsers/ObjParser.cs new file mode 100644 index 0000000..8f64b49 --- /dev/null +++ b/Pack3r.Core/Parsers/ObjParser.cs @@ -0,0 +1,35 @@ +using Pack3r.Extensions; +using Pack3r.IO; +using Pack3r.Models; + +namespace Pack3r.Parsers; + +public partial class ObjParser( + ILineReader reader) : IReferenceParser +{ + public string Description => "model"; + + public bool CanParse(ReadOnlyMemory resource) => resource.EndsWithF(".obj"); + + public async Task Parse(IAsset asset, CancellationToken cancellationToken) + { + ResourceList resources = []; + + await foreach (var line in reader.ReadLines(asset, cancellationToken)) + { + const string UseMaterial = "usemtl "; + + if (line.HasValue && line.Value.Span.StartsWithF(UseMaterial)) + { + QString materialName = ((QString)line.Value[UseMaterial.Length..].Trim()).TrimTextureExtension(); + + if (!materialName.IsEmpty) + { + resources.Add(Resource.Shader(materialName, in line)); + } + } + } + + return resources; + } +} diff --git a/Pack3r.Tests/ObjParserTests.cs b/Pack3r.Tests/ObjParserTests.cs new file mode 100644 index 0000000..530d2df --- /dev/null +++ b/Pack3r.Tests/ObjParserTests.cs @@ -0,0 +1,405 @@ + +using Pack3r.Parsers; + +namespace Pack3r.Tests; + +public static class ObjParserTests +{ + [Fact] + public static async Task Should_Parse() + { + var reader = new StringLineReader(Data); + var parser = new ObjParser(reader); + + Assert.True(parser.CanParse("model.obj".AsMemory())); + Assert.False(parser.CanParse("model.md3".AsMemory())); + + var result = await parser.Parse(new MockAsset(), default); + + Assert.NotNull(result); + Assert.Equal( + [ + "textures/venice_ase_c1/se_wal1", + "textures/venice_ase_c1/oldspain_kediwall5", + "textures/venice_ase_c1/er_white", + ], result.Select(s => s.Value.ToString())); + } + + private const string Data = """ +# Blender 4.5.3 LTS +# www.blender.org +mtllib arch_side_ammo_1st.mtl +o venice +v -111.125000 13.125000 0.000011 +v -112.000000 56.000000 0.000014 +v -112.000000 -0.000000 0.000010 +v -108.500000 24.500000 0.000011 +v -104.125000 34.125000 0.000012 +v -98.000000 42.000000 0.000012 +v -90.125000 48.125000 0.000012 +v -80.500000 52.500000 0.000011 +v -69.125000 55.125000 0.000010 +v -56.000000 56.000000 0.000009 +v -42.875000 55.125000 0.000008 +v 0.000000 56.000000 0.000004 +v -56.000000 56.000000 0.000009 +v -31.500000 52.500000 0.000007 +v -21.875000 48.125000 0.000006 +v -14.000000 42.000000 0.000004 +v -7.875000 34.125000 0.000003 +v -3.500000 24.500000 0.000002 +v -0.875000 13.125000 0.000001 +v -0.000000 -0.000000 -0.000000 +v -112.000000 56.000000 16.000013 +v -111.125000 13.124999 16.000011 +v -112.000000 -0.000001 16.000010 +v -108.500000 24.499998 16.000011 +v -104.125000 34.125000 16.000011 +v -98.000000 42.000000 16.000011 +v -90.125000 48.125000 16.000011 +v -80.500000 52.500000 16.000011 +v -69.125000 55.125000 16.000010 +v -56.000000 56.000000 16.000010 +v 0.000001 56.000000 16.000004 +v -42.875000 55.125000 16.000008 +v -31.499998 52.500000 16.000008 +v -21.874998 48.125000 16.000006 +v -13.999999 42.000000 16.000004 +v -7.874999 34.125000 16.000004 +v -3.499999 24.499998 16.000002 +v -0.874999 13.124999 16.000002 +v 0.000001 -0.000001 16.000000 +v -95.375000 9.374999 16.000010 +v -111.125000 13.124999 16.000011 +v -108.500000 24.499998 16.000011 +v -112.000000 -0.000001 16.000010 +v -93.500000 17.499998 16.000010 +v -90.375000 24.374998 16.000010 +v -86.000000 29.999998 16.000010 +v -98.000000 42.000000 16.000011 +v -96.000000 -0.000001 16.000008 +v -104.125000 34.125000 16.000011 +v -80.375000 34.375000 16.000010 +v -73.500000 37.500000 16.000010 +v -80.500000 52.500000 16.000011 +v -90.125000 48.125000 16.000011 +v -65.375000 39.375000 16.000010 +v -56.000000 40.000000 16.000008 +v -56.000000 56.000000 16.000010 +v -69.125000 55.125000 16.000010 +v -46.625000 39.375000 16.000008 +v -42.875000 55.125000 16.000008 +v -31.499998 52.500000 16.000008 +v -38.500000 37.500000 16.000006 +v -31.624998 34.375000 16.000006 +v -21.874998 48.125000 16.000006 +v -13.999999 42.000000 16.000004 +v -25.999998 29.999998 16.000004 +v -21.624998 24.374998 16.000004 +v -7.874999 34.125000 16.000004 +v -3.499999 24.499998 16.000002 +v -18.499998 17.499998 16.000004 +v -16.624998 9.374999 16.000002 +v -0.874999 13.124999 16.000002 +v 0.000001 -0.000001 16.000000 +v -15.999999 -0.000001 16.000002 +v -111.125000 13.125000 0.000011 +v -95.375000 9.375000 0.000009 +v -108.500000 24.500000 0.000011 +v -112.000000 -0.000000 0.000010 +v -96.000000 -0.000000 0.000008 +v -93.500000 17.500000 0.000009 +v -90.375000 24.375000 0.000010 +v -86.000000 30.000000 0.000010 +v -98.000000 42.000000 0.000012 +v -104.125000 34.125000 0.000012 +v -80.375000 34.375000 0.000010 +v -73.500000 37.500000 0.000009 +v -80.500000 52.500000 0.000011 +v -90.125000 48.125000 0.000012 +v -65.375000 39.375000 0.000009 +v -56.000000 40.000000 0.000008 +v -56.000000 56.000000 0.000009 +v -69.125000 55.125000 0.000010 +v -46.625000 39.375000 0.000007 +v -42.875000 55.125000 0.000008 +v -31.500000 52.500000 0.000007 +v -38.500000 37.500000 0.000006 +v -31.625000 34.375000 0.000005 +v -21.875000 48.125000 0.000006 +v -14.000000 42.000000 0.000004 +v -26.000000 30.000000 0.000005 +v -21.625000 24.375000 0.000004 +v -7.875000 34.125000 0.000003 +v -3.500000 24.500000 0.000002 +v -18.500000 17.500000 0.000003 +v -16.625000 9.375000 0.000002 +v -0.875000 13.125000 0.000001 +v -0.000000 -0.000000 -0.000000 +v -16.000000 -0.000000 0.000001 +v -96.000000 -0.000001 16.000008 +v -95.375000 9.374999 16.000010 +v -95.375000 9.375000 0.000009 +v -96.000000 -0.000000 0.000008 +v -93.500000 17.499998 16.000010 +v -93.500000 17.500000 0.000009 +v -90.375000 24.375000 0.000010 +v -90.375000 24.374998 16.000010 +v -86.000000 29.999998 16.000010 +v -86.000000 30.000000 0.000010 +v -86.000000 29.999998 16.000010 +v -80.375000 34.375000 16.000010 +v -80.375000 34.375000 0.000010 +v -86.000000 30.000000 0.000010 +v -73.500000 37.500000 16.000010 +v -65.375000 39.375000 16.000010 +v -65.375000 39.375000 0.000009 +v -73.500000 37.500000 0.000009 +v -56.000000 40.000000 16.000008 +v -56.000000 40.000000 0.000008 +v -56.000000 40.000000 16.000008 +v -46.625000 39.375000 16.000008 +v -56.000000 40.000000 0.000008 +v -46.625000 39.375000 0.000007 +v -38.500000 37.500000 0.000006 +v -38.500000 37.500000 16.000006 +v -31.624998 34.375000 16.000006 +v -31.625000 34.375000 0.000005 +v -26.000000 30.000000 0.000005 +v -25.999998 29.999998 16.000004 +v -21.624998 24.374998 16.000004 +v -21.625000 24.375000 0.000004 +v -26.000000 30.000000 0.000005 +v -25.999998 29.999998 16.000004 +v -18.500000 17.500000 0.000003 +v -16.624998 9.374999 16.000002 +v -16.625000 9.375000 0.000002 +v -18.499998 17.499998 16.000004 +v -16.000000 -0.000000 0.000001 +v -15.999999 -0.000001 16.000002 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 -0.0000 1.0000 +vn 0.9808 -0.1951 -0.0000 +vn 0.9239 -0.3827 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn 0.8315 -0.5555 -0.0000 +vn 0.8315 -0.5556 -0.0000 +vn 0.7072 -0.7070 -0.0000 +vn 0.5556 -0.8315 -0.0000 +vn 0.5555 -0.8315 -0.0000 +vn 0.3827 -0.9239 -0.0000 +vn 0.7070 -0.7072 -0.0000 +vn 0.1951 -0.9808 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.1951 -0.9808 -0.0000 +vn -0.3827 -0.9239 -0.0000 +vn -0.5555 -0.8315 -0.0000 +vn -0.5556 -0.8315 -0.0000 +vn -0.7070 -0.7072 -0.0000 +vn -0.8315 -0.5555 -0.0000 +vn -0.8315 -0.5556 -0.0000 +vn -0.9239 -0.3827 -0.0000 +vn -0.7072 -0.7070 -0.0000 +vn -0.9808 -0.1951 -0.0000 +vn -1.0000 -0.0000 -0.0000 +vt -0.305664 0.696289 +vt -0.312500 1.031250 +vt -0.285156 0.785156 +vt -0.250977 0.860352 +vt -0.203125 0.921875 +vt -0.312500 0.593750 +vt -0.141602 0.969727 +vt -0.066406 1.003906 +vt 0.022461 1.024414 +vt 0.125000 1.031250 +vt 0.071327 1.040039 +vt -0.031200 1.046875 +vt 0.406250 1.046875 +vt 0.160185 1.019531 +vt 0.235371 0.985352 +vt 0.296886 0.937500 +vt 0.344734 0.875977 +vt 0.378910 0.800781 +vt 0.399414 0.711914 +vt 0.406250 0.609375 +vt 0.625000 1.062500 +vt 0.631836 0.727539 +vt 0.652344 0.816406 +vt 0.686523 0.891602 +vt 0.734375 0.953125 +vt 0.795898 1.000977 +vt 0.625000 0.625000 +vt 0.871094 1.035156 +vt 0.959961 1.055664 +vt 1.062500 1.062500 +vt 0.125000 0.890625 +vt 0.000000 0.781250 +vt 0.000000 0.890625 +vt 0.125000 0.781250 +vt 0.000000 1.000000 +vt 0.125000 0.671875 +vt 0.125000 1.000000 +vt 0.000000 0.671875 +vt 0.125000 0.562500 +vt 0.000000 0.562500 +vt 0.125000 0.453125 +vt 0.000000 0.453125 +vt 0.000000 0.343750 +vt 0.125000 0.343750 +vt 0.125000 0.234375 +vt 0.000000 0.234375 +vt 0.000000 0.125000 +vt 0.125000 0.125000 +vt 0.546875 1.000000 +vt 0.546875 0.875000 +vt 0.468750 1.000000 +vt 0.468750 0.875000 +vt 0.625000 1.000000 +vt 0.625000 0.875000 +vt 0.390625 0.875000 +vt 0.390625 1.000000 +vt 0.312500 1.000000 +vt 0.312500 0.875000 +vt 0.234375 1.000000 +vt 0.234375 0.875000 +vt 0.156250 1.000000 +vt 0.156250 0.875000 +vt 0.078125 0.875000 +vt 0.078125 1.000000 +vt 0.000000 0.875000 +s 1 +usemtl textures/venice_ase_c1/se_wal1 +f 1/1/1 2/2/1 4/3/1 +f 2/2/1 5/4/1 4/3/1 +f 2/2/1 6/5/1 5/4/1 +f 1/1/1 3/6/1 2/2/1 +f 2/2/1 7/7/1 6/5/1 +f 2/2/1 8/8/1 7/7/1 +f 2/2/1 9/9/1 8/8/1 +f 2/2/1 10/10/1 9/9/1 +f 11/11/1 13/12/1 12/13/1 +f 11/11/1 12/13/1 14/14/1 +f 12/13/1 15/15/1 14/14/1 +f 12/13/1 16/16/1 15/15/1 +f 12/13/1 17/17/1 16/16/1 +f 12/13/1 18/18/1 17/17/1 +f 12/13/1 19/19/1 18/18/1 +f 12/13/1 20/20/1 19/19/1 +usemtl textures/venice_ase_c1/oldspain_kediwall5 +f 21/21/2 22/22/2 24/23/2 +f 21/21/2 24/23/2 25/24/2 +f 21/21/2 25/24/2 26/25/2 +f 21/21/2 26/25/2 27/26/2 +f 21/21/2 23/27/2 22/22/2 +f 21/21/2 27/26/2 28/28/2 +f 21/21/2 28/28/2 29/29/2 +f 21/21/2 29/29/2 30/30/2 +f 30/30/2 32/29/2 31/21/2 +f 31/21/2 32/29/2 33/28/2 +f 31/21/2 33/28/2 34/26/2 +f 31/21/2 34/26/2 35/25/2 +f 31/21/2 35/25/2 36/24/2 +f 31/21/2 36/24/2 37/23/2 +f 31/21/2 37/23/2 38/22/2 +f 31/21/2 38/22/2 39/27/2 +usemtl textures/venice_ase_c1/er_white +f 40/31/2 42/32/2 41/33/2 +f 40/31/2 44/34/2 42/32/2 +f 40/31/2 41/33/2 43/35/2 +f 42/32/2 44/34/2 45/36/2 +f 40/31/2 43/35/2 48/37/2 +f 42/32/2 45/36/2 49/38/2 +f 45/36/2 46/39/2 47/40/2 +f 45/36/2 47/40/2 49/38/2 +f 46/39/2 50/41/2 47/40/2 +f 47/40/2 50/41/2 53/42/2 +f 50/41/2 52/43/2 53/42/2 +f 50/41/2 51/44/2 52/43/2 +f 51/44/2 54/45/2 52/43/2 +f 52/43/2 54/45/2 57/46/2 +f 54/45/2 56/47/2 57/46/2 +f 54/45/2 55/48/2 56/47/2 +f 55/48/2 58/45/2 56/47/2 +f 56/47/2 58/45/2 59/46/2 +f 58/45/2 60/43/2 59/46/2 +f 58/45/2 61/44/2 60/43/2 +f 60/43/2 61/44/2 62/41/2 +f 60/43/2 62/41/2 63/42/2 +f 62/41/2 64/40/2 63/42/2 +f 62/41/2 65/39/2 64/40/2 +f 64/40/2 65/39/2 66/36/2 +f 64/40/2 66/36/2 67/38/2 +f 66/36/2 68/32/2 67/38/2 +f 66/36/2 69/34/2 68/32/2 +f 68/32/2 69/34/2 70/31/2 +f 68/32/2 70/31/2 71/33/2 +f 70/31/2 72/35/2 71/33/2 +f 70/31/2 73/37/2 72/35/2 +f 74/33/1 76/32/1 75/31/1 +f 75/31/1 76/32/1 79/34/1 +f 74/33/1 75/31/1 77/35/1 +f 75/31/1 78/37/1 77/35/1 +f 76/32/1 80/36/1 79/34/1 +f 76/32/1 83/38/1 80/36/1 +f 80/36/1 83/38/1 82/40/1 +f 80/36/1 82/40/1 81/39/1 +f 81/39/1 82/40/1 84/41/1 +f 82/40/1 87/42/1 84/41/1 +f 84/41/1 87/42/1 86/43/1 +f 84/41/1 86/43/1 85/44/1 +f 85/44/1 86/43/1 88/45/1 +f 86/43/1 91/46/1 88/45/1 +f 88/45/1 91/46/1 90/47/1 +f 88/45/1 90/47/1 89/48/1 +f 89/48/1 90/47/1 92/45/1 +f 90/47/1 93/46/1 92/45/1 +f 92/45/1 93/46/1 94/43/1 +f 92/45/1 94/43/1 95/44/1 +f 94/43/1 96/41/1 95/44/1 +f 94/43/1 97/42/1 96/41/1 +f 96/41/1 97/42/1 98/40/1 +f 96/41/1 98/40/1 99/39/1 +f 98/40/1 100/36/1 99/39/1 +f 98/40/1 101/38/1 100/36/1 +f 100/36/1 101/38/1 102/32/1 +f 100/36/1 102/32/1 103/34/1 +f 102/32/1 104/31/1 103/34/1 +f 102/32/1 105/33/1 104/31/1 +f 104/31/1 105/33/1 106/35/1 +f 104/31/1 106/35/1 107/37/1 +f 109/49/3 110/50/3 112/51/4 +f 110/50/3 113/52/4 112/51/4 +f 108/53/5 110/50/3 109/49/3 +f 108/53/5 111/54/5 110/50/3 +f 112/51/4 113/52/4 114/55/6 +f 112/51/4 114/55/6 115/56/7 +f 114/55/6 116/57/8 115/56/7 +f 114/55/6 117/58/8 116/57/8 +f 119/59/9 120/60/10 122/61/11 +f 120/60/10 125/62/11 122/61/11 +f 118/57/12 120/60/10 119/59/9 +f 118/57/12 121/58/12 120/60/10 +f 122/61/11 125/62/11 124/63/13 +f 122/61/11 124/63/13 123/64/13 +f 123/64/13 124/63/13 126/35/14 +f 124/63/13 127/65/14 126/35/14 +f 128/65/14 130/35/14 129/63/15 +f 129/63/15 130/35/14 131/64/15 +f 129/63/15 131/64/15 132/61/16 +f 129/63/15 132/61/16 133/62/16 +f 132/61/16 134/60/17 133/62/16 +f 132/61/16 135/59/18 134/60/17 +f 134/60/17 135/59/18 136/57/19 +f 134/60/17 136/57/19 137/58/19 +f 138/55/20 139/56/21 142/51/22 +f 138/55/20 142/51/22 145/52/22 +f 138/55/20 140/57/23 139/56/21 +f 138/55/20 141/58/23 140/57/23 +f 142/51/22 143/50/24 145/52/22 +f 142/51/22 144/49/24 143/50/24 +f 143/50/24 144/49/24 146/53/25 +f 143/50/24 146/53/25 147/54/25 +"""; +} + + From 439c8965fce428144816cdc3d04ceac5dd888e07 Mon Sep 17 00:00:00 2001 From: ovska Date: Thu, 23 Oct 2025 11:53:53 +0300 Subject: [PATCH 4/5] feat: add support for non-q3map2 generated ASE models --- Pack3r.Core/Parsers/AseParser.cs | 17 +++-- Pack3r.Tests/AseParserTests.cs | 105 +++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 Pack3r.Tests/AseParserTests.cs diff --git a/Pack3r.Core/Parsers/AseParser.cs b/Pack3r.Core/Parsers/AseParser.cs index be6d40e..d8c87b6 100644 --- a/Pack3r.Core/Parsers/AseParser.cs +++ b/Pack3r.Core/Parsers/AseParser.cs @@ -20,24 +20,26 @@ public partial class AseParser( await foreach (var line in reader.ReadLines(asset, cancellationToken)) { - if (Comment().IsMatch(line.Value.Span)) + ReadOnlySpan span = line.Value.Span; + + if (Comment().IsMatch(span)) { - if (!line.Value.Span.Contains("Generated by Q3Map2", StringComparison.Ordinal)) + if (!span.Contains("Generated by Q3Map2", StringComparison.Ordinal)) { - logger.Warn($"Unsupported .ASE model (not generated by Q3Map2): '{asset.FullPath}'"); - return null; + logger.Warn($"Potentially unsupported .ASE model (not generated by Q3Map2): '{asset.FullPath}'"); } } - else if (line.Value.Span.StartsWithF("*MAP_NAME")) + else if (Bitmap().IsMatch(span)) { if (line.Value.TryReadPastWhitespace(out var token) && !token.EqualsF("\"NOSHADER\"") && + !token.EqualsF("\"default\"") && !token.EqualsF("\"textures/common/nodraw\"")) { resources.Add(Resource.Shader(token.Trim('"'), in line)); } } - else if (line.Value.Span.StartsWithF("*GEOMOBJECT")) + else if (span.StartsWithF("*GEOMOBJECT")) { break; } @@ -48,4 +50,7 @@ public partial class AseParser( [GeneratedRegex("""\*COMMENT\s""", RegexOptions.Singleline, 1000)] private static partial Regex Comment(); + + [GeneratedRegex("""\*BITMAP\s""", RegexOptions.Singleline, 1000)] + private static partial Regex Bitmap(); } diff --git a/Pack3r.Tests/AseParserTests.cs b/Pack3r.Tests/AseParserTests.cs new file mode 100644 index 0000000..851182d --- /dev/null +++ b/Pack3r.Tests/AseParserTests.cs @@ -0,0 +1,105 @@ + +using Pack3r.Logging; +using Pack3r.Parsers; + +namespace Pack3r.Tests; + +public static class AseParserTests +{ + [Fact] + public static async Task Should_Parse() + { + var reader = new StringLineReader(Data); + var parser = new AseParser(NullLogger.Instance, reader); + + Assert.True(parser.CanParse("model.ase".AsMemory())); + Assert.False(parser.CanParse("model.obj".AsMemory())); + + var result = await parser.Parse(new MockAsset(), default); + + Assert.NotNull(result); + Assert.Equal( + [ + "textures/venice_ase_c1/box_m02", + "textures/venice_ase_c1/box_m03", + "textures/venice_ase_c1/box_m01", + ], result.Select(s => s.Value.ToString())); + } + + private const string Data = + """ + *3DSMAX_ASCIIEXPORT 200 +*COMMENT "Generated by Q3Map2 (ydnar) -convert -format ase" +*SCENE { + *SCENE_FILENAME "box_m02.bsp" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 100 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 3 + *MATERIAL 0 { + *MATERIAL_NAME "textures/venice_ase_c1/box_m02" + *MATERIAL_CLASS "Standard" + *MATERIAL_DIFFUSE 1.000000 0.880850 0.571197 + *MATERIAL_SHADING Phong + *MAP_DIFFUSE { + *MAP_NAME "textures/venice_ase_c1/box_m02" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0 + *MAP_TYPE Screen + *BITMAP "textures/venice_ase_c1/box_m02" + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 1 { + *MATERIAL_NAME "textures/venice_ase_c1/box_m03" + *MATERIAL_CLASS "Standard" + *MATERIAL_DIFFUSE 1.000000 0.886761 0.619810 + *MATERIAL_SHADING Phong + *MAP_DIFFUSE { + *MAP_NAME "textures/venice_ase_c1/box_m03" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0 + *MAP_TYPE Screen + *BITMAP "textures/venice_ase_c1/box_m03" + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "textures/venice_ase_c1/box_m01" + *MATERIAL_CLASS "Standard" + *MATERIAL_DIFFUSE 1.000000 0.887614 0.607439 + *MATERIAL_SHADING Phong + *MAP_DIFFUSE { + *MAP_NAME "textures/venice_ase_c1/box_m01" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0 + *MAP_TYPE Screen + *BITMAP "textures/venice_ase_c1/box_m01" + *BITMAP_FILTER Pyramidal + } + } +} +*GEOMOBJECT { + *NODE_NAME "mat2model0surf0" + *NODE_TM { + *NODE_NAME "mat2model0surf0" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0 0 0 + *TM_ROW1 0 1.0 0 + *TM_ROW2 0 0 1.0 + *TM_ROW3 0 0 0 + *TM_POS 0.000000 0.000000 0.000000 + } +} +"""; +} From b1f28965ff843c2d37710786636e38e7fc0f4f07 Mon Sep 17 00:00:00 2001 From: ovska Date: Thu, 23 Oct 2025 12:18:25 +0300 Subject: [PATCH 5/5] fix: pak0 shaders packed if they are in etmain --- Pack3r.Core/Services/Packager.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Pack3r.Core/Services/Packager.cs b/Pack3r.Core/Services/Packager.cs index 0936863..4c8a477 100644 --- a/Pack3r.Core/Services/Packager.cs +++ b/Pack3r.Core/Services/Packager.cs @@ -235,7 +235,25 @@ void AddCompileFile(string absolutePath) void AddShaderFile(Shader shader, Resource resource) { if (shader.Source.NotPacked) + { + handledFiles.Add(shader.DestinationPath); return; + } + + foreach (var source in map.AssetSources) + { + if (source.NotPacked && + source.Assets.ContainsKey(shader.DestinationPath)) + { + if (options.ShaderDebug) + { + logger.Debug($"Shader not packed, also present in {source.Name}: '{shader.GetAbsolutePath()}'"); + } + + handledFiles.Add(shader.DestinationPath); + return; + } + } if (TryAddFileFromSource(shader.Source, shader.DestinationPath.AsMemory(), resource, shader)) return;