├── 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 | --------------------------------------------------------------------------------