├── M3G2FBX
├── app.config
├── Properties
│ └── AssemblyInfo.cs
├── M3G2FBX.csproj
└── Program.cs
├── M3G2FBX.sln
├── .gitattributes
└── .gitignore
/M3G2FBX/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/M3G2FBX.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M3G2FBX", "M3G2FBX\M3G2FBX.csproj", "{CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|x86 = Debug|x86
9 | Release|x86 = Release|x86
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}.Debug|x86.ActiveCfg = Debug|x86
13 | {CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}.Debug|x86.Build.0 = Debug|x86
14 | {CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}.Release|x86.ActiveCfg = Release|x86
15 | {CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}.Release|x86.Build.0 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/M3G2FBX/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("M3G2FBX")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("M3G2FBX")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("8f56e4bd-b145-41f0-966f-83ad5bbb2d4a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/M3G2FBX/M3G2FBX.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 8.0.30703
7 | 2.0
8 | {CBD0B44F-45B6-457B-A847-FEEB08FB5D9E}
9 | Exe
10 | Properties
11 | M3G2FBX
12 | M3G2FBX
13 | v3.5
14 | Client
15 | 512
16 |
17 |
18 | x86
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | x86
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
63 |
--------------------------------------------------------------------------------
/.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 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | # DNX
42 | project.lock.json
43 | artifacts/
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding add-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | ## TODO: Comment the next line if you want to checkin your
137 | ## web deploy settings but do note that will include unencrypted
138 | ## passwords
139 | #*.pubxml
140 |
141 | *.publishproj
142 |
143 | # NuGet Packages
144 | *.nupkg
145 | # The packages folder can be ignored because of Package Restore
146 | **/packages/*
147 | # except build/, which is used as an MSBuild target.
148 | !**/packages/build/
149 | # Uncomment if necessary however generally it will be regenerated when needed
150 | #!**/packages/repositories.config
151 |
152 | # Windows Azure Build Output
153 | csx/
154 | *.build.csdef
155 |
156 | # Windows Store app package directory
157 | AppPackages/
158 |
159 | # Visual Studio cache files
160 | # files ending in .cache can be ignored
161 | *.[Cc]ache
162 | # but keep track of directories ending in .cache
163 | !*.[Cc]ache/
164 |
165 | # Others
166 | ClientBin/
167 | [Ss]tyle[Cc]op.*
168 | ~$*
169 | *~
170 | *.dbmdl
171 | *.dbproj.schemaview
172 | *.pfx
173 | *.publishsettings
174 | node_modules/
175 | orleans.codegen.cs
176 |
177 | # RIA/Silverlight projects
178 | Generated_Code/
179 |
180 | # Backup & report files from converting an old project file
181 | # to a newer Visual Studio version. Backup files are not needed,
182 | # because we have git ;-)
183 | _UpgradeReport_Files/
184 | Backup*/
185 | UpgradeLog*.XML
186 | UpgradeLog*.htm
187 |
188 | # SQL Server files
189 | *.mdf
190 | *.ldf
191 |
192 | # Business Intelligence projects
193 | *.rdl.data
194 | *.bim.layout
195 | *.bim_*.settings
196 |
197 | # Microsoft Fakes
198 | FakesAssemblies/
199 |
200 | # Node.js Tools for Visual Studio
201 | .ntvs_analysis.dat
202 |
203 | # Visual Studio 6 build log
204 | *.plg
205 |
206 | # Visual Studio 6 workspace options file
207 | *.opt
208 |
209 | # LightSwitch generated files
210 | GeneratedArtifacts/
211 | _Pvt_Extensions/
212 | ModelManifest.xml
213 |
--------------------------------------------------------------------------------
/M3G2FBX/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.IO;
6 | using System.IO.Compression;
7 |
8 | /* TODO:
9 | * flip texture cordinates
10 | * decompress zlibbed sections even though they are not used
11 | * calculate triangle strips with implicit indices
12 | * calculate vertex arrays with encoding 1
13 | */
14 |
15 | namespace M3G2FBX
16 | {
17 | class Program
18 | {
19 | static byte[] JSR184 = new byte[12] { 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
20 | static byte[] IM2M3G = new byte[12] { 0xAB, 0x49, 0x4D, 0x32, 0x4D, 0x33, 0x47, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
21 | static byte[] IM3M3G = new byte[12] { 0xAB, 0x49, 0x4D, 0x33, 0x4D, 0x33, 0x47, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
22 |
23 | static string currentPath = AppDomain.CurrentDomain.BaseDirectory;
24 | static int version = 0;
25 | static byte[] FileIdentifier = new byte[12];
26 | static bool makeUnique = false;
27 |
28 | public class M3Gobject
29 | {
30 | public byte type;
31 | public int length;
32 | public long offset;
33 | public bool connected;
34 | public int[] appearance;
35 | }
36 |
37 | static void Main(string[] args)
38 | {
39 | System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
40 | SearchOption recurse = SearchOption.AllDirectories;
41 |
42 | string input = "";
43 | int totalConverted = 0;
44 |
45 | switch (args.Length)
46 | {
47 | case 0:
48 | input = AppDomain.CurrentDomain.BaseDirectory;
49 | recurse = SearchOption.TopDirectoryOnly;
50 | break;
51 | case 1:
52 | if (args[0] == "-u")
53 | {
54 | makeUnique = true;
55 | goto case 0;
56 | }
57 | else { input = args[0]; }
58 | break;
59 | case 2:
60 | if (args[0] == "-u")
61 | {
62 | makeUnique = true;
63 | input = args[1];
64 | }
65 | break;
66 | default:
67 | Console.WriteLine("M3G to FBX model converter\nby Chipicao - www.KotsChopShop.com\n\nUsage: M3G2FBX.exe [-u] [input_file/folder]");
68 | break;
69 | }
70 |
71 | if (File.Exists(input))
72 | {
73 | //in case only filename is entered
74 | //input = Path.GetFullPath(input) + "\\" + Path.GetFileName(input);
75 | input = Path.GetFullPath(input);
76 | currentPath = Path.GetDirectoryName(input) + "\\";
77 | ReadM3G(input);
78 | Console.WriteLine("Finished.");
79 | }
80 | else if (Directory.Exists(input))
81 | {
82 | input = Path.GetFullPath(input);
83 | string[] inputFiles = Directory.GetFiles(input, "*.m3g", recurse);
84 | foreach (var inputFile in inputFiles)
85 | {
86 | currentPath = Path.GetDirectoryName(inputFile) + "\\";
87 | totalConverted += ReadM3G(inputFile);
88 | }
89 | Console.WriteLine("Finished converting {0} files.", totalConverted);
90 | Console.WriteLine("Press any key to exit...");
91 | Console.ReadKey();
92 | }
93 | else { Console.WriteLine("Invalid input file/folder: {0}\n\nUsage: M3G2FBX.exe [-u] [input_file/folder]", input); }
94 | }
95 |
96 | private static int ReadM3G(string M3Gfile)
97 | {
98 | string FBXfile = Path.GetDirectoryName(M3Gfile) + "\\" + Path.GetFileNameWithoutExtension(M3Gfile) + ".fbx";
99 | if (File.Exists(FBXfile))
100 | {
101 | Console.WriteLine("File already converted: " + Path.GetFileName(FBXfile));
102 | return 0;
103 | }
104 | else
105 | {
106 | Console.WriteLine("Converting " + Path.GetFileName(M3Gfile));
107 | //byte[] FileIdentifier = new byte[12];
108 | int converted = 0;
109 |
110 | using (FileStream inStream = File.OpenRead(M3Gfile))
111 | {
112 | inStream.Read(FileIdentifier, 0, 12);
113 |
114 | if (FileIdentifier[0] == 0x1F && FileIdentifier[1] == 0x8C && FileIdentifier[6] == 0x1F && FileIdentifier[7] == 0x8B) //gzip
115 | {
116 | int uncompressedSize = BitConverter.ToInt32(new byte[4] { FileIdentifier[2], FileIdentifier[3], FileIdentifier[4], FileIdentifier[5] }, 0);
117 | byte[] decompressionBuffer = new byte[uncompressedSize];
118 |
119 | int gzipSize = (int)inStream.Length - 6;
120 | byte[] gzipbuffer = new byte[gzipSize];
121 | inStream.Position = 6;
122 | inStream.Read(gzipbuffer, 0, gzipSize);
123 |
124 | using (MemoryStream gzipStream = new MemoryStream(gzipbuffer))
125 | using (GZipStream compressedStream = new GZipStream(gzipStream, CompressionMode.Decompress))
126 | using (MemoryStream decompressedStream = new MemoryStream(decompressionBuffer))
127 | {
128 | compressedStream.Read(decompressionBuffer, 0, uncompressedSize); //so this still applies to decompressedStream; nice!
129 | decompressedStream.Read(FileIdentifier, 0, 12);
130 | if (FileIdentifier.SequenceEqual(JSR184)) { version = 1; }
131 | else if (FileIdentifier.SequenceEqual(IM2M3G)) { version = 2; }
132 | else if (FileIdentifier.SequenceEqual(IM3M3G)) { version = 3; }
133 | else
134 | {
135 | Console.WriteLine("Unsupported file type: " + Path.GetFileName(M3Gfile));
136 | return 0;
137 | }
138 |
139 | converted = ConvertM3G(decompressedStream, FBXfile);
140 | }
141 | }
142 | else
143 | {
144 | if (FileIdentifier.SequenceEqual(JSR184)) { version = 1; }
145 | else if (FileIdentifier.SequenceEqual(IM2M3G)) { version = 2; }
146 | else if (FileIdentifier.SequenceEqual(IM3M3G)) { version = 3; }
147 | else
148 | {
149 | Console.WriteLine("Unsupported file type: " + Path.GetFileName(M3Gfile));
150 | return 0;
151 | }
152 |
153 | converted = ConvertM3G(inStream, FBXfile);
154 | }
155 | }
156 | return converted;
157 | }
158 | }
159 |
160 | public static int ConvertM3G(Stream M3Gstream, string outFile)
161 | {
162 | var streamLen = M3Gstream.Length;
163 | List ObjectList = new List();
164 | ObjectList.Add(new M3Gobject()); //reserved
165 |
166 | using (BinaryReader binStream = new BinaryReader(M3Gstream))
167 | using (TextWriter sw = new StreamWriter(outFile))
168 | {
169 | int FBXmodelCount = 0;
170 | int FBXgeometryCount = 0;
171 | int FBXmaterialCount = 0;
172 | int FBXtextureCount = 0;
173 |
174 | #region Write generic FBX data
175 | var timestamp = DateTime.Now;
176 | StringBuilder fbx = new StringBuilder();
177 | fbx.Append("; FBX 7.1.0 project file");
178 | fbx.Append("\nFBXHeaderExtension: {\n\tFBXHeaderVersion: 1003\n\tFBXVersion: 7100\n\tCreationTimeStamp: {\n\t\tVersion: 1000");
179 | fbx.Append("\n\t\tYear: " + timestamp.Year);
180 | fbx.Append("\n\t\tMonth: " + timestamp.Month);
181 | fbx.Append("\n\t\tDay: " + timestamp.Day);
182 | fbx.Append("\n\t\tHour: " + timestamp.Hour);
183 | fbx.Append("\n\t\tMinute: " + timestamp.Minute);
184 | fbx.Append("\n\t\tSecond: " + timestamp.Second);
185 | fbx.Append("\n\t\tMillisecond: " + timestamp.Millisecond);
186 | fbx.Append("\n\t}\n\tCreator: \"M3G2FBX by Chipicao\"\n}\n");
187 |
188 | fbx.Append("\nGlobalSettings: {");
189 | fbx.Append("\n\tVersion: 1000");
190 | fbx.Append("\n\tProperties70: {");
191 | fbx.Append("\n\t\tP: \"UpAxis\", \"int\", \"Integer\", \"\",1");
192 | fbx.Append("\n\t\tP: \"UpAxisSign\", \"int\", \"Integer\", \"\",-1");
193 | fbx.Append("\n\t\tP: \"FrontAxis\", \"int\", \"Integer\", \"\",2");
194 | fbx.Append("\n\t\tP: \"FrontAxisSign\", \"int\", \"Integer\", \"\",1");
195 | fbx.Append("\n\t\tP: \"CoordAxis\", \"int\", \"Integer\", \"\",0");
196 | fbx.Append("\n\t\tP: \"CoordAxisSign\", \"int\", \"Integer\", \"\",1");
197 | fbx.Append("\n\t\tP: \"OriginalUpAxis\", \"int\", \"Integer\", \"\",1");
198 | fbx.Append("\n\t\tP: \"OriginalUpAxisSign\", \"int\", \"Integer\", \"\",-1");
199 | fbx.Append("\n\t\tP: \"UnitScaleFactor\", \"double\", \"Number\", \"\",1");
200 | fbx.Append("\n\t\tP: \"OriginalUnitScaleFactor\", \"double\", \"Number\", \"\",1");
201 | //sb.Append("\n\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"\",0,0,0");
202 | //sb.Append("\n\t\tP: \"DefaultCamera\", \"KString\", \"\", \"\", \"Producer Perspective\"");
203 | //sb.Append("\n\t\tP: \"TimeMode\", \"enum\", \"\", \"\",6");
204 | //sb.Append("\n\t\tP: \"TimeProtocol\", \"enum\", \"\", \"\",2");
205 | //sb.Append("\n\t\tP: \"SnapOnFrameMode\", \"enum\", \"\", \"\",0");
206 | //sb.Append("\n\t\tP: \"TimeSpanStart\", \"KTime\", \"Time\", \"\",0");
207 | //sb.Append("\n\t\tP: \"TimeSpanStop\", \"KTime\", \"Time\", \"\",153953860000");
208 | //sb.Append("\n\t\tP: \"CustomFrameRate\", \"double\", \"Number\", \"\",-1");
209 | //sb.Append("\n\t\tP: \"TimeMarker\", \"Compound\", \"\", \"\"");
210 | //sb.Append("\n\t\tP: \"CurrentTimeMarker\", \"int\", \"Integer\", \"\",-1");
211 | fbx.Append("\n\t}\n}\n");
212 |
213 | fbx.Append("\nDocuments: {");
214 | fbx.Append("\n\tCount: 1");
215 | fbx.Append("\n\tDocument: 1234567890, \"\", \"Scene\" {");
216 | fbx.Append("\n\t\tProperties70: {");
217 | fbx.Append("\n\t\t\tP: \"SourceObject\", \"object\", \"\", \"\"");
218 | fbx.Append("\n\t\t\tP: \"ActiveAnimStackName\", \"KString\", \"\", \"\", \"\"");
219 | fbx.Append("\n\t\t}");
220 | fbx.Append("\n\t\tRootNode: 0");
221 | fbx.Append("\n\t}\n}\n");
222 | fbx.Append("\nReferences: {\n}\n");
223 |
224 | fbx.Append("\nDefinitions: {");
225 | fbx.Append("\n\tVersion: 100");
226 |
227 | fbx.Append("\n\tObjectType: \"GlobalSettings\" {");
228 | fbx.Append("\n\t\tCount: 1");
229 | fbx.Append("\n\t}");
230 |
231 | fbx.Append("\n\tObjectType: \"Model\" {");
232 | //sb.Append("\n\t\tCount: " + FBXmodelCount);
233 | fbx.Append("\n\t}");
234 |
235 | fbx.Append("\n\tObjectType: \"Geometry\" {");
236 | //sb.Append("\n\t\tCount: " + FBXgeometryCount);
237 | fbx.Append("\n\t}");
238 |
239 | fbx.Append("\n\tObjectType: \"Material\" {");
240 | //sb.Append("\n\t\tCount: " + FBXmaterialCount);
241 | fbx.Append("\n\t}");
242 |
243 | fbx.Append("\n\tObjectType: \"Texture\" {");
244 | //sb.Append("\n\t\tCount: " + FBXtextureCount);
245 | fbx.Append("\n\t}");
246 |
247 | fbx.Append("\n\tObjectType: \"Video\" {");
248 | //sb.Append("\n\t\tCount: " + FBXtextureCount);
249 | fbx.Append("\n\t}");
250 | fbx.Append("\n}\n");
251 | fbx.Append("\nObjects: {");
252 |
253 | sw.Write(fbx.ToString());
254 | fbx.Length = 0;
255 | #endregion
256 |
257 | StringBuilder ob = new StringBuilder(); //objects builder
258 | //ob.Append("\nObjects: {");
259 |
260 | StringBuilder cb = new StringBuilder(); //connections builder
261 | cb.Append("\n}\n");//Objects end
262 | cb.Append("\nConnections: {");
263 |
264 | /*binStream.Read(FileIdentifier, 0, 12);
265 |
266 | if (FileIdentifier.SequenceEqual(JSR184)) { version = 1; }
267 | else if (FileIdentifier.SequenceEqual(IM3M3G)) { version = 3; }
268 | else { return 0; }*/
269 |
270 | while (M3Gstream.Position < streamLen) //loop sections
271 | {
272 | byte CompressionScheme = binStream.ReadByte();
273 | int TotalSectionLength = binStream.ReadInt32();
274 | int UncompressedLength = binStream.ReadInt32(); //version1: TotalSectionLength -13 unless compressed
275 | var sectionEnd = M3Gstream.Position + TotalSectionLength - 13;
276 |
277 | if (CompressionScheme == 0)
278 | {
279 |
280 | while (M3Gstream.Position < sectionEnd) //loop objects
281 | {
282 | M3Gobject M3Gobj = new M3Gobject();
283 | M3Gobj.type = binStream.ReadByte();
284 | M3Gobj.length = binStream.ReadInt32();
285 | M3Gobj.offset = M3Gstream.Position;
286 | ObjectList.Add(M3Gobj);
287 | int objIndex = ObjectList.Count - 1;
288 | long nextObject = M3Gstream.Position + M3Gobj.length;
289 |
290 | switch (M3Gobj.type)
291 | {
292 | #region Image2D
293 | case 10: //Image2D
294 | {
295 | FBXtextureCount++;
296 | string textureName = "";
297 | string textureFile = "";
298 | string relativePath = "";
299 |
300 | M3Gstream.Position += 8;
301 | int userParameterCount = binStream.ReadInt32();
302 | for (int i = 0; i < userParameterCount; i++)
303 | {
304 | int parameterID = binStream.ReadInt32();
305 | int parameterValueLength = binStream.ReadInt32();
306 | long nextParam = M3Gstream.Position + parameterValueLength;
307 |
308 | switch (parameterID)
309 | {
310 | case 0: //texture name
311 | {
312 | textureName = ReadStr(binStream, parameterValueLength);
313 | break;
314 | }
315 | case 900: //relative texure path
316 | {
317 | relativePath = ReadStr(binStream, parameterValueLength);
318 |
319 | string sbaFile = "";
320 | string convertedFile = "";
321 |
322 | //first check in car folder
323 | string[] textureFiles = Directory.GetFiles(currentPath, Path.GetFileNameWithoutExtension(relativePath) + ".*");
324 | //then try original location
325 | if (textureFiles.Length == 0)
326 | {
327 | string absolutePath = Path.GetFullPath(currentPath + Path.GetDirectoryName(relativePath));
328 | if (!Directory.Exists(absolutePath))
329 | {
330 | absolutePath = absolutePath.Replace("\\published\\", "\\published.texture_pvrtc\\");
331 | }
332 |
333 | if (Directory.Exists(absolutePath))
334 | {
335 | textureFiles = Directory.GetFiles(absolutePath, Path.GetFileNameWithoutExtension(relativePath) + ".*");
336 |
337 | if (textureFiles.Length == 0)
338 | {
339 | absolutePath = absolutePath.Replace("\\published\\", "\\published.texture_pvrtc\\");
340 | if (Directory.Exists(absolutePath))
341 | {
342 | textureFiles = Directory.GetFiles(absolutePath, Path.GetFileNameWithoutExtension(relativePath) + ".*");
343 | }
344 | }
345 | }
346 | }
347 | //last but not least check every folder from the same level
348 | if (textureFiles.Length == 0)
349 | {
350 | textureFiles = Directory.GetFiles(Directory.GetParent(currentPath.TrimEnd('\\')).ToString(), Path.GetFileNameWithoutExtension(relativePath) + ".*", SearchOption.AllDirectories);
351 | }
352 |
353 | foreach (var tfile in textureFiles)
354 | {//textures should already be sorted by extension: dds, png, pvr, sba
355 | switch (Path.GetExtension(tfile))
356 | {
357 | case ".dds":
358 | case ".png":
359 | case ".pvr":
360 | convertedFile = tfile;
361 | break;
362 | case ".sba":
363 | sbaFile = tfile;
364 | break;
365 | }
366 | }
367 |
368 |
369 | if (convertedFile != "")
370 | {
371 | textureFile = convertedFile;
372 | relativePath = MakeRelative(convertedFile, currentPath);
373 | }
374 | else if (sbaFile != "")
375 | {
376 | //textureFile = ConvertImage(sbaFile);
377 | textureFile = sbaFile;
378 | relativePath = MakeRelative(textureFile, currentPath);
379 | }
380 | else
381 | {
382 | textureFile = relativePath;
383 | }
384 |
385 | break;
386 | }
387 | default:
388 | {
389 | M3Gstream.Position += parameterValueLength;
390 | break;
391 | }
392 | }
393 | M3Gstream.Position = nextParam; //failsafe
394 | }
395 |
396 | byte format = binStream.ReadByte();
397 | int width = binStream.ReadInt32();
398 | int height = binStream.ReadInt32();
399 | M3Gstream.Position += 12; //could be a bool in here
400 |
401 | ob.Append("\n\tTexture: " + (500000 + objIndex) + ", \"Texture::" + textureName + "\", \"\" {");
402 | ob.Append("\n\t\tType: \"TextureVideoClip\"");
403 | ob.Append("\n\t\tVersion: 202");
404 | ob.Append("\n\t\tTextureName: \"Texture::" + textureName + "\"");
405 | ob.Append("\n\t\tProperties70: {");
406 | ob.Append("\n\t\t\tP: \"UVSet\", \"KString\", \"\", \"\", \"UVChannel_0\"");
407 | ob.Append("\n\t\t\tP: \"UseMaterial\", \"bool\", \"\", \"\",1");
408 | ob.Append("\n\t\t}");
409 | ob.Append("\n\t\tMedia: \"Video::" + textureName + "\"");
410 | ob.Append("\n\t\tFileName: \"" + textureFile + "\"");
411 | ob.Append("\n\t\tRelativeFilename: \"" + relativePath + "\"");
412 | ob.Append("\n\t}");
413 |
414 | ob.Append("\n\tVideo: " + (600000 + objIndex) + ", \"Video::" + textureName + "\", \"Clip\" {");
415 | ob.Append("\n\t\tType: \"Clip\"");
416 | ob.Append("\n\t\tProperties70: {");
417 | ob.Append("\n\t\t\tP: \"Path\", \"KString\", \"XRefUrl\", \"\", \"" + textureFile + "\"");
418 | ob.Append("\n\t\t}");
419 | ob.Append("\n\t\tFileName: \"" + textureFile + "\"");
420 | ob.Append("\n\t\tRelativeFilename: \"" + relativePath + "\"");
421 | ob.Append("\n\t}");
422 |
423 | //connect video to texture
424 | cb.Append("\n\tC: \"OO\"," + (600000 + objIndex) + "," + (500000 + objIndex));
425 |
426 | break;
427 | }
428 | #endregion
429 | #region Appearance
430 | case 3: //Appearance
431 | {
432 | FBXmaterialCount++;
433 | M3Gstream.Position += 4; //could be AnimationControllers
434 | int AnimationTracks = binStream.ReadInt32();
435 | M3Gstream.Position += AnimationTracks * 4; //ObjectIndex
436 |
437 | string materialName = "";
438 | float[] diffuseColor = new float[4] { 0.7f, 0.7f, 0.7f, 0.7f };
439 |
440 | int userParameterCount = binStream.ReadInt32();
441 | for (int i = 0; i < userParameterCount; i++)
442 | {
443 | int parameterID = binStream.ReadInt32();
444 | int parameterValueLength = binStream.ReadInt32();
445 | long nextParam = M3Gstream.Position + parameterValueLength;
446 |
447 | switch (parameterID)
448 | {
449 | case 0: //material name
450 | {
451 | materialName = ReadStr(binStream, parameterValueLength);
452 | break;
453 | }
454 | case 1:
455 | {
456 | string paramType = ReadStr(binStream, binStream.ReadByte());
457 | float paramValue = binStream.ReadSingle();
458 | break;
459 | }
460 | case 2: //shader name?
461 | {
462 | string paramType = ReadStr(binStream, binStream.ReadByte());
463 | string paramName = ReadStr(binStream, binStream.ReadInt32());
464 | break;
465 | }
466 | case 600:
467 | {
468 | float paramValue = binStream.ReadSingle();
469 | break;
470 | }
471 | case 601:
472 | {
473 | diffuseColor = new float[4] { binStream.ReadSingle(), binStream.ReadSingle(), binStream.ReadSingle(), binStream.ReadSingle() };
474 | break;
475 | }
476 | default:
477 | {
478 | M3Gstream.Position += parameterValueLength;
479 | break;
480 | }
481 | }
482 | M3Gstream.Position = nextParam; //failsafe
483 | }
484 |
485 | ob.Append("\n\tMaterial: " + (400000 + objIndex) + ", \"Material::" + materialName + "\", \"\" {");
486 | ob.Append("\n\t\tVersion: 102");
487 | ob.Append("\n\t\tShadingModel: \"phong\"");
488 | ob.Append("\n\t\tMultiLayer: 0");
489 | ob.Append("\n\t\tProperties70: {");
490 | ob.Append("\n\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"phong\"");
491 | ob.Append("\n\t\t\tP: \"AmbientColor\", \"Color\", \"\", \"A\"," + diffuseColor[0] + "," + diffuseColor[1] + "," + diffuseColor[2]);
492 | ob.Append("\n\t\t\tP: \"DiffuseColor\", \"Color\", \"\", \"A\"," + diffuseColor[0] + "," + diffuseColor[1] + "," + diffuseColor[2]);
493 | ob.Append("\n\t\t\tP: \"SpecularColor\", \"Color\", \"\", \"A\",0.8,0.8,0.8");
494 | ob.Append("\n\t\t\tP: \"SpecularFactor\", \"Number\", \"\", \"A\",0");
495 | ob.Append("\n\t\t\tP: \"ShininessExponent\", \"Number\", \"\", \"A\",2");
496 | ob.Append("\n\t\t}");
497 | ob.Append("\n\t}");
498 |
499 | byte layer = binStream.ReadByte();
500 | int compositingMode = binStream.ReadInt32(); //ObjectIndex
501 | int fog = binStream.ReadInt32(); //ObjectIndex
502 | int polygonMode = binStream.ReadInt32(); //ObjectIndex
503 | int material = binStream.ReadInt32(); //ObjectIndex
504 | int textureCount = binStream.ReadInt32();
505 | for (int t = 0; t < textureCount; t++)
506 | {
507 | int textureIndex = binStream.ReadInt32();
508 | long nextTexture = M3Gstream.Position;
509 | M3Gstream.Position = ObjectList[textureIndex].offset + 14;
510 | int imageIndex = binStream.ReadInt32();
511 |
512 | //connect Texture to Material
513 | cb.Append("\n\tC: \"OP\"," + (500000 + imageIndex) + "," + (400000 + objIndex) + ", \"");
514 | switch (t)
515 | {
516 | case 0:
517 | cb.Append("DiffuseColor\"");
518 | break;
519 | case 1:
520 | cb.Append("NormalMap\"");
521 | break;
522 | default:
523 | cb.Append("Unknown\""); //don't be a smartass
524 | break;
525 | }
526 |
527 | /*byte ColorR = binStream.ReadByte();
528 | byte ColorG = binStream.ReadByte();
529 | byte ColorB = binStream.ReadByte();
530 | byte blending = binStream.ReadByte();
531 | byte wrappingS = binStream.ReadByte();
532 | byte wrappingT = binStream.ReadByte();
533 | byte levelFilter = binStream.ReadByte();
534 | byte imageFilter = binStream.ReadByte();*/
535 | M3Gstream.Position = nextTexture;
536 | }
537 | break;
538 | }
539 | #endregion
540 | #region Mesh
541 | case 14: //Mesh
542 | {
543 | FBXgeometryCount++;
544 | M3Gstream.Position += 8;
545 |
546 | #region if Real Racing 3
547 | if (version == 1)
548 | {
549 | int materialID;
550 | //this is bad and you should feel bad
551 | int something = binStream.ReadInt32();
552 | switch (something)
553 | {
554 | case 5:
555 | M3Gstream.Position += 48;
556 | break;
557 | case 6:
558 | M3Gstream.Position += 56;
559 | materialID = binStream.ReadInt32();
560 | cb.Append("\n\tC: \"OO\"," + (40000 + materialID) + "," + (200000 + objIndex));
561 | break;
562 | case 7:
563 | M3Gstream.Position += 81;
564 | materialID = binStream.ReadInt32();
565 | cb.Append("\n\tC: \"OO\"," + (40000 + materialID) + "," + (200000 + objIndex));
566 | break;
567 | default:
568 | M3Gstream.Position = nextObject;
569 | Console.WriteLine("Problem reading mesh; contact me.");
570 | continue;
571 | //break;
572 | }
573 |
574 |
575 |
576 | string meshName = ReadStringToNull(binStream);
577 | if (makeUnique) { meshName = objIndex.ToString() + "_" + meshName; }
578 |
579 | FBXmodelCount++;
580 | //create Mesh object
581 | ob.Append("\n\tModel: " + (200000 + objIndex) + ", \"Model::" + meshName + "\", \"Mesh\" {");
582 | ob.Append("\n\t\tVersion: 232");
583 | ob.Append("\n\t\tProperties70: {");
584 | ob.Append("\n\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
585 | ob.Append("\n\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
586 | ob.Append("\n\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
587 | //ob.Append("\n\t\t\tP: \"UDP3DSMAX\", \"KString\", \"\", \"U\", \"MapChannel:1 = UVChannel_1&cr;&lf;MapChannel:2 = UVChannel_2&cr;&lf;\"");
588 | //ob.Append("\n\t\t\tP: \"MaxHandle\", \"int\", \"Integer\", \"UH\"," + (j + 2 + pmodel.nodeList.Count));
589 | ob.Append("\n\t\t}");
590 | ob.Append("\n\t\tShading: T");
591 | ob.Append("\n\t\tCulling: \"CullingOff\"");
592 | ob.Append("\n\t}"); //Model end
593 |
594 | //connect Geometry to Model
595 | cb.Append("\n\tC: \"OO\"," + (100000 + objIndex) + "," + (200000 + objIndex));
596 |
597 | //connect Model to scene; assuming hierarchy is not used in version 1
598 | cb.Append("\n\tC: \"OO\"," + (200000 + objIndex) + ",0");
599 | ObjectList[objIndex].connected = true;
600 |
601 | M3Gstream.Position += 50;
602 | }
603 | #endregion
604 | else { M3Gstream.Position += 14; }
605 |
606 | int vertexBuffer = binStream.ReadInt32();
607 | int submeshCount = binStream.ReadInt32();
608 |
609 | M3Gobj.appearance = new int[submeshCount];
610 | List IndexBuffers = new List();
611 | int totalIndexCount = 0;
612 | //don't collect material IDs, just write them directly when writing index arrays
613 | for (int s = 0; s < submeshCount; s++)
614 | {
615 | int indexBuffer = binStream.ReadInt32();
616 | if (version == 1) { int appearance = binStream.ReadInt32(); } //untested
617 | long nextSubmesh = M3Gstream.Position;
618 |
619 | M3Gstream.Position = ObjectList[indexBuffer].offset;
620 | bool isTriStrip = false;
621 |
622 | switch (ObjectList[indexBuffer].type)
623 | {
624 | case 11: //TriangleStripArray, but usually a list
625 | {
626 | M3Gstream.Position += 20;
627 | isTriStrip = binStream.ReadBoolean();
628 | break;
629 | }
630 | case 100:
631 | case 103:
632 | {
633 | M3Gstream.Position += 12;
634 | int realIndexBuffer = binStream.ReadInt32(); //type 101
635 | int appearance = binStream.ReadInt32();
636 | M3Gobj.appearance[s] = appearance;
637 |
638 | M3Gstream.Position = ObjectList[realIndexBuffer].offset + 12;
639 | break;
640 | }
641 | default:
642 | {
643 | Console.WriteLine("Unknown Index Buffer; conversion stopped.");
644 | return 0;
645 | }
646 | }
647 |
648 | #region read indices
649 | byte encoding = binStream.ReadByte();
650 | int[] iarr = new int[0];
651 | switch (encoding)
652 | {
653 | case 0:
654 | {
655 | int startindex = binStream.ReadInt32();
656 |
657 | int stripLengthsCount = binStream.ReadInt32();
658 | int[] stripLengths = new int[stripLengthsCount];
659 | for (int l = 0; l < stripLengthsCount; l++)
660 | {
661 | stripLengths[l] = binStream.ReadInt32();
662 | totalIndexCount += stripLengths[l];
663 | }
664 | break;
665 | }
666 | case 1:
667 | {
668 | byte startindex = binStream.ReadByte();
669 |
670 | int stripLengthsCount = binStream.ReadInt32();
671 | int[] stripLengths = new int[stripLengthsCount];
672 | for (int l = 0; l < stripLengthsCount; l++)
673 | {
674 | stripLengths[l] = binStream.ReadInt32();
675 | totalIndexCount += stripLengths[l];
676 | }
677 | break;
678 | }
679 | case 2:
680 | {
681 | ushort startindex = binStream.ReadUInt16();
682 |
683 | int stripLengthsCount = binStream.ReadInt32();
684 | int[] stripLengths = new int[stripLengthsCount];
685 | for (int l = 0; l < stripLengthsCount; l++)
686 | {
687 | stripLengths[l] = binStream.ReadInt32();
688 | totalIndexCount += stripLengths[l];
689 | }
690 | break;
691 | }
692 | case 128:
693 | {
694 | int indexCount = binStream.ReadInt32();
695 | totalIndexCount += indexCount;
696 | iarr = new int[indexCount];
697 | for (int i = 0; i < indexCount; i++)
698 | {
699 | iarr[i] = binStream.ReadInt32();
700 | }
701 | break;
702 | }
703 | case 129:
704 | {
705 | int indexCount = binStream.ReadInt32();
706 | totalIndexCount += indexCount;
707 | iarr = new int[indexCount];
708 | for (int i = 0; i < indexCount; i++)
709 | {
710 | iarr[i] = binStream.ReadByte();
711 | }
712 | break;
713 | }
714 | case 130:
715 | {
716 | int indexCount = binStream.ReadInt32();
717 | totalIndexCount += indexCount;
718 | iarr = new int[indexCount];
719 | for (int i = 0; i < indexCount; i++)
720 | {
721 | iarr[i] = binStream.ReadUInt16();
722 | }
723 | break;
724 | }
725 | }
726 |
727 | if (isTriStrip)
728 | {
729 | List farr = new List();
730 | for (int i = 0; i < iarr.Length - 2; i++)
731 | {
732 | int fa = iarr[i];
733 | int fb = iarr[i+1];
734 | int fc = iarr[i+2];
735 | if ((fa != fb) && (fa != fc) && (fb != fc))
736 | {
737 | farr.Add(fa);
738 | if (i % 2 == 0)
739 | {
740 | farr.Add(fb);
741 | farr.Add(fc);
742 | }
743 | else
744 | {
745 | farr.Add(fc);
746 | farr.Add(fb);
747 | }
748 | }
749 | }
750 | totalIndexCount += farr.Count - iarr.Length;
751 | IndexBuffers.Add(farr.ToArray());
752 | }
753 | else { IndexBuffers.Add(iarr); }
754 |
755 | #endregion
756 |
757 | M3Gstream.Position = nextSubmesh;
758 | }
759 |
760 | #region read VertexBuffer
761 | M3Gstream.Position = ObjectList[vertexBuffer].offset;
762 | M3Gstream.Position += 12;
763 | byte[] ColorRGBA = new byte[4] { binStream.ReadByte(), binStream.ReadByte(), binStream.ReadByte(), binStream.ReadByte() };
764 | int positions = binStream.ReadInt32();
765 | float[] positionBias = new float[3] { binStream.ReadSingle(), binStream.ReadSingle(), binStream.ReadSingle() };
766 | float positionScale = binStream.ReadSingle();
767 | int normals = binStream.ReadInt32();
768 | int colors = binStream.ReadInt32();
769 |
770 | int texcoordArrayCount = binStream.ReadInt32();
771 | texcoordArrayCount = Math.Abs(texcoordArrayCount); //there must be soemthing special about negative numbers. but what?
772 | List texcoordArrays = new List();
773 | for (int t = 0; t < texcoordArrayCount; t++)
774 | {
775 | int texCoords = binStream.ReadInt32();
776 | float[] texCoordBias = new float[3] { binStream.ReadSingle(), (1 - binStream.ReadSingle()), binStream.ReadSingle() };
777 | float texCoordScale = binStream.ReadSingle();
778 | if (version == 1) { texCoordScale /= 4; } //WTF?? RR3
779 | long nextTexcoords = M3Gstream.Position;
780 |
781 | if (texCoords > 0)
782 | {
783 | M3Gstream.Position = ObjectList[texCoords].offset + 12;
784 | texcoordArrays.Add(ReadVertexArray(binStream, texCoordBias, new float[3] {texCoordScale, -texCoordScale, texCoordScale}, false));
785 | }
786 |
787 | M3Gstream.Position = nextTexcoords;
788 | }
789 | int tangents = binStream.ReadInt32();
790 | int binormals = binStream.ReadInt32();
791 | #endregion
792 |
793 | #region write Geometry
794 | ob.Append("\n\tGeometry: " + (100000 + objIndex) + ", \"Geometry::\", \"Mesh\" {");
795 | ob.Append("\n\t\tProperties70: {");
796 | var randomColor = RandomColorGenerator((100000 + objIndex).ToString());
797 | ob.Append("\n\t\t\tP: \"Color\", \"ColorRGB\", \"Color\", \"\"," + ((float)randomColor[0] / 255) + "," + ((float)randomColor[1] / 255) + "," + ((float)randomColor[2] / 255));
798 | ob.Append("\n\t\t}");
799 | if (positions > 0)
800 | {
801 | M3Gstream.Position = ObjectList[positions].offset + 12;
802 | var vertexPositions = ReadVertexArray(binStream, positionBias, new float[3] { positionScale, positionScale, positionScale }, false);
803 |
804 | ob.Append("\n\t\tVertices: *");
805 | ob.Append(vertexPositions);
806 | ob.Append("\n\t\t}");
807 | }
808 |
809 | StringBuilder ib = new StringBuilder();
810 | StringBuilder mb = new StringBuilder();
811 | ob.Append("\n\t\tPolygonVertexIndex: *" + totalIndexCount.ToString() + " {\n\t\t\ta: ");
812 | for (int i = 0; i < IndexBuffers.Count; i++)
813 | {
814 | var iarr = IndexBuffers[i];
815 | for (int f = 0; f < (iarr.Length / 3); f++)
816 | {
817 | ib.Append(iarr[f * 3]);
818 | ib.Append(',');
819 | ib.Append(iarr[f * 3 + 1]);
820 | ib.Append(',');
821 | ib.Append(-iarr[f * 3 + 2] - 1);
822 | ib.Append(',');
823 | mb.Append(i + ',' + i + ',' + i + ',');//shouldn't this be just one index per face?
824 | }
825 | }
826 | ib.Length -= 1; //remove last ,
827 | mb.Length -= 1;
828 | ob.Append(SplitLine(ib.ToString()));
829 | ob.Append("\n\t\t}");
830 | ob.Append("\n\t\tGeometryVersion: 124");
831 | ib.Length = 0;
832 | mb.Length = 0; //why?
833 | //verify
834 | if (normals > 0)
835 | {
836 | M3Gstream.Position = ObjectList[normals].offset + 12;
837 | var vertexNormals = ReadVertexArray(binStream, new float[3] { 0, 0, 0 }, new float[3] {1, 1, 1}, true);
838 |
839 | ob.Append("\n\t\tLayerElementNormal: 0 {");
840 | ob.Append("\n\t\t\tVersion: 101");
841 | ob.Append("\n\t\t\tName: \"\"");
842 | ob.Append("\n\t\t\tMappingInformationType: \"ByVertice\"");
843 | ob.Append("\n\t\t\tReferenceInformationType: \"Direct\"");
844 | ob.Append("\n\t\t\tNormals: *");
845 | ob.Append(vertexNormals);
846 | ob.Append("\n\t\t\t}\n\t\t}");
847 | }
848 |
849 |
850 | if (colors > 0)
851 | {
852 | M3Gstream.Position = ObjectList[colors].offset + 12;
853 | var vertexColors = ReadVertexArray(binStream, new float[3] { 0, 0, 0 }, new float[3] { 1, 1, 1 }, true);
854 |
855 | ob.Append("\n\t\tLayerElementColor: 0 {");
856 | ob.Append("\n\t\t\tVersion: 101");
857 | ob.Append("\n\t\t\tName: \"\"");
858 | ob.Append("\n\t\t\tMappingInformationType: \"ByVertice\"");
859 | ob.Append("\n\t\t\tReferenceInformationType: \"Direct\"");
860 | ob.Append("\n\t\t\tColors: *");
861 | ob.Append(vertexColors);
862 | ob.Append("\n\t\t\t}\n\t\t}");
863 | }
864 |
865 | for (int t = 0; t < texcoordArrays.Count; t++)
866 | {
867 | ob.Append("\n\t\tLayerElementUV: " + t + " {");
868 | ob.Append("\n\t\t\tVersion: 101");
869 | ob.Append("\n\t\t\tName: \"UVChannel_" + t + "\"");
870 | ob.Append("\n\t\t\tMappingInformationType: \"ByVertice\"");
871 | ob.Append("\n\t\t\tReferenceInformationType: \"Direct\"");
872 | ob.Append("\n\t\t\tUV: *");
873 | ob.Append(texcoordArrays[t]);
874 | ob.Append("\n\t\t\t}\n\t\t}");
875 | }
876 |
877 | ob.Append("\n\t\tLayerElementMaterial: 0 {");
878 | ob.Append("\n\t\t\tVersion: 101");
879 | ob.Append("\n\t\t\tName: \"\"");
880 | ob.Append("\n\t\t\tMappingInformationType: \"");
881 | if (IndexBuffers.Count == 1) { ob.Append("AllSame\""); }
882 | else { ob.Append("ByPolygon\""); }
883 | ob.Append("\n\t\t\tReferenceInformationType: \"IndexToDirect\"");
884 | ob.Append("\n\t\t\tMaterials: *" + IndexBuffers.Count + " {");
885 | //no, this is supposed to be index count
886 | ob.Append("\n\t\t\t\t");
887 | if (IndexBuffers.Count == 1) { ob.Append("0"); }
888 | else { ob.Append(SplitLine(mb.ToString())); }
889 | ob.Append("\n\t\t\t}");
890 | ob.Append("\n\t\t}");
891 |
892 | ob.Append("\n\t\tLayer: 0 {");
893 | ob.Append("\n\t\t\tVersion: 100");
894 | if (normals > 0)
895 | {
896 | ob.Append("\n\t\t\tLayerElement: {");
897 | ob.Append("\n\t\t\t\tType: \"LayerElementNormal\"");
898 | ob.Append("\n\t\t\t\tTypedIndex: 0");
899 | ob.Append("\n\t\t\t}");
900 | }
901 |
902 | ob.Append("\n\t\t\tLayerElement: {");
903 | ob.Append("\n\t\t\t\tType: \"LayerElementMaterial\"");
904 | ob.Append("\n\t\t\t\tTypedIndex: 0");
905 | ob.Append("\n\t\t\t}");
906 | ob.Append("\n\t\t\tLayerElement: {");
907 | ob.Append("\n\t\t\t\tType: \"LayerElementTexture\"");
908 | ob.Append("\n\t\t\t\tTypedIndex: 0");
909 | ob.Append("\n\t\t\t}");
910 | ob.Append("\n\t\t\tLayerElement: {");
911 | ob.Append("\n\t\t\t\tType: \"LayerElementBumpTextures\"");
912 | ob.Append("\n\t\t\t\tTypedIndex: 0");
913 | ob.Append("\n\t\t\t}");
914 | if (colors > 0)
915 | {
916 | ob.Append("\n\t\t\tLayerElement: {");
917 | ob.Append("\n\t\t\t\tType: \"LayerElementColor\"");
918 | ob.Append("\n\t\t\t\tTypedIndex: 0");
919 | ob.Append("\n\t\t\t}");
920 | }
921 | if (texcoordArrays.Count > 0)
922 | {
923 | ob.Append("\n\t\t\tLayerElement: {");
924 | ob.Append("\n\t\t\t\tType: \"LayerElementUV\"");
925 | ob.Append("\n\t\t\t\tTypedIndex: 0");
926 | ob.Append("\n\t\t\t}");
927 | }
928 | ob.Append("\n\t\t}"); //Layer 0 end
929 |
930 | for (int t = 1; t < texcoordArrays.Count; t++)
931 | {
932 | ob.Append("\n\t\tLayer: " + t + " {");
933 | ob.Append("\n\t\t\tVersion: 100");
934 | ob.Append("\n\t\t\tLayerElement: {");
935 | ob.Append("\n\t\t\t\tType: \"LayerElementUV\"");
936 | ob.Append("\n\t\t\t\tTypedIndex: " + t);
937 | ob.Append("\n\t\t\t}");
938 | ob.Append("\n\t\t}"); //Layer end
939 | }
940 |
941 | ob.Append("\n\t}"); //Geometry end
942 | #endregion
943 |
944 | sw.Write(ob.ToString());
945 | ob.Length = 0;
946 | break;
947 | }
948 | #endregion
949 | #region Group
950 | case 9: //Group
951 | {
952 | FBXmodelCount++;
953 | M3Gstream.Position += 4; //could be AnimationControllers
954 |
955 | int AnimationTracks = binStream.ReadInt32();
956 | M3Gstream.Position += AnimationTracks * 4; //ObjectIndex
957 |
958 | string groupName = "";
959 | int userParameterCount = binStream.ReadInt32();
960 | for (int i = 0; i < userParameterCount; i++)
961 | {
962 | int parameterID = binStream.ReadInt32();
963 | int parameterValueLength = binStream.ReadInt32();
964 | long nextParam = M3Gstream.Position + parameterValueLength;
965 |
966 | switch (parameterID)
967 | {
968 | case 0: //group name
969 | {
970 | groupName = ReadStr(binStream, parameterValueLength);
971 | break;
972 | }
973 | case 2:
974 | {
975 | string paramType = ReadStr(binStream, binStream.ReadByte());
976 | string paramName = ReadStr(binStream, binStream.ReadInt32());
977 | break;
978 | }
979 | default:
980 | {
981 | M3Gstream.Position += parameterValueLength;
982 | break;
983 | }
984 | }
985 | M3Gstream.Position = nextParam; //failsafe
986 | }
987 |
988 | float[] translation = new float[3];
989 | float[] scale = new float[3];
990 | float[] EulerAngles = new float[3];
991 | bool hasComponentTransform = binStream.ReadBoolean();
992 | if (hasComponentTransform)
993 | {
994 | translation[0] = binStream.ReadSingle();
995 | translation[1] = binStream.ReadSingle();
996 | translation[2] = binStream.ReadSingle();
997 | scale[0] = binStream.ReadSingle();
998 | scale[1] = binStream.ReadSingle();
999 | scale[2] = binStream.ReadSingle();
1000 | float orientationAngle = binStream.ReadSingle();
1001 | float orientationAxisX = binStream.ReadSingle();
1002 | float orientationAxisY = binStream.ReadSingle();
1003 | float orientationAxisZ = binStream.ReadSingle();
1004 | EulerAngles = AngleAxisToEuler(orientationAngle, orientationAxisX, orientationAxisY, orientationAxisZ);
1005 | }
1006 |
1007 | bool hasGeneralTransform = binStream.ReadBoolean();
1008 | if (hasGeneralTransform) //untested
1009 | {
1010 | M3Gstream.Position += 16 * 4;
1011 | }
1012 |
1013 | M3Gstream.Position += 7;
1014 | bool hasSomething = binStream.ReadBoolean();
1015 | if (hasSomething)
1016 | {
1017 | short count = binStream.ReadInt16();
1018 | M3Gstream.Position += count * 16; //some sort of indices
1019 | short count2 = binStream.ReadInt16();
1020 | for (int i = 0; i < count2; i++)
1021 | {
1022 | string name = ReadStr(binStream, binStream.ReadInt16());
1023 | M3Gstream.Position += 9;
1024 | }
1025 | }
1026 |
1027 | int childrenCount = binStream.ReadInt32();
1028 | int[] childArr = new int[childrenCount];
1029 | for (int i = 0; i < childrenCount; i++) //only collect indices for further testing
1030 | {
1031 | int childIndex = binStream.ReadInt32();
1032 | childArr[i] = childIndex;
1033 | }
1034 |
1035 | if (childrenCount == 1 && ObjectList[childArr[0]].type == 14) //single Mesh, do not create a useless dummy node
1036 | {
1037 | cb.Append("\n\tC: \"OO\"," + (100000 + childArr[0]) + "," + (200000 + objIndex)); //connect Geometry to Mesh
1038 | ObjectList[childArr[0]].connected = true;
1039 |
1040 | //connect Materials to future Model
1041 | foreach (var appearance in ObjectList[childArr[0]].appearance)
1042 | {
1043 | cb.Append("\n\tC: \"OO\"," + (400000 + appearance) + "," + (200000 + objIndex));
1044 | }
1045 |
1046 | if (makeUnique) { groupName = objIndex.ToString() + "_" + groupName; }
1047 | ob.Append("\n\tModel: " + (200000 + objIndex) + ", \"Model::" + groupName + "\", \"Mesh\" {");
1048 | }
1049 | else
1050 | {
1051 | foreach (var childIndex in childArr)
1052 | {
1053 | if (ObjectList[childIndex].type == 14)
1054 | {
1055 | FBXmodelCount++;
1056 | //create Mesh object
1057 | string meshName = childIndex.ToString() + "_" + groupName;
1058 | ob.Append("\n\tModel: " + (200000 + childIndex) + ", \"Model::" + meshName + "\", \"Mesh\" {");
1059 | ob.Append("\n\t\tVersion: 232");
1060 | ob.Append("\n\t\tProperties70: {");
1061 | ob.Append("\n\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
1062 | ob.Append("\n\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
1063 | ob.Append("\n\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
1064 | //ob.Append("\n\t\t\tP: \"UDP3DSMAX\", \"KString\", \"\", \"U\", \"MapChannel:1 = UVChannel_1&cr;&lf;MapChannel:2 = UVChannel_2&cr;&lf;\"");
1065 | //ob.Append("\n\t\t\tP: \"MaxHandle\", \"int\", \"Integer\", \"UH\"," + (j + 2 + pmodel.nodeList.Count));
1066 | ob.Append("\n\t\t}");
1067 | ob.Append("\n\t\tShading: T");
1068 | ob.Append("\n\t\tCulling: \"CullingOff\"");
1069 | ob.Append("\n\t}"); //Model end
1070 |
1071 | //connect Geometry to Model
1072 | cb.Append("\n\tC: \"OO\"," + (100000 + childIndex) + "," + (200000 + childIndex));
1073 |
1074 | //connect Materials to Model
1075 | foreach (var appearance in ObjectList[childIndex].appearance)
1076 | {
1077 | cb.Append("\n\tC: \"OO\"," + (400000 + appearance) + "," + (200000 + objIndex));
1078 | }
1079 |
1080 | //connect Model to parent
1081 | cb.Append("\n\tC: \"OO\"," + (200000 + childIndex) + "," + (200000 + objIndex));
1082 | ObjectList[childIndex].connected = true;
1083 | }
1084 | else if (ObjectList[childIndex].type == 9)
1085 | {
1086 | //connect child Model to parent Model
1087 | cb.Append("\n\tC: \"OO\"," + (200000 + childIndex) + "," + (200000 + objIndex));
1088 | ObjectList[childIndex].connected = true;
1089 | }
1090 | }
1091 |
1092 | ob.Append("\n\tModel: " + (200000 + objIndex) + ", \"Model::" + groupName + "\", \"Null\" {");
1093 | }
1094 |
1095 | //finish writing Model, be it Mesh or Null
1096 | ob.Append("\n\t\tVersion: 232");
1097 | ob.Append("\n\t\tProperties70: {");
1098 | ob.Append("\n\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
1099 | ob.Append("\n\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
1100 | ob.Append("\n\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
1101 | if (hasComponentTransform)
1102 | {
1103 | ob.Append("\n\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A\"," + translation[0] + "," + translation[1] + "," + translation[2]);
1104 | ob.Append("\n\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A\"," + EulerAngles[0] + "," + EulerAngles[1] + "," + EulerAngles[2]);
1105 | ob.Append("\n\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\"," + scale[0] + "," + scale[1] + "," + scale[2]);
1106 | }
1107 | //ob.Append("\n\t\t\tP: \"UDP3DSMAX\", \"KString\", \"\", \"U\", \"MapChannel:1 = UVChannel_1&cr;&lf;MapChannel:2 = UVChannel_2&cr;&lf;\"");
1108 | //ob.Append("\n\t\t\tP: \"MaxHandle\", \"int\", \"Integer\", \"UH\"," + (j + 2 + pmodel.nodeList.Count));
1109 | ob.Append("\n\t\t}");
1110 | ob.Append("\n\t\tShading: T");
1111 | ob.Append("\n\t\tCulling: \"CullingOff\"");
1112 | ob.Append("\n\t}"); //Model end
1113 |
1114 | sw.Write(ob.ToString());
1115 | ob.Length = 0;
1116 |
1117 | break;
1118 | }
1119 | #endregion
1120 | case 24: //Material list in version 1
1121 | {
1122 | int materialCount = binStream.ReadInt32();
1123 | for (int m = 0; m < materialCount; m++)
1124 | {
1125 | string materialName = ReadStringToNull(binStream);
1126 | ob.Append("\n\tMaterial: " + (40000 + m) + ", \"Material::" + materialName + "\", \"\" {");
1127 | ob.Append("\n\t\tVersion: 102");
1128 | ob.Append("\n\t\tShadingModel: \"phong\"");
1129 | ob.Append("\n\t\tMultiLayer: 0");
1130 | ob.Append("\n\t\tProperties70: {");
1131 | ob.Append("\n\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"phong\"");
1132 | ob.Append("\n\t\t\tP: \"AmbientColor\", \"Color\", \"\", \"A\",0.7,0.7,0.7");
1133 | ob.Append("\n\t\t\tP: \"DiffuseColor\", \"Color\", \"\", \"A\",0.7,0.7,0.7");
1134 | ob.Append("\n\t\t\tP: \"SpecularColor\", \"Color\", \"\", \"A\",0.8,0.8,0.8");
1135 | ob.Append("\n\t\t\tP: \"SpecularFactor\", \"Number\", \"\", \"A\",0");
1136 | ob.Append("\n\t\t\tP: \"ShininessExponent\", \"Number\", \"\", \"A\",2");
1137 | ob.Append("\n\t\t}");
1138 | ob.Append("\n\t}");
1139 | }
1140 | break;
1141 | }
1142 | default:
1143 | {
1144 | M3Gstream.Position += M3Gobj.length;
1145 | break;
1146 | }
1147 | }
1148 |
1149 | M3Gstream.Position = nextObject; //failsafe
1150 | }
1151 | }
1152 | else
1153 | {
1154 | //this will break object order if not decompressed
1155 | M3Gstream.Position = sectionEnd;
1156 | Console.WriteLine("Compressed section found; conversion stopped.");
1157 | return 0;
1158 | }
1159 |
1160 | int checksum = binStream.ReadInt32();
1161 | }
1162 |
1163 | //leave no one behind
1164 | for (int i = 1; i < ObjectList.Count; i++)
1165 | {
1166 | var M3Gobj = ObjectList[i];
1167 | switch (M3Gobj.type)
1168 | {
1169 | case 9:
1170 | {
1171 | if (!M3Gobj.connected) //connect root node to scene
1172 | {
1173 | cb.Append("\n\tC: \"OO\"," + (200000 + i) + ",0");
1174 | M3Gobj.connected = true;
1175 | }
1176 | break;
1177 | }
1178 | case 14:
1179 | {
1180 | if (!M3Gobj.connected)
1181 | {
1182 | FBXmodelCount++;
1183 | //create Model object
1184 | ob.Append("\n\tModel: " + (200000 + i) + ", \"Model::" + i.ToString() + "\", \"Mesh\" {");
1185 | ob.Append("\n\t\tVersion: 232");
1186 | ob.Append("\n\t\tProperties70: {");
1187 | ob.Append("\n\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
1188 | ob.Append("\n\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
1189 | ob.Append("\n\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
1190 | //ob.Append("\n\t\t\tP: \"UDP3DSMAX\", \"KString\", \"\", \"U\", \"MapChannel:1 = UVChannel_1&cr;&lf;MapChannel:2 = UVChannel_2&cr;&lf;\"");
1191 | //ob.Append("\n\t\t\tP: \"MaxHandle\", \"int\", \"Integer\", \"UH\"," + (j + 2 + pmodel.nodeList.Count));
1192 | ob.Append("\n\t\t}");
1193 | ob.Append("\n\t\tShading: T");
1194 | ob.Append("\n\t\tCulling: \"CullingOff\"");
1195 | ob.Append("\n\t}"); //Model end
1196 |
1197 | //connect Geometry to Model
1198 | cb.Append("\n\tC: \"OO\"," + (100000 + i) + "," + (200000 + i));
1199 |
1200 | //connect Materials to Model
1201 | foreach (var appearance in M3Gobj.appearance)
1202 | {
1203 | cb.Append("\n\tC: \"OO\"," + (400000 + appearance) + "," + (200000 + i));
1204 | }
1205 |
1206 | //connect Model to scene
1207 | cb.Append("\n\tC: \"OO\"," + (200000 + i) + ",0"); //connect child to parent
1208 | M3Gobj.connected = true;
1209 | }
1210 | break;
1211 | }
1212 | }
1213 | }
1214 | cb.Append("\n}"); //Connections end
1215 |
1216 | sw.Write(ob.ToString());
1217 | sw.Write(cb.ToString());
1218 | }
1219 |
1220 | return 1;
1221 | }
1222 |
1223 | private static string ReadVertexArray(BinaryReader str, float[] bias, float[] scale, bool normalize)
1224 | {
1225 | StringBuilder result = new StringBuilder();
1226 | byte componentSize = str.ReadByte();
1227 | byte componentCount = str.ReadByte();
1228 | byte encoding = str.ReadByte();
1229 | ushort vertexCount = str.ReadUInt16();
1230 | //float[] VertexArray = new float[vertexCount * componentCount];
1231 |
1232 | switch (componentSize)
1233 | {
1234 | case 1:
1235 | {
1236 | if (normalize)
1237 | {
1238 | scale[0] /= 127;
1239 | scale[1] /= 127;
1240 | scale[2] /= 127;
1241 | }
1242 | if (encoding == 0)
1243 | {
1244 | if (componentCount == 4) //colors
1245 | {
1246 | for (int v = 0; v < vertexCount * 4; v++)
1247 | {
1248 | //VertexArray[v] = (float)str.ReadByte() / 255;
1249 | result.Append((float)str.ReadByte() / 255);
1250 | result.Append(',');
1251 | }
1252 | }
1253 | else
1254 | {
1255 | for (int v = 0; v < vertexCount; v++)
1256 | {
1257 | for (int c = 0; c < componentCount; c++)
1258 | {
1259 | //VertexArray[v * componentCount + c] = (float)str.ReadSByte() * scale[c] + bias[c];
1260 | result.Append((float)str.ReadSByte() * scale[c] + bias[c]);
1261 | result.Append(',');
1262 | }
1263 | }
1264 | }
1265 | }
1266 | break;
1267 | }
1268 | case 2:
1269 | {
1270 | if (normalize)
1271 | {
1272 | scale[0] /= 4096;
1273 | scale[1] /= 4096;
1274 | scale[2] /= 4096;
1275 | }
1276 | if (encoding == 0)
1277 | {
1278 | for (int v = 0; v < vertexCount; v++)
1279 | {
1280 | for (int c = 0; c < componentCount; c++)
1281 | {
1282 | //VertexArray[v * componentCount + c] = (float)str.ReadInt16() * scale[c] + bias[c];
1283 | result.Append((float)str.ReadInt16() * scale[c] + bias[c]);
1284 | result.Append(',');
1285 | }
1286 | }
1287 | }
1288 | break;
1289 | }
1290 | case 4:
1291 | {
1292 | if (encoding == 0)
1293 | {
1294 | for (int v = 0; v < vertexCount; v++)
1295 | {
1296 | for (int c = 0; c < componentCount; c++)
1297 | {
1298 | //VertexArray[v * componentCount + c] = str.ReadSingle() * scale + bias[c];
1299 | result.Append(str.ReadSingle() * scale[c] + bias[c]);
1300 | result.Append(',');
1301 | }
1302 | }
1303 | }
1304 | break;
1305 | }
1306 | }
1307 |
1308 | //return VertexArray;
1309 | result.Length -= 1; //remove last ,
1310 | return ((vertexCount * componentCount) + " {\n\t\t\ta: " + SplitLine(result.ToString()));
1311 | }
1312 |
1313 | /*private static string ConvertImage(string sbaFile)
1314 | {
1315 | using (BinaryReader sba = new BinaryReader(File.OpenRead(sbaFile)))
1316 | {
1317 | long sbaSize = sba.BaseStream.Length;
1318 | sba.BaseStream.Position = 8;
1319 |
1320 | byte[] imageHeader = new byte[0];
1321 | byte[] imageData = new byte[0];
1322 |
1323 | while (sba.BaseStream.Position < sbaSize)
1324 | {
1325 | string head = ReadStr(sba, 4);
1326 | int length = sba.ReadInt32();
1327 | int checksum = sba.ReadInt32();
1328 |
1329 | switch (head)
1330 | {
1331 | case "CHDR": //string offsets adn lengths in CDAT
1332 | {
1333 | for (int i = 0; i < length / 8; i++)
1334 | {
1335 | int stringOffset = sba.ReadInt32();
1336 | int stringLength = sba.ReadInt32();
1337 | }
1338 | break;
1339 | }
1340 | case "BARG":
1341 | {
1342 | //imageData = new byte[length];
1343 | imageData = sba.ReadBytes(length);
1344 | break;
1345 | }
1346 | default:
1347 | {
1348 | sba.BaseStream.Position += length;
1349 | break;
1350 | }
1351 | }
1352 |
1353 | if ((sba.BaseStream.Position % 4) != 0) { sba.BaseStream.Position += 4 - (sba.BaseStream.Position % 4); }
1354 | }
1355 | }
1356 | }*/
1357 |
1358 | private static string ReadStr(BinaryReader str, int len)
1359 | {
1360 | //int len = str.ReadInt32();
1361 | byte[] stringData = new byte[len];
1362 | str.Read(stringData, 0, len);
1363 | var result = System.Text.Encoding.UTF8.GetString(stringData);
1364 | return result;
1365 | }
1366 |
1367 | public static string ReadStringToNull(BinaryReader str)
1368 | {
1369 | string result = "";
1370 | char c;
1371 | for (int i = 0; i < str.BaseStream.Length; i++)
1372 | {
1373 | if ((c = (char)str.ReadByte()) == 0)
1374 | {
1375 | break;
1376 | }
1377 | result += c.ToString();
1378 | }
1379 | return result;
1380 | }
1381 |
1382 | public static float[] AngleAxisToEuler(double angle, float x, float y, float z) //every god damn time
1383 | {
1384 | angle *= Math.PI / 180;
1385 | double heading = 0;
1386 | double attitude = 0;
1387 | double bank = 0;
1388 |
1389 | double s = Math.Sin(angle);
1390 | double c = Math.Cos(angle);
1391 | double t = 1 - c;
1392 | // if axis is not already normalised then uncomment this
1393 | // double magnitude = Math.sqrt(x*x + y*y + z*z);
1394 | // if (magnitude==0) throw error;
1395 | // x /= magnitude;
1396 | // y /= magnitude;
1397 | // z /= magnitude;
1398 | if ((x * y * t + z * s) > 0.998)
1399 | { // north pole singularity detected
1400 | heading = 2 * Math.Atan2(x * Math.Sin(angle / 2), Math.Cos(angle / 2));
1401 | attitude = Math.PI / 2;
1402 | bank = 0;
1403 | }
1404 | else if ((x * y * t + z * s) < -0.998)
1405 | { // south pole singularity detected
1406 | heading = -2 * Math.Atan2(x * Math.Sin(angle / 2), Math.Cos(angle / 2));
1407 | attitude = -Math.PI / 2;
1408 | bank = 0;
1409 | }
1410 | else
1411 | {
1412 | heading = Math.Atan2(y * s - x * z * t, 1 - (y * y + z * z) * t);
1413 | attitude = Math.Asin(x * y * t + z * s);
1414 | bank = Math.Atan2(x * s - y * z * t, 1 - (x * x + z * z) * t);
1415 | }
1416 |
1417 | heading *= 180 / Math.PI;
1418 | attitude *= 180 / Math.PI;
1419 | bank *= 180 / Math.PI;
1420 |
1421 | return new float[3] { (float)bank, (float)heading, -(float)attitude };
1422 |
1423 | //thank you Martin John Baker!
1424 | }
1425 |
1426 | private static byte[] RandomColorGenerator(string name)
1427 | {
1428 | int nameHash = name.GetHashCode();
1429 | Random r = new Random(nameHash);
1430 | //Random r = new Random(DateTime.Now.Millisecond);
1431 |
1432 | byte red = (byte)r.Next(0, 255);
1433 | byte green = (byte)r.Next(0, 255);
1434 | byte blue = (byte)r.Next(0, 255);
1435 |
1436 | return new byte[3] { red, green, blue };
1437 | }
1438 |
1439 | private static string MakeRelative(string filePath, string referencePath)
1440 | {
1441 | if (filePath != "" && referencePath != "")
1442 | {
1443 | var fileUri = new Uri(filePath);
1444 | var referenceUri = new Uri(referencePath);
1445 | return referenceUri.MakeRelativeUri(fileUri).ToString().Replace('/', Path.DirectorySeparatorChar);
1446 | }
1447 | else
1448 | {
1449 | return "";
1450 | }
1451 | }
1452 |
1453 | private static string SplitLine(string inputLine) //for FBX 2011
1454 | {
1455 | string outputLines = inputLine;
1456 | int vbSplit = 0;
1457 | for (int v = 0; v < inputLine.Length / 2048; v++)
1458 | {
1459 | vbSplit += 2048;
1460 | if (vbSplit < outputLines.Length)
1461 | {
1462 | vbSplit = outputLines.IndexOf(",", vbSplit) + 1;
1463 | if (vbSplit > 0) { outputLines = outputLines.Insert(vbSplit, "\n"); }
1464 | }
1465 | }
1466 | return outputLines;
1467 | }
1468 | }
1469 | }
1470 |
--------------------------------------------------------------------------------