├── .gitattributes
├── .gitignore
├── AssetDumper
├── AssetDumper.csproj
├── BaseCommand.cs
├── Collada.cs
├── ExportBundleCommand.cs
└── Program.cs
├── ChunkView
├── ChunkView.csproj
├── ChunkViewCompare.Designer.cs
├── ChunkViewCompare.cs
├── ChunkViewCompare.resx
├── ChunkViewMain.Designer.cs
├── ChunkViewMain.cs
├── ChunkViewMain.resx
├── Program.cs
└── Resources
│ └── ChunkDef.txt
├── Common.Windows
├── Common.Windows.csproj
└── MessageUtil.cs
├── Common
├── BasicResource.cs
├── BinaryUtil.cs
├── Chunk.cs
├── ChunkManager.cs
├── ChunkStream.cs
├── Common.csproj
├── Compression.cs
├── GameDetector.cs
├── Geometry
│ ├── CarbonSolidListReader.cs
│ ├── CarbonSolidReader.cs
│ ├── Data
│ │ ├── CarbonObject.cs
│ │ ├── IEffectBasedMaterial.cs
│ │ ├── IMorphableSolid.cs
│ │ ├── ISortedMaterial.cs
│ │ ├── MostWantedObject.cs
│ │ ├── ProStreetObject.cs
│ │ ├── SolidList.cs
│ │ ├── SolidMeshVertex.cs
│ │ ├── SolidObject.cs
│ │ ├── SolidObjectMaterial.cs
│ │ ├── UndercoverObject.cs
│ │ ├── Underground2Object.cs
│ │ ├── UndergroundObject.cs
│ │ ├── World09Object.cs
│ │ └── World15Object.cs
│ ├── MostWantedSolidListReader.cs
│ ├── MostWantedSolidReader.cs
│ ├── ProStreetSolidListReader.cs
│ ├── ProStreetSolidReader.cs
│ ├── SolidListReader.cs
│ ├── SolidReader.cs
│ ├── UndercoverEffectId.cs
│ ├── UndercoverSolidListReader.cs
│ ├── UndercoverSolidReader.cs
│ ├── Underground2SolidListReader.cs
│ ├── Underground2SolidReader.cs
│ ├── UndergroundSolidListReader.cs
│ ├── UndergroundSolidReader.cs
│ ├── World09SolidListReader.cs
│ ├── World09SolidReader.cs
│ ├── WorldSolidListReader.cs
│ └── WorldSolidReader.cs
├── Hasher.cs
├── Lights
│ ├── Data
│ │ └── LightPack.cs
│ ├── LightPackReader.cs
│ └── Structures
│ │ ├── LightData.cs
│ │ └── LightPackHeader.cs
├── Scenery
│ ├── CarbonScenery.cs
│ ├── Data
│ │ └── ScenerySection.cs
│ ├── MostWantedScenery.cs
│ ├── ProStreetScenery.cs
│ ├── SceneryManager.cs
│ ├── Structures
│ │ ├── PackedRotationMatrix.cs
│ │ └── RotationMatrix.cs
│ ├── UndercoverScenery.cs
│ ├── Underground2Scenery.cs
│ ├── UndergroundScenery.cs
│ └── WorldScenery.cs
└── Textures
│ ├── Data
│ ├── DDSHeader.cs
│ └── TexturePack.cs
│ ├── TpkManager.cs
│ ├── Version1Tpk.cs
│ ├── Version2Tpk.cs
│ ├── Version3Tpk.cs
│ ├── Version4Tpk.cs
│ ├── Version5Tpk.cs
│ └── World10Tpk.cs
├── FBXSharp
├── Connection.cs
├── Core
│ ├── CoordSystem.cs
│ ├── DataView.cs
│ ├── Footer.cs
│ ├── FrameRate.cs
│ ├── FrontVector.cs
│ ├── Header.cs
│ ├── IElement.cs
│ ├── IElementAttribute.cs
│ ├── IElementProperty.cs
│ ├── IScene.cs
│ └── UpVector.cs
├── Element.cs
├── Elementary
│ ├── ArrayBooleanAttribute.cs
│ ├── ArrayDoubleAttribute.cs
│ ├── ArrayInt32Attribute.cs
│ ├── ArrayInt64Attribute.cs
│ ├── ArraySingleAttribute.cs
│ ├── BinaryAttribute.cs
│ ├── ByteAttribute.cs
│ ├── DoubleAttribute.cs
│ ├── Int16Attribute.cs
│ ├── Int32Attribute.cs
│ ├── Int64Attribute.cs
│ ├── SingleAttribute.cs
│ └── StringAttribute.cs
├── ElementaryFactory.cs
├── FBXExporter7400.cs
├── FBXImporter.cs
├── FBXObject.cs
├── FBXProperty.cs
├── FBXSharp.csproj
├── GlobalSettings.cs
├── IOExtensions.cs
├── LoadFlags.cs
├── MathExtensions.cs
├── Objective
│ ├── AnimationCurve.cs
│ ├── AnimationCurveNode.cs
│ ├── AnimationLayer.cs
│ ├── AnimationStack.cs
│ ├── BindPose.cs
│ ├── BlendShape.cs
│ ├── BlendShapeChannel.cs
│ ├── Camera.cs
│ ├── Clip.cs
│ ├── Cluster.cs
│ ├── Geometry.cs
│ ├── Light.cs
│ ├── LimbNode.cs
│ ├── Material.cs
│ ├── Mesh.cs
│ ├── Model.cs
│ ├── NullNode.cs
│ ├── Root.cs
│ ├── Shape.cs
│ ├── Skin.cs
│ └── Texture.cs
├── PropertyFactory.cs
├── Scene.cs
├── TakeInfo.cs
├── TemplateFactory.cs
├── TemplateObject.cs
└── ValueTypes
│ ├── BinaryBlob.cs
│ ├── Color.cs
│ ├── Distance.cs
│ ├── Enumeration.cs
│ ├── Half.cs
│ ├── Matrix4x4.cs
│ ├── Quaternion.cs
│ ├── Reference.cs
│ ├── RotationOrder.cs
│ ├── TimeBase.cs
│ ├── Vector2.cs
│ ├── Vector3.cs
│ └── Vector4.cs
├── NFS ModTools.sln
└── Viewer
├── App.xaml
├── App.xaml.cs
├── AssetRegistry.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── ObservableConcurrentDictionary.cs
├── Properties
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── RenderManager.cs
├── Viewer.csproj
└── nfsw_IDI_ICON1.ico
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.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 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/AssetDumper/AssetDumper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | Exe
5 | true
6 |
7 |
8 | 2.3.0
9 | heyitsleo
10 | NFSTools
11 | AssetDumper
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/AssetDumper/BaseCommand.cs:
--------------------------------------------------------------------------------
1 | namespace AssetDumper;
2 |
3 | public abstract class BaseCommand
4 | {
5 | public abstract int Execute();
6 | }
--------------------------------------------------------------------------------
/AssetDumper/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommandLine;
3 | using Serilog;
4 |
5 | namespace AssetDumper
6 | {
7 | internal static class Program
8 | {
9 | ///
10 | /// The entry point of the application.
11 | ///
12 | ///
13 | public static int Main(string[] args)
14 | {
15 | Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().CreateLogger();
16 |
17 | try
18 | {
19 | return Parser.Default.ParseArguments(args, typeof(ExportBundleCommand))
20 | .MapResult((BaseCommand cmd) => cmd.Execute(), _ => 1);
21 | }
22 | catch (Exception e)
23 | {
24 | Log.Error(e, "An unhandled error occurred in the application.");
25 | return 1;
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/ChunkView/ChunkView.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0-windows
4 | WinExe
5 | false
6 | true
7 | true
8 | Debug;Release
9 | AnyCPU
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ChunkView/ChunkViewCompare.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ChunkView
2 | {
3 | partial class ChunkViewCompare
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.hexBox1 = new Be.Windows.Forms.HexBox();
32 | this.hexBox2 = new Be.Windows.Forms.HexBox();
33 | this.SuspendLayout();
34 | //
35 | // hexBox1
36 | //
37 | this.hexBox1.BackColor = System.Drawing.Color.Gainsboro;
38 | this.hexBox1.Font = new System.Drawing.Font("Segoe UI", 9F);
39 | this.hexBox1.Location = new System.Drawing.Point(1, 12);
40 | this.hexBox1.Name = "hexBox1";
41 | this.hexBox1.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255)))));
42 | this.hexBox1.Size = new System.Drawing.Size(487, 684);
43 | this.hexBox1.TabIndex = 0;
44 | //
45 | // hexBox2
46 | //
47 | this.hexBox2.BackColor = System.Drawing.Color.Gainsboro;
48 | this.hexBox2.Font = new System.Drawing.Font("Segoe UI", 9F);
49 | this.hexBox2.Location = new System.Drawing.Point(494, 12);
50 | this.hexBox2.Name = "hexBox2";
51 | this.hexBox2.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255)))));
52 | this.hexBox2.Size = new System.Drawing.Size(513, 684);
53 | this.hexBox2.TabIndex = 1;
54 | //
55 | // ChunkViewCompare
56 | //
57 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
58 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
59 | this.ClientSize = new System.Drawing.Size(1008, 729);
60 | this.Controls.Add(this.hexBox2);
61 | this.Controls.Add(this.hexBox1);
62 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
63 | this.Name = "ChunkViewCompare";
64 | this.Text = "ChunkViewCompare";
65 | this.ResumeLayout(false);
66 |
67 | }
68 |
69 | #endregion
70 |
71 | private Be.Windows.Forms.HexBox hexBox1;
72 | private Be.Windows.Forms.HexBox hexBox2;
73 | }
74 | }
--------------------------------------------------------------------------------
/ChunkView/ChunkViewCompare.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace ChunkView
12 | {
13 | public partial class ChunkViewCompare : Form
14 | {
15 | public ChunkViewCompare()
16 | {
17 | hexBox1.Font = new Font("Consolas", 10.25F);
18 | hexBox1.ReadOnly = true;
19 | hexBox1.LineInfoVisible = true;
20 | hexBox1.ShadowSelectionColor = Color.FromArgb(100, 60, 188, 255);
21 | hexBox1.StringViewVisible = true;
22 | hexBox1.UseFixedBytesPerLine = true;
23 | hexBox1.VScrollBarVisible = true;
24 | hexBox1.ColumnInfoVisible = true;
25 |
26 | hexBox2.Font = new Font("Consolas", 10.25F);
27 | hexBox2.ReadOnly = true;
28 | hexBox2.LineInfoVisible = true;
29 | hexBox2.ShadowSelectionColor = Color.FromArgb(100, 60, 188, 255);
30 | hexBox2.StringViewVisible = true;
31 | hexBox2.UseFixedBytesPerLine = true;
32 | hexBox2.VScrollBarVisible = true;
33 | hexBox2.ColumnInfoVisible = true;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ChunkView/ChunkViewCompare.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/ChunkView/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | namespace ChunkView
8 | {
9 | static class Program
10 | {
11 | ///
12 | /// The main entry point for the application.
13 | ///
14 | [STAThread]
15 | static void Main()
16 | {
17 | Application.EnableVisualStyles();
18 | Application.SetCompatibleTextRenderingDefault(false);
19 | Application.Run(new ChunkViewMain());
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ChunkView/Resources/ChunkDef.txt:
--------------------------------------------------------------------------------
1 | 0xb3300000 BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS
2 | 0xb0300100 BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS_ANIM
3 | 0x80134000 BCHUNK_SPEED_ESOLID_LIST_CHUNKS
4 | 0x80034100 BCHUNK_SPEED_SCENERY_SECTION
5 | 0x00034101 BCHUNK_SPEED_SCENERY_SECTION_HEADER
6 | 0x00034102 BCHUNK_SPEED_SCENERY_SECTION_INFOS
7 | 0x00034103 BCHUNK_SPEED_SCENERY_SECTION_INSTANCES
8 | 0x00034105 BCHUNK_SPEED_SCENERY_SECTION_TREE
9 | 0x00034106 BCHUNK_SPEED_SCENERY_SECTION_OVERRIDES
10 | 0x00034107 BCHUNK_SPEED_SCENERY_SECTION_PRECULLER_INFO
11 | 0x0003410A BCHUNK_SPEED_SCENERY_SECTION_NGBBS
12 | 0x00034C03 BCHUNK_SPEED_SCENERY_SECTION_VECTORIZED_INFO
13 | 0x0003410D BCHUNK_SPEED_SCENERY_SECTION_LIGHT_TEXTURES
14 | 0x0003410E BCHUNK_SPEED_SCENERY_SECTION_VECTORIZED_CULL_INFO
15 | 0x0003410F BCHUNK_SPEED_SCENERY_SECTION_VECTORIZED_INSTANCE_INFO
16 | 0x00034027 BCHUNK_SPEED_SMOKEABLE_SPAWNER
17 | 0x00037260 BCHUNK_SPEED_BBGANIM_INSTANCE_TREE
18 | 0x00037250 BCHUNK_SPEED_BBGANIM_INSTANCE_NODE
19 | 0x00037240 BCHUNK_SPEED_BBGANIM_KEYFRAMES
20 | 0x00037270 BCHUNK_SPEED_BBGANIM_ENDPACKHEADER
21 | 0x80135000 BCHUNK_SPEED_ELIGHT_CHUNKS
22 | 0x00135001 BCHUNK_SPEED_ELIGHT_CHUNKS_HEADER
23 | 0x00135002 BCHUNK_SPEED_ELIGHT_CHUNKS_LIGHTTREE
24 | 0x00135003 BCHUNK_SPEED_ELIGHT_CHUNKS_LIGHTS
25 | 0x80036000 BCHUNK_SPEED_EMTRIGGER_PACK
26 | 0x00036001 BCHUNK_SPEED_EMTRIGGER_PACK_HEADER
27 | 0x00036002 BCHUNK_SPEED_EMTRIGGER_PACK_EVENTTREE
28 | 0x00036003 BCHUNK_SPEED_EMTRIGGER_PACK_TRIGGERS
29 | 0x00037220 BCHUNK_SPEED_BBGANIM_BLOCKHEADER
30 | 0x0003bc00 BCHUNK_SPEED_EMITTER_LIBRARY
31 | 0x00030201 BCHUNK_FENG_FONT
32 | 0x00030210 BCHUNK_FENG_PACKAGE_COMPRESSED
33 | 0x00030203 BCHUNK_FENG_PACKAGE
34 | 0x00135200 BCHUNK_ELIGHTS
35 | 0x00034600 BCHUNK_CARINFO_ARRAY
36 | 0x00034601 BCHUNK_CARINFO_SKININFO
37 | 0x00034608 BCHUNK_CARINFO_ANIMHOOKUPTABLE
38 | 0x00034609 BCHUNK_CARINFO_ANIMHIDETABLES
39 | 0x00034607 BCHUNK_CARINFO_SLOTTYPES
40 | 0x80034602 BCHUNK_CARINFO_CARPART
41 | 0x00034201 BCHUNK_TRACKINFO
42 | 0x00034202 BCHUNK_SUN
43 | 0x80035000 BCHUNK_ACIDFX
44 | 0x80035010 BCHUNK_ACIDFX
45 | 0x00035021 BCHUNK_ACIDFX
46 | 0x00035020 BCHUNK_ACIDFX_EMITTER
47 | 0x00034b00 BCHUNK_DIFFICULTYINFO
48 | 0x00034a07 BCHUNK_STYLEMOMENTSINFO
49 | 0x00030220 BCHUNK_FEPRESETCARS
50 | 0x00e34009 BCHUNK_EAGLSKELETONS
51 | 0x00e34010 BCHUNK_EAGLANIMATIONS
52 | 0x00039020 BCHUNK_MOVIECATALOG
53 | 0x8003b900 BCHUNK_BOUNDS
54 | 0x0003b901 BCHUNK_BOUNDS_COLLECTION
55 | 0x0003bd00 BCHUNK_EMITTERSYSTEM_TEXTUREPAGE
56 | 0xb0300300 BCHUNK_PCAWEIGHTS
57 | 0x30300201 BCHUNK_COLORCUBE
58 | 0x80037050 BCHUNK_ANIMDIRECTORYDATA
59 | 0x8003b200 BCHUNK_ICECAMERASET
60 | 0x8003B201 BCHUNK_ICECAMERASET
61 | 0x8003b202 BCHUNK_ICECAMERASET
62 | 0x8003b203 BCHUNK_ICECAMERASET
63 | 0x8003b500 BCHUNK_SOUNDSTICHS
64 | 0x80034147 BCHUNK_TRACKPATHMANAGER
65 | 0x0003414A BCHUNK_TRACKPATHMANAGER_ZONES
66 | 0x00034146 BCHUNK_TRACKPOSITIONMARKERS
67 | 0x00034158 BCHUNK_VISIBLESECTIONOVERLAY
68 | 0x80034150 BCHUNK_VISIBLESECTIONMANAGER
69 | 0x00034151 BCHUNK_VISIBLESECTIONMANAGER_INFO
70 | 0x00034152 BCHUNK_VISIBLESECTIONMANAGER_BOUNDARIES
71 | 0x00034153 BCHUNK_VISIBLESECTIONMANAGER_DRIVABLE_SECTIONS
72 | 0x00034155 BCHUNK_VISIBLESECTIONMANAGER_LOADING_SECTIONS
73 | 0x00034156 BCHUNK_VISIBLESECTIONMANAGER_ELEVATION_POLYS
74 | 0x00034157 BCHUNK_VISIBLESECTIONMANAGER_FRAGMENT_FILE_MAP
75 | 0x00034250 BCHUNK_WEATHERMAN
76 | 0x8003b000 BCHUNK_QUICKSPLINE
77 | 0x8003b600 BCHUNK_PARAMETERMAPS
78 | 0x8003b601 BCHUNK_PARAMETERMAP
79 | 0x0003b602 BCHUNK_PARAMETERMAP_HEADER
80 | 0x0003b603 BCHUNK_PARAMETERMAP_FIELD_TYPES
81 | 0x0003b604 BCHUNK_PARAMETERMAP_FIELD_OFFSETS
82 | 0x0003b605 BCHUNK_PARAMETERMAP_PARAMETER_DATA
83 | 0x0003b607 BCHUNK_PARAMETERMAP_PARAMETER_QUAD8
84 | 0x0003b608 BCHUNK_PARAMETERMAP_PARAMETER_QUAD16
85 | 0x00034108 BCHUNK_SCENERY_OVERRIDES
86 | 0x00034109 BCHUNK_SCENERY_GROUPS
87 | 0x8003410b BCHUNK_MODEL_HIERARCHIES
88 | 0x0003410c BCHUNK_MODEL_HIERARCHY
89 | 0x0003b800 BCHUNK_WWORLD
90 | 0x0003b801 BCHUNK_CARP_WCOLLISIONPACK
91 | 0x8003b810 BCHUNK_EVENTSEQUENCE
92 | 0x0003414d BCHUNK_TRACKPATHMANAGER_BARRIERS
93 | 0x00037080 BCHUNK_WORLDANIMENTITYDATA
94 | 0x00037110 BCHUNK_WORLDANIMTREEMARKER
95 | 0x00037150 BCHUNK_WORLDANIMINSTANCEENTRY
96 | 0x00037090 BCHUNK_WORLDANIMDIRECTORYDATA
97 | 0x30300200 BCHUNK_DDSTEXTURE
98 | 0x0003ce12 BCHUNK_SKINREGIONDATABASE
99 | 0x0003ce13 BCHUNK_VINYLMETADATA
100 | 0x0003b200 BCHUNK_ICECAMERAS
101 | 0x00039000 BCHUNK_LANGUAGE
102 | 0x00039001 BCHUNK_LANGUAGEHISTOGRAM
103 | 0x00034a08 BCHUNK_STYLEREWARDCHUNK
104 | 0x00030230 BCHUNK_MAGAZINES
105 | 0x00034026 BCHUNK_SMOKEABLES
106 | 0x00034492 BCHUNK_CAMERA
107 | 0x80034405 BCHUNK_CAMERA
108 | 0x80034425 BCHUNK_CAMERA
109 | 0x80034410 BCHUNK_CAMERA
110 | 0x80034415 BCHUNK_CAMERA
111 | 0x80034420 BCHUNK_CAMERA
112 | 0x0003a000 BCHUNK_ELIPSE_TABLE
113 | 0x00034036 BCHUNK_NIS_SCENE_MAPPER_DATA
114 | 0x00034121 BCHUNK_TRACKROUTE_MANAGER
115 | 0x00034122 BCHUNK_TRACKROUTE_SIGNPOSTS
116 | 0x00034123 BCHUNK_TRACKROUTE_TRAFFIC_INTERSECTIONS
117 | 0x00034124 BCHUNK_TRACKROUTE_CROSS_TRAFFIC_EMITTERS
118 | 0x00034130 BCHUNK_TOPOLOGYTREE
119 | 0x00034131 BCHUNK_TOPOLOGYTREE
120 | 0x00034132 BCHUNK_TOPOLOGYTREE
121 | 0x00034133 BCHUNK_TOPOLOGYTREE
122 | 0x00034134 BCHUNK_TOPOLOGYTREE
123 | 0x0003b300 BCHUNK_WORLDOBJECTS
124 | 0x00034a09 BCHUNK_PERFUPGRADELEVELINFOCHUNK
125 | 0x00034a0a BCHUNK_PERFUPGRADEPACKAGECHUNK
126 | 0x00030240 BCHUNK_WIDEDECALS
127 | 0x00034a03 BCHUNK_RANKINGLADDERS
128 | 0x00039010 BCHUNK_SUBTITLES
129 | 0x00034035 BCHUNK_NISSCENEDATA
130 | 0x80037020 BCHUNK_ANIMSCENEDATA
131 | 0x0003b811 BCHUNK_EVENTSEQUENCE
132 | 0x0003b802 BCHUNK_CARP_WGRIDISLAND
133 | 0x00034110 BCHUNK_TRACKSTREAMER_SECTIONS
134 | 0x00034111 BCHUNK_TRACKSTREAMER_INFO
135 | 0x00034112 BCHUNK_TRACKSTREAMER_BARRIERS
136 | 0x00034113 BCHUNK_TRACKSTREAMER_DISC_SECTIONS
137 | 0x00034114 BCHUNK_SCENERY_GROUP_OVERRIDES
138 | 0x00034185 BCHUNK_VISCURTAINS
--------------------------------------------------------------------------------
/Common.Windows/Common.Windows.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0-windows
4 | true
5 | Debug;Release
6 | AnyCPU
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Common.Windows/MessageUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace Common.Windows
4 | {
5 | public static class MessageUtil
6 | {
7 | private static string _appName = "UNLOCALIZED";
8 |
9 | public static void SetAppName(string appName)
10 | {
11 | _appName = appName;
12 | }
13 |
14 | public static DialogResult ShowInfo(string message)
15 | {
16 | return MessageBox.Show(message, _appName, MessageBoxButtons.OK, MessageBoxIcon.Information);
17 | }
18 |
19 | public static DialogResult ShowError(string message)
20 | {
21 | return MessageBox.Show(message, _appName, MessageBoxButtons.OK, MessageBoxIcon.Error);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Common/BasicResource.cs:
--------------------------------------------------------------------------------
1 | namespace Common;
2 |
3 | public class BasicResource
4 | {
5 | }
--------------------------------------------------------------------------------
/Common/Chunk.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common;
4 |
5 | public class Chunk
6 | {
7 | public uint Id { get; set; }
8 |
9 | public uint Size { get; set; }
10 |
11 | public long Offset { get; set; }
12 |
13 | public byte[] Data { get; set; }
14 | public BasicResource Resource { get; set; }
15 |
16 | public List SubChunks { get; set; }
17 | }
--------------------------------------------------------------------------------
/Common/ChunkStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using SysStream = System.IO.Stream;
6 |
7 | namespace Common
8 | {
9 | // Code in this file is based on Arushan's Most Wanted Geometry Compiler.
10 |
11 | public struct FixedLenString
12 | {
13 | private int _length;
14 | private string _string;
15 |
16 | public FixedLenString(string data)
17 | {
18 | _length = data.Length + 4 - data.Length % 4;
19 | _string = data;
20 | }
21 |
22 | public FixedLenString(string data, int length)
23 | {
24 | _length = length;
25 | _string = data;
26 | }
27 |
28 | public FixedLenString(BinaryReader br, int length)
29 | {
30 | _length = 0;
31 | _string = "";
32 | Read(br, length);
33 | }
34 |
35 | public FixedLenString(BinaryReader br)
36 | {
37 | _length = 0;
38 | _string = "";
39 | Read(br);
40 | }
41 |
42 | public byte Length => (byte)_length;
43 |
44 | public void Read(BinaryReader br, int length)
45 | {
46 | _length = length;
47 | var bytes = br.ReadBytes(length);
48 | var data = Encoding.ASCII.GetString(bytes);
49 | _string = data.TrimEnd((char)0);
50 | }
51 |
52 | public void Read(BinaryReader br)
53 | {
54 | _length = 0;
55 | _string = "";
56 | while (true)
57 | {
58 | var bytes = br.ReadBytes(4);
59 | var data = Encoding.ASCII.GetString(bytes);
60 | _string += data;
61 | _length += 4;
62 | if (data.IndexOf((char)0) < 0) continue;
63 | _string = _string.TrimEnd((char)0);
64 | break;
65 | }
66 | }
67 |
68 | public void Write(BinaryWriter bw)
69 | {
70 | var data = _string.PadRight(_length, (char)0);
71 | var bytes = Encoding.ASCII.GetBytes(data);
72 | bw.Write(bytes);
73 | }
74 |
75 | public override string ToString()
76 | {
77 | return _string;
78 | }
79 | }
80 |
81 | public class RealChunk
82 | {
83 | public bool IsParent => (Type & 0x80000000) != 0;
84 |
85 | public uint Type { get; set; }
86 |
87 | public long EndOffset
88 | {
89 | get => Offset + Length + 0x8;
90 | set => Length = (uint)(value - Offset - 0x8);
91 | }
92 |
93 | public long Offset { get; set; }
94 |
95 | public uint Length { get; set; }
96 |
97 | public void Read(BinaryReader br)
98 | {
99 | Offset = (int)br.BaseStream.Position;
100 | Type = br.ReadUInt32();
101 | Length = br.ReadUInt32();
102 | }
103 |
104 | public void Write(BinaryWriter bw)
105 | {
106 | var offset = bw.BaseStream.Position;
107 | bw.BaseStream.Seek(Offset, SeekOrigin.Begin);
108 | bw.Write(Type);
109 | bw.Write(Length);
110 | bw.BaseStream.Seek(offset, SeekOrigin.Begin);
111 | }
112 |
113 | public void GoToStart(SysStream fs)
114 | {
115 | fs.Seek(Offset + 8, SeekOrigin.Begin);
116 | }
117 |
118 | public void Skip(SysStream fs)
119 | {
120 | fs.Seek(EndOffset, SeekOrigin.Begin);
121 | }
122 |
123 | public override string ToString()
124 | {
125 | return "RealChunk {\n\tType=" + $"{Type:X}"
126 | + ",\n\tOffset=" + $"{Offset:X}"
127 | + ",\n\tLength=" + $"{Length:X}"
128 | + "\n}";
129 | }
130 | }
131 |
132 | public class ChunkStream : IDisposable
133 | {
134 | private readonly BinaryWriter _binaryWriter;
135 | private readonly Stack _chunkStack;
136 | private readonly SysStream _stream;
137 | private bool _canWrite;
138 |
139 | public ChunkStream(SysStream stream)
140 | {
141 | _stream = stream;
142 | _chunkStack = new Stack();
143 | _binaryWriter = new BinaryWriter(_stream);
144 | _canWrite = true;
145 | }
146 |
147 | public ChunkStream(BinaryReader binaryReader)
148 | {
149 | _stream = binaryReader.BaseStream;
150 | _chunkStack = new Stack();
151 | }
152 |
153 | public ChunkStream(BinaryWriter binaryWriter)
154 | {
155 | _stream = binaryWriter.BaseStream;
156 | _chunkStack = new Stack();
157 | _binaryWriter = binaryWriter;
158 | _canWrite = true;
159 | }
160 |
161 | ///
162 | public void Dispose()
163 | {
164 | _stream?.Dispose();
165 |
166 | if (!_canWrite) return;
167 |
168 | _binaryWriter?.Dispose();
169 | _canWrite = false;
170 | }
171 |
172 | public SysStream GetStream()
173 | {
174 | return _stream;
175 | }
176 | }
177 | }
--------------------------------------------------------------------------------
/Common/Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | Library
5 | false
6 | true
7 | Debug;Release
8 | AnyCPU
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Common/Compression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using CompLib;
4 |
5 | namespace Common
6 | {
7 | ///
8 | /// Wrapper around CompLib API
9 | ///
10 | public static class Compression
11 | {
12 | public static Span Decompress(ReadOnlySpan input)
13 | {
14 | return BlobDecompressor.Decompress(input);
15 | }
16 |
17 | public static long DecompressCip(Stream src, Stream dst, long srcSize)
18 | {
19 | CipDecompressor.Decompress(src, dst, srcSize, out var decompressedSize);
20 |
21 | return decompressedSize;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Common/GameDetector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Common
5 | {
6 | public static class GameDetector
7 | {
8 | public enum Game
9 | {
10 | MostWanted,
11 | Carbon,
12 | ProStreet,
13 | ProStreetTest, // special value
14 | World,
15 | World09,
16 | World10,
17 | Underground2,
18 | Underground,
19 | Unknown,
20 | Undercover
21 | }
22 |
23 | ///
24 | /// Attempt to determine the NFS game installed in the given directory.
25 | ///
26 | ///
27 | ///
28 | public static Game DetectGame(string directory)
29 | {
30 | // speed.exe can be UG1 or MW
31 | if (File.Exists(Path.Combine(directory, "speed.exe")))
32 | {
33 | var tracksPath = Path.Combine(directory, "TRACKS");
34 | if (!Directory.Exists(tracksPath))
35 | {
36 | throw new ArgumentException("TRACKS folder does not exist! Cannot determine game.");
37 | }
38 |
39 | if (File.Exists(Path.Combine(tracksPath, "L2RA.BUN"))
40 | && File.Exists(Path.Combine(tracksPath, "STREAML2RA.BUN")))
41 | {
42 | return Game.MostWanted;
43 | }
44 |
45 | if (File.Exists(Path.Combine(tracksPath, "STREAML1RA.BUN")))
46 | {
47 | return Game.Underground;
48 | }
49 | }
50 |
51 | if (File.Exists(Path.Combine(directory, "speed2.exe")))
52 | {
53 | return Game.Underground2;
54 | }
55 |
56 | if (File.Exists(Path.Combine(directory, "nfsc.exe")))
57 | {
58 | return Game.Carbon;
59 | }
60 |
61 | if (File.Exists(Path.Combine(directory, "nfs.exe")))
62 | {
63 | var tracksPath = Path.Combine(directory, "TRACKS");
64 | if (!Directory.Exists(tracksPath))
65 | {
66 | throw new ArgumentException("TRACKS folder does not exist! Cannot determine game.");
67 | }
68 |
69 | if (File.Exists(Path.Combine(tracksPath, "L8R_MW2.BUN"))
70 | && File.Exists(Path.Combine(tracksPath, "STREAML8R_MW2.BUN")))
71 | {
72 | return Game.Undercover;
73 | }
74 |
75 | return Game.ProStreet;
76 | }
77 |
78 | return File.Exists(Path.Combine(directory, "nfsw.exe")) ? Game.World : Game.Unknown;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/Common/Geometry/CarbonSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class CarbonMaterial : SolidObjectMaterial, IEffectBasedMaterial
9 | {
10 | public uint EffectId { get; set; }
11 | }
12 |
13 | public class CarbonSolidListReader : SolidListReader
14 | {
15 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
16 | uint chunkSize)
17 | {
18 | switch (chunkId)
19 | {
20 | case 0x134002:
21 | var info = BinaryUtil.ReadStruct(binaryReader);
22 | solidList.Filename = info.Filename;
23 | solidList.GroupName = info.GroupName;
24 | return true;
25 | case 0x134003:
26 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
27 | return true;
28 | case 0x134004:
29 | ProcessStreamingTable(solidList, binaryReader, chunkSize);
30 | return
31 | false; // We need to stop after processing the streaming table, otherwise we'll run into bad stuff
32 | default:
33 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
34 | }
35 | }
36 |
37 | private void ProcessStreamingTable(SolidList solidList, BinaryReader binaryReader, uint chunkSize)
38 | {
39 | var chunkEndPos = binaryReader.BaseStream.Position + chunkSize;
40 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
41 | while (binaryReader.BaseStream.Position < chunkEndPos)
42 | {
43 | var och = BinaryUtil.ReadUnmanagedStruct(binaryReader);
44 | var curPos = binaryReader.BaseStream.Position;
45 | binaryReader.BaseStream.Position = och.Offset;
46 |
47 | Debug.Assert(och.Length >= 8, "och.Length >= 8");
48 |
49 | if (och.Length == och.LengthCompressed)
50 | {
51 | // Assume that the object is uncompressed.
52 | // If this ever turns out to be false, I don't know what I'll do.
53 | binaryReader.BaseStream.Position += 8;
54 | solidList.Objects.Add(CreateObjectReader().Read(binaryReader, och.Length - 8));
55 | }
56 | else
57 | {
58 | // Load decompressed object
59 | using var ms = new MemoryStream();
60 | Compression.DecompressCip(binaryReader.BaseStream, ms, och.LengthCompressed);
61 |
62 | ms.Position = 8;
63 | using var dcr = new BinaryReader(ms);
64 | solidList.Objects.Add(CreateObjectReader().Read(dcr, och.Length - 8));
65 | }
66 |
67 | binaryReader.BaseStream.Position = curPos;
68 | }
69 | }
70 |
71 | protected override SolidReader CreateObjectReader()
72 | {
73 | return new CarbonSolidReader();
74 | }
75 |
76 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
77 | private struct SolidListInfo
78 | {
79 | public readonly long Blank;
80 |
81 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
82 |
83 | public readonly int NumObjects;
84 |
85 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
86 | public readonly string Filename;
87 |
88 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
89 | public readonly string GroupName;
90 |
91 | public readonly int UnknownOffset;
92 | public readonly int UnknownSize;
93 | }
94 |
95 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
96 | private struct SolidObjectOffset
97 | {
98 | public uint Hash;
99 | public uint Offset;
100 | public readonly uint LengthCompressed;
101 | public readonly uint Length;
102 | public uint Flags;
103 | private uint blank;
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/CarbonObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common.Geometry.Data
4 | {
5 | public class CarbonObject : SolidObject, IMorphableSolid
6 | {
7 | public List MorphTargets { get; } = new();
8 | }
9 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/IEffectBasedMaterial.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data;
2 |
3 | public interface IEffectBasedMaterial
4 | {
5 | uint EffectId { get; set; }
6 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/IMorphableSolid.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common.Geometry.Data;
4 |
5 | public interface IMorphableSolid
6 | {
7 | List MorphTargets { get; }
8 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/ISortedMaterial.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data;
2 |
3 | public interface ISortedMaterial
4 | {
5 | uint SortKey { get; set; }
6 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/MostWantedObject.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data
2 | {
3 | public class MostWantedObject : SolidObject
4 | {
5 | //
6 | }
7 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/ProStreetObject.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data
2 | {
3 | public class ProStreetObject : SolidObject
4 | {
5 | //
6 | }
7 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/SolidList.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common.Geometry.Data
4 | {
5 | public class SolidList : BasicResource
6 | {
7 | public string Filename { get; set; }
8 |
9 | public string GroupName { get; set; }
10 |
11 | public List Objects { get; } = new List();
12 | }
13 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/SolidMeshVertex.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 |
3 | namespace Common.Geometry.Data
4 | {
5 | public struct SolidMeshVertex
6 | {
7 | // REQUIRED attributes
8 | public Vector3 Position { get; set; }
9 | public Vector2 TexCoords { get; set; }
10 |
11 | // OPTIONAL attributes
12 | public Vector3? Normal { get; set; }
13 | public Vector3? Tangent { get; set; }
14 | public Vector3? BlendWeight { get; set; }
15 | public Vector3? BlendIndices { get; set; }
16 |
17 | public uint? Color { get; set; }
18 |
19 | // This one is NFSW-specific
20 | public uint? Color2 { get; set; }
21 | }
22 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/SolidObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Numerics;
3 |
4 | namespace Common.Geometry.Data
5 | {
6 | public abstract class SolidObject : BasicResource
7 | {
8 | public static IEqualityComparer HashComparer { get; } = new HashEqualityComparer();
9 |
10 | public string Name { get; set; }
11 |
12 | public Matrix4x4 PivotMatrix { get; set; }
13 |
14 | public Vector3 MinPoint { get; set; }
15 |
16 | public Vector3 MaxPoint { get; set; }
17 |
18 | public uint Hash { get; set; }
19 |
20 | public List Materials { get; } = new();
21 |
22 | public List> VertexSets { get; } = new();
23 |
24 | public List TextureHashes { get; } = new();
25 |
26 | private sealed class HashEqualityComparer : IEqualityComparer
27 | {
28 | public bool Equals(SolidObject x, SolidObject y)
29 | {
30 | if (ReferenceEquals(x, y)) return true;
31 | if (ReferenceEquals(x, null)) return false;
32 | if (ReferenceEquals(y, null)) return false;
33 | if (x.GetType() != y.GetType()) return false;
34 | return x.Hash == y.Hash;
35 | }
36 |
37 | public int GetHashCode(SolidObject obj)
38 | {
39 | return (int)obj.Hash;
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/SolidObjectMaterial.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 |
3 | namespace Common.Geometry.Data
4 | {
5 | public class SolidObjectMaterial
6 | {
7 | public Vector3 MinPoint { get; set; }
8 |
9 | public Vector3 MaxPoint { get; set; }
10 |
11 | public uint Hash { get; set; }
12 |
13 | public uint Flags { get; set; }
14 |
15 | public uint NumVerts { get; set; }
16 |
17 | public int VertexSetIndex { get; set; }
18 |
19 | public uint NumIndices { get; set; } // NumTris * 3
20 |
21 | public uint DiffuseTextureHash { get; set; }
22 | public uint? NormalTextureHash { get; set; }
23 | public uint? SpecularTextureHash { get; set; }
24 |
25 | public string Name { get; set; }
26 | public ushort[] Indices { get; set; }
27 | }
28 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/UndercoverObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common.Geometry.Data
4 | {
5 | public class UndercoverObject : SolidObject
6 | {
7 | public UndercoverObject()
8 | {
9 | TextureTypeList = new List();
10 | }
11 |
12 | public List TextureTypeList { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/Underground2Object.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data
2 | {
3 | public class Underground2Object : SolidObject
4 | {
5 | //
6 | }
7 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/UndergroundObject.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry.Data
2 | {
3 | public class UndergroundObject : SolidObject
4 | {
5 | //
6 | }
7 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/World09Object.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common.Geometry.Data;
4 |
5 | public class World09Object : SolidObject, IMorphableSolid
6 | {
7 | public World09Object()
8 | {
9 | // MorphLists = new List>();
10 | // MorphMatrices = new List();
11 | MorphTargets = new List();
12 | }
13 |
14 | // public List> MorphLists { get; set; }
15 | // public List MorphMatrices { get; set; }
16 |
17 | // public struct MorphInfo
18 | // {
19 | // // morph range is [VertexStartIndex, VertexEndIndex]
20 | // public int VertexStartIndex;
21 | // public int VertexEndIndex;
22 | // public int MorphMatrixIndex;
23 | // }
24 | public List MorphTargets { get; }
25 | }
--------------------------------------------------------------------------------
/Common/Geometry/Data/World15Object.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Numerics;
3 |
4 | namespace Common.Geometry.Data
5 | {
6 | public class World15Object : SolidObject
7 | {
8 | public World15Object()
9 | {
10 | MorphLists = new List>();
11 | MorphMatrices = new List();
12 | }
13 |
14 | public List> MorphLists { get; set; }
15 | public List MorphMatrices { get; set; }
16 |
17 | public struct MorphInfo
18 | {
19 | // morph range is [VertexStartIndex, VertexEndIndex]
20 | public int VertexStartIndex;
21 | public int VertexEndIndex;
22 | public int MorphMatrixIndex;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Common/Geometry/MostWantedSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class MostWantedMaterial : SolidObjectMaterial, IEffectBasedMaterial
9 | {
10 | public uint EffectId { get; set; }
11 | }
12 |
13 | public class MostWantedSolidListReader : SolidListReader
14 | {
15 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
16 | uint chunkSize)
17 | {
18 | switch (chunkId)
19 | {
20 | case 0x134002:
21 | var info = BinaryUtil.ReadStruct(binaryReader);
22 | solidList.Filename = info.Filename;
23 | solidList.GroupName = info.GroupName;
24 | return true;
25 | case 0x134003:
26 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
27 | return true;
28 | case 0x134004:
29 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
30 | return true;
31 | default:
32 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
33 | }
34 | }
35 |
36 | protected override SolidReader CreateObjectReader()
37 | {
38 | return new MostWantedSolidReader();
39 | }
40 |
41 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
42 | private struct SolidListInfo
43 | {
44 | public readonly long Blank;
45 |
46 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
47 |
48 | public readonly int NumObjects;
49 |
50 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
51 | public readonly string Filename;
52 |
53 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
54 | public readonly string GroupName;
55 |
56 | public readonly int UnknownOffset;
57 | public readonly int UnknownSize;
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/Common/Geometry/ProStreetSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class ProStreetMaterial : SolidObjectMaterial, IEffectBasedMaterial
9 | {
10 | public uint EffectId { get; set; }
11 | }
12 |
13 | public class ProStreetSolidListReader : SolidListReader
14 | {
15 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
16 | uint chunkSize)
17 | {
18 | switch (chunkId)
19 | {
20 | case 0x134002:
21 | var info = BinaryUtil.ReadStruct(binaryReader);
22 | solidList.Filename = info.Filename;
23 | solidList.GroupName = info.GroupName;
24 | return true;
25 | case 0x134003:
26 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
27 | return true;
28 | case 0x134004:
29 | ProcessStreamingTable(solidList, binaryReader, chunkSize);
30 | return
31 | false; // We need to stop after processing the streaming table, otherwise we'll run into bad stuff
32 | default:
33 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
34 | }
35 | }
36 |
37 | private void ProcessStreamingTable(SolidList solidList, BinaryReader binaryReader, uint chunkSize)
38 | {
39 | var chunkEndPos = binaryReader.BaseStream.Position + chunkSize;
40 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
41 | while (binaryReader.BaseStream.Position < chunkEndPos)
42 | {
43 | var och = BinaryUtil.ReadUnmanagedStruct(binaryReader);
44 | var curPos = binaryReader.BaseStream.Position;
45 | binaryReader.BaseStream.Position = och.Offset;
46 |
47 | Debug.Assert(och.Length >= 8, "och.Length >= 8");
48 |
49 | if (och.Length == och.LengthCompressed)
50 | {
51 | // Assume that the object is uncompressed.
52 | // If this ever turns out to be false, I don't know what I'll do.
53 | binaryReader.BaseStream.Position += 8;
54 | solidList.Objects.Add(CreateObjectReader().Read(binaryReader, och.Length - 8));
55 | }
56 | else
57 | {
58 | // Load decompressed object
59 | using var ms = new MemoryStream();
60 | Compression.DecompressCip(binaryReader.BaseStream, ms, och.LengthCompressed);
61 |
62 | ms.Position = 8;
63 | using var dcr = new BinaryReader(ms);
64 | solidList.Objects.Add(CreateObjectReader().Read(dcr, och.Length - 8));
65 | }
66 |
67 | binaryReader.BaseStream.Position = curPos;
68 | }
69 | }
70 |
71 | protected override SolidReader CreateObjectReader()
72 | {
73 | return new ProStreetSolidReader();
74 | }
75 |
76 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
77 | private struct SolidListInfo
78 | {
79 | public long Blank;
80 |
81 | public int Marker; // this doesn't change between games for some reason... rather unfortunate
82 |
83 | public int NumObjects;
84 |
85 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
86 | public readonly string Filename;
87 |
88 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
89 | public readonly string GroupName;
90 |
91 | public int UnknownOffset, UnknownSize;
92 | }
93 |
94 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
95 | private struct SolidObjectOffset
96 | {
97 | public uint Hash;
98 | public uint Offset;
99 | public readonly uint LengthCompressed;
100 | public readonly uint Length;
101 | public uint Flags;
102 | private uint blank;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/Common/Geometry/SolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry;
7 |
8 | public abstract class SolidListReader
9 | {
10 | public SolidList ReadSolidList(BinaryReader br, uint containerSize)
11 | {
12 | var solidList = new SolidList();
13 |
14 | ReadChunks(solidList, br, containerSize);
15 |
16 | return solidList;
17 | }
18 |
19 | private void ReadChunks(SolidList solidList, BinaryReader binaryReader, uint containerSize)
20 | {
21 | var readState = ReadState.Init;
22 |
23 | var endPos = binaryReader.BaseStream.Position + containerSize;
24 |
25 | while (binaryReader.BaseStream.Position < endPos)
26 | {
27 | var chunkId = binaryReader.ReadUInt32();
28 | var chunkSize = binaryReader.ReadUInt32();
29 | var chunkPos = binaryReader.BaseStream.Position;
30 | var chunkEndPos = chunkPos + chunkSize;
31 |
32 | if (chunkId == 0)
33 | {
34 | binaryReader.BaseStream.Position = chunkEndPos;
35 | }
36 | else if ((chunkId & 0x80000000) == 0)
37 | {
38 | if (readState == ReadState.Headers)
39 | {
40 | // We can return false from a header processor to completely stop reading chunks.
41 | if (!ProcessHeaderChunk(solidList, binaryReader, chunkId, chunkSize)) return;
42 | }
43 | else
44 | {
45 | throw new Exception("Impossible state reached");
46 | }
47 |
48 | Debug.Assert(chunkPos <= binaryReader.BaseStream.Position,
49 | "chunkPos <= binaryReader.BaseStream.Position");
50 | Debug.Assert(binaryReader.BaseStream.Position <= chunkEndPos,
51 | "binaryReader.BaseStream.Position <= chunkEndPos");
52 |
53 | binaryReader.BaseStream.Position = chunkEndPos;
54 | }
55 | else
56 | {
57 | switch (chunkId)
58 | {
59 | case 0x80134001:
60 | Debug.Assert(readState == ReadState.Init, "readState == ReadState.Init");
61 | readState = ReadState.Headers;
62 | break;
63 | case 0x80134008:
64 | Debug.Assert(chunkSize == 0, "chunkSize == 0");
65 | break;
66 | case 0x80134010:
67 | Debug.Assert(readState != ReadState.Init, "readState != ReadState.Init");
68 | readState = ReadState.Object;
69 | var solidObjectReader = CreateObjectReader();
70 | solidList.Objects.Add(solidObjectReader.Read(binaryReader, chunkSize));
71 | break;
72 | default:
73 | throw new InvalidDataException(
74 | $"Not sure what to make of parent chunk: 0x{chunkId:X8} @ 0x{chunkPos:X}");
75 | }
76 | }
77 | }
78 | }
79 |
80 | protected abstract bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
81 | uint chunkSize);
82 |
83 | protected abstract SolidReader CreateObjectReader();
84 |
85 | private enum ReadState
86 | {
87 | Init,
88 | Headers,
89 | Object
90 | }
91 | }
--------------------------------------------------------------------------------
/Common/Geometry/UndercoverEffectId.cs:
--------------------------------------------------------------------------------
1 | namespace Common.Geometry;
2 |
3 | public enum UndercoverEffectId
4 | {
5 | car,
6 | car_a,
7 | car_a_nzw,
8 | car_nm,
9 | car_nm_a,
10 | car_nm_v_s,
11 | car_nm_v_s_a,
12 | car_si,
13 | car_si_a,
14 | car_t,
15 | car_t_a,
16 | car_t_nm,
17 | car_v,
18 | diffuse_spec_2sided,
19 | mw2_branches,
20 | mw2_car_heaven,
21 | mw2_car_heaven_default,
22 | mw2_cardebris,
23 | mw2_carhvn_floor,
24 | mw2_combo_refl,
25 | mw2_constant,
26 | mw2_constant_alpha_bias,
27 | mw2_dif_spec_a_bias,
28 | mw2_diffuse_spec,
29 | mw2_diffuse_spec_alpha,
30 | mw2_diffuse_spec_illum,
31 | mw2_diffuse_spec_salpha,
32 | mw2_dirt,
33 | mw2_dirt_overlay,
34 | mw2_dirt_rock,
35 | mw2_fol_alwaysfacing,
36 | mw2_foliage,
37 | mw2_foliage_lod,
38 | mw2_glass_no_n,
39 | mw2_glass_refl,
40 | mw2_grass,
41 | mw2_grass_dirt,
42 | mw2_grass_rock,
43 | mw2_icon,
44 | mw2_illuminated,
45 | mw2_indicator,
46 | mw2_matte,
47 | mw2_matte_alpha,
48 | mw2_normalmap,
49 | mw2_normalmap_bias,
50 | mw2_ocean,
51 | mw2_pano,
52 | mw2_parallax,
53 | mw2_road,
54 | mw2_road_lite,
55 | mw2_road_overlay,
56 | mw2_road_refl,
57 | mw2_road_refl_lite,
58 | mw2_road_refl_overlay,
59 | mw2_road_refl_tile,
60 | mw2_road_tile,
61 | mw2_rock,
62 | mw2_rock_overlay,
63 | mw2_scrub,
64 | mw2_scrub_lod,
65 | mw2_sky,
66 | mw2_smokegeo,
67 | mw2_texture_scroll,
68 | mw2_trunk,
69 | mw2_tunnel_illum,
70 | mw2_tunnel_road,
71 | mw2_tunnel_wall,
72 | normalmap2sided,
73 | shadowmesh,
74 | standardeffect,
75 | ubereffect,
76 | ubereffectblend,
77 | watersplash,
78 | worldbone,
79 | worldbonenocull,
80 | worldbonetransparency,
81 | none
82 | }
--------------------------------------------------------------------------------
/Common/Geometry/UndercoverSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class UndercoverMaterial : SolidObjectMaterial, IEffectBasedMaterial
9 | {
10 | public uint NumReducedIndices { get; set; }
11 | public uint EffectId { get; set; }
12 | }
13 |
14 | public class UndercoverSolidListReader : SolidListReader
15 | {
16 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
17 | uint chunkSize)
18 | {
19 | switch (chunkId)
20 | {
21 | case 0x134002:
22 | var info = BinaryUtil.ReadStruct(binaryReader);
23 | solidList.Filename = info.Filename;
24 | solidList.GroupName = info.GroupName;
25 | return true;
26 | case 0x134003:
27 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
28 | return true;
29 | case 0x134004:
30 | ProcessStreamingTable(solidList, binaryReader, chunkSize);
31 | return
32 | false; // We need to stop after processing the streaming table, otherwise we'll run into bad stuff
33 | default:
34 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
35 | }
36 | }
37 |
38 | private void ProcessStreamingTable(SolidList solidList, BinaryReader binaryReader, uint chunkSize)
39 | {
40 | var chunkEndPos = binaryReader.BaseStream.Position + chunkSize;
41 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
42 | while (binaryReader.BaseStream.Position < chunkEndPos)
43 | {
44 | var och = BinaryUtil.ReadUnmanagedStruct(binaryReader);
45 | var curPos = binaryReader.BaseStream.Position;
46 | binaryReader.BaseStream.Position = och.Offset;
47 |
48 | Debug.Assert(och.Length >= 8, "och.Length >= 8");
49 |
50 | if (och.Length == och.LengthCompressed)
51 | {
52 | // Assume that the object is uncompressed.
53 | // If this ever turns out to be false, I don't know what I'll do.
54 | binaryReader.BaseStream.Position += 8;
55 | solidList.Objects.Add(CreateObjectReader().Read(binaryReader, och.Length - 8));
56 | }
57 | else
58 | {
59 | // Load decompressed object
60 | using var ms = new MemoryStream();
61 | Compression.DecompressCip(binaryReader.BaseStream, ms, och.LengthCompressed);
62 |
63 | ms.Position = 8;
64 | using var dcr = new BinaryReader(ms);
65 | solidList.Objects.Add(CreateObjectReader().Read(dcr, och.Length - 8));
66 | }
67 |
68 | binaryReader.BaseStream.Position = curPos;
69 | }
70 | }
71 |
72 | protected override SolidReader CreateObjectReader()
73 | {
74 | return new UndercoverSolidReader();
75 | }
76 |
77 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
78 | private struct SolidListInfo
79 | {
80 | public readonly long Blank;
81 |
82 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
83 |
84 | public readonly int NumObjects;
85 |
86 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
87 | public readonly string Filename;
88 |
89 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
90 | public readonly string GroupName;
91 |
92 | public readonly int UnknownOffset;
93 | public readonly int UnknownSize;
94 | }
95 |
96 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
97 | private struct SolidObjectOffset
98 | {
99 | public uint Hash;
100 | public uint Offset;
101 | public readonly uint LengthCompressed;
102 | public readonly uint Length;
103 | public uint Flags;
104 | private uint blank;
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/Common/Geometry/Underground2SolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry;
7 |
8 | public class Underground2Material : SolidObjectMaterial
9 | {
10 | }
11 |
12 | public class Underground2SolidListReader : SolidListReader
13 | {
14 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
15 | uint chunkSize)
16 | {
17 | switch (chunkId)
18 | {
19 | case 0x134002:
20 | var info = BinaryUtil.ReadStruct(binaryReader);
21 | solidList.Filename = info.Filename;
22 | solidList.GroupName = info.GroupName;
23 | return true;
24 | case 0x134003:
25 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
26 | return true;
27 | case 0x134004:
28 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
29 | return true;
30 | default:
31 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
32 | }
33 | }
34 |
35 | protected override SolidReader CreateObjectReader()
36 | {
37 | return new Underground2SolidReader();
38 | }
39 |
40 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
41 | private struct SolidListInfo
42 | {
43 | public readonly long Blank;
44 |
45 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
46 |
47 | public readonly int NumObjects;
48 |
49 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
50 | public readonly string Filename;
51 |
52 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
53 | public readonly string GroupName;
54 |
55 | public readonly int UnknownOffset;
56 | public readonly int UnknownSize;
57 | }
58 | }
--------------------------------------------------------------------------------
/Common/Geometry/UndergroundSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class UndergroundMaterial : SolidObjectMaterial
9 | {
10 | }
11 |
12 | public class UndergroundSolidListReader : SolidListReader
13 | {
14 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
15 | uint chunkSize)
16 | {
17 | switch (chunkId)
18 | {
19 | case 0x134002:
20 | var info = BinaryUtil.ReadStruct(binaryReader);
21 | solidList.Filename = info.Filename;
22 | solidList.GroupName = info.GroupName;
23 | return true;
24 | case 0x134003:
25 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
26 | return true;
27 | case 0x134004:
28 | Debug.Assert(chunkSize % 20 == 0, "chunkSize % 20 == 0");
29 | return true;
30 | default:
31 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
32 | }
33 | }
34 |
35 | protected override SolidReader CreateObjectReader()
36 | {
37 | return new UndergroundSolidReader();
38 | }
39 |
40 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
41 | private struct SolidListInfo
42 | {
43 | public readonly long Blank;
44 |
45 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
46 |
47 | public readonly int NumObjects;
48 |
49 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
50 | public readonly string Filename;
51 |
52 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
53 | public readonly string GroupName;
54 |
55 | public readonly int UnknownOffset;
56 | public readonly int UnknownSize;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/Common/Geometry/World09SolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry;
7 |
8 | public class World09Material : SolidObjectMaterial, IEffectBasedMaterial
9 | {
10 | public uint EffectId { get; set; }
11 | }
12 |
13 | public class World09SolidListReader : SolidListReader
14 | {
15 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
16 | uint chunkSize)
17 | {
18 | switch (chunkId)
19 | {
20 | case 0x134002:
21 | var info = BinaryUtil.ReadStruct(binaryReader);
22 | solidList.Filename = info.Filename;
23 | solidList.GroupName = info.GroupName;
24 | return true;
25 | case 0x134003:
26 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
27 | return true;
28 | case 0x134004:
29 | ProcessStreamingTable(solidList, binaryReader, chunkSize);
30 | return
31 | false; // We need to stop after processing the streaming table, otherwise we'll run into bad stuff
32 | default:
33 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
34 | }
35 | }
36 |
37 | private void ProcessStreamingTable(SolidList solidList, BinaryReader binaryReader, uint chunkSize)
38 | {
39 | var chunkEndPos = binaryReader.BaseStream.Position + chunkSize;
40 | Debug.Assert(chunkSize % 24 == 0, "chunkSize % 24 == 0");
41 | while (binaryReader.BaseStream.Position < chunkEndPos)
42 | {
43 | var och = BinaryUtil.ReadUnmanagedStruct(binaryReader);
44 | var curPos = binaryReader.BaseStream.Position;
45 | binaryReader.BaseStream.Position = och.Offset;
46 |
47 | Debug.Assert(och.Length >= 8, "och.Length >= 8");
48 |
49 | if (och.Length == och.LengthCompressed)
50 | {
51 | // Assume that the object is uncompressed.
52 | // If this ever turns out to be false, I don't know what I'll do.
53 | binaryReader.BaseStream.Position += 8;
54 | solidList.Objects.Add(CreateObjectReader().Read(binaryReader, och.Length - 8));
55 | }
56 | else
57 | {
58 | // Load decompressed object
59 | using var ms = new MemoryStream();
60 | Compression.DecompressCip(binaryReader.BaseStream, ms, och.LengthCompressed);
61 |
62 | ms.Position = 8;
63 | using var dcr = new BinaryReader(ms);
64 | solidList.Objects.Add(CreateObjectReader().Read(dcr, och.Length - 8));
65 | }
66 |
67 | binaryReader.BaseStream.Position = curPos;
68 | }
69 | }
70 |
71 | protected override SolidReader CreateObjectReader()
72 | {
73 | return new World09SolidReader();
74 | }
75 |
76 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
77 | private struct SolidListInfo
78 | {
79 | public readonly long Blank;
80 |
81 | public readonly int Marker; // this doesn't change between games for some reason... rather unfortunate
82 |
83 | public readonly int NumObjects;
84 |
85 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
86 | public readonly string Filename;
87 |
88 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
89 | public readonly string GroupName;
90 |
91 | public readonly int UnknownOffset;
92 | public readonly int UnknownSize;
93 | }
94 |
95 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
96 | private struct SolidObjectOffset
97 | {
98 | public uint Hash;
99 | public uint Offset;
100 | public readonly uint LengthCompressed;
101 | public readonly uint Length;
102 | public uint Flags;
103 | private uint blank;
104 | }
105 | }
--------------------------------------------------------------------------------
/Common/Geometry/WorldSolidListReader.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using Common.Geometry.Data;
5 |
6 | namespace Common.Geometry
7 | {
8 | public class World15Material : SolidObjectMaterial, IEffectBasedMaterial, ISortedMaterial
9 | {
10 | public uint EffectId { get; set; }
11 | public uint SortKey { get; set; }
12 | }
13 |
14 | public class WorldSolidListReader : SolidListReader
15 | {
16 | protected override bool ProcessHeaderChunk(SolidList solidList, BinaryReader binaryReader, uint chunkId,
17 | uint chunkSize)
18 | {
19 | switch (chunkId)
20 | {
21 | case 0x134002:
22 | var info = BinaryUtil.ReadStruct(binaryReader);
23 | solidList.Filename = info.Filename;
24 | solidList.GroupName = info.GroupName;
25 | return true;
26 | case 0x134003:
27 | Debug.Assert(chunkSize % 8 == 0, "chunkSize % 8 == 0");
28 | return true;
29 | case 0x134004:
30 | ProcessStreamingTable(solidList, binaryReader, chunkSize);
31 | return
32 | false; // We need to stop after processing the streaming table, otherwise we'll run into bad stuff
33 | default:
34 | throw new InvalidDataException($"Unexpected header chunk: 0x{chunkId:X}");
35 | }
36 | }
37 |
38 | private void ProcessStreamingTable(SolidList solidList, BinaryReader binaryReader, uint chunkSize)
39 | {
40 | var chunkEndPos = binaryReader.BaseStream.Position + chunkSize;
41 | Debug.Assert(chunkSize % 36 == 0, "chunkSize % 36 == 0");
42 | while (binaryReader.BaseStream.Position < chunkEndPos)
43 | {
44 | var och = BinaryUtil.ReadUnmanagedStruct(binaryReader);
45 | var curPos = binaryReader.BaseStream.Position;
46 | binaryReader.BaseStream.Position = och.Offset;
47 |
48 | Debug.Assert(och.Length >= 8, "och.Length >= 8");
49 |
50 | if (och.Length == och.LengthCompressed)
51 | {
52 | // Assume that the object is uncompressed.
53 | // If this ever turns out to be false, I don't know what I'll do.
54 | binaryReader.BaseStream.Position += 8;
55 | solidList.Objects.Add(CreateObjectReader().Read(binaryReader, och.Length - 8));
56 | }
57 | else
58 | {
59 | // Load decompressed object
60 | using var ms = new MemoryStream();
61 | Compression.DecompressCip(binaryReader.BaseStream, ms, och.LengthCompressed);
62 |
63 | ms.Position = 8;
64 | using var dcr = new BinaryReader(ms);
65 | solidList.Objects.Add(CreateObjectReader().Read(dcr, och.Length - 8));
66 | }
67 |
68 | binaryReader.BaseStream.Position = curPos;
69 | }
70 | }
71 |
72 | protected override SolidReader CreateObjectReader()
73 | {
74 | return new WorldSolidReader();
75 | }
76 |
77 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
78 | private struct SolidListInfo
79 | {
80 | public long Blank;
81 |
82 | public int Marker; // this doesn't change between games for some reason... rather unfortunate
83 |
84 | public int NumObjects;
85 |
86 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x38)]
87 | public readonly string Filename;
88 |
89 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
90 | public readonly string GroupName;
91 |
92 | public long Blank2;
93 |
94 | public uint UnknownOffset;
95 | }
96 |
97 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
98 | private struct SolidObjectOffset
99 | {
100 | public readonly uint Hash;
101 | public readonly uint Offset;
102 | public readonly uint LengthCompressed;
103 | public readonly uint Length;
104 |
105 | public uint Unknown, Unknown2, Unknown3, Unknown4, Unknown5;
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/Common/Lights/Data/LightPack.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Numerics;
3 |
4 | namespace Common.Lights.Data;
5 |
6 | public class LightPack : BasicResource
7 | {
8 | public uint ScenerySectionNumber { get; set; }
9 | public List Lights { get; set; }
10 | }
11 |
12 | public class Light
13 | {
14 | public uint NameHash { get; set; }
15 | public string Name { get; set; }
16 | public uint Color { get; set; }
17 | public Vector3 Position { get; set; }
18 | public float Size { get; set; }
19 | public float Intensity { get; set; }
20 | public float FarStart { get; set; }
21 | public float FarEnd { get; set; }
22 | public float Falloff { get; set; }
23 | }
--------------------------------------------------------------------------------
/Common/Lights/LightPackReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using Common.Lights.Data;
5 | using Common.Lights.Structures;
6 |
7 | namespace Common.Lights;
8 |
9 | public class LightPackReader
10 | {
11 | private LightPack _lightPack;
12 |
13 | public LightPack ReadLights(BinaryReader br, uint containerSize)
14 | {
15 | _lightPack = new LightPack();
16 | ReadChunks(br, containerSize);
17 | return _lightPack;
18 | }
19 |
20 | private void ReadChunks(BinaryReader br, uint containerSize)
21 | {
22 | var endPos = br.BaseStream.Position + containerSize;
23 |
24 | while (br.BaseStream.Position < endPos)
25 | {
26 | var chunkId = br.ReadUInt32();
27 | var chunkSize = br.ReadUInt32();
28 | var chunkEndPos = br.BaseStream.Position + chunkSize;
29 |
30 | chunkSize -= BinaryUtil.AlignReader(br, 0x10);
31 |
32 | switch (chunkId)
33 | {
34 | case 0x135001:
35 | {
36 | ReadHeader(br);
37 | break;
38 | }
39 | case 0x135003:
40 | {
41 | ReadLightList(br, chunkSize);
42 | break;
43 | }
44 | }
45 |
46 | br.BaseStream.Position = chunkEndPos;
47 | }
48 | }
49 |
50 | private void ReadHeader(BinaryReader br)
51 | {
52 | var header = BinaryUtil.ReadStruct(br);
53 |
54 | _lightPack.ScenerySectionNumber = header.ScenerySectionNumber;
55 | _lightPack.Lights = new List(header.NumLights);
56 | }
57 |
58 | private void ReadLightList(BinaryReader br, uint chunkSize)
59 | {
60 | if (chunkSize % 0x60 != 0)
61 | throw new Exception("Light list chunk is weirdly sized");
62 | for (var i = 0; i < chunkSize / 0x60; i++)
63 | {
64 | var light = BinaryUtil.ReadStruct(br);
65 |
66 | _lightPack.Lights.Add(new Light
67 | {
68 | NameHash = light.NameHash,
69 | Name = light.Name,
70 | Color = light.Color,
71 | Position = light.Position,
72 | Size = light.Size,
73 | Intensity = light.Intensity,
74 | FarStart = light.FarStart,
75 | FarEnd = light.FarEnd,
76 | Falloff = light.FarEnd
77 | });
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/Common/Lights/Structures/LightData.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Common.Lights.Structures;
5 |
6 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
7 | public struct LightData
8 | {
9 | public uint NameHash;
10 | public byte Type, AttenuationType, Shape, State;
11 | public uint ExcludeNameHash, Color;
12 | public Vector3 Position;
13 | public float Size;
14 | public Vector3 Direction;
15 | public float Intensity;
16 | public float FarStart;
17 | public float FarEnd;
18 | public float Falloff;
19 | public short ScenerySectionNumber;
20 |
21 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 34)]
22 | public string Name;
23 | }
--------------------------------------------------------------------------------
/Common/Lights/Structures/LightPackHeader.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Common.Lights.Structures;
4 |
5 | [StructLayout(LayoutKind.Explicit, Size = 0x20)]
6 | public struct LightPackHeader
7 | {
8 | [FieldOffset(0x08)] public ushort Version;
9 |
10 | [FieldOffset(0x0C)] public uint ScenerySectionNumber;
11 |
12 | [FieldOffset(0x1C)] public int NumLights;
13 | }
--------------------------------------------------------------------------------
/Common/Scenery/Data/ScenerySection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Numerics;
3 |
4 | namespace Common.Scenery.Data
5 | {
6 | public class SceneryInfo
7 | {
8 | public string Name { get; set; }
9 | public uint SolidKey { get; set; }
10 | public bool IsDeinstanced { get; set; }
11 | }
12 |
13 | public class SceneryInstance
14 | {
15 | public int InfoIndex { get; set; }
16 | public Matrix4x4 Transform { get; set; }
17 | }
18 |
19 | public class ScenerySection : BasicResource
20 | {
21 | public int SectionNumber { get; set; }
22 | public List Infos { get; set; } = new List();
23 | public List Instances { get; set; } = new List();
24 | }
25 | }
--------------------------------------------------------------------------------
/Common/Scenery/MostWantedScenery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Numerics;
5 | using System.Runtime.InteropServices;
6 | using Common.Scenery.Data;
7 | using Common.Scenery.Structures;
8 |
9 | namespace Common.Scenery
10 | {
11 | public class MostWantedScenery : SceneryManager
12 | {
13 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
14 | public struct ScenerySectionHeader // 0x00034101
15 | {
16 | public long Pointer1;
17 | public int Pointer2;
18 | public int SectionNumber;
19 | public int Pointer3;
20 | public long Pointer4;
21 | public long Pointer5;
22 | public long Pointer6;
23 | public long Pointer7;
24 | public long Pointer8;
25 | }
26 |
27 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
28 | public struct SceneryInfoStruct // 0x00034102
29 | {
30 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
31 | public string Name;
32 |
33 | public uint SolidMeshKey1;
34 | public uint SolidMeshKey2;
35 | public uint SolidMeshKey3;
36 | public uint SolidMeshKey4;
37 |
38 | public uint SolidMeshPointer1;
39 | public uint SolidMeshPointer2;
40 | public uint SolidMeshPointer3;
41 | public uint SolidMeshPointer4;
42 |
43 | public float Radius;
44 | public uint MeshChecksum;
45 | public uint HierarchyNameHash;
46 | public uint HierarchyPointer;
47 | }
48 |
49 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
50 | public struct SceneryInstanceInternal // 0x00034103
51 | {
52 | public Vector3 BBoxMin;
53 | public Vector3 BBoxMax;
54 | public uint ExcludeFlags;
55 | public short PrecullerInfoIndex;
56 | public short LightingContextNumber;
57 | public Vector3 Position;
58 | public PackedRotationMatrix Rotation;
59 | public short SceneryInfoNumber;
60 | }
61 |
62 | private ScenerySection _scenerySection;
63 |
64 | public override ScenerySection ReadScenery(BinaryReader br, uint containerSize)
65 | {
66 | _scenerySection = new ScenerySection();
67 | ReadChunks(br, containerSize);
68 | return _scenerySection;
69 | }
70 |
71 | protected override void ReadChunks(BinaryReader br, uint containerSize)
72 | {
73 | var endPos = br.BaseStream.Position + containerSize;
74 |
75 | while (br.BaseStream.Position < endPos)
76 | {
77 | var chunkId = br.ReadUInt32();
78 | var chunkSize = br.ReadUInt32();
79 | var chunkEndPos = br.BaseStream.Position + chunkSize;
80 |
81 | switch (chunkId)
82 | {
83 | case 0x00034101:
84 | {
85 | ReadScenerySectionHeader(br);
86 | break;
87 | }
88 | case 0x00034102:
89 | {
90 | ReadSceneryInfos(br, chunkSize);
91 | break;
92 | }
93 | case 0x00034103:
94 | {
95 | ReadSceneryInstances(br, chunkSize);
96 | break;
97 | }
98 | default:
99 | //Console.WriteLine($"0x{chunkId:X8} [{chunkSize}] @{br.BaseStream.Position}");
100 | break;
101 | }
102 |
103 | br.BaseStream.Position = chunkEndPos;
104 | }
105 | }
106 | private void ReadScenerySectionHeader(BinaryReader br)
107 | {
108 | var header = BinaryUtil.ReadUnmanagedStruct(br);
109 | _scenerySection.SectionNumber = header.SectionNumber;
110 | //Debug.Log($"ScenerySection number is {_scenerySection.SectionNumber}");
111 | }
112 | private void ReadSceneryInfos(BinaryReader br, uint size)
113 | {
114 | Debug.Assert(size % 0x48 == 0);
115 | var count = (int)size / 0x48;
116 | _scenerySection.Infos = new List(count);
117 | for (int i = 0; i < count; ++i)
118 | {
119 | var info = BinaryUtil.ReadStruct(br);
120 | _scenerySection.Infos.Add(new SceneryInfo
121 | {
122 | Name = info.Name,
123 | SolidKey = info.SolidMeshKey1
124 | });
125 | }
126 | //Debug.Log($"Loaded {_scenerySection.SceneryInfos.Count} scenery definitions for ScenerySection {_scenerySection.SectionNumber}");
127 | }
128 |
129 | private void ReadSceneryInstances(BinaryReader br, uint size)
130 | {
131 | size -= BinaryUtil.AlignReader(br, 0x10);
132 | Debug.Assert(size % 0x40 == 0);
133 | var count = (int)size / 0x40;
134 | _scenerySection.Instances = new List(count);
135 |
136 | for (int i = 0; i < count; ++i)
137 | {
138 | var instance = new SceneryInstance();
139 | var internalInstance = BinaryUtil.ReadUnmanagedStruct(br);
140 |
141 | instance.InfoIndex = internalInstance.SceneryInfoNumber;
142 | instance.Transform = Matrix4x4.Multiply(internalInstance.Rotation,
143 | Matrix4x4.CreateTranslation(internalInstance.Position));
144 |
145 | _scenerySection.Instances.Add(instance);
146 | }
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/Common/Scenery/SceneryManager.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Common.Geometry.Data;
3 | using Common.Scenery.Data;
4 |
5 | namespace Common.Scenery
6 | {
7 | public abstract class SceneryManager
8 | {
9 | protected SceneryManager() { }
10 |
11 | ///
12 | /// Read a scenery pack from the given binary stream.
13 | ///
14 | ///
15 | ///
16 | ///
17 | public abstract ScenerySection ReadScenery(BinaryReader br, uint containerSize);
18 |
19 | protected abstract void ReadChunks(BinaryReader br, uint containerSize);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Common/Scenery/Structures/PackedRotationMatrix.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Common.Scenery.Structures;
5 |
6 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
7 | public struct PackedRotationMatrix
8 | {
9 | public short Value11;
10 | public short Value12;
11 | public short Value13;
12 | public short Value21;
13 | public short Value22;
14 | public short Value23;
15 | public short Value31;
16 | public short Value32;
17 | public short Value33;
18 |
19 | public static implicit operator Matrix4x4(PackedRotationMatrix prm)
20 | {
21 | var vec0 = new Vector3(prm.Value11, prm.Value12, prm.Value13) / 8192f;
22 | var vec1 = new Vector3(prm.Value21, prm.Value22, prm.Value23) / 8192f;
23 | var vec2 = new Vector3(prm.Value31, prm.Value32, prm.Value33) / 8192f;
24 |
25 | return new Matrix4x4
26 | {
27 | M11 = vec0.X,
28 | M12 = vec0.Y,
29 | M13 = vec0.Z,
30 |
31 | M21 = vec1.X,
32 | M22 = vec1.Y,
33 | M23 = vec1.Z,
34 |
35 | M31 = vec2.X,
36 | M32 = vec2.Y,
37 | M33 = vec2.Z,
38 |
39 | M44 = 1
40 | };
41 | }
42 | }
--------------------------------------------------------------------------------
/Common/Scenery/Structures/RotationMatrix.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Common.Scenery.Structures;
5 |
6 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
7 | public struct RotationMatrix
8 | {
9 | public Vector3 Rotation0;
10 | public Vector3 Rotation1;
11 | public Vector3 Rotation2;
12 |
13 | public RotationMatrix(Vector3 rotation0, Vector3 rotation1, Vector3 rotation2)
14 | {
15 | Rotation0 = rotation0;
16 | Rotation1 = rotation1;
17 | Rotation2 = rotation2;
18 | }
19 |
20 | public static implicit operator Matrix4x4(RotationMatrix rm)
21 | {
22 | return new Matrix4x4(
23 | rm.Rotation0.X, rm.Rotation0.Y, rm.Rotation0.Z, 0,
24 | rm.Rotation1.X, rm.Rotation1.Y, rm.Rotation1.Z, 0,
25 | rm.Rotation2.X, rm.Rotation2.Y, rm.Rotation2.Z, 0,
26 | 0, 0, 0, 1);
27 | }
28 | }
--------------------------------------------------------------------------------
/Common/Textures/Data/DDSHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Common.Textures.Data
5 | {
6 | [StructLayout(LayoutKind.Sequential)]
7 | public struct PixelFormat
8 | {
9 | public uint Size;
10 | public uint Flags;
11 | public uint FourCC;
12 | public int RGBBitCount;
13 | public int RBitMask;
14 | public int GBitMask;
15 | public int BBitMask;
16 | public int AlphaBitMask;
17 | }
18 |
19 | [StructLayout(LayoutKind.Sequential)]
20 | public struct DDSCaps
21 | {
22 | public int Caps1;
23 | public int Caps2;
24 |
25 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
26 | public int[] Reserved;
27 | }
28 |
29 | static class DdsConstants
30 | {
31 | #region DDSStruct Flags
32 | public const int DdsdCaps = 0x00000001;
33 | public const int DdsdHeight = 0x00000002;
34 | public const int DdsdWidth = 0x00000004;
35 | public const int DdsdPitch = 0x00000008;
36 | public const int DdsdPixelformat = 0x00001000;
37 | public const int DdsdMipmapcount = 0x00020000;
38 | public const int DdsdLinearsize = 0x00080000;
39 | public const int DdsdDepth = 0x00800000;
40 | #endregion
41 |
42 | #region pixelformat values
43 | public const int DdpfAlphapixels = 0x00000001;
44 | public const int DdpfFourcc = 0x00000004;
45 | public const int DdpfRgb = 0x00000040;
46 | public const int DdpfLuminance = 0x00020000;
47 | #endregion
48 |
49 | #region ddscaps
50 | // caps1
51 | public const int DdscapsComplex = 0x00000008;
52 | public const int DdscapsTexture = 0x00001000;
53 | public const int DdscapsMipmap = 0x00400000;
54 | // caps2
55 | public const int Ddscaps2Cubemap = 0x00000200;
56 | public const int Ddscaps2CubemapPositivex = 0x00000400;
57 | public const int Ddscaps2CubemapNegativex = 0x00000800;
58 | public const int Ddscaps2CubemapPositivey = 0x00001000;
59 | public const int Ddscaps2CubemapNegativey = 0x00002000;
60 | public const int Ddscaps2CubemapPositivez = 0x00004000;
61 | public const int Ddscaps2CubemapNegativez = 0x00008000;
62 | public const int Ddscaps2Volume = 0x00200000;
63 | #endregion
64 | }
65 |
66 | [StructLayout(LayoutKind.Sequential)]
67 | public struct DDSHeader
68 | {
69 | public int Magic;
70 | public int Size;
71 | public uint Flags;
72 | public uint Height;
73 | public uint Width;
74 | public uint PitchOrLinearSize;
75 | public int Depth;
76 | public uint MipMapCount;
77 |
78 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
79 | public int[] Reserved1;
80 |
81 | public PixelFormat PixelFormat;
82 | public DDSCaps DDSCaps;
83 |
84 | public int Reserved2;
85 |
86 | ///
87 | /// Initialize the DDS header structure with the given texture data.
88 | ///
89 | ///
90 | public void Init(Texture texture)
91 | {
92 | Magic = 0x20534444; // "DDS "
93 | Size = 0x7C;
94 | Flags = DdsConstants.DdsdCaps | DdsConstants.DdsdHeight | DdsConstants.DdsdWidth |
95 | DdsConstants.DdsdPixelformat | DdsConstants.DdsdMipmapcount;
96 | Height = texture.Height;
97 | Width = texture.Width;
98 | Depth = 1;
99 | MipMapCount = texture.MipMapCount;
100 | PixelFormat = new PixelFormat();
101 | DDSCaps = new DDSCaps();
102 |
103 | PixelFormat.Size = 0x20;
104 | DDSCaps.Caps1 = DdsConstants.DdscapsComplex | DdsConstants.DdscapsTexture |
105 | DdsConstants.DdscapsMipmap;
106 |
107 | if ((texture.Format & 0x00545844) == 0x00545844) // DXT check
108 | {
109 | PitchOrLinearSize = /*Width * Height*/texture.PitchOrLinearSize;
110 | PixelFormat.Flags = DdsConstants.DdpfFourcc;
111 | PixelFormat.FourCC = texture.Format;
112 | Flags |= DdsConstants.DdsdLinearsize;
113 | }
114 | else if ((texture.Format & 0x00495441) == 0x00495441) // ATI check
115 | {
116 | PitchOrLinearSize = /*Width * Height*/texture.PitchOrLinearSize;
117 | PixelFormat.Flags = DdsConstants.DdpfFourcc;
118 | PixelFormat.FourCC = texture.Format;
119 | Flags |= DdsConstants.DdsdLinearsize;
120 | }
121 | else
122 | {
123 | PixelFormat.Flags = DdsConstants.DdpfAlphapixels | DdsConstants.DdpfRgb;
124 | PixelFormat.RGBBitCount = 0x20;
125 | PixelFormat.RBitMask = 16711680;
126 | PixelFormat.GBitMask = 65280;
127 | PixelFormat.BBitMask = 255;
128 | PixelFormat.AlphaBitMask = unchecked((int)4278190080);
129 | Flags |= DdsConstants.DdsdPitch;
130 | PitchOrLinearSize = /*(Width * 0x20 + 7) / 8*/texture.PitchOrLinearSize;
131 | }
132 |
133 | Reserved1 = new int[11];
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Common/Textures/Data/TexturePack.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Common.Textures.Data
6 | {
7 | public enum TextureCompressionType : byte
8 | {
9 | TEXCOMP_DEFAULT = 0x0,
10 | TEXCOMP_4BIT = 0x4,
11 | TEXCOMP_8BIT = 0x8,
12 | TEXCOMP_16BIT = 0x10,
13 | TEXCOMP_24BIT = 0x18,
14 | TEXCOMP_32BIT = 0x20,
15 | TEXCOMP_DXT = 0x21,
16 | TEXCOMP_S3TC = 0x22,
17 | TEXCOMP_DXTC1 = 0x22,
18 | TEXCOMP_DXTC3 = 0x24,
19 | TEXCOMP_DXTC5 = 0x26,
20 | TEXCOMP_DXTN = 0x27,
21 | TEXCOMP_L8 = 0x28,
22 | TEXCOMP_DXTC1_AIR = 0x29,
23 | TEXCOMP_DXTC1_AIG = 0x2A,
24 | TEXCOMP_DXTC1_AIB = 0x2B,
25 | TEXCOMP_16BIT_1555 = 0x11,
26 | TEXCOMP_16BIT_565 = 0x12,
27 | TEXCOMP_16BIT_3555 = 0x13,
28 | TEXCOMP_8BIT_16 = 0x80,
29 | TEXCOMP_8BIT_64 = 0x81,
30 | TEXCOMP_8BIT_IA8 = 0x82,
31 | TEXCOMP_4BIT_IA8 = 0x83,
32 | TEXCOMP_4BIT_RGB24_A8 = 0x8C,
33 | TEXCOMP_8BIT_RGB24_A8 = 0x8D,
34 | TEXCOMP_4BIT_RGB16_A8 = 0x8E,
35 | TEXCOMP_8BIT_RGB16_A8 = 0x8F,
36 | }
37 |
38 | public class Texture : BasicResource
39 | {
40 | public string Name { get; set; }
41 |
42 | public uint TexHash { get; set; }
43 |
44 | public uint TypeHash { get; set; }
45 |
46 | public uint Width { get; set; }
47 |
48 | public uint Height { get; set; }
49 |
50 | public uint MipMapCount { get; set; }
51 |
52 | public uint DataSize { get; set; }
53 |
54 | public uint DataOffset { get; set; }
55 |
56 | public uint PaletteSize { get; set; }
57 |
58 | public uint PaletteOffset { get; set; }
59 |
60 | public byte[] Palette { get; set; }
61 | public byte[] Data { get; set; }
62 |
63 | public uint PitchOrLinearSize { get; set; }
64 |
65 | public uint Format { get; set; }
66 | public TextureCompressionType CompressionType { get; set; }
67 |
68 | ///
69 | /// Writes DDS data to the given stream.
70 | ///
71 | ///
72 | public void GenerateImage(Stream stream)
73 | {
74 | var bw = new BinaryWriter(stream);
75 |
76 | var dh = new DDSHeader();
77 | dh.Init(this);
78 |
79 | bw.PutStruct(dh);
80 |
81 | if (Format == 0x29)
82 | {
83 | var palette = new uint[0x100];
84 | var result = new byte[Data.Length * 4];
85 |
86 | for (var loop = 0; loop < 0x100; ++loop)
87 | {
88 | palette[loop] = BitConverter.ToUInt32(Palette, loop * 4);
89 | }
90 |
91 | for (var loop = 0; loop < Data.Length; ++loop)
92 | {
93 | var color = palette[Data[loop]];
94 | Array.ConstrainedCopy(BitConverter.GetBytes(color), 0, result, loop * 4, 4);
95 | }
96 |
97 | bw.Write(result);
98 | }
99 | else
100 | {
101 | bw.Write(Data);
102 | }
103 | }
104 |
105 | public void DumpToFile(string path)
106 | {
107 | using (var fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
108 | GenerateImage(fs);
109 | }
110 | }
111 |
112 | public class TexturePack : BasicResource
113 | {
114 | public string Name { get; set; }
115 |
116 | public string PipelinePath { get; set; }
117 |
118 | public uint Hash { get; set; }
119 |
120 | public uint Version { get; set; }
121 |
122 | public List Textures { get; set; } = new List();
123 |
124 | public Texture Find(uint hash) => Textures.Find(t => t.TexHash == hash);
125 | public Texture Find(string name) => Textures.Find(t => t.Name.Contains(name));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Common/Textures/TpkManager.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Common.Textures.Data;
3 |
4 | namespace Common.Textures
5 | {
6 | public abstract class TpkManager
7 | {
8 | ///
9 | /// Read a texture pack from the given binary stream.
10 | ///
11 | ///
12 | ///
13 | ///
14 | public abstract TexturePack ReadTexturePack(BinaryReader br, uint containerSize);
15 |
16 | protected abstract void ReadChunks(BinaryReader br, uint containerSize);
17 | }
18 | }
--------------------------------------------------------------------------------
/FBXSharp/Connection.cs:
--------------------------------------------------------------------------------
1 | using FBXSharp.Core;
2 |
3 | namespace FBXSharp
4 | {
5 | public class Connection
6 | {
7 | public enum ConnectionType
8 | {
9 | Object,
10 | Property
11 | }
12 |
13 | public ConnectionType Type { get; }
14 | public long Source { get; }
15 | public long Destination { get; }
16 | public IElementAttribute Property { get; }
17 |
18 | public Connection(ConnectionType type, long src, long dest, IElementAttribute property = null)
19 | {
20 | Type = type;
21 | Source = src;
22 | Destination = dest;
23 | Property = property;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/CoordSystem.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public enum CoordSystem
4 | {
5 | RightHanded = 0,
6 | LeftHanded = 1
7 | }
8 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/DataView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace FBXSharp.Core
5 | {
6 | public struct DataView
7 | {
8 | public long Start { get; }
9 | public long End { get; }
10 | public bool IsBinary { get; }
11 | public BinaryReader Reader { get; }
12 |
13 | public DataView(long start, long end, bool isBinary, BinaryReader reader)
14 | {
15 | Start = start;
16 | End = end;
17 | IsBinary = isBinary;
18 | Reader = reader;
19 | }
20 |
21 | public override bool Equals(object obj)
22 | {
23 | return obj is DataView dataView && dataView == this;
24 | }
25 |
26 | public override int GetHashCode()
27 | {
28 | return Tuple.Create(Start, End, IsBinary, Reader).GetHashCode();
29 | }
30 |
31 | public static bool operator ==(DataView dataView1, DataView dataView2)
32 | {
33 | return dataView1.Start == dataView2.Start && dataView1.End == dataView2.End &&
34 | dataView1.IsBinary == dataView2.IsBinary && dataView1.Reader == dataView2.Reader;
35 | }
36 |
37 | public static bool operator !=(DataView dataView1, DataView dataView2)
38 | {
39 | return !(dataView1 == dataView2);
40 | }
41 |
42 | public int ToInt32(int offset = 0)
43 | {
44 | if (Reader is null) return 0;
45 |
46 | var current = Reader.BaseStream.Position;
47 | Reader.BaseStream.Position = Start + offset;
48 |
49 | if (IsBinary)
50 | {
51 | var result = Reader.ReadInt32();
52 | Reader.BaseStream.Position = current;
53 | return result;
54 | }
55 | else
56 | {
57 | var result = Reader.ReadTextInt32();
58 | Reader.BaseStream.Position = current;
59 | return result;
60 | }
61 | }
62 |
63 | public uint ToUInt32(int offset = 0)
64 | {
65 | if (Reader is null) return 0;
66 |
67 | var current = Reader.BaseStream.Position;
68 | Reader.BaseStream.Position = Start + offset;
69 |
70 | if (IsBinary)
71 | {
72 | var result = Reader.ReadUInt32();
73 | Reader.BaseStream.Position = current;
74 | return result;
75 | }
76 | else
77 | {
78 | var result = Reader.ReadTextUInt32();
79 | Reader.BaseStream.Position = current;
80 | return result;
81 | }
82 | }
83 |
84 | public long ToInt64(int offset = 0)
85 | {
86 | if (Reader is null) return 0;
87 |
88 | var current = Reader.BaseStream.Position;
89 | Reader.BaseStream.Position = Start + offset;
90 |
91 | if (IsBinary)
92 | {
93 | var result = Reader.ReadInt64();
94 | Reader.BaseStream.Position = current;
95 | return result;
96 | }
97 | else
98 | {
99 | var result = Reader.ReadTextInt64();
100 | Reader.BaseStream.Position = current;
101 | return result;
102 | }
103 | }
104 |
105 | public ulong ToUInt64(int offset = 0)
106 | {
107 | if (Reader is null) return 0;
108 |
109 | var current = Reader.BaseStream.Position;
110 | Reader.BaseStream.Position = Start + offset;
111 |
112 | if (IsBinary)
113 | {
114 | var result = Reader.ReadUInt64();
115 | Reader.BaseStream.Position = current;
116 | return result;
117 | }
118 | else
119 | {
120 | var result = Reader.ReadTextUInt64();
121 | Reader.BaseStream.Position = current;
122 | return result;
123 | }
124 | }
125 |
126 | public float ToSingle(int offset = 0)
127 | {
128 | if (Reader is null) return 0;
129 |
130 | var current = Reader.BaseStream.Position;
131 | Reader.BaseStream.Position = Start + offset;
132 |
133 | if (IsBinary)
134 | {
135 | var result = Reader.ReadSingle();
136 | Reader.BaseStream.Position = current;
137 | return result;
138 | }
139 | else
140 | {
141 | var result = Reader.ReadTextSingle();
142 | Reader.BaseStream.Position = current;
143 | return result;
144 | }
145 | }
146 |
147 | public double ToDouble(int offset = 0)
148 | {
149 | if (Reader is null) return 0;
150 |
151 | var current = Reader.BaseStream.Position;
152 | Reader.BaseStream.Position = Start + offset;
153 |
154 | if (IsBinary)
155 | {
156 | var result = Reader.ReadDouble();
157 | Reader.BaseStream.Position = current;
158 | return result;
159 | }
160 | else
161 | {
162 | var result = Reader.ReadTextDouble();
163 | Reader.BaseStream.Position = current;
164 | return result;
165 | }
166 | }
167 |
168 | public override string ToString()
169 | {
170 | if (Reader is null) return string.Empty;
171 |
172 | var current = Reader.BaseStream.Position;
173 | Reader.BaseStream.Position = Start;
174 | var size = (int)(End - Start);
175 |
176 | var result = Reader.ReadNullTerminated(size);
177 | Reader.BaseStream.Position = current;
178 | return result;
179 | }
180 | }
181 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/Footer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace FBXSharp.Core
4 | {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public struct Footer
7 | {
8 | public int Reserved;
9 | public uint Version;
10 |
11 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x78)]
12 | public byte[] Padding;
13 |
14 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
15 | public byte[] Magic;
16 |
17 | public const int SizeOf = 0x80;
18 |
19 | public static Footer CreateNew(uint version)
20 | {
21 | return new Footer
22 | {
23 | Reserved = 0,
24 | Version = version,
25 | Padding = new byte[0x78],
26 | Magic = new byte[0x10]
27 | {
28 | 0xF8,
29 | 0x5A,
30 | 0x8C,
31 | 0x6A,
32 | 0xDE,
33 | 0xF5,
34 | 0xD9,
35 | 0x7E,
36 | 0xEC,
37 | 0xE9,
38 | 0x0C,
39 | 0xE3,
40 | 0x75,
41 | 0x8F,
42 | 0x29,
43 | 0x0B
44 | }
45 | };
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/FrameRate.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public enum FrameRate
4 | {
5 | RateDefault = 0,
6 | Rate120 = 1,
7 | Rate100 = 2,
8 | Rate60 = 3,
9 | Rate50 = 4,
10 | Rate48 = 5,
11 | Rate30 = 6,
12 | Rate30Drop = 7,
13 | RateNTSCDropFrame = 8,
14 | RateNTSCFullFrame = 9,
15 | RatePAL = 10,
16 | RateCinema = 11,
17 | Rate1000 = 12,
18 | RateCinemaND = 13,
19 | RateCustom = 14
20 | }
21 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/FrontVector.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public enum FrontVector
4 | {
5 | ParityEven = 0,
6 | ParityOdd = 1
7 | }
8 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/Header.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace FBXSharp.Core
4 | {
5 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
6 | public struct Header
7 | {
8 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x15)]
9 | public byte[] Magic;
10 |
11 | public byte Reserved1;
12 | public byte Reserved2;
13 | public uint Version;
14 |
15 | public const int SizeOf = 0x1B; // amazing lmao
16 |
17 | public static Header CreateNew(uint version)
18 | {
19 | return new Header
20 | {
21 | Version = version,
22 | Reserved1 = 0x1A,
23 | Reserved2 = 0x00,
24 | Magic = new byte[0x15]
25 | {
26 | (byte)'K',
27 | (byte)'a',
28 | (byte)'y',
29 | (byte)'d',
30 | (byte)'a',
31 | (byte)'r',
32 | (byte)'a',
33 | (byte)' ',
34 | (byte)'F',
35 | (byte)'B',
36 | (byte)'X',
37 | (byte)' ',
38 | (byte)'B',
39 | (byte)'i',
40 | (byte)'n',
41 | (byte)'a',
42 | (byte)'r',
43 | (byte)'y',
44 | (byte)' ',
45 | (byte)' ',
46 | 0
47 | }
48 | };
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/IElement.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public interface IElement
4 | {
5 | string Name { get; }
6 | IElement[] Children { get; }
7 | IElementAttribute[] Attributes { get; }
8 |
9 | IElement FindChild(string name);
10 | }
11 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/IElementAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public enum IElementAttributeType : byte
4 | {
5 | Byte = (byte)'C',
6 | Int16 = (byte)'Y',
7 | Int32 = (byte)'I',
8 | Int64 = (byte)'L',
9 | Single = (byte)'F',
10 | Double = (byte)'D',
11 | String = (byte)'S',
12 | ArrayBoolean = (byte)'b',
13 | ArrayInt32 = (byte)'i',
14 | ArrayInt64 = (byte)'l',
15 | ArraySingle = (byte)'f',
16 | ArrayDouble = (byte)'d',
17 | Binary = (byte)'R'
18 | }
19 |
20 | public interface IElementAttribute
21 | {
22 | IElementAttributeType Type { get; }
23 | int Stride { get; }
24 | int Length { get; }
25 | int Size { get; }
26 |
27 | object GetElementValue();
28 | }
29 |
30 | public interface IGenericAttribute : IElementAttribute
31 | {
32 | T Value { get; }
33 | }
34 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/IElementProperty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FBXSharp.Core
4 | {
5 | [Flags]
6 | public enum IElementPropertyFlags
7 | {
8 | None = 0,
9 | Static = 1 << 0,
10 | Animatable = 1 << 1,
11 | Animated = 1 << 2,
12 | Imported = 1 << 3,
13 | UserDefined = 1 << 4,
14 | Hidden = 1 << 5,
15 | NotSavable = 1 << 6,
16 |
17 | LockedMember0 = 1 << 7,
18 | LockedMember1 = 1 << 8,
19 | LockedMember2 = 1 << 9,
20 | LockedMember3 = 1 << 10,
21 | LockedAll = LockedMember0 | LockedMember1 | LockedMember2 | LockedMember3,
22 | MutedMember0 = 1 << 11,
23 | MutedMember1 = 1 << 12,
24 | MutedMember2 = 1 << 13,
25 | MutedMember3 = 1 << 14,
26 | MutedAll = MutedMember0 | MutedMember1 | MutedMember2 | MutedMember3,
27 |
28 | UIDisabled = 1 << 15,
29 | UIGroup = 1 << 16,
30 | UIBoolGroup = 1 << 17,
31 | UIExpanded = 1 << 18,
32 | UINoCaption = 1 << 19,
33 | UIPanel = 1 << 20,
34 | UILeftLabel = 1 << 21,
35 | UIHidden = 1 << 22,
36 |
37 | CtrlFlags =
38 | Static | Animatable | Animated | Imported | UserDefined | Hidden | NotSavable | LockedAll | MutedAll,
39 | UIFlags = UIDisabled | UIGroup | UIBoolGroup | UIExpanded | UINoCaption | UIPanel | UILeftLabel | UIHidden,
40 | AllFlags = CtrlFlags | UIFlags,
41 |
42 | FlagCount = 23
43 | }
44 |
45 | public enum IElementPropertyType
46 | {
47 | Undefined,
48 | SByte,
49 | Byte,
50 | Short,
51 | UShort,
52 | UInt,
53 | Long,
54 | ULong,
55 | Half,
56 | Bool,
57 | Int,
58 | Float,
59 | Double,
60 | Double2,
61 | Double3,
62 | Double4,
63 | Double4x4,
64 | Enum,
65 | String,
66 | Time,
67 | Reference,
68 | Blob,
69 | Distance,
70 | DateTime,
71 | TypeCount
72 | }
73 |
74 | public interface IElementProperty
75 | {
76 | string Name { get; set; }
77 |
78 | IElementPropertyFlags Flags { get; set; }
79 |
80 | IElementPropertyType Type { get; }
81 |
82 | string Primary { get; }
83 |
84 | string Secondary { get; }
85 |
86 | bool SupportsMinMax { get; }
87 |
88 | Type GetPropertyType();
89 |
90 | object GetPropertyValue();
91 | object GetPropertyMin();
92 | object GetPropertyMax();
93 |
94 | void SetPropertyValue(object value);
95 | void SetPropertyMin(object value);
96 | void SetPropertyMax(object value);
97 | }
98 |
99 | public interface IGenericProperty : IElementProperty
100 | {
101 | T Value { get; set; }
102 |
103 | bool GetMinValue(out T min);
104 | bool GetMaxValue(out T max);
105 |
106 | void SetMinValue(in T min);
107 | void SetMaxValue(in T max);
108 | }
109 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/IScene.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace FBXSharp.Core
4 | {
5 | public interface IScene
6 | {
7 | FBXObject Root { get; }
8 | GlobalSettings Settings { get; }
9 | IReadOnlyList Objects { get; }
10 | IReadOnlyList TakeInfos { get; }
11 | IReadOnlyList Templates { get; }
12 |
13 | TemplateObject CreateEmptyTemplate(FBXClassType classType, TemplateCreationType creationType);
14 | TemplateObject CreatePredefinedTemplate(FBXClassType classType, TemplateCreationType creationType);
15 |
16 | TemplateObject GetTemplateObject(string name);
17 | TemplateObject GetTemplateObject(FBXClassType classType);
18 |
19 | void AddTakeInfo(TakeInfo takeInfo);
20 | void RemoveTakeInfo(TakeInfo takeInfo);
21 |
22 | FBXObject CreateFBXObject(FBXClassType classType, FBXObjectType objectType, IElement element);
23 | void DestroyFBXObject(FBXObject @object);
24 |
25 | IEnumerable GetObjectsOfType() where T : FBXObject;
26 | IEnumerable GetObjectsOfType(FBXClassType classType, FBXObjectType objectType);
27 | }
28 | }
--------------------------------------------------------------------------------
/FBXSharp/Core/UpVector.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp.Core
2 | {
3 | public enum UpVector
4 | {
5 | AxisX = 0,
6 | AxisY = 1,
7 | AxisZ = 2
8 | }
9 | }
--------------------------------------------------------------------------------
/FBXSharp/Element.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp
6 | {
7 | [DebuggerDisplay("{this.Name}")]
8 | public class Element : IElement
9 | {
10 | public string Name { get; }
11 | public IElement[] Children { get; }
12 | public IElementAttribute[] Attributes { get; }
13 |
14 | public Element(string name, IElement[] children, IElementAttribute[] attributes)
15 | {
16 | Name = name ?? string.Empty;
17 | Children = children ?? Array.Empty();
18 | Attributes = attributes ?? Array.Empty();
19 | }
20 |
21 | public IElement FindChild(string name)
22 | {
23 | return Array.Find(Children, _ => _.Name == name);
24 | }
25 |
26 | public static Element WithAttribute(string name, IElementAttribute attribute)
27 | {
28 | return new Element(name, null, new[] { attribute });
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ArrayBooleanAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Boolean : {this.Value.Length} Items")]
8 | public class ArrayBooleanAttribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.ArrayBoolean;
11 |
12 | public static readonly int PropertyStride = 1;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 12 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public bool[] Value { get; }
25 |
26 | public ArrayBooleanAttribute(bool[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ArrayDoubleAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Double : {this.Value.Length} Items")]
8 | public class ArrayDoubleAttribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.ArrayDouble;
11 |
12 | public static readonly int PropertyStride = 8;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 12 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public double[] Value { get; }
25 |
26 | public ArrayDoubleAttribute(double[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ArrayInt32Attribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Int32 : {this.Value.Length} Items")]
8 | public class ArrayInt32Attribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.ArrayInt32;
11 |
12 | public static readonly int PropertyStride = 4;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 12 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public int[] Value { get; }
25 |
26 | public ArrayInt32Attribute(int[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ArrayInt64Attribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Int64 : {this.Value.Length} Items")]
8 | public class ArrayInt64Attribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.ArrayInt64;
11 |
12 | public static readonly int PropertyStride = 8;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 12 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public long[] Value { get; }
25 |
26 | public ArrayInt64Attribute(long[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ArraySingleAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Single : {this.Value.Length} Items")]
8 | public class ArraySingleAttribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.ArraySingle;
11 |
12 | public static readonly int PropertyStride = 4;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 12 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public float[] Value { get; }
25 |
26 | public ArraySingleAttribute(float[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/BinaryAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Elementary
6 | {
7 | [DebuggerDisplay("Raw : {this.Value.Length} bytes")]
8 | public class BinaryAttribute : IGenericAttribute
9 | {
10 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Binary;
11 |
12 | public static readonly int PropertyStride = 1;
13 |
14 | public static readonly int PropertyLength = -1;
15 |
16 | public IElementAttributeType Type => PropertyType;
17 |
18 | public int Size => 4 + PropertyStride * Length;
19 |
20 | public int Stride => PropertyStride;
21 |
22 | public int Length { get; }
23 |
24 | public byte[] Value { get; }
25 |
26 | public BinaryAttribute(byte[] value)
27 | {
28 | Value = value ?? Array.Empty();
29 | Length = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/ByteAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}B")]
7 | public class ByteAttribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Byte;
10 |
11 | public static readonly int PropertyStride = 1;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public byte Value { get; }
24 |
25 | public ByteAttribute(byte value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/DoubleAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}D")]
7 | public class DoubleAttribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Double;
10 |
11 | public static readonly int PropertyStride = 8;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public double Value { get; }
24 |
25 | public DoubleAttribute(double value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/Int16Attribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}S")]
7 | public class Int16Attribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Int16;
10 |
11 | public static readonly int PropertyStride = 2;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public short Value { get; }
24 |
25 | public Int16Attribute(short value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/Int32Attribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}")]
7 | public class Int32Attribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Int32;
10 |
11 | public static readonly int PropertyStride = 4;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public int Value { get; }
24 |
25 | public Int32Attribute(int value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/Int64Attribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}L")]
7 | public class Int64Attribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Int64;
10 |
11 | public static readonly int PropertyStride = 8;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public long Value { get; }
24 |
25 | public Int64Attribute(long value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/SingleAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}F")]
7 | public class SingleAttribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.Single;
10 |
11 | public static readonly int PropertyStride = 4;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => PropertyStride;
18 |
19 | public int Stride => PropertyStride;
20 |
21 | public int Length => PropertyLength;
22 |
23 | public float Value { get; }
24 |
25 | public SingleAttribute(float value)
26 | {
27 | Value = value;
28 | }
29 |
30 | public object GetElementValue()
31 | {
32 | return Value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/FBXSharp/Elementary/StringAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Elementary
5 | {
6 | [DebuggerDisplay("{this.Value}")]
7 | public class StringAttribute : IGenericAttribute
8 | {
9 | public static readonly IElementAttributeType PropertyType = IElementAttributeType.String;
10 |
11 | public static readonly int PropertyStride = -1;
12 |
13 | public static readonly int PropertyLength = 1;
14 |
15 | public IElementAttributeType Type => PropertyType;
16 |
17 | public int Size => 4 + Stride;
18 |
19 | public int Stride { get; }
20 |
21 | public int Length => PropertyLength;
22 |
23 | public string Value { get; }
24 |
25 | public StringAttribute(string value)
26 | {
27 | // 4 bytes char count + string length
28 | Value = value ?? string.Empty;
29 | Stride = Value.Length;
30 | }
31 |
32 | public object GetElementValue()
33 | {
34 | return Value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/FBXSharp/FBXSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net471
5 | Elementary
6 |
7 |
8 |
9 | true
10 |
11 |
12 |
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/FBXSharp/GlobalSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FBXSharp.Core;
3 | using FBXSharp.ValueTypes;
4 |
5 | namespace FBXSharp
6 | {
7 | public class GlobalSettings : FBXObject
8 | {
9 | public static readonly FBXObjectType FType = FBXObjectType.GlobalSettings;
10 |
11 | public static readonly FBXClassType FClass = FBXClassType.GlobalSettings;
12 |
13 | public override FBXObjectType Type => FType;
14 |
15 | public override FBXClassType Class => FClass;
16 |
17 | public int? UpAxis
18 | {
19 | get => InternalGetPrimitive(nameof(UpAxis), IElementPropertyType.Int);
20 | set => InternalSetPrimitive(nameof(UpAxis), IElementPropertyType.Int, value, "int", "Integer");
21 | }
22 |
23 | public int? UpAxisSign
24 | {
25 | get => InternalGetPrimitive(nameof(UpAxisSign), IElementPropertyType.Int);
26 | set => InternalSetPrimitive(nameof(UpAxisSign), IElementPropertyType.Int, value, "int", "Integer");
27 | }
28 |
29 | public int? FrontAxis
30 | {
31 | get => InternalGetPrimitive(nameof(FrontAxis), IElementPropertyType.Int);
32 | set => InternalSetPrimitive(nameof(FrontAxis), IElementPropertyType.Int, value, "int", "Integer");
33 | }
34 |
35 | public int? FrontAxisSign
36 | {
37 | get => InternalGetPrimitive(nameof(FrontAxisSign), IElementPropertyType.Int);
38 | set => InternalSetPrimitive(nameof(FrontAxisSign), IElementPropertyType.Int, value, "int", "Integer");
39 | }
40 |
41 | public int? CoordAxis
42 | {
43 | get => InternalGetPrimitive(nameof(CoordAxis), IElementPropertyType.Int);
44 | set => InternalSetPrimitive(nameof(CoordAxis), IElementPropertyType.Int, value, "int", "Integer");
45 | }
46 |
47 | public int? CoordAxisSign
48 | {
49 | get => InternalGetPrimitive(nameof(CoordAxisSign), IElementPropertyType.Int);
50 | set => InternalSetPrimitive(nameof(CoordAxisSign), IElementPropertyType.Int, value, "int", "Integer");
51 | }
52 |
53 | public int? OriginalUpAxis
54 | {
55 | get => InternalGetPrimitive(nameof(OriginalUpAxis), IElementPropertyType.Int);
56 | set => InternalSetPrimitive(nameof(OriginalUpAxis), IElementPropertyType.Int, value, "int", "Integer");
57 | }
58 |
59 | public int? OriginalUpAxisSign
60 | {
61 | get => InternalGetPrimitive(nameof(OriginalUpAxisSign), IElementPropertyType.Int);
62 | set => InternalSetPrimitive(nameof(OriginalUpAxisSign), IElementPropertyType.Int, value, "int", "Integer");
63 | }
64 |
65 | public double? UnitScaleFactor
66 | {
67 | get => InternalGetPrimitive(nameof(UnitScaleFactor), IElementPropertyType.Double);
68 | set => InternalSetPrimitive(nameof(UnitScaleFactor), IElementPropertyType.Double, value, "double",
69 | "Number");
70 | }
71 |
72 | public double? OriginalUnitScaleFactor
73 | {
74 | get => InternalGetPrimitive(nameof(OriginalUnitScaleFactor), IElementPropertyType.Double);
75 | set => InternalSetPrimitive(nameof(OriginalUnitScaleFactor), IElementPropertyType.Double, value, "double",
76 | "Number");
77 | }
78 |
79 | public TimeBase? TimeSpanStart
80 | {
81 | get => InternalGetPrimitive(nameof(TimeSpanStart), IElementPropertyType.Time);
82 | set => InternalSetPrimitive(nameof(TimeSpanStart), IElementPropertyType.Time, value, "KTime", "Time");
83 | }
84 |
85 | public TimeBase? TimeSpanStop
86 | {
87 | get => InternalGetPrimitive(nameof(TimeSpanStop), IElementPropertyType.Time);
88 | set => InternalSetPrimitive(nameof(TimeSpanStop), IElementPropertyType.Time, value, "KTime", "Time");
89 | }
90 |
91 | public double? CustomFrameRate
92 | {
93 | get => InternalGetPrimitive(nameof(CustomFrameRate), IElementPropertyType.Double);
94 | set => InternalSetPrimitive(nameof(CustomFrameRate), IElementPropertyType.Double, value, "double",
95 | "Number");
96 | }
97 |
98 | public Enumeration TimeMode
99 | {
100 | get => InternalGetEnumeration(nameof(TimeMode));
101 | set => InternalSetEnumeration(nameof(TimeMode), value, "enum", string.Empty);
102 | }
103 |
104 | internal GlobalSettings(IElement element, IScene scene) : base(element, scene)
105 | {
106 | Name = nameof(GlobalSettings);
107 | }
108 |
109 | internal void InternalFillWithElement(IElement element)
110 | {
111 | FromElement(element);
112 | }
113 |
114 | public override IElement AsElement(bool binary)
115 | {
116 | throw new NotSupportedException("Global Settings cannot be serialized");
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/FBXSharp/LoadFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FBXSharp
4 | {
5 | [Flags]
6 | public enum LoadFlags
7 | {
8 | None = 0,
9 | Triangulate = 1 << 0,
10 | RemapSubmeshes = 1 << 1,
11 | IgnoreGeometry = 1 << 2,
12 | IgnoreBlendShapes = 1 << 3,
13 | LoadVideoFiles = 1 << 4
14 | }
15 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/AnimationCurve.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Objective
5 | {
6 | public class AnimationCurve : FBXObject
7 | {
8 | private long[] m_keyTimes;
9 | private float[] m_keyValues;
10 |
11 | public static readonly FBXObjectType FType = FBXObjectType.AnimationCurve;
12 |
13 | public static readonly FBXClassType FClass = FBXClassType.AnimationCurve;
14 |
15 | public override FBXObjectType Type => FType;
16 |
17 | public override FBXClassType Class => FClass;
18 |
19 | public long[] KeyTimes
20 | {
21 | get => m_keyTimes;
22 | set => m_keyTimes = value ?? Array.Empty();
23 | }
24 |
25 | public float[] KeyValues
26 | {
27 | get => m_keyValues;
28 | set => m_keyValues = value ?? Array.Empty();
29 | }
30 |
31 | internal AnimationCurve(IElement element, IScene scene) : base(element, scene)
32 | {
33 | m_keyTimes = Array.Empty();
34 | m_keyValues = Array.Empty();
35 |
36 | if (element is null) return;
37 |
38 | var times = element.FindChild("KeyTime");
39 | var value = element.FindChild("KeyValueFloat");
40 |
41 | if (!(times is null) && times.Attributes.Length > 0 &&
42 | times.Attributes[0].GetElementValue() is Array timesArray)
43 | {
44 | m_keyTimes = new long[timesArray.Length];
45 | Array.Copy(timesArray, m_keyTimes, m_keyTimes.Length);
46 | }
47 |
48 | if (!(value is null) && value.Attributes.Length > 0 &&
49 | value.Attributes[0].GetElementValue() is Array valueArray)
50 | {
51 | m_keyValues = new float[valueArray.Length];
52 | Array.Copy(valueArray, m_keyValues, m_keyValues.Length);
53 | }
54 |
55 | if (m_keyValues.Length != m_keyTimes.Length)
56 | throw new Exception($"Invalid animation curve with name {Name}");
57 | }
58 |
59 | public override IElement AsElement(bool binary)
60 | {
61 | return new Element(Class.ToString(), null, BuildAttributes("AnimCurve", string.Empty, binary)); // #TODO
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/AnimationCurveNode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Objective
5 | {
6 | public class AnimationCurveNode : FBXObject
7 | {
8 | private AnimationCurve m_curveX;
9 | private AnimationCurve m_curveY;
10 | private AnimationCurve m_curveZ;
11 |
12 | public static readonly FBXObjectType FType = FBXObjectType.AnimationCurveNode;
13 |
14 | public static readonly FBXClassType FClass = FBXClassType.AnimationCurveNode;
15 |
16 | public override FBXObjectType Type => FType;
17 |
18 | public override FBXClassType Class => FClass;
19 |
20 | public double DeltaX { get; set; }
21 | public double DeltaY { get; set; }
22 | public double DeltaZ { get; set; }
23 |
24 | public AnimationCurve CurveX
25 | {
26 | get => m_curveX;
27 | set => InternalSetCurve(value, ref m_curveX);
28 | }
29 |
30 | public AnimationCurve CurveY
31 | {
32 | get => m_curveY;
33 | set => InternalSetCurve(value, ref m_curveY);
34 | }
35 |
36 | public AnimationCurve CurveZ
37 | {
38 | get => m_curveZ;
39 | set => InternalSetCurve(value, ref m_curveZ);
40 | }
41 |
42 | internal AnimationCurveNode(IElement element, IScene scene) : base(element, scene)
43 | {
44 | }
45 |
46 | private void InternalSetCurve(AnimationCurve curve, ref AnimationCurve target)
47 | {
48 | if (curve is null || curve.Scene == Scene)
49 | {
50 | target = curve;
51 |
52 | return;
53 | }
54 |
55 | throw new Exception("Animation curve should share same scene with animation curve node");
56 | }
57 |
58 | public override Connection[] GetConnections()
59 | {
60 | var noCurveX = m_curveX is null;
61 | var noCurveY = m_curveY is null;
62 | var noCurveZ = m_curveZ is null;
63 |
64 | if (noCurveX && noCurveY && noCurveZ) return Array.Empty();
65 |
66 | var currentlyAt = 0;
67 | var thisHashKey = GetHashCode();
68 | var connections = new Connection[(noCurveX ? 0 : 1) + (noCurveY ? 0 : 1) + (noCurveZ ? 0 : 1)];
69 |
70 | if (!noCurveX)
71 | connections[currentlyAt++] = new Connection
72 | (
73 | Connection.ConnectionType.Property,
74 | m_curveX.GetHashCode(),
75 | thisHashKey,
76 | ElementaryFactory.GetElementAttribute("d|X")
77 | );
78 |
79 | if (!noCurveY)
80 | connections[currentlyAt++] = new Connection
81 | (
82 | Connection.ConnectionType.Property,
83 | m_curveY.GetHashCode(),
84 | thisHashKey,
85 | ElementaryFactory.GetElementAttribute("d|Y")
86 | );
87 |
88 | if (!noCurveZ)
89 | connections[currentlyAt++] = new Connection
90 | (
91 | Connection.ConnectionType.Property,
92 | m_curveZ.GetHashCode(),
93 | thisHashKey,
94 | ElementaryFactory.GetElementAttribute("d|Z")
95 | );
96 |
97 | return connections;
98 | }
99 |
100 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
101 | {
102 | if (linker.Class == FBXClassType.AnimationCurve && linker.Type == FBXObjectType.AnimationCurve)
103 | if (attribute.Type == IElementAttributeType.String)
104 | switch (attribute.GetElementValue().ToString())
105 | {
106 | case "d|X":
107 | CurveX = linker as AnimationCurve;
108 | return;
109 | case "d|Y":
110 | CurveY = linker as AnimationCurve;
111 | return;
112 | case "d|Z":
113 | CurveZ = linker as AnimationCurve;
114 | return;
115 | }
116 | }
117 |
118 | public override IElement AsElement(bool binary)
119 | {
120 | var elements = Properties.Count == 0
121 | ? Array.Empty()
122 | : new[] { BuildProperties70() };
123 |
124 | return new Element(Class.ToString(), elements, BuildAttributes("AnimCurveNode", string.Empty, binary));
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/AnimationLayer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class AnimationLayer : FBXObject
8 | {
9 | private readonly List m_curves;
10 |
11 | public static readonly FBXObjectType FType = FBXObjectType.AnimationLayer;
12 |
13 | public static readonly FBXClassType FClass = FBXClassType.AnimationLayer;
14 |
15 | public override FBXObjectType Type => FType;
16 |
17 | public override FBXClassType Class => FClass;
18 |
19 | public IReadOnlyList CurveNodes => m_curves;
20 |
21 | internal AnimationLayer(IElement element, IScene scene) : base(element, scene)
22 | {
23 | m_curves = new List();
24 | }
25 |
26 | public override Connection[] GetConnections()
27 | {
28 | if (m_curves.Count == 0) return Array.Empty();
29 |
30 | var thisHashKey = GetHashCode();
31 | var connections = new Connection[m_curves.Count];
32 |
33 | for (var i = 0; i < connections.Length; ++i)
34 | connections[i] = new Connection(Connection.ConnectionType.Object, m_curves[i].GetHashCode(),
35 | thisHashKey);
36 |
37 | return connections;
38 | }
39 |
40 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
41 | {
42 | if (linker.Class == FBXClassType.AnimationCurveNode && linker.Type == FBXObjectType.AnimationCurveNode)
43 | m_curves.Add(linker as AnimationCurveNode); // #TODO
44 | }
45 |
46 | public override IElement AsElement(bool binary)
47 | {
48 | var elements = Properties.Count == 0
49 | ? Array.Empty()
50 | : new[] { BuildProperties70() };
51 |
52 | return new Element(Class.ToString(), elements, BuildAttributes("AnimLayer", string.Empty, binary));
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/AnimationStack.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class AnimationStack : FBXObject
8 | {
9 | private readonly List m_layers;
10 |
11 | public static readonly FBXObjectType FType = FBXObjectType.AnimationStack;
12 |
13 | public static readonly FBXClassType FClass = FBXClassType.AnimationStack;
14 |
15 | public override FBXObjectType Type => FType;
16 |
17 | public override FBXClassType Class => FClass;
18 |
19 | public IReadOnlyList Layers => m_layers;
20 |
21 | internal AnimationStack(IElement element, IScene scene) : base(element, scene)
22 | {
23 | m_layers = new List();
24 | }
25 |
26 | public override Connection[] GetConnections()
27 | {
28 | if (m_layers.Count == 0) return Array.Empty();
29 |
30 | var thisHashKey = GetHashCode();
31 | var connections = new Connection[m_layers.Count];
32 |
33 | for (var i = 0; i < connections.Length; ++i)
34 | connections[i] = new Connection(Connection.ConnectionType.Object, m_layers[i].GetHashCode(),
35 | thisHashKey);
36 |
37 | return connections;
38 | }
39 |
40 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
41 | {
42 | if (linker.Class == FBXClassType.AnimationLayer && linker.Type == FBXObjectType.AnimationLayer)
43 | m_layers.Add(linker as AnimationLayer); // #TODO
44 | }
45 |
46 | public override IElement AsElement(bool binary)
47 | {
48 | var elements = Properties.Count == 0
49 | ? Array.Empty()
50 | : new[] { BuildProperties70() };
51 |
52 | return new Element(Class.ToString(), elements, BuildAttributes("AnimStack", string.Empty, binary));
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/BlendShape.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class BlendShape : FBXObject
8 | {
9 | private readonly List m_channels;
10 |
11 | public static readonly FBXObjectType FType = FBXObjectType.BlendShape;
12 |
13 | public static readonly FBXClassType FClass = FBXClassType.Deformer;
14 |
15 | public override FBXObjectType Type => FType;
16 |
17 | public override FBXClassType Class => FClass;
18 |
19 | public IReadOnlyList Channels => m_channels;
20 |
21 | internal BlendShape(IElement element, IScene scene) : base(element, scene)
22 | {
23 | m_channels = new List();
24 | }
25 |
26 | public void AddChannel(BlendShapeChannel channel)
27 | {
28 | if (channel is null) return;
29 |
30 | if (channel.Scene != Scene)
31 | throw new Exception("Blend shape channel should share same scene with blend shape");
32 |
33 | m_channels.Add(channel);
34 | }
35 |
36 | public void RemoveChannel(BlendShapeChannel channel)
37 | {
38 | if (channel is null || channel.Scene != Scene) return;
39 |
40 | _ = m_channels.Remove(channel);
41 | }
42 |
43 | public void AddChannelAt(BlendShapeChannel channel, int index)
44 | {
45 | if (channel is null) return;
46 |
47 | if (channel.Scene != Scene)
48 | throw new Exception("Blend shape channel should share same scene with blend shape");
49 |
50 | if (index < 0 || index > m_channels.Count)
51 | throw new ArgumentOutOfRangeException(
52 | "Index should be in range 0 to blend shape channel count inclusively");
53 |
54 | m_channels.Insert(index, channel);
55 | }
56 |
57 | public void RemoveChannelAt(int index)
58 | {
59 | if (index < 0 || index >= m_channels.Count)
60 | throw new ArgumentOutOfRangeException("Index should be in 0 to blend shape channel count range");
61 |
62 | m_channels.RemoveAt(index);
63 | }
64 |
65 | public override Connection[] GetConnections()
66 | {
67 | if (m_channels.Count == 0)
68 | {
69 | return Array.Empty();
70 | }
71 |
72 | var thisHashKey = GetHashCode();
73 | var connections = new Connection[m_channels.Count];
74 |
75 | for (var i = 0; i < connections.Length; ++i)
76 | connections[i] = new Connection(Connection.ConnectionType.Object, m_channels[i].GetHashCode(),
77 | thisHashKey);
78 |
79 | return connections;
80 | }
81 |
82 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
83 | {
84 | if (linker.Class == FBXClassType.Deformer && linker.Type == FBXObjectType.BlendShapeChannel)
85 | AddChannel(linker as BlendShapeChannel);
86 | }
87 |
88 | public override IElement AsElement(bool binary)
89 | {
90 | var hasAnyProperties = Properties.Count != 0;
91 |
92 | var elements = new IElement[1 + (hasAnyProperties ? 1 : 0)];
93 |
94 | elements[0] = Element.WithAttribute("Version", ElementaryFactory.GetElementAttribute(100));
95 |
96 | if (hasAnyProperties) elements[1] = BuildProperties70();
97 |
98 | return new Element(Class.ToString(), elements, BuildAttributes("Deformer", Type.ToString(), binary));
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/BlendShapeChannel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class BlendShapeChannel : FBXObject
8 | {
9 | private readonly List m_shapes;
10 | private double[] m_fullWeights;
11 |
12 | public static readonly FBXObjectType FType = FBXObjectType.BlendShapeChannel;
13 |
14 | public static readonly FBXClassType FClass = FBXClassType.Deformer;
15 |
16 | public override FBXObjectType Type => FType;
17 |
18 | public override FBXClassType Class => FClass;
19 |
20 | public double DeformPercent { get; set; }
21 |
22 | public double[] FullWeights
23 | {
24 | get => m_fullWeights;
25 | set => m_fullWeights = value ?? Array.Empty();
26 | }
27 |
28 | public IReadOnlyList Shapes => m_shapes;
29 |
30 | internal BlendShapeChannel(IElement element, IScene scene) : base(element, scene)
31 | {
32 | m_shapes = new List();
33 | m_fullWeights = Array.Empty();
34 |
35 | if (element is null) return;
36 |
37 | var percent = element.FindChild("DeformPercent");
38 |
39 | if (!(percent is null) && percent.Attributes.Length > 0 &&
40 | percent.Attributes[0].Type == IElementAttributeType.Double)
41 | DeformPercent = Convert.ToDouble(percent.Attributes[0].GetElementValue());
42 |
43 | var weights = element.FindChild("FullWeights");
44 |
45 | if (!(weights is null) && weights.Attributes.Length > 0 &&
46 | weights.Attributes[0].Type == IElementAttributeType.ArrayDouble)
47 | _ = ElementaryFactory.ToDoubleArray(weights.Attributes[0], out m_fullWeights);
48 | }
49 |
50 | public void AddShape(Shape shape)
51 | {
52 | if (shape is null) return;
53 |
54 | if (shape.Scene != Scene) throw new Exception("Shape should share same scene with blend shape channel");
55 |
56 | m_shapes.Add(shape);
57 | }
58 |
59 | public void RemoveShape(Shape shape)
60 | {
61 | if (shape is null || shape.Scene != Scene) return;
62 |
63 | _ = m_shapes.Remove(shape);
64 | }
65 |
66 | public void AddShapeAt(Shape shape, int index)
67 | {
68 | if (shape is null) return;
69 |
70 | if (shape.Scene != Scene) throw new Exception("Shape should share same scene with blend shape channel");
71 |
72 | if (index < 0 || index > m_shapes.Count)
73 | throw new ArgumentOutOfRangeException("Index should be in range 0 to shape count inclusively");
74 |
75 | m_shapes.Insert(index, shape);
76 | }
77 |
78 | public void RemoveMaterialAt(int index)
79 | {
80 | if (index < 0 || index >= m_shapes.Count)
81 | throw new ArgumentOutOfRangeException("Index should be in 0 to shape count range");
82 |
83 | m_shapes.RemoveAt(index);
84 | }
85 |
86 | public override Connection[] GetConnections()
87 | {
88 | if (m_shapes.Count == 0)
89 | {
90 | return Array.Empty();
91 | }
92 |
93 | var thisHashKey = GetHashCode();
94 | var connections = new Connection[m_shapes.Count];
95 |
96 | for (var i = 0; i < connections.Length; ++i)
97 | connections[i] = new Connection(Connection.ConnectionType.Object, m_shapes[i].GetHashCode(),
98 | thisHashKey);
99 |
100 | return connections;
101 | }
102 |
103 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
104 | {
105 | if (linker.Class == FBXClassType.Geometry && linker.Type == FBXObjectType.Shape) AddShape(linker as Shape);
106 | }
107 |
108 | public override IElement AsElement(bool binary)
109 | {
110 | var elements = new IElement[3];
111 |
112 | elements[0] = Element.WithAttribute("Version", ElementaryFactory.GetElementAttribute(100));
113 | elements[1] = Element.WithAttribute("DeformPercent", ElementaryFactory.GetElementAttribute(DeformPercent));
114 | elements[2] = Element.WithAttribute("FullWeights", ElementaryFactory.GetElementAttribute(m_fullWeights));
115 |
116 | return new Element(Class.ToString(), elements,
117 | BuildAttributes("SubDeformer", Type.ToString(), binary)); // #TODO
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/Light.cs:
--------------------------------------------------------------------------------
1 | using FBXSharp.Core;
2 |
3 | namespace FBXSharp.Objective
4 | {
5 | public class Light : Model
6 | {
7 | public static readonly FBXObjectType FType = FBXObjectType.Light;
8 |
9 | public override FBXObjectType Type => FType;
10 |
11 | public override bool SupportsAttribute => true;
12 |
13 | internal Light(IElement element, IScene scene) : base(element, scene)
14 | {
15 | }
16 |
17 | public override IElement AsElement(bool binary)
18 | {
19 | return MakeElement("Model", binary);
20 | }
21 | }
22 |
23 | public class LightAttribute : NodeAttribute
24 | {
25 | public static readonly FBXObjectType FType = FBXObjectType.Light;
26 |
27 | public override FBXObjectType Type => FType;
28 |
29 | internal LightAttribute(IElement element, IScene scene) : base(element, scene)
30 | {
31 | }
32 |
33 | public override IElement AsElement(bool binary)
34 | {
35 | var elements = new IElement[3];
36 |
37 | elements[0] = Element.WithAttribute("TypeFlags", ElementaryFactory.GetElementAttribute("Light"));
38 | elements[1] = Element.WithAttribute("GeometryVersion", ElementaryFactory.GetElementAttribute(124));
39 | elements[2] = BuildProperties70();
40 |
41 | return new Element(Class.ToString(), elements, BuildAttributes("NodeAttribute", Type.ToString(), binary));
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/LimbNode.cs:
--------------------------------------------------------------------------------
1 | using FBXSharp.Core;
2 |
3 | namespace FBXSharp.Objective
4 | {
5 | public class LimbNode : Model
6 | {
7 | public static readonly FBXObjectType FType = FBXObjectType.LimbNode;
8 |
9 | public override FBXObjectType Type => FType;
10 |
11 | public override bool SupportsAttribute => true;
12 |
13 | internal LimbNode(IElement element, IScene scene) : base(element, scene)
14 | {
15 | }
16 |
17 | public override IElement AsElement(bool binary)
18 | {
19 | return MakeElement("Model", binary);
20 | }
21 | }
22 |
23 | public class LimbNodeAttribute : NodeAttribute
24 | {
25 | private string m_typeFlags;
26 |
27 | public static readonly FBXObjectType FType = FBXObjectType.LimbNode;
28 |
29 | public override FBXObjectType Type => FType;
30 |
31 | public string TypeFlags
32 | {
33 | get => m_typeFlags;
34 | set => m_typeFlags = value ?? string.Empty;
35 | }
36 |
37 | internal LimbNodeAttribute(IElement element, IScene scene) : base(element, scene)
38 | {
39 | if (element is null) return;
40 |
41 | var typeFlags = element.FindChild("TypeFlags");
42 |
43 | if (typeFlags is null || typeFlags.Attributes.Length == 0 ||
44 | typeFlags.Attributes[0].Type != IElementAttributeType.String)
45 | m_typeFlags = string.Empty;
46 | else
47 | m_typeFlags = typeFlags.Attributes[0].GetElementValue().ToString();
48 | }
49 |
50 | public override IElement AsElement(bool binary)
51 | {
52 | var hasAnyProperties = Properties.Count != 0;
53 |
54 | var elements = new IElement[1 + (hasAnyProperties ? 1 : 0)];
55 |
56 | elements[0] = Element.WithAttribute("TypeFlags", ElementaryFactory.GetElementAttribute(m_typeFlags));
57 |
58 | if (hasAnyProperties) elements[1] = BuildProperties70();
59 |
60 | return new Element(Class.ToString(), elements, BuildAttributes("NodeAttribute", Type.ToString(), binary));
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/Mesh.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class Mesh : Model
8 | {
9 | private readonly List m_materials;
10 | private Geometry m_geometry;
11 |
12 | public static readonly FBXObjectType FType = FBXObjectType.Mesh;
13 |
14 | public override FBXObjectType Type => FType;
15 |
16 | public override bool SupportsAttribute => false;
17 |
18 | public Geometry Geometry
19 | {
20 | get => m_geometry;
21 | set => InternalSetGeometry(value);
22 | }
23 |
24 | public IReadOnlyList Materials => m_materials;
25 |
26 | internal Mesh(IElement element, IScene scene) : base(element, scene)
27 | {
28 | m_materials = new List();
29 | }
30 |
31 | private void InternalSetGeometry(Geometry geometry)
32 | {
33 | if (geometry is null)
34 | {
35 | m_geometry = null;
36 | return;
37 | }
38 |
39 | if (geometry.Scene != Scene) throw new Exception("Geometry should share same scene with model");
40 |
41 | m_geometry = geometry;
42 | }
43 |
44 | public void AddMaterial(Material material)
45 | {
46 | if (material is null) return;
47 |
48 | if (material.Scene != Scene) throw new Exception("Material should share same scene with model");
49 |
50 | m_materials.Add(material);
51 | }
52 |
53 | public void RemoveMaterial(Material material)
54 | {
55 | if (material is null || material.Scene != Scene) return;
56 |
57 | _ = m_materials.Remove(material);
58 | }
59 |
60 | public void AddMaterialAt(Material material, int index)
61 | {
62 | if (material is null) return;
63 |
64 | if (material.Scene != Scene) throw new Exception("Material should share same scene with model");
65 |
66 | if (index < 0 || index > m_materials.Count)
67 | throw new ArgumentOutOfRangeException("Index should be in range 0 to material count inclusively");
68 |
69 | m_materials.Insert(index, material);
70 | }
71 |
72 | public void RemoveMaterialAt(int index)
73 | {
74 | if (index < 0 || index >= m_materials.Count)
75 | throw new ArgumentOutOfRangeException("Index should be in 0 to material count range");
76 |
77 | m_materials.RemoveAt(index);
78 | }
79 |
80 | public override Connection[] GetConnections()
81 | {
82 | var counter = 0;
83 | var indexer = 0;
84 |
85 | counter += m_geometry is null ? 0 : 1;
86 | counter += m_materials.Count;
87 | counter += Children.Count;
88 |
89 | if (counter == 0) return Array.Empty();
90 |
91 | var connections = new Connection[counter];
92 |
93 | if (!(m_geometry is null))
94 | connections[indexer++] = new Connection(Connection.ConnectionType.Object, m_geometry.GetHashCode(),
95 | GetHashCode());
96 |
97 | for (var i = 0; i < m_materials.Count; ++i)
98 | connections[indexer++] = new Connection(Connection.ConnectionType.Object, m_materials[i].GetHashCode(),
99 | GetHashCode());
100 |
101 | for (var i = 0; i < Children.Count; ++i)
102 | connections[indexer++] = new Connection(Connection.ConnectionType.Object, Children[i].GetHashCode(),
103 | GetHashCode());
104 |
105 | return connections;
106 | }
107 |
108 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
109 | {
110 | if (linker.Class == FBXClassType.Model)
111 | {
112 | AddChild(linker as Model);
113 |
114 | return;
115 | }
116 |
117 | if (linker.Class == FBXClassType.Geometry)
118 | if (linker.Type == FBXObjectType.Mesh)
119 | {
120 | InternalSetGeometry(linker as Geometry);
121 |
122 | return;
123 | }
124 |
125 | if (linker.Class == FBXClassType.Material)
126 | {
127 | AddMaterial(linker as Material);
128 | }
129 | }
130 |
131 | public override IElement AsElement(bool binary)
132 | {
133 | return MakeElement("Model", binary);
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/NullNode.cs:
--------------------------------------------------------------------------------
1 | using FBXSharp.Core;
2 |
3 | namespace FBXSharp.Objective
4 | {
5 | public class NullNode : Model
6 | {
7 | public static readonly FBXObjectType FType = FBXObjectType.Null;
8 |
9 | public override FBXObjectType Type => FType;
10 |
11 | public override bool SupportsAttribute => true;
12 |
13 | internal NullNode(IElement element, IScene scene) : base(element, scene)
14 | {
15 | }
16 |
17 | public override IElement AsElement(bool binary)
18 | {
19 | return MakeElement("Model", binary);
20 | }
21 | }
22 |
23 | public class NullAttribute : NodeAttribute
24 | {
25 | public static readonly FBXObjectType FType = FBXObjectType.Null;
26 |
27 | public override FBXObjectType Type => FType;
28 |
29 | internal NullAttribute(IElement element, IScene scene) : base(element, scene)
30 | {
31 | }
32 |
33 | public override IElement AsElement(bool binary)
34 | {
35 | var elements = new IElement[2];
36 |
37 | elements[0] = Element.WithAttribute("TypeFlags", ElementaryFactory.GetElementAttribute("Null"));
38 | elements[1] = BuildProperties70();
39 |
40 | return new Element(Class.ToString(), elements, BuildAttributes("NodeAttribute", Type.ToString(), binary));
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/Root.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp.Objective
5 | {
6 | public class Root : Model
7 | {
8 | public static readonly FBXObjectType FType = FBXObjectType.Root;
9 |
10 | public override FBXObjectType Type => FType;
11 |
12 | public override bool SupportsAttribute => false;
13 |
14 | internal Root(IScene scene) : base(null, scene)
15 | {
16 | Name = "RootNode";
17 | }
18 |
19 | public override Connection[] GetConnections()
20 | {
21 | if (Children.Count == 0) return Array.Empty();
22 |
23 | var connections = new Connection[Children.Count];
24 |
25 | for (var i = 0; i < connections.Length; ++i)
26 | connections[i] = new Connection(Connection.ConnectionType.Object, Children[i].GetHashCode(), 0L);
27 |
28 | return connections;
29 | }
30 |
31 | public override IElement AsElement(bool binary)
32 | {
33 | throw new NotSupportedException("Root nodes cannot be serialized");
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/Shape.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 | using FBXSharp.ValueTypes;
5 |
6 | namespace FBXSharp.Objective
7 | {
8 | public class Shape : FBXObject
9 | {
10 | private int[] m_indices;
11 | private Vector3[] m_vertices;
12 | private Vector3[] m_normals;
13 |
14 | public static readonly FBXObjectType FType = FBXObjectType.Shape;
15 |
16 | public static readonly FBXClassType FClass = FBXClassType.Geometry;
17 |
18 | public override FBXObjectType Type => FType;
19 |
20 | public override FBXClassType Class => FClass;
21 |
22 | public IReadOnlyList Indices => m_indices;
23 |
24 | public IReadOnlyList Vertices => m_vertices;
25 |
26 | public IReadOnlyList Normals => m_normals;
27 |
28 | internal Shape(IElement element, IScene scene) : base(element, scene)
29 | {
30 | m_indices = Array.Empty();
31 | m_vertices = Array.Empty();
32 | m_normals = Array.Empty();
33 |
34 | if (element is null) return;
35 |
36 | var indices = element.FindChild("Indexes");
37 |
38 | if (!(indices is null) && indices.Attributes.Length > 0 &&
39 | indices.Attributes[0].Type == IElementAttributeType.ArrayInt32)
40 | _ = ElementaryFactory.ToInt32Array(indices.Attributes[0], out m_indices);
41 |
42 | var vertexs = element.FindChild("Vertices");
43 |
44 | if (!(vertexs is null) && vertexs.Attributes.Length > 0 &&
45 | vertexs.Attributes[0].Type == IElementAttributeType.ArrayDouble)
46 | _ = ElementaryFactory.ToVector3Array(vertexs.Attributes[0], out m_vertices);
47 |
48 | var normals = element.FindChild("Normals");
49 |
50 | if (!(normals is null) && normals.Attributes.Length > 0 &&
51 | normals.Attributes[0].Type == IElementAttributeType.ArrayDouble)
52 | _ = ElementaryFactory.ToVector3Array(normals.Attributes[0], out m_normals);
53 | }
54 |
55 | public void SetupMorphTarget(int[] indices, Vector3[] vertices, Vector3[] normals)
56 | {
57 | if (indices is null || vertices is null || normals is null)
58 | {
59 | m_indices = Array.Empty();
60 | m_normals = Array.Empty();
61 | m_vertices = Array.Empty();
62 |
63 | return;
64 | }
65 |
66 | if (indices.Length != vertices.Length || indices.Length != vertices.Length)
67 | throw new Exception("Indices, vertices, and normals arrays should all have the same length");
68 |
69 | m_indices = indices;
70 | m_vertices = vertices;
71 | m_normals = normals;
72 | }
73 |
74 | public override IElement AsElement(bool binary)
75 | {
76 | var elements = new IElement[5];
77 |
78 | var indexes = new int[m_indices.Length];
79 | var vertexs = new double[m_vertices.Length * 3];
80 | var normals = new double[m_normals.Length * 3];
81 |
82 | Array.Copy(m_indices, indexes, indexes.Length);
83 |
84 | for (int i = 0, k = 0; i < m_vertices.Length; ++i)
85 | {
86 | var vector = m_vertices[i];
87 |
88 | vertexs[k++] = vector.X;
89 | vertexs[k++] = vector.Y;
90 | vertexs[k++] = vector.Z;
91 | }
92 |
93 | for (int i = 0, k = 0; i < m_normals.Length; ++i)
94 | {
95 | var vector = m_normals[i];
96 |
97 | normals[k++] = vector.X;
98 | normals[k++] = vector.Y;
99 | normals[k++] = vector.Z;
100 | }
101 |
102 | elements[0] = BuildProperties70();
103 | elements[1] = Element.WithAttribute("Version", ElementaryFactory.GetElementAttribute(100));
104 | elements[2] = Element.WithAttribute("Indexes", ElementaryFactory.GetElementAttribute(indexes));
105 | elements[3] = Element.WithAttribute("Vertices", ElementaryFactory.GetElementAttribute(vertexs));
106 | elements[4] = Element.WithAttribute("Normals", ElementaryFactory.GetElementAttribute(normals));
107 |
108 | return new Element(Class.ToString(), elements, BuildAttributes("Geometry", Type.ToString(), binary));
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/FBXSharp/Objective/Skin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using FBXSharp.Core;
4 |
5 | namespace FBXSharp.Objective
6 | {
7 | public class Skin : FBXObject
8 | {
9 | private readonly List m_clusters;
10 |
11 | public static readonly FBXObjectType FType = FBXObjectType.Skin;
12 |
13 | public static readonly FBXClassType FClass = FBXClassType.Deformer;
14 |
15 | public override FBXObjectType Type => FType;
16 |
17 | public override FBXClassType Class => FClass;
18 |
19 | public double DeformAccuracy { get; set; }
20 |
21 | public IReadOnlyList Clusters => m_clusters;
22 |
23 | internal Skin(IElement element, IScene scene) : base(element, scene)
24 | {
25 | m_clusters = new List();
26 |
27 | if (element is null) return;
28 |
29 | var deformAccuracy = element.FindChild("Link_DeformAcuracy");
30 |
31 | if (deformAccuracy is null || deformAccuracy.Attributes.Length == 0 ||
32 | deformAccuracy.Attributes[0].Type != IElementAttributeType.Double) return;
33 |
34 | DeformAccuracy = Convert.ToDouble(deformAccuracy.Attributes[0].GetElementValue());
35 | }
36 |
37 | public void AddCluster(Cluster cluster)
38 | {
39 | AddClusterAt(cluster, m_clusters.Count);
40 | }
41 |
42 | public void RemoveCluster(Cluster cluster)
43 | {
44 | if (cluster is null || cluster.Scene != Scene) return;
45 |
46 | _ = m_clusters.Remove(cluster);
47 | }
48 |
49 | public void AddClusterAt(Cluster cluster, int index)
50 | {
51 | if (cluster is null) return;
52 |
53 | if (cluster.Scene != Scene) throw new Exception("Cluster should share same scene with skin");
54 |
55 | if (index < 0 || index > m_clusters.Count)
56 | throw new ArgumentOutOfRangeException("Index should be in range 0 to cluster count inclusively");
57 |
58 | m_clusters.Insert(index, cluster);
59 | }
60 |
61 | public void RemoveClusterAt(int index)
62 | {
63 | if (index < 0 || index >= m_clusters.Count)
64 | throw new ArgumentOutOfRangeException("Index should be in 0 to cluster count range");
65 |
66 | m_clusters.RemoveAt(index);
67 | }
68 |
69 | public override Connection[] GetConnections()
70 | {
71 | if (m_clusters.Count == 0) return Array.Empty();
72 |
73 | var thisHashKey = GetHashCode();
74 | var connections = new Connection[m_clusters.Count];
75 |
76 | for (var i = 0; i < connections.Length; ++i)
77 | connections[i] = new Connection(Connection.ConnectionType.Object, m_clusters[i].GetHashCode(),
78 | thisHashKey);
79 |
80 | return connections;
81 | }
82 |
83 | public override void ResolveLink(FBXObject linker, IElementAttribute attribute)
84 | {
85 | if (linker.Class == FBXClassType.Deformer && linker.Type == FBXObjectType.Cluster)
86 | AddCluster(linker as Cluster);
87 | }
88 |
89 | public override IElement AsElement(bool binary)
90 | {
91 | var hasAnyProperties = Properties.Count != 0;
92 |
93 | var elements = new IElement[2 + (hasAnyProperties ? 1 : 0)];
94 |
95 | elements[0] = Element.WithAttribute("Version", ElementaryFactory.GetElementAttribute(101));
96 | elements[1] = Element.WithAttribute("Link_DeformAcuracy",
97 | ElementaryFactory.GetElementAttribute(DeformAccuracy));
98 |
99 | if (hasAnyProperties) elements[2] = BuildProperties70();
100 |
101 | return new Element(Class.ToString(), elements, BuildAttributes("Deformer", Type.ToString(), binary));
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/FBXSharp/TakeInfo.cs:
--------------------------------------------------------------------------------
1 | namespace FBXSharp
2 | {
3 | public class TakeInfo
4 | {
5 | public string Name { get; }
6 | public string Filename { get; }
7 | public double LocalTimeFrom { get; }
8 | public double LocalTimeTo { get; }
9 | public double ReferenceTimeFrom { get; }
10 | public double ReferenceTimeTo { get; }
11 |
12 | public TakeInfo(string name, string filename, double localFrom, double localTo, double refFrom, double refTo)
13 | {
14 | Name = name ?? string.Empty;
15 | Filename = filename ?? string.Empty;
16 | LocalTimeFrom = localFrom;
17 | LocalTimeTo = localTo;
18 | ReferenceTimeFrom = refFrom;
19 | ReferenceTimeTo = refTo;
20 | }
21 |
22 | public override string ToString()
23 | {
24 | return Name;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/FBXSharp/TemplateObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FBXSharp.Core;
3 |
4 | namespace FBXSharp
5 | {
6 | public enum TemplateCreationType
7 | {
8 | DontCreateIfDuplicated,
9 | MergeIfExistingIsFound,
10 | NewOverrideAnyExisting
11 | }
12 |
13 | public class TemplateObject : FBXObject
14 | {
15 | public static readonly FBXObjectType FType = FBXObjectType.Template;
16 |
17 | public static readonly FBXClassType FClass = FBXClassType.Template;
18 |
19 | public override FBXObjectType Type => FType;
20 |
21 | public override FBXClassType Class => FClass;
22 |
23 | public FBXClassType OverridableType { get; }
24 |
25 | public TemplateObject(FBXClassType type, IElement element, IScene scene) : base(null, scene)
26 | {
27 | OverridableType = type;
28 |
29 | Initialize(element);
30 | }
31 |
32 | public void Initialize(IElement element)
33 | {
34 | if (element is null) return;
35 |
36 | RemoveAllProperties();
37 |
38 | if (element.Attributes.Length > 0 && element.Attributes[0].Type == IElementAttributeType.String)
39 | Name = element.Attributes[0].GetElementValue().ToString();
40 |
41 | ParseProperties70(element);
42 | }
43 |
44 | public void MergeWith(TemplateObject template)
45 | {
46 | if (template is null) throw new ArgumentNullException("Merger template passed cannot be null");
47 |
48 | if (template.OverridableType != OverridableType)
49 | throw new ArgumentException(
50 | $"Cannot merge template of type {OverridableType} with template of type {template.OverridableType}");
51 |
52 | foreach (var property in template.Properties) AddProperty(property);
53 | }
54 |
55 | public override IElement AsElement(bool binary)
56 | {
57 | throw new NotSupportedException("Templates cannot be serialized");
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/FBXSharp/ValueTypes/BinaryBlob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FBXSharp.ValueTypes
4 | {
5 | public class BinaryBlob
6 | {
7 | private object[] m_datas;
8 |
9 | public object[] Datas
10 | {
11 | get => m_datas;
12 | set => m_datas = value ?? Array.Empty