Skip to content

Commit 20edef4

Browse files
authored
Merge pull request #38 from DomCR/obj-reader
Obj Reader
2 parents 414c6f3 + 329286d commit 20edef4

26 files changed

Lines changed: 725 additions & 102 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,5 @@ MigrationBackup/
360360
!MeshIO.OBJ
361361
/local
362362
/src/Tests/outFiles
363+
!src/Tests/inFiles/obj/
364+
!src/Tests/inFiles/obj/*.obj

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ For more [information](https://github.com/DomCR/MeshIO/tree/master/src/MeshIO.ST
3030

3131
For more [information](https://github.com/DomCR/MeshIO/tree/master/src/MeshIO.GLTF).
3232

33+
### MeshIO.OBJ
34+
35+
- Read Wavefront OBJ files
36+
37+
For more [information](https://github.com/DomCR/MeshIO/tree/master/src/MeshIO.OBJ).
38+
39+
3340
Contributing
3441
------------
3542

src/MeshIO.FBX/FbxReader.cs

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,17 @@ public class FbxReader : ReaderBase
1010
{
1111
public FbxReaderOptions Options { get; } = new FbxReaderOptions();
1212

13-
private Stream _stream;
14-
1513
/// <summary>
1614
/// Initializes a new instance of the <see cref="FbxReader"/> class for the specified file.
1715
/// </summary>
18-
/// <param name="path">The complete file path to read to.</param>
19-
public FbxReader(string path) : this(File.OpenRead(path)) { }
16+
/// <param name="path">The complete file path to read from</param>
17+
public FbxReader(string path) : base(File.OpenRead(path)) { }
2018

2119
/// <summary>
2220
/// Initializes a new instance of the <see cref="FbxReader"/> class for the specified stream.
2321
/// </summary>
24-
/// <param name="stream">The stream to write to.</param>
25-
public FbxReader(Stream stream)
26-
{
27-
if (stream == null)
28-
throw new ArgumentNullException(nameof(stream));
29-
30-
if (!stream.CanSeek)
31-
throw new ArgumentException("The stream must support seeking. Try reading the data into a buffer first");
32-
33-
this._stream = stream;
34-
}
22+
/// <param name="stream">The stream to read from</param>
23+
public FbxReader(Stream stream) : base(stream) { }
3524

3625
/// <summary>
3726
/// Read a fbx file into an scene
@@ -76,7 +65,7 @@ public FbxRootNode Parse()
7665
/// <summary>
7766
/// Read the FBX file
7867
/// </summary>
79-
public Scene Read()
68+
public override Scene Read()
8069
{
8170
FbxRootNode root = this.Parse();
8271
var reader = FbxFileBuilderBase.Create(root, this.Options);
@@ -85,12 +74,6 @@ public Scene Read()
8574
return reader.Read();
8675
}
8776

88-
/// <inheritdoc/>
89-
public override void Dispose()
90-
{
91-
_stream.Dispose();
92-
}
93-
9477
private static IFbxParser getParser(Stream stream, FbxReaderOptions options)
9578
{
9679
IFbxParser parser = null;

src/MeshIO.FBX/Templates/FbxMeshTemplate.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ private void readPolygons()
137137

138138
protected List<Polygon> mapPolygons(int[] arr)
139139
{
140-
List<Polygon> Polygons = new List<Polygon>();
140+
List<Polygon> polygons = new List<Polygon>();
141141

142142
if (arr == null)
143-
return Polygons;
143+
return polygons;
144144

145145
//Check if the arr are faces or quads
146146
if (arr[2] < 0)
@@ -153,7 +153,7 @@ protected List<Polygon> mapPolygons(int[] arr)
153153
//Substract a unit to the last
154154
Math.Abs(arr[i]) - 1);
155155

156-
Polygons.Add(tmp);
156+
polygons.Add(tmp);
157157
}
158158
}
159159
//Quads
@@ -168,11 +168,11 @@ protected List<Polygon> mapPolygons(int[] arr)
168168
//Substract a unit to the last
169169
Math.Abs(arr[i]) - 1);
170170

171-
Polygons.Add(tmp);
171+
polygons.Add(tmp);
172172
}
173173
}
174174

175-
return Polygons;
175+
return polygons;
176176
}
177177

178178
protected int[] polygonsArray(Mesh mesh)

src/MeshIO.GLTF/GltfReader.cs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,42 @@ public class GltfReader : ReaderBase
1313
{
1414
private GlbHeader _header;
1515
private GltfRoot _root;
16-
private readonly StreamIO _stream;
1716
private StreamIO _binaryStream;
1817

18+
private readonly StreamIO _streamIO;
19+
1920
public GltfReader(string path) : this(new FileStream(path, FileMode.Open)) { }
2021

21-
public GltfReader(Stream stream)
22+
public GltfReader(Stream stream) : base(stream)
2223
{
23-
if (stream == null)
24-
throw new ArgumentNullException(nameof(stream));
25-
26-
if (!stream.CanSeek)
27-
throw new ArgumentException("The stream must support seeking. Try reading the data into a buffer first");
28-
29-
this._stream = new StreamIO(stream);
24+
this._streamIO = new StreamIO(this._stream);
3025
}
3126

3227
/// <summary>
3328
/// Read the GLTF file
3429
/// </summary>
35-
public Scene Read()
30+
public override Scene Read()
3631
{
3732
//The 12-byte header consists of three 4-byte entries:
3833
this._header = new GlbHeader();
3934
//magic equals 0x46546C67. It is ASCII string glTF, and can be used to identify data as Binary glTF.
40-
this._header.Magic = this._stream.ReadUInt<LittleEndianConverter>();
35+
this._header.Magic = this._streamIO.ReadUInt<LittleEndianConverter>();
4136
//version indicates the version of the Binary glTF container format. This specification defines version 2.
42-
this._header.Version = this._stream.ReadUInt<LittleEndianConverter>();
37+
this._header.Version = this._streamIO.ReadUInt<LittleEndianConverter>();
4338
//length is the total length of the Binary glTF, including Header and all Chunks, in bytes.
44-
this._header.Length = this._stream.ReadUInt<LittleEndianConverter>();
39+
this._header.Length = this._streamIO.ReadUInt<LittleEndianConverter>();
4540

4641
if (this._header.Version != 2)
4742
throw new NotSupportedException($"Version {this._header.Version} not supported");
4843

4944
//Chunk 0 Json
50-
uint jsonChunkLength = this._stream.ReadUInt<LittleEndianConverter>();
51-
string jsonChunkType = this._stream.ReadString(4);
45+
uint jsonChunkLength = this._streamIO.ReadUInt<LittleEndianConverter>();
46+
string jsonChunkType = this._streamIO.ReadString(4);
5247

5348
if (jsonChunkType != "JSON")
54-
throw new GltfReaderException("Chunk type does not match", this._stream.Position);
49+
throw new GltfReaderException("Chunk type does not match", this._streamIO.Position);
5550

56-
string json = this._stream.ReadString((int)jsonChunkLength);
51+
string json = this._streamIO.ReadString((int)jsonChunkLength);
5752

5853
#if NETFRAMEWORK || NETSTANDARD
5954
_root = Newtonsoft.Json.JsonConvert.DeserializeObject<GltfRoot>(json);
@@ -62,14 +57,14 @@ public Scene Read()
6257
#endif
6358

6459
//Chunk 1 bin
65-
uint binChunkLength = this._stream.ReadUInt<LittleEndianConverter>();
66-
string binChunkType = this._stream.ReadString(4);
60+
uint binChunkLength = this._streamIO.ReadUInt<LittleEndianConverter>();
61+
string binChunkType = this._streamIO.ReadString(4);
6762

6863
//Check the chunk type
6964
if (binChunkType != "BIN\0")
70-
throw new GltfReaderException("Chunk type does not match", this._stream.Position);
65+
throw new GltfReaderException("Chunk type does not match", this._streamIO.Position);
7166

72-
byte[] binChunk = this._stream.ReadBytes((int)binChunkLength);
67+
byte[] binChunk = this._streamIO.ReadBytes((int)binChunkLength);
7368
this._binaryStream = new StreamIO(binChunk);
7469

7570
var reader = GltfBinaryReaderBase.GetBynaryReader((int)this._header.Version, this._root, binChunk);
@@ -81,7 +76,6 @@ public Scene Read()
8176
/// <inheritdoc/>
8277
public override void Dispose()
8378
{
84-
this._stream.Dispose();
8579
this._binaryStream?.Dispose();
8680
}
8781
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
10+
<PackageReference Include="xunit" Version="2.4.1" />
11+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
<PrivateAssets>all</PrivateAssets>
14+
</PackageReference>
15+
<PackageReference Include="coverlet.collector" Version="3.1.0">
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17+
<PrivateAssets>all</PrivateAssets>
18+
</PackageReference>
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\MeshIO.OBJ\MeshIO.OBJ.csproj" />
23+
</ItemGroup>
24+
25+
<Import Project="..\MeshIO.Tests.Shared\MeshIO.Tests.Shared.projitems" Label="Shared" />
26+
27+
</Project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using MeshIO.Tests.Shared;
2+
using System.IO;
3+
using Xunit;
4+
using Xunit.Abstractions;
5+
6+
namespace MeshIO.OBJ.Tests
7+
{
8+
public class ObjReaderTest : IOTestsBase
9+
{
10+
public static readonly TheoryData<string> Files;
11+
12+
static ObjReaderTest()
13+
{
14+
Files = new TheoryData<string>();
15+
foreach (string file in Directory.GetFiles(FolderPath.InFilesObj, "*.obj"))
16+
{
17+
Files.Add(file);
18+
}
19+
}
20+
21+
public ObjReaderTest(ITestOutputHelper output) : base(output) { }
22+
23+
[Theory]
24+
[MemberData(nameof(Files))]
25+
public void ReadTest(string test)
26+
{
27+
Scene scene = null;
28+
using (ObjReader reader = new ObjReader(test))
29+
{
30+
reader.OnNotification += this.onNotification;
31+
scene = reader.Read();
32+
}
33+
34+
Assert.NotNull(scene);
35+
Assert.NotEmpty(scene.RootNode.Nodes);
36+
}
37+
}
38+
}

src/MeshIO.OBJ/MeshIO.OBJ.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net6.0;net5.0;net48;netstandard2.1</TargetFrameworks>
5+
<PackageId>MeshIO.OBJ</PackageId>
6+
<PackageTags>C# 3D obj</PackageTags>
7+
<Description>MeshIO module for obj format.</Description>
58
</PropertyGroup>
69

710
<ItemGroup>

src/MeshIO.OBJ/ObjData.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using CSMath;
2+
using System.Collections.Generic;
3+
4+
namespace MeshIO.OBJ
5+
{
6+
internal class ObjData
7+
{
8+
public ObjTemplate Current { get; private set; }
9+
10+
public ObjTemplate Placeholder { get; private set; }
11+
12+
public List<ObjTemplate> Templates { get; private set; } = [];
13+
14+
public ObjData()
15+
{
16+
Placeholder = new ObjTemplate(string.Empty);
17+
}
18+
19+
public void CreateIndexer(string line)
20+
{
21+
this.MoveNext();
22+
23+
this.Current = new ObjTemplate(line);
24+
}
25+
26+
public void MoveNext()
27+
{
28+
if (this.Current == null)
29+
{
30+
return;
31+
}
32+
33+
this.Current.Vertices.AddRange(this.Placeholder.Vertices);
34+
this.Current.Normals.AddRange(this.Placeholder.Normals);
35+
this.Current.UVs.AddRange(this.Placeholder.UVs);
36+
37+
this.Current.MeshPolygons.AddRange(this.Placeholder.MeshPolygons);
38+
this.Current.TexturePolygons.AddRange(this.Placeholder.TexturePolygons);
39+
this.Current.NormalPolygons.AddRange(this.Placeholder.NormalPolygons);
40+
41+
this.Templates.Add(this.Current);
42+
43+
this.Placeholder.Vertices.Clear();
44+
this.Placeholder.Normals.Clear();
45+
this.Placeholder.UVs.Clear();
46+
47+
this.Placeholder.MeshPolygons.Clear();
48+
this.Placeholder.TexturePolygons.Clear();
49+
this.Placeholder.NormalPolygons.Clear();
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)