├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── build
├── BuildRelease.ps1
├── Resources
│ ├── PowershellFunctions.ps1
│ └── nuget.exe
└── nuspec
│ └── FileFormatWavefront
│ ├── FileFormatWavefront.nuspec
│ └── lib
│ └── net40
│ ├── FileFormatWavefront.XML
│ └── FileFormatWavefront.dll
├── models
├── Motherboard
│ ├── Motherboard.mtl
│ └── Textures
│ │ ├── mb-ps-2.png
│ │ └── p4sba-mb.jpg
└── Palm
│ ├── Bottom_T.jpg
│ ├── Bottom_Trunk.bmp
│ ├── Date Palm.mtl
│ └── Source.md
└── source
├── FileFormatWavefront.sln
├── FileFormatWavefront
├── Extensions
│ └── StringExtensions.cs
├── FileFormatMtl.cs
├── FileFormatObj.cs
├── FileFormatWavefront.csproj
├── FileLoadResult.cs
├── Internals
│ └── LineData.cs
├── Message.cs
├── MessageType.cs
├── Model
│ ├── Colour.cs
│ ├── Face.cs
│ ├── Group.cs
│ ├── Index.cs
│ ├── Material.cs
│ ├── Scene.cs
│ ├── TextureMap.cs
│ ├── UV.cs
│ └── Vertex.cs
└── Properties
│ └── AssemblyInfo.cs
├── ObjValidator
├── ObjValidator.csproj
├── Program.cs
└── Properties
│ └── AssemblyInfo.cs
└── WavefrontObjectLoader
├── App.config
├── Details
├── DetailsUi.cs
├── DetailsUiBuilder.cs
├── FacesUi.cs
├── IDetailsUi.cs
├── PropertyGridUi.cs
├── UvsUi.cs
└── VerticesUi.cs
├── Extensions
└── RichTextBoxExtensions.cs
├── FormModelLoader.Designer.cs
├── FormModelLoader.cs
├── FormModelLoader.resx
├── Program.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
└── WavefrontObjectLoader.csproj
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # Support 'GitHub Sponsors' funding.
2 | github: dwmkerr
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | [Bb]in/
15 | [Oo]bj/
16 |
17 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
18 | !packages/*/build/
19 |
20 | # MSTest test Results
21 | [Tt]est[Rr]esult*/
22 | [Bb]uild[Ll]og.*
23 |
24 | *_i.c
25 | *_p.c
26 | *.ilk
27 | *.meta
28 | *.obj
29 | *.pch
30 | *.pdb
31 | *.pgc
32 | *.pgd
33 | *.rsp
34 | *.sbr
35 | *.tlb
36 | *.tli
37 | *.tlh
38 | *.tmp
39 | *.tmp_proj
40 | *.log
41 | *.vspscc
42 | *.vssscc
43 | .builds
44 | *.pidb
45 | *.log
46 | *.scc
47 |
48 | # Visual C++ cache files
49 | ipch/
50 | *.aps
51 | *.ncb
52 | *.opensdf
53 | *.sdf
54 | *.cachefile
55 |
56 | # Visual Studio profiler
57 | *.psess
58 | *.vsp
59 | *.vspx
60 |
61 | # Guidance Automation Toolkit
62 | *.gpState
63 |
64 | # ReSharper is a .NET coding add-in
65 | _ReSharper*/
66 | *.[Rr]e[Ss]harper
67 |
68 | # TeamCity is a build add-in
69 | _TeamCity*
70 |
71 | # DotCover is a Code Coverage Tool
72 | *.dotCover
73 |
74 | # NCrunch
75 | *.ncrunch*
76 | .*crunch*.local.xml
77 |
78 | # Installshield output folder
79 | [Ee]xpress/
80 |
81 | # DocProject is a documentation generator add-in
82 | DocProject/buildhelp/
83 | DocProject/Help/*.HxT
84 | DocProject/Help/*.HxC
85 | DocProject/Help/*.hhc
86 | DocProject/Help/*.hhk
87 | DocProject/Help/*.hhp
88 | DocProject/Help/Html2
89 | DocProject/Help/html
90 |
91 | # Click-Once directory
92 | publish/
93 |
94 | # Publish Web Output
95 | *.Publish.xml
96 | *.pubxml
97 |
98 | # NuGet Packages Directory
99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
100 | #packages/
101 |
102 | # Windows Azure Build Output
103 | csx
104 | *.build.csdef
105 |
106 | # Windows Store app package directory
107 | AppPackages/
108 |
109 | # Others
110 | sql/
111 | *.Cache
112 | ClientBin/
113 | [Ss]tyle[Cc]op.*
114 | ~$*
115 | *~
116 | *.dbmdl
117 | *.[Pp]ublish.xml
118 | *.pfx
119 | *.publishsettings
120 |
121 | # RIA/Silverlight projects
122 | Generated_Code/
123 |
124 | # Backup & report files from converting an old project file to a newer
125 | # Visual Studio version. Backup files are not needed, because we have git ;-)
126 | _UpgradeReport_Files/
127 | Backup*/
128 | UpgradeLog*.XML
129 | UpgradeLog*.htm
130 |
131 | # SQL Server files
132 | App_Data/*.mdf
133 | App_Data/*.ldf
134 |
135 | # =========================
136 | # Windows detritus
137 | # =========================
138 |
139 | # Windows image file caches
140 | Thumbs.db
141 | ehthumbs.db
142 |
143 | # Folder config file
144 | Desktop.ini
145 |
146 | # Recycle Bin used on file shares
147 | $RECYCLE.BIN/
148 |
149 | # Mac crap
150 | .DS_Store
151 | build/1.*
152 | build/nuspec/FileFormatWavefront/lib
153 | build/nuspec/FileFormatWavefront/lib/
154 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Dave Kerr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | file-format-wavefront
2 | =====================
3 |
4 | A simple .NET library to load data from Wavefront *.obj and *.mlb files.
5 |
6 | ❗ **Deprecated** - this library bas been deprecated. I recommened using the [`SharpGL.Serialization`](https://github.com/dwmkerr/sharpgl/tree/master/source/SharpGL/Core/SharpGL.Serialization) package which supports Wavefront, Discreet, and more.
7 |
8 | Installation
9 | ------------
10 |
11 | You can use Nuget to install the library.
12 |
13 | ````
14 | PM> Install-Package FileFormatWavefront
15 | ````
16 |
17 | Usage
18 | -----
19 |
20 | To load data from a Wavefront Object File, use the ``FileFormatObj`` class:
21 |
22 | ````csharp
23 | // Use the File Format object to load the test data.
24 | bool loadTextureImages = true;
25 | var result = FileFormatObj.Load("MyFile.obj", loadTextureImages);
26 | ````
27 |
28 | The object that is returned is a ``FileLoadResult`` object. The object contains a property ``Model`` which is the Scene of data loaded.
29 |
30 | Texture map images can optionally be loaded, but you can skip this if you are not going to need the image or will load it yourself (whether or not the image is loaded, a ``TextureMap`` object is still created with the image path as part of it).
31 |
32 | The object also contains a collection of ``Message`` objects describing any warnings or errors encountered loading the file. An example of iterating through the vertices and messages would look like this:
33 |
34 | ````csharp
35 |
36 | // Show each vertex.
37 | foreach(Vertex v in result.Model.Vertices)
38 | {
39 | Console.Write("{0} {1} {2}", v.x, v.y, v.z);
40 | }
41 |
42 | // Show each message.
43 | foreach (var message in result.Messages)
44 | {
45 | Console.WriteLine("{0}: {1}", message.MessageType, message.Details);
46 | Console.WriteLine("{0}: {1}", message.FileName, message.LineNumber);
47 | }
48 |
49 | ````
50 |
51 | The ``Model`` property is of type ``Scene`` and contains the following members:
52 |
53 | * ``Vertices`` all vertex data.
54 | * ``Uvs`` all material coordinate data.
55 | * ``Normals`` all normal data.
56 | * ``Materials`` all material data.
57 | * ``Groups`` all objects (groups of faces).
58 | * ``UngroupedFaces`` all faces not grouped into objects.
59 |
60 | Associated *.mlb (material library) files are loaded automatically. If you want to load a material library only, you can use the ``FileFormatMlb`` class in the same way as the ``FileFormatObj`` class.
61 |
--------------------------------------------------------------------------------
/build/BuildRelease.ps1:
--------------------------------------------------------------------------------
1 | # IMPORTANT: Make sure that the path to msbuild is correct!
2 | $msbuild = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe"
3 | if ((Test-Path $msbuild) -eq $false) {
4 | Write-Host "Cannot find msbuild at '$msbuild'."
5 | Break
6 | }
7 |
8 | # Load useful functions.
9 | . .\Resources\PowershellFunctions.ps1
10 |
11 | # Keep track of the 'release' folder location - it's the root of everything else.
12 | # We can also build paths to the key locations we'll use.
13 | $scriptParentPath = Split-Path -parent $MyInvocation.MyCommand.Definition
14 | $folderReleaseRoot = $scriptParentPath
15 | $folderSourceRoot = Split-Path -parent $folderReleaseRoot
16 | $folderSolutionsRoot = Join-Path $folderSourceRoot "source"
17 | $folderNuspecRoot = Join-Path $folderSourceRoot "build\nuspec"
18 |
19 | # Part 1 - Build the main libraries.
20 | Write-Host "Preparing to build the core library..."
21 | $solutionCoreLibraries = Join-Path $folderSolutionsRoot "FileFormatWavefront.sln"
22 | . $msbuild $solutionCoreLibraries /p:Configuration=Release /verbosity:minimal
23 |
24 | # Part 2 - Get the version number of the core library, use this to build the destination release folder.
25 | $folderBinaries = Join-Path $folderSolutionsRoot "FileFormatWavefront\bin\Release"
26 | $releaseVersion = [Reflection.Assembly]::LoadFile((Join-Path $folderBinaries "FileFormatWavefront.dll")).GetName().Version
27 | Write-Host "Built Core Library. Release Version: $releaseVersion"
28 |
29 | # Part 3 - Copy the core library to the release.
30 | $folderRelease = Join-Path $folderReleaseRoot $releaseVersion
31 | $folderReleaseCore = Join-Path $folderRelease "Core"
32 | EnsureEmptyFolderExists($folderReleaseCore)
33 | CopyItems (Join-Path $folderBinaries "*.*") $folderReleaseCore
34 |
35 | # Part 4 - Build the Nuget Package
36 | Write-Host "Preparing to build the Nuget Package..."
37 | $folderReleasePackage = Join-Path $folderRelease "Package"
38 | EnsureEmptyFolderExists($folderReleasePackage)
39 | $nuget = Join-Path $scriptParentPath "Resources\nuget.exe"
40 |
41 | CopyItems (Join-Path $folderReleaseCore "*.*") (Join-Path $folderNuspecRoot "FileFormatWavefront\lib\net40")
42 | . $nuget pack (Join-Path $folderNuspecRoot "FileFormatWavefront\FileFormatWavefront.nuspec") -Version $releaseVersion -OutputDirectory $folderReleasePackage
43 |
44 | # We're done!
45 | Write-Host "Successfully built version: $releaseVersion"
--------------------------------------------------------------------------------
/build/Resources/PowershellFunctions.ps1:
--------------------------------------------------------------------------------
1 | # Copy items to a destination folder, creating the folder if needed.
2 | function CopyItems($source, $destinationFolder) {
3 |
4 | # Create the any folders or subfolders up to the destination that don't exist.
5 | EnsureFolderExists($destinationFolder)
6 |
7 | # Now copy the items.
8 | Copy-Item $source -Destination $destinationFolder
9 | }
10 |
11 | # Ensures that a folder exists.
12 | function EnsureFolderExists($folder) {
13 |
14 | # Create the any folders or subfolders up to the destination that don't exist.
15 | if (!(Test-Path -path $folder)) {
16 | New-Item $folder -Type Directory
17 | }
18 | }
19 |
20 | # Ensures that a folder exists and deletes anything in it.
21 | function EnsureEmptyFolderExists($folder) {
22 | EnsureFolderExists($folder)
23 | Remove-Item -Recurse -Force $folder
24 | EnsureFolderExists($folder)
25 | }
--------------------------------------------------------------------------------
/build/Resources/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/build/Resources/nuget.exe
--------------------------------------------------------------------------------
/build/nuspec/FileFormatWavefront/FileFormatWavefront.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileFormatWavefront
5 | 0.0.1.1
6 | FileFormatWavefront
7 | Dave Kerr
8 |
9 | https://github.com/dwmkerr/file-format-wavefront
10 | false
11 | FileFormatWavefront is a simple class library that lets you load Wavefront *.obj or *.mlb files.
12 | FileFormatWavefront is a simple class library that lets you load Wavefront *.obj or *.mlb files.
13 | Copyright © Dave Kerr 2014
14 |
15 |
--------------------------------------------------------------------------------
/build/nuspec/FileFormatWavefront/lib/net40/FileFormatWavefront.XML:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FileFormatWavefront
5 |
6 |
7 |
8 |
9 | Loads a scene into a FileLoadResult object from a Wavefromt *.obj file.
10 | All warning and error messages are in the returned object's 'Messages' collection.
11 | The actual scene data is in the returned object's 'Model' data.
12 |
13 | The path to the *.obj file.
14 | A containing the file load result.
15 |
16 |
17 |
18 | Internally used to reads the scene.
19 |
20 | The reader.
21 | The path.
22 | The file load result.
23 |
24 |
25 |
26 | The data separators, any valid value that can separate data in a line.
27 |
28 |
29 |
30 |
31 | A file loader for the Wavefront *.mtl file format.
32 |
33 |
34 |
35 |
36 | Loads materials from the specified stream.
37 |
38 | The path.
39 | The results of the file load.
40 |
41 |
42 |
43 | Read the material data from the specified stream.
44 |
45 | The reader.
46 | The path. This can be null - it is only used for recording diagnostic information.
47 | The results of the file load.
48 |
49 |
50 |
51 | Represents the results of a file loading operation.
52 |
53 | The type of the model.
54 |
55 |
56 |
57 | Initializes a new instance of the class.
58 |
59 | The messages.
60 | The model.
61 |
62 |
63 |
64 | Gets the loaded model.
65 |
66 |
67 |
68 |
69 | Gets the file load messages.
70 |
71 |
72 |
73 |
74 | This helper class aids us in dealing with Wavefront line data.
75 |
76 |
77 |
78 |
79 | Represents a colour.
80 |
81 |
82 |
83 |
84 | The red component.
85 |
86 |
87 |
88 |
89 | The green component.
90 |
91 |
92 |
93 |
94 | The blue component.
95 |
96 |
97 |
98 |
99 | The alpha component.
100 |
101 |
102 |
103 |
104 | Represents a face.
105 |
106 |
107 |
108 |
109 | Gets the material.
110 |
111 |
112 |
113 |
114 | Gets the indices.
115 |
116 |
117 |
118 |
119 | Represents a group of faces.
120 |
121 |
122 |
123 |
124 | Gets the group names. A group doesn't necessarily have to have any names.
125 | Groups can also share names (i.e one group can be part of another).
126 |
127 |
128 |
129 |
130 | Gets the smoothing group.
131 |
132 |
133 |
134 |
135 | Gets the faces.
136 |
137 |
138 |
139 |
140 | Represents an index.
141 |
142 |
143 |
144 |
145 | The vertex index.
146 |
147 |
148 |
149 |
150 | The uv index.
151 |
152 |
153 |
154 |
155 | The normal index.
156 |
157 |
158 |
159 |
160 | Represents a material.
161 |
162 |
163 |
164 |
165 | Gets the name.
166 |
167 |
168 |
169 |
170 | Gets the ambient.
171 |
172 |
173 |
174 |
175 | Gets the diffuse.
176 |
177 |
178 |
179 |
180 | Gets the specular.
181 |
182 |
183 |
184 |
185 | Gets the shininess.
186 |
187 |
188 |
189 |
190 | Gets the transparency.
191 |
192 |
193 |
194 |
195 | Gets the ambient texture map.
196 |
197 |
198 |
199 |
200 | Gets the diffuse texture map.
201 |
202 |
203 |
204 |
205 | Gets the specular texture map.
206 |
207 |
208 |
209 |
210 | Gets the specular highlight texture map.
211 |
212 |
213 |
214 |
215 | Gets the alpha texture map.
216 |
217 |
218 |
219 |
220 | Gets the bump texture map.
221 |
222 |
223 |
224 |
225 | Gets the illumination model.
226 | See the specification at http://paulbourke.net/dataformats/mtl/ for details on this value.
227 |
228 |
229 | The illumination model.
230 |
231 |
232 |
233 |
234 | Represent a scene of data loaded from an *.obj file.
235 |
236 |
237 |
238 |
239 | Gets the vertices.
240 |
241 |
242 |
243 |
244 | Gets the uvs.
245 |
246 |
247 |
248 |
249 | Gets the normals.
250 |
251 |
252 |
253 |
254 | Gets the faces which don't belong to any groups.
255 |
256 |
257 |
258 |
259 | Gets the groups.
260 |
261 |
262 |
263 |
264 | Gets the materials.
265 |
266 |
267 |
268 |
269 | Represents a message of a specific severity relating to the loading of data from a file.
270 |
271 |
272 |
273 |
274 | The message type.
275 |
276 |
277 |
278 |
279 | The file name. May be null.
280 |
281 |
282 |
283 |
284 | The line number. May be null.
285 |
286 |
287 |
288 |
289 | The actual message details.
290 |
291 |
292 |
293 |
294 | The exception associated with the message, may be null.
295 |
296 |
297 |
298 |
299 | Initializes a new instance of the class.
300 |
301 | Type of the message.
302 | Name of the file.
303 | The line number.
304 | The message details.
305 | The exception.
306 |
307 |
308 |
309 | Gets the type of the message.
310 |
311 |
312 |
313 |
314 | Gets the name of the file.
315 |
316 |
317 |
318 |
319 | Gets the line number.
320 |
321 |
322 |
323 |
324 | Gets the details.
325 |
326 |
327 |
328 |
329 | Gets the exception.
330 |
331 |
332 |
333 |
334 | Represents a message type for a loading message.
335 |
336 |
337 |
338 |
339 | A warning message - indicates something may be wrong with the data.
340 |
341 |
342 |
343 |
344 | An error message - indicates that we KNOW something is wrong with the data.
345 |
346 |
347 |
348 |
349 | Represents a texture map.
350 |
351 |
352 |
353 |
354 | Gets the path to the texture file.
355 |
356 |
357 |
358 |
359 | Represents a texture coordinate.
360 |
361 |
362 |
363 |
364 | The u coordinate.
365 |
366 |
367 |
368 |
369 | The v coordinate.
370 |
371 |
372 |
373 |
374 | Represents a vertex.
375 |
376 |
377 |
378 |
379 | The x coordinate.
380 |
381 |
382 |
383 |
384 | The y cooredinate.
385 |
386 |
387 |
388 |
389 | The z coordinate.
390 |
391 |
392 |
393 |
394 | String extensions used internally just make make the comparison with a string
395 | and a line type a little cleaner.
396 |
397 |
398 |
399 |
400 | Determines whether this string matches the specified line type.
401 |
402 | The string.
403 | The line type.
404 | True if the string matches the linetype.
405 |
406 |
407 |
408 |
--------------------------------------------------------------------------------
/build/nuspec/FileFormatWavefront/lib/net40/FileFormatWavefront.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/build/nuspec/FileFormatWavefront/lib/net40/FileFormatWavefront.dll
--------------------------------------------------------------------------------
/models/Motherboard/Motherboard.mtl:
--------------------------------------------------------------------------------
1 | # Max2Mtl Version 4.0 Mar 10th, 2001
2 | #
3 | # Multi/Sub Material__35 (8) to come
4 | #
5 | newmtl 01_-_Standard
6 | Ka 0.6 0.6 0.6
7 | Kd 0.6 0.6 0.6
8 | Ks 0.9 0.9 0.9
9 | d 1.0
10 | Ns 0.0
11 | illum 2
12 | #
13 | newmtl Material__147
14 | Ka 0.8 0.8 0.8
15 | Kd 0.8 0.8 0.8
16 | Ks 0.9 0.9 0.9
17 | d 1.0
18 | Ns 0.0
19 | illum 2
20 | #
21 | newmtl Material__148
22 | Ka 0.8 0.6 0.3
23 | Kd 0.8 0.6 0.3
24 | Ks 0.9 0.9 0.9
25 | d 1.0
26 | Ns 15.0
27 | illum 2
28 | #
29 | newmtl Material__151
30 | Ka 0.9 0.9 0.9
31 | Kd 0.9 0.9 0.9
32 | Ks 0.9 0.9 0.9
33 | d 1.0
34 | Ns 10.8
35 | illum 2
36 | #
37 | newmtl Material__149
38 | Ka 0.2 0.2 0.5
39 | Kd 0.2 0.2 0.5
40 | Ks 0.9 0.9 0.9
41 | d 1.0
42 | Ns 2.6
43 | illum 2
44 | #
45 | newmtl Material__154
46 | Ka 0.8 0.8 0.8
47 | Kd 0.8 0.8 0.8
48 | Ks 0.9 0.9 0.9
49 | d 1.0
50 | Ns 0.0
51 | illum 2
52 | #
53 | newmtl Material__155
54 | Ka 0.2 0.3 0.2
55 | Kd 0.2 0.3 0.2
56 | Ks 0.9 0.9 0.9
57 | d 1.0
58 | Ns 0.0
59 | illum 2
60 | #
61 | newmtl Material__150
62 | Ka 0.1 0.1 0.1
63 | Kd 0.1 0.1 0.1
64 | Ks 0.9 0.9 0.9
65 | d 1.0
66 | Ns 0.0
67 | illum 2
68 | #
69 | # Multi/Sub Material__35 done
70 | #
71 | # EOF
72 |
--------------------------------------------------------------------------------
/models/Motherboard/Textures/mb-ps-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/models/Motherboard/Textures/mb-ps-2.png
--------------------------------------------------------------------------------
/models/Motherboard/Textures/p4sba-mb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/models/Motherboard/Textures/p4sba-mb.jpg
--------------------------------------------------------------------------------
/models/Palm/Bottom_T.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/models/Palm/Bottom_T.jpg
--------------------------------------------------------------------------------
/models/Palm/Bottom_Trunk.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dwmkerr/file-format-wavefront/42c1b22c718ecae55bcea3d48966800cc964c675/models/Palm/Bottom_Trunk.bmp
--------------------------------------------------------------------------------
/models/Palm/Date Palm.mtl:
--------------------------------------------------------------------------------
1 | newmtl Bottom_Trunk
2 | Ka 0 0 0
3 | Ns 1
4 | Ks 1 1 1
5 | Kd 0.440946 0.417621 0.371041
6 | map_Kd Bottom_Trunk.bmp
7 |
--------------------------------------------------------------------------------
/models/Palm/Source.md:
--------------------------------------------------------------------------------
1 | http://tf3dm.com/3d-model/date-palm-2286.html
--------------------------------------------------------------------------------
/source/FileFormatWavefront.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileFormatWavefront", "FileFormatWavefront\FileFormatWavefront.csproj", "{878EC1C4-D57E-4749-AE1F-41F393977655}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WavefrontObjectLoader", "WavefrontObjectLoader\WavefrontObjectLoader.csproj", "{EA7A4D48-969F-4E19-8BAB-BBC051F1C3EE}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {878EC1C4-D57E-4749-AE1F-41F393977655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {878EC1C4-D57E-4749-AE1F-41F393977655}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {878EC1C4-D57E-4749-AE1F-41F393977655}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {878EC1C4-D57E-4749-AE1F-41F393977655}.Release|Any CPU.Build.0 = Release|Any CPU
18 | {EA7A4D48-969F-4E19-8BAB-BBC051F1C3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {EA7A4D48-969F-4E19-8BAB-BBC051F1C3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {EA7A4D48-969F-4E19-8BAB-BBC051F1C3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {EA7A4D48-969F-4E19-8BAB-BBC051F1C3EE}.Release|Any CPU.Build.0 = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FileFormatWavefront.Extensions
4 | {
5 | ///
6 | /// String extensions used internally just make make the comparison with a string
7 | /// and a line type a little cleaner.
8 | ///
9 | internal static class StringExtensions
10 | {
11 | ///
12 | /// Determines whether this string matches the specified line type.
13 | ///
14 | /// The string.
15 | /// The line type.
16 | /// True if the string matches the linetype.
17 | public static bool IsLineType(this string @this, string lineType)
18 | {
19 | return string.Equals(@this, lineType, StringComparison.InvariantCultureIgnoreCase);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/FileFormatMtl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Linq;
6 | using FileFormatWavefront.Extensions;
7 | using FileFormatWavefront.Internals;
8 | using FileFormatWavefront.Model;
9 |
10 | namespace FileFormatWavefront
11 | {
12 | ///
13 | /// A file loader for the Wavefront *.mtl file format.
14 | ///
15 | public static class FileFormatMtl
16 | {
17 | ///
18 | /// Loads materials from the specified stream.
19 | ///
20 | /// The path.
21 | /// if set to true texture images
22 | /// will be loaded and set in the property.
23 | /// The results of the file load.
24 | public static FileLoadResult> Load(string path, bool loadTextureImages)
25 | {
26 | // Create a streamreader and read the data.
27 | using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
28 | using (var streamReader = new StreamReader(stream))
29 | return Read(streamReader, path, loadTextureImages);
30 | }
31 |
32 | ///
33 | /// Read the material data from the specified stream.
34 | ///
35 | /// The reader.
36 | /// The path. This can be null - it is only used for recording diagnostic information.
37 | /// if set to true [load texture images].
38 | ///
39 | /// The results of the file load.
40 | ///
41 | private static FileLoadResult> Read(TextReader reader, string path, bool loadTextureImages)
42 | {
43 | // The model we are loading is a list of materials. During loading, we'll keep
44 | // track of messages that may be useful to consumers.
45 | var materials = new List();
46 | var messages = new List();
47 |
48 | // As we load, we're enriching the data of a Material object.
49 | Material currentMaterial = null;
50 |
51 | // Go through each line, keeping track of the line number.
52 | var lineNumberCounter = 0;
53 | string line;
54 | while ((line = reader.ReadLine()) != null)
55 | {
56 | ++lineNumberCounter;
57 |
58 | // Strip any comments from the line and skip empty lines.
59 | line = LineData.StripComments(line);
60 | if (string.IsNullOrWhiteSpace(line))
61 | continue;
62 |
63 | // Try and read the line type and data.
64 | string lineType, lineData;
65 | if (LineData.TryReadLineType(line, out lineType, out lineData) == false)
66 | continue;
67 |
68 | if (lineType.IsLineType(LineTypeNewMaterial))
69 | {
70 | // Add a new material to the list, store it as the current one and set the name.
71 | currentMaterial = new Material { Name = lineData };
72 | materials.Add(currentMaterial);
73 | }
74 | else if (currentMaterial != null)
75 | {
76 | if (lineType.IsLineType(LineTypeMaterialAmbient))
77 | {
78 | currentMaterial.Ambient = ReadColour(lineData);
79 | }
80 | else if (lineType.IsLineType(LineTypeMaterialDiffuse))
81 | {
82 | currentMaterial.Diffuse = ReadColour(lineData);
83 | }
84 | else if (lineType.IsLineType(LineTypeMaterialSpecular))
85 | {
86 | currentMaterial.Specular = ReadColour(lineData);
87 | }
88 | else if (lineType.IsLineType(LineTypeMaterialShininess))
89 | {
90 | currentMaterial.Shininess = float.Parse(lineData);
91 | }
92 | else if (lineType.IsLineType(LineTypeOpticalDensity))
93 | {
94 | currentMaterial.OpticalDensity = float.Parse(lineData);
95 | }
96 | else if (lineType.IsLineType(LineTypeBumpStrength))
97 | {
98 | currentMaterial.BumpStrength = float.Parse(lineData);
99 | }
100 | else if (lineType.IsLineType(LineTypeTextureMapAmbient))
101 | {
102 | currentMaterial.TextureMapAmbient = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
103 | }
104 | else if (lineType.IsLineType(LineTypeTextureMapDiffuse))
105 | {
106 | currentMaterial.TextureMapDiffuse = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
107 | }
108 | else if (lineType.IsLineType(LineTypeTextureMapSpecular))
109 | {
110 | currentMaterial.TextureMapSpecular = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
111 | }
112 | else if (lineType.IsLineType(LineTypeTextureMapSpecularHighlight))
113 | {
114 | currentMaterial.TextureMapSpecularHighlight = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
115 | }
116 | else if (lineType.IsLineType(LineTypeTextureMapAlpha))
117 | {
118 | currentMaterial.TextureMapAlpha = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
119 | }
120 | else if (lineType.IsLineType(LineTypeTextureMapBump))
121 | {
122 | currentMaterial.TextureMapBump = ReadTextureMap(path, lineNumberCounter, messages, lineData, loadTextureImages);
123 | }
124 | else if (lineType.IsLineType(LineTypeDissolve) || lineType.IsLineType(LineTypeTransparent))
125 | {
126 | // Read the transparency.
127 | currentMaterial.Transparency = float.Parse(lineData);
128 | }
129 | else if(lineType.IsLineType(LineTypeIlluminationModel))
130 | {
131 | currentMaterial.IlluminationModel = int.Parse(lineData);
132 | }
133 | else
134 | {
135 | // Anything we encounter here we don't understand.
136 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
137 | string.Format("Skipped unknown line type '{0}'.", lineType)));
138 | }
139 | }
140 | else
141 | {
142 | // Anything we encounter here we don't understand.
143 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
144 | string.Format("Skipped unknown or out of context line type '{0}'.", lineType)));
145 | }
146 |
147 | }
148 |
149 | // Return the model and messages as a file load result.
150 | return new FileLoadResult>(materials, messages);
151 | }
152 |
153 | private static TextureMap ReadTextureMap(string fileName, int lineNumber, List messages, string lineData, bool loadTextureBitmaps)
154 | {
155 | // TODO: Support texture map options. http://paulbourke.net/dataformats/mtl/
156 | var textureMap = new TextureMap { Path = lineData };
157 | if(loadTextureBitmaps == false)
158 | return textureMap;
159 |
160 | // If we have quotes, we've got a way to get the path explicitly.
161 | string textureFileName;
162 | var quotePos = lineData.IndexOf('"');
163 | if(quotePos != -1)
164 | {
165 | var quoteEndPos = lineData.IndexOf('"', quotePos + 1);
166 | if(quoteEndPos == -1)
167 | {
168 | messages.Add(new Message(MessageType.Error, fileName, lineNumber,
169 | "The texture file is specified incorrectly."));
170 | return null;
171 | }
172 | textureFileName = lineData.Substring(quoteEndPos + 1, quoteEndPos - quotePos - 1);
173 | }
174 | else
175 | {
176 | // If we don't have quotes, we'll have to assume that the last part of the line is the texture.
177 | textureFileName = lineData.Split(' ').Last();
178 | }
179 |
180 | try
181 | {
182 | var path = Path.Combine(Path.GetDirectoryName(fileName), textureFileName);
183 | textureMap.Image = Image.FromFile(path);
184 | }
185 | catch (Exception exception)
186 | {
187 | messages.Add(new Message(MessageType.Error, fileName, lineNumber,
188 | string.Format("Failed to load the texture map image file '{0}'", lineData), exception));
189 | }
190 | return textureMap;
191 | }
192 |
193 | private static Colour ReadColour(string lineData)
194 | {
195 |
196 | // Get the colour parts.
197 | var colourParts = lineData.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
198 | return new Colour
199 | {
200 | r = float.Parse(colourParts[0]),
201 | g = float.Parse(colourParts[1]),
202 | b = float.Parse(colourParts[2]),
203 | a = colourParts.Length == 4 ? float.Parse(colourParts[3]) : 1.0f,
204 | };
205 | }
206 |
207 | private const string LineTypeNewMaterial = "newmtl";
208 | private const string LineTypeMaterialAmbient = "Ka";
209 | private const string LineTypeMaterialDiffuse = "Kd";
210 | private const string LineTypeMaterialSpecular = "Ks";
211 | private const string LineTypeMaterialShininess = "Ns";
212 | private const string LineTypeOpticalDensity = "Ni";
213 | private const string LineTypeBumpStrength = "Km";
214 | private const string LineTypeDissolve = "d";
215 | private const string LineTypeTransparent = "Tr";
216 | private const string LineTypeTextureMapAmbient = "map_Ka";
217 | private const string LineTypeTextureMapDiffuse = "map_Kd";
218 | private const string LineTypeTextureMapSpecular = "map_Ks";
219 | private const string LineTypeTextureMapSpecularHighlight = "map_Ns";
220 | private const string LineTypeTextureMapAlpha = "map_d";
221 | private const string LineTypeTextureMapBump = "map_bump";
222 | private const string LineTypeIlluminationModel = "illum";
223 |
224 | }
225 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/FileFormatObj.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using FileFormatWavefront.Extensions;
6 | using FileFormatWavefront.Internals;
7 | using FileFormatWavefront.Model;
8 |
9 | namespace FileFormatWavefront
10 | {
11 | public static class FileFormatObj
12 | {
13 |
14 | ///
15 | /// Loads a scene into a FileLoadResult object from a Wavefromt *.obj file.
16 | /// All warning and error messages are in the returned object's 'Messages' collection.
17 | /// The actual scene data is in the returned object's 'Model' data.
18 | ///
19 | /// The path to the *.obj file.
20 | /// if set to true texture images
21 | /// will be loaded and set in the property.
22 | ///
23 | /// A containing the file load result.
24 | ///
25 | public static FileLoadResult Load(string path, bool loadTextureImages)
26 | {
27 | // Create a stream reader.
28 | using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
29 | {
30 | using (var reader = new StreamReader(stream))
31 | {
32 | // Read the scene.
33 | return ReadScene(reader, path, loadTextureImages);
34 | }
35 | }
36 | }
37 |
38 | ///
39 | /// Internally used to reads the scene.
40 | ///
41 | /// The reader.
42 | /// The path.
43 | /// if set to true [load texture images].
44 | ///
45 | /// The file load result.
46 | ///
47 | private static FileLoadResult ReadScene(StreamReader reader, string path, bool loadTextureImages)
48 | {
49 | // Keep track of messages and the raw data we will use to build a scene.
50 | var messages = new List();
51 | var uvs = new List();
52 | var normals = new List();
53 | var vertices = new List();
54 | var interimFaces = new List();
55 | var materials = new List();
56 | var groups = new List();
57 | string objectName = null;
58 |
59 | // State changing data is loaded as we go through the file - once loaded, state changing
60 | // data applies to all subsequent elements until it is explicitly changed by introducing
61 | // new state changing data.
62 | Group currentGroup = null;
63 | string currentMaterialName = null;
64 |
65 | // Read line by line.
66 | string line;
67 | int lineNumberCounter = 0;
68 | while ((line = reader.ReadLine()) != null)
69 | {
70 | ++lineNumberCounter;
71 |
72 | // Strip any comments from the line and skip empty lines.
73 | line = LineData.StripComments(line);
74 | if (string.IsNullOrWhiteSpace(line))
75 | continue;
76 |
77 | // Try and read the line type and data.
78 | string lineType, lineData;
79 | if (LineData.TryReadLineType(line, out lineType, out lineData) == false)
80 | continue;
81 |
82 | // Read texture coordinates.
83 | if (lineType.IsLineType(LineTypeTextureCoordinate))
84 | {
85 | try
86 | {
87 | // Split the line data into texture coordinates.
88 | var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries);
89 |
90 | // Add the UV.
91 | uvs.Add(new UV
92 | {
93 | u = float.Parse(dataStrings[0]),
94 | v = float.Parse(dataStrings[1])
95 | });
96 | }
97 | catch (Exception exception)
98 | {
99 | messages.Add(new Message(MessageType.Error, path, lineNumberCounter,
100 | "There was an error reading the texture coordinate data.", exception));
101 | }
102 | }
103 | else if (lineType.IsLineType(LineTypeNormalCoordinate))
104 | {
105 | try
106 | {
107 | // Split the line data into normal coordinates.
108 | var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries);
109 | normals.Add(new Vertex
110 | {
111 | x = float.Parse(dataStrings[0]),
112 | y = float.Parse(dataStrings[1]),
113 | z = float.Parse(dataStrings[2])
114 | });
115 | }
116 | catch (Exception exception)
117 | {
118 | messages.Add(new Message(MessageType.Error, path, lineNumberCounter,
119 | "There was an error reading the normal data.", exception));
120 | }
121 | }
122 | else if (lineType.IsLineType(LineTypeVertex))
123 | {
124 | try
125 | {
126 | // Split the line data into vertex coordinates.
127 | var dataStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries);
128 | vertices.Add(new Vertex
129 | {
130 | x = float.Parse(dataStrings[0]),
131 | y = float.Parse(dataStrings[1]),
132 | z = float.Parse(dataStrings[2])
133 | });
134 | }
135 | catch (Exception exception)
136 | {
137 | messages.Add(new Message(MessageType.Error, path, lineNumberCounter,
138 | "There was an error reading the vertex data.", exception));
139 | }
140 | }
141 | else if (lineType.IsLineType(LineTypeFace))
142 | {
143 | try
144 | {
145 | var indices = new List();
146 |
147 | // Split the line data into index strings.
148 | var indexStrings = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries);
149 | foreach (var indexString in indexStrings)
150 | {
151 | // Split the parts.
152 | var parts = indexString.Split(new[] { '/' }, StringSplitOptions.None);
153 | var vertex = MapIndex(vertices.Count, int.Parse(parts[0]));
154 | var uv = (parts.Length > 1 && parts[1].Length > 0) ? (int?)MapIndex(uvs.Count, int.Parse(parts[1])) : null;
155 | var normal = (parts.Length > 2 && parts[2].Length > 0) ? (int?)MapIndex(normals.Count, int.Parse(parts[2])) : null;
156 | indices.Add(new Index
157 | {
158 | vertex = vertex,
159 | uv = uv,
160 | normal = normal
161 | });
162 | }
163 | interimFaces.Add(new InterimFace
164 | {
165 | materialName = currentMaterialName,
166 | indices = indices,
167 | group = currentGroup
168 | });
169 | }
170 | catch (Exception exception)
171 | {
172 | messages.Add(new Message(MessageType.Error, path, lineNumberCounter,
173 | "There was an error reading the index data.", exception));
174 | }
175 | }
176 | else if (lineType.IsLineType(LineTypeMaterialLibrary))
177 | {
178 | // The material file path is the line data.
179 | var materialPath = lineData;
180 |
181 | // If the path is relative, make it absolute based on the current directory (if we've been passed a path).
182 | if (Path.IsPathRooted(lineData) == false && path != null)
183 | materialPath = Path.Combine(Path.GetDirectoryName(path), materialPath);
184 |
185 | // Read the material file.
186 | try
187 | {
188 | var fileLoadResult = FileFormatMtl.Load(materialPath, loadTextureImages);
189 | materials.AddRange(fileLoadResult.Model);
190 | messages.AddRange(fileLoadResult.Messages);
191 | }
192 | catch (Exception exception)
193 | {
194 | messages.Add(new Message(MessageType.Error, path, lineNumberCounter,
195 | string.Format("Failed to load material file '{0}'.", materialPath), exception));
196 | }
197 | }
198 | else if (lineType.IsLineType(LineTypeUseMaterial))
199 | {
200 | // The material name is simply the line data.
201 | currentMaterialName = lineData;
202 | }
203 | else if(lineType.IsLineType(LineTypeGroup))
204 | {
205 | // Create a new group.
206 | var groupNames = lineData.Split(dataSeparators, StringSplitOptions.RemoveEmptyEntries);
207 | currentGroup = new Group(groupNames);
208 | groups.Add(currentGroup);
209 | }
210 | else if(lineType.IsLineType(LineTypeSmoothingGroup))
211 | {
212 | // If we have no current group, we cannot set a smoothing group.
213 | if(currentGroup == null)
214 | {
215 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
216 | string.Format("Cannot set smoothing group '{0}' as the current context has no group.", lineData)));
217 | }
218 | else
219 | {
220 | // The smoothing group is an int, if we can get it.
221 | int smoothingGroup;
222 | if(int.TryParse(lineData, out smoothingGroup))
223 | currentGroup.SetSmoothingGroup(smoothingGroup);
224 | currentGroup.SetSmoothingGroup(null);
225 | }
226 | }
227 | else if(lineType.IsLineType(LineTypeObjectName))
228 | {
229 | // Set the object name, warning if it's already set.
230 | if (objectName != null)
231 | {
232 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
233 | string.Format("An object name statement to set the name to '{0}' will overwrite the current object name '{1}'.", lineData, objectName)));
234 | }
235 | objectName = lineData;
236 | }
237 | else
238 | {
239 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
240 | string.Format("Skipped unknown line type '{0}'.", lineType)));
241 | }
242 | }
243 |
244 | // Currently we don't have faces, just indexes and material names. But now that we've loaded
245 | // the entire file, we can map the material names to the actual materials.
246 | var ungroupedFaces = new List();
247 | foreach (var interimFace in interimFaces)
248 | {
249 | // If we have a material named but not in the set of materials, warn.
250 | var material = materials.FirstOrDefault(m => m.Name == interimFace.materialName);
251 | if (material == null)
252 | messages.Add(new Message(MessageType.Warning, path, lineNumberCounter,
253 | string.Format("Material '{0}' is referenced for a face, but not included in any material files.", interimFace.materialName)));
254 |
255 | // If the face is grouped, add it to the group. Otherwise add it to the ungrouped faces.
256 | var face = new Face(material, interimFace.indices);
257 | if (interimFace.group != null)
258 | interimFace.group.AddFace(face);
259 | else
260 | ungroupedFaces.Add(face);
261 | }
262 |
263 | return new FileLoadResult(new Scene(vertices, uvs, normals, ungroupedFaces, groups, materials, objectName), messages);
264 | }
265 |
266 | ///
267 | /// Maps an index defined in the file.
268 | /// Indexes are 1 based, so we fix this. Also, if they're negative they're 1 based
269 | /// but going backwards from the last element - which is why we need the .
270 | ///
271 | /// The current element count.
272 | /// The index.
273 | ///
274 | private static int MapIndex(int currentElementCount, int index)
275 | {
276 | return (index > 0) ? index - 1 : currentElementCount + index;
277 | }
278 |
279 | private const string LineTypeTextureCoordinate = "vt";
280 | private const string LineTypeNormalCoordinate = "vn";
281 | private const string LineTypeVertex = "v";
282 | private const string LineTypeFace = "f";
283 | private const string LineTypeMaterialLibrary = "mtllib";
284 | private const string LineTypeUseMaterial = "usemtl";
285 | private const string LineTypeGroup = "g";
286 | private const string LineTypeSmoothingGroup = "s";
287 | private const string LineTypeObjectName = "o";
288 |
289 | ///
290 | /// The data separators, any valid value that can separate data in a line.
291 | ///
292 | private static readonly char[] dataSeparators = new[] { ' ' };
293 |
294 | internal class InterimFace
295 | {
296 | public Group group;
297 | public string materialName;
298 | public List indices;
299 | public bool smoothShading;
300 | }
301 | }
302 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/FileFormatWavefront.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {878EC1C4-D57E-4749-AE1F-41F393977655}
8 | Library
9 | Properties
10 | FileFormatWavefront
11 | FileFormatWavefront
12 | v4.0
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 | bin\Release\FileFormatWavefront.XML
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
70 |
--------------------------------------------------------------------------------
/source/FileFormatWavefront/FileLoadResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 |
4 | namespace FileFormatWavefront
5 | {
6 | ///
7 | /// Represents the results of a file loading operation.
8 | ///
9 | /// The type of the model.
10 | public class FileLoadResult
11 | {
12 | private readonly TModel model;
13 | private readonly List messages;
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// The messages.
19 | /// The model.
20 | internal FileLoadResult(TModel model, List messages)
21 | {
22 | this.model = model;
23 | this.messages = messages;
24 | }
25 |
26 | ///
27 | /// Gets the loaded model.
28 | ///
29 | public TModel Model
30 | {
31 | get { return model; }
32 | }
33 |
34 | ///
35 | /// Gets the file load messages.
36 | ///
37 | public ReadOnlyCollection Messages
38 | {
39 | get { return messages.AsReadOnly(); }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Internals/LineData.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Internals
2 | {
3 | ///
4 | /// This helper class aids us in dealing with Wavefront line data.
5 | ///
6 | internal static class LineData
7 | {
8 | public static string StripComments(string line)
9 | {
10 | // If there's a comment character, remove everything that follows it.
11 | var commentStartIndex = line.IndexOf('#');
12 | return commentStartIndex == -1 ? line : line.Substring(0, commentStartIndex);
13 | }
14 |
15 | public static bool TryReadLineType(string line, out string lineType, out string lineData)
16 | {
17 | // The line type is the first set of characters in the line, such as:
18 | // vt, vn, v etc.
19 | // Remember - lines can have whitespace.
20 | var strippedLine = line.TrimStart();
21 |
22 | // Loop through the string until we find the first whitespace character - everything before it
23 | // is the line type.
24 | for(var i=0; i
6 | /// Represents a message of a specific severity relating to the loading of data from a file.
7 | ///
8 | public class Message
9 | {
10 | ///
11 | /// The message type.
12 | ///
13 | private readonly MessageType messageType;
14 |
15 | ///
16 | /// The file name. May be null.
17 | ///
18 | private readonly string fileName;
19 |
20 | ///
21 | /// The line number. May be null.
22 | ///
23 | private readonly int? lineNumber;
24 |
25 | ///
26 | /// The actual message details.
27 | ///
28 | private readonly string details;
29 |
30 | ///
31 | /// The exception associated with the message, may be null.
32 | ///
33 | private readonly Exception exception;
34 |
35 | ///
36 | /// Initializes a new instance of the class.
37 | ///
38 | /// Type of the message.
39 | /// Name of the file.
40 | /// The line number.
41 | /// The message details.
42 | /// The exception.
43 | public Message(MessageType messageType, string fileName, int? lineNumber, string details, Exception exception = null)
44 | {
45 | this.messageType = messageType;
46 | this.fileName = fileName;
47 | this.lineNumber = lineNumber;
48 | this.details = details;
49 | this.exception = exception;
50 | }
51 |
52 | ///
53 | /// Gets the type of the message.
54 | ///
55 | public MessageType MessageType
56 | {
57 | get { return messageType; }
58 | }
59 |
60 | ///
61 | /// Gets the name of the file.
62 | ///
63 | public string FileName
64 | {
65 | get { return fileName; }
66 | }
67 |
68 | ///
69 | /// Gets the line number.
70 | ///
71 | public int? LineNumber
72 | {
73 | get { return lineNumber; }
74 | }
75 |
76 | ///
77 | /// Gets the details.
78 | ///
79 | public string Details
80 | {
81 | get { return details; }
82 | }
83 |
84 | ///
85 | /// Gets the exception.
86 | ///
87 | public Exception Exception
88 | {
89 | get { return exception; }
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/MessageType.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront
2 | {
3 | ///
4 | /// Represents a message type for a loading message.
5 | ///
6 | public enum MessageType
7 | {
8 | ///
9 | /// A warning message - indicates something may be wrong with the data.
10 | ///
11 | Warning,
12 |
13 | ///
14 | /// An error message - indicates that we KNOW something is wrong with the data.
15 | ///
16 | Error
17 | }
18 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Colour.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Model
2 | {
3 | ///
4 | /// Represents a colour.
5 | ///
6 | public struct Colour
7 | {
8 | ///
9 | /// The red component.
10 | ///
11 | public float r;
12 |
13 | ///
14 | /// The green component.
15 | ///
16 | public float g;
17 |
18 | ///
19 | /// The blue component.
20 | ///
21 | public float b;
22 |
23 | ///
24 | /// The alpha component.
25 | ///
26 | public float a;
27 | }
28 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Face.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 |
4 | namespace FileFormatWavefront.Model
5 | {
6 | ///
7 | /// Represents a face.
8 | ///
9 | public class Face
10 | {
11 | private readonly List indices;
12 | private readonly Material material;
13 |
14 | internal Face(Material material, List indices)
15 | {
16 | this.material = material;
17 | this.indices = indices;
18 | }
19 |
20 | ///
21 | /// Gets the material.
22 | ///
23 | public Material Material
24 | {
25 | get { return material; }
26 | }
27 |
28 |
29 | ///
30 | /// Gets the indices.
31 | ///
32 | public ReadOnlyCollection Indices
33 | {
34 | get { return indices.AsReadOnly(); }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Group.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace FileFormatWavefront.Model
8 | {
9 | ///
10 | /// Represents a group of faces.
11 | ///
12 | public class Group
13 | {
14 | private readonly List names = new List();
15 | private readonly List faces = new List();
16 | private int? smoothingGroup;
17 |
18 | internal Group(IEnumerable names)
19 | {
20 | this.names.AddRange(names);
21 | }
22 |
23 | internal void SetSmoothingGroup(int? smoothingGroup)
24 | {
25 | this.smoothingGroup = smoothingGroup;
26 | }
27 | internal void AddFace(Face face)
28 | {
29 | faces.Add(face);
30 | }
31 |
32 | ///
33 | /// Gets the group names. A group doesn't necessarily have to have any names.
34 | /// Groups can also share names (i.e one group can be part of another).
35 | ///
36 | public List Names
37 | {
38 | get { return names; }
39 | }
40 |
41 | ///
42 | /// Gets the smoothing group.
43 | ///
44 | public int? SmoothingGroup
45 | {
46 | get { return smoothingGroup; }
47 | }
48 |
49 | ///
50 | /// Gets the faces.
51 | ///
52 | public ReadOnlyCollection Faces
53 | {
54 | get { return faces.AsReadOnly(); }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Index.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Model
2 | {
3 | ///
4 | /// Represents an index.
5 | ///
6 | public struct Index
7 | {
8 | ///
9 | /// The vertex index.
10 | ///
11 | public int vertex;
12 |
13 | ///
14 | /// The uv index.
15 | ///
16 | public int? uv;
17 |
18 | ///
19 | /// The normal index.
20 | ///
21 | public int? normal;
22 | }
23 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Material.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Model
2 | {
3 | ///
4 | /// Represents a material.
5 | ///
6 | public class Material
7 | {
8 | ///
9 | /// Gets the name.
10 | ///
11 | public string Name { get; internal set; }
12 |
13 | ///
14 | /// Gets the ambient.
15 | ///
16 | public Colour Ambient { get; internal set; }
17 |
18 | ///
19 | /// Gets the diffuse.
20 | ///
21 | public Colour Diffuse { get; internal set; }
22 | ///
23 | /// Gets the specular.
24 | ///
25 | public Colour Specular { get; internal set; }
26 | ///
27 | /// Gets the shininess.
28 | ///
29 | public float Shininess { get; internal set; }
30 | ///
31 | /// Gets the transparency.
32 | ///
33 | public float? Transparency { get; internal set; }
34 |
35 | ///
36 | /// Gets the ambient texture map.
37 | ///
38 | public TextureMap TextureMapAmbient { get; internal set; }
39 |
40 | ///
41 | /// Gets the diffuse texture map.
42 | ///
43 | public TextureMap TextureMapDiffuse { get; internal set; }
44 |
45 | ///
46 | /// Gets the specular texture map.
47 | ///
48 | public TextureMap TextureMapSpecular { get; internal set; }
49 |
50 | ///
51 | /// Gets the specular highlight texture map.
52 | ///
53 | public TextureMap TextureMapSpecularHighlight { get; internal set; }
54 |
55 | ///
56 | /// Gets the alpha texture map.
57 | ///
58 | public TextureMap TextureMapAlpha { get; internal set; }
59 |
60 | ///
61 | /// Gets the bump texture map.
62 | ///
63 | public TextureMap TextureMapBump { get; internal set; }
64 |
65 | ///
66 | /// Gets the illumination model.
67 | /// See the specification at http://paulbourke.net/dataformats/mtl/ for details on this value.
68 | ///
69 | ///
70 | /// The illumination model.
71 | ///
72 | public int IlluminationModel { get; internal set; }
73 |
74 | ///
75 | /// Gets the optical density, also known as the Index of Refraction.
76 | ///
77 | public float? OpticalDensity { get; internal set; }
78 |
79 | ///
80 | /// Gets the occasionally used bump strength.
81 | ///
82 | public float? BumpStrength { get; internal set; }
83 | }
84 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Scene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace FileFormatWavefront.Model
8 | {
9 | ///
10 | /// Represent a scene of data loaded from an *.obj file.
11 | ///
12 | public class Scene
13 | {
14 | private readonly List vertices;
15 | private readonly List uvs;
16 | private readonly List normals;
17 | private readonly List ungroupedFaces;
18 | private readonly List groups;
19 | private readonly List materials;
20 | private readonly string objectName;
21 |
22 | internal Scene(List vertices, List uvs, List normals, List ungroupedFaces, List groups, List materials,
23 | string objectName)
24 | {
25 | this.vertices = vertices;
26 | this.uvs = uvs;
27 | this.normals = normals;
28 | this.ungroupedFaces = ungroupedFaces;
29 | this.groups = groups;
30 | this.materials = materials;
31 | this.objectName = objectName;
32 | }
33 |
34 | ///
35 | /// Gets the vertices.
36 | ///
37 | public ReadOnlyCollection Vertices
38 | {
39 | get { return vertices.AsReadOnly(); }
40 | }
41 |
42 | ///
43 | /// Gets the uvs.
44 | ///
45 | public ReadOnlyCollection Uvs
46 | {
47 | get { return uvs.AsReadOnly(); }
48 | }
49 |
50 | ///
51 | /// Gets the normals.
52 | ///
53 | public ReadOnlyCollection Normals
54 | {
55 | get { return normals.AsReadOnly(); }
56 | }
57 |
58 | ///
59 | /// Gets the faces which don't belong to any groups.
60 | ///
61 | public ReadOnlyCollection UngroupedFaces
62 | {
63 | get { return ungroupedFaces.AsReadOnly(); }
64 | }
65 |
66 | ///
67 | /// Gets the groups.
68 | ///
69 | public ReadOnlyCollection Groups
70 | {
71 | get { return groups.AsReadOnly(); }
72 | }
73 |
74 | ///
75 | /// Gets the materials.
76 | ///
77 | public ReadOnlyCollection Materials
78 | {
79 | get { return materials.AsReadOnly(); }
80 | }
81 |
82 | ///
83 | /// Gets the name of the object in the file. This can be (and in many cases will be) null.
84 | ///
85 | public string ObjectName { get { return objectName; } }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/TextureMap.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 |
3 | namespace FileFormatWavefront.Model
4 | {
5 | ///
6 | /// Represents a texture map.
7 | ///
8 | public class TextureMap
9 | {
10 | ///
11 | /// Gets the path to the texture file.
12 | ///
13 | public string Path { get; internal set; }
14 |
15 | ///
16 | /// Gets the texture image.
17 | /// Note that this is only set if the file is loaded with the 'loadTextureImages' option set to true.
18 | ///
19 | public Image Image { get; internal set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/UV.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Model
2 | {
3 | ///
4 | /// Represents a texture coordinate.
5 | ///
6 | public struct UV
7 | {
8 | ///
9 | /// The u coordinate.
10 | ///
11 | public float u;
12 |
13 | ///
14 | /// The v coordinate.
15 | ///
16 | public float v;
17 | }
18 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Model/Vertex.cs:
--------------------------------------------------------------------------------
1 | namespace FileFormatWavefront.Model
2 | {
3 | ///
4 | /// Represents a vertex.
5 | ///
6 | public struct Vertex
7 | {
8 | ///
9 | /// The x coordinate.
10 | ///
11 | public float x;
12 |
13 | ///
14 | /// The y cooredinate.
15 | ///
16 | public float y;
17 |
18 | ///
19 | /// The z coordinate.
20 | ///
21 | public float z;
22 | }
23 | }
--------------------------------------------------------------------------------
/source/FileFormatWavefront/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FileFormatWavefront")]
9 | [assembly: AssemblyDescription("A simple library for loading data from Wavefront object and material files.")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FileFormatWavefront")]
13 | [assembly: AssemblyCopyright("Copyright © Dave Kerr 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("531450da-a9e7-408f-a841-6dfcbf6d5a4a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.3.0")]
36 | [assembly: AssemblyFileVersion("1.0.3.0")]
37 |
--------------------------------------------------------------------------------
/source/ObjValidator/ObjValidator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {497F077E-A8E2-4B49-AC6E-9BECDCD4CBA9}
8 | Exe
9 | Properties
10 | ObjValidator
11 | ObjValidator
12 | v4.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {878ec1c4-d57e-4749-ae1f-41f393977655}
50 | FileFormatWavefront
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/source/ObjValidator/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using FileFormatWavefront;
4 |
5 | namespace ObjValidator
6 | {
7 | class Program
8 | {
9 | static int Main(string[] args)
10 | {
11 | // Get the path.
12 | if(args.Length < 1 || string.IsNullOrWhiteSpace(args[0]))
13 | {
14 | Console.WriteLine("Please provide a path to an *.obj file.");
15 | return 1;
16 | }
17 |
18 | // Use the File Format object to load the test data.
19 | var result = FileFormatObj.Load(args[0]);
20 |
21 | // If there are no messages, then the file is completely valid.
22 | if(!result.Messages.Any())
23 | {
24 | Console.WriteLine("The file loaded with no validation errors or warnings.");
25 | }
26 |
27 | // Show each message.
28 | foreach (var message in result.Messages)
29 | {
30 | Console.WriteLine("{0}: {1}", message.MessageType, message.Details);
31 | Console.WriteLine("{0}: {1}", message.FileName, message.LineNumber);
32 | }
33 |
34 | Console.WriteLine("Complete. Loaded:");
35 | Console.WriteLine(" Vertices: {0}", result.Model.Vertices.Count);
36 | Console.WriteLine(" UVS: {0}", result.Model.Uvs.Count);
37 | Console.WriteLine(" Normals: {0}", result.Model.Normals.Count);
38 | Console.WriteLine(" Ungrouped Faces: {0}", result.Model.UngroupedFaces.Count);
39 | Console.WriteLine(" Groups: {0}", result.Model.Groups.Count);
40 | Console.WriteLine(" Faces in Groups: {0}", result.Model.Groups.Select(g => g.Faces.Count).Sum());
41 | Console.WriteLine(" Materials: {0}", result.Model.Materials.Count);
42 | Console.WriteLine("with:");
43 | Console.WriteLine(" Warnings: {0}", result.Messages.Count(m => m.MessageType == MessageType.Warning));
44 | Console.WriteLine(" Errors: {0}", result.Messages.Count(m => m.MessageType == MessageType.Error));
45 |
46 | Console.WriteLine("Any key to close.");
47 | Console.ReadKey();
48 |
49 | return 0;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/source/ObjValidator/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ObjValidator")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ObjValidator")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("6f2b6f85-33d1-4a13-98ca-70a7fb76330c")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/Details/DetailsUi.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace WavefrontObjectLoader.Details
4 | {
5 | public abstract class DetailsUi : IDetailsUi
6 | {
7 | public abstract void Create(TModel model);
8 |
9 | public abstract Control Ui { get; }
10 | public abstract void Destroy();
11 | }
12 | }
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/Details/DetailsUiBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using FileFormatWavefront.Model;
8 |
9 | namespace WavefrontObjectLoader.Details
10 | {
11 | public static class DetailsUiBuilder
12 | {
13 | public static IDetailsUi BuildUi(object data)
14 | {
15 | var vertices = data as ReadOnlyCollection;
16 | var uvs = data as ReadOnlyCollection;
17 | var faces = data as ReadOnlyCollection;
18 | if (vertices != null)
19 | {
20 | var ui = new VerticesUi();
21 | ui.Create(vertices);
22 | return ui;
23 | }
24 | else if (uvs != null)
25 | {
26 | var ui = new UvsUi();
27 | ui.Create(uvs);
28 | return ui;
29 | }
30 | else if (faces != null)
31 | {
32 | var ui = new FacesUi();
33 | ui.Create(faces);
34 | return ui;
35 | }
36 | else
37 | {
38 | var ui = new PropertyGridUi();
39 | ui.Create(data);
40 | return ui;
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/Details/FacesUi.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Text;
3 | using System.Windows.Forms;
4 | using FileFormatWavefront.Model;
5 |
6 | namespace WavefrontObjectLoader.Details
7 | {
8 | public class FacesUi : DetailsUi>
9 | {
10 | private DataGridView dataGridView;
11 | private ReadOnlyCollection faces;
12 |
13 | public override void Create(ReadOnlyCollection model)
14 | {
15 | faces = model;
16 |
17 | // Create a datagrid view.
18 | dataGridView = new DataGridView();
19 | dataGridView.Dock = DockStyle.Fill;
20 |
21 | // Enable virtual mode.
22 | dataGridView.VirtualMode = true;
23 | dataGridView.ReadOnly = true;
24 | dataGridView.AllowUserToAddRows = false;
25 | dataGridView.AllowUserToDeleteRows = false;
26 |
27 | // Connect the virtual-mode events to event handlers.
28 | dataGridView.CellValueNeeded += DataGridViewOnCellValueNeeded;
29 |
30 | // Add the columns.
31 | dataGridView.Columns.Add(new DataGridViewTextBoxColumn
32 | {
33 | HeaderText = "Face Index"
34 | });
35 | dataGridView.Columns.Add(new DataGridViewTextBoxColumn
36 | {
37 | HeaderText = "Index Count"
38 | });
39 | dataGridView.Columns.Add(new DataGridViewTextBoxColumn
40 | {
41 | HeaderText = "Indices"
42 | });
43 |
44 | dataGridView.AutoSizeColumnsMode =
45 | DataGridViewAutoSizeColumnsMode.DisplayedCells;
46 | dataGridView.RowCount = model.Count;
47 | }
48 |
49 | private void DataGridViewOnCellValueNeeded(object sender, DataGridViewCellValueEventArgs dataGridViewCellValueEventArgs)
50 | {
51 | switch (dataGridViewCellValueEventArgs.ColumnIndex)
52 | {
53 | case 0:
54 | dataGridViewCellValueEventArgs.Value = dataGridViewCellValueEventArgs.RowIndex;
55 | break;
56 | case 1:
57 | dataGridViewCellValueEventArgs.Value = faces[dataGridViewCellValueEventArgs.RowIndex].Indices.Count;
58 | break;
59 | case 2:
60 | {
61 | var face = faces[dataGridViewCellValueEventArgs.RowIndex];
62 | var builder = new StringBuilder();
63 | foreach (var index in face.Indices)
64 | {
65 | builder.AppendFormat("{0}/{1}/{2} ", index.vertex,
66 | index.uv.HasValue ? index.uv.ToString() : string.Empty,
67 | index.normal.HasValue ? index.normal.ToString() : string.Empty);
68 | }
69 |
70 | dataGridViewCellValueEventArgs.Value = builder.ToString();
71 | }
72 | break;
73 | }
74 | }
75 |
76 | public override Control Ui
77 | {
78 | get { return dataGridView; }
79 | }
80 |
81 | public override void Destroy()
82 | {
83 | dataGridView.CellValueNeeded -= DataGridViewOnCellValueNeeded;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/Details/IDetailsUi.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace WavefrontObjectLoader.Details
4 | {
5 | public interface IDetailsUi
6 | {
7 | Control Ui { get; }
8 | void Destroy();
9 | }
10 | }
--------------------------------------------------------------------------------
/source/WavefrontObjectLoader/Details/PropertyGridUi.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace WavefrontObjectLoader.Details
4 | {
5 | public class PropertyGridUi : DetailsUi