/// <summary> /// Saves changes made to the currently loaded stage. /// </summary> /// <param name="autoCloseDialog">Defines whether or not the progress dialog should close on completion.</param> private void SaveStage(bool autoCloseDialog) { if (!isStageLoaded) return; ProgressDialog progress = new ProgressDialog("Saving stage: " + levelName, 5, true, autoCloseDialog); progress.Show(this); Application.DoEvents(); IniLevelData level = ini.Levels[levelID]; string syspath = Path.Combine(Environment.CurrentDirectory, ini.SystemPath); string modpath = ini.ModPath; SA1LevelAct levelact = new SA1LevelAct(level.LevelID); progress.SetTaskAndStep("Saving:", "Geometry..."); if (LevelData.geo != null) { LevelData.geo.Tool = "SADXLVL2"; LevelData.geo.SaveToFile(level.LevelGeometry, LandTableFormat.SA1); } progress.StepProgress(); progress.Step = "Start positions..."; Application.DoEvents(); for (int i = 0; i < LevelData.StartPositions.Length; i++) { Dictionary<SA1LevelAct, SA1StartPosInfo> posini = SA1StartPosList.Load(ini.Characters[LevelData.Characters[i]].StartPositions); if (posini.ContainsKey(levelact)) posini.Remove(levelact); if (LevelData.StartPositions[i].Position.X != 0 || LevelData.StartPositions[i].Position.Y != 0 || LevelData.StartPositions[i].Position.Z != 0 || LevelData.StartPositions[i].Rotation.Y != 0) { posini.Add(levelact, new SA1StartPosInfo() { Position = LevelData.StartPositions[i].Position, YRotation = LevelData.StartPositions[i].Rotation.Y }); } posini.Save(ini.Characters[LevelData.Characters[i]].StartPositions); } progress.StepProgress(); progress.Step = "Death zones..."; Application.DoEvents(); if (LevelData.DeathZones != null) { DeathZoneFlags[] dzini = new DeathZoneFlags[LevelData.DeathZones.Count]; string path = Path.GetDirectoryName(level.DeathZones); for (int i = 0; i < LevelData.DeathZones.Count; i++) dzini[i] = LevelData.DeathZones[i].Save(path, i); dzini.Save(level.DeathZones); } progress.StepProgress(); #region Saving SET Items progress.Step = "SET items..."; Application.DoEvents(); if (LevelData.SETItems != null) { for (int i = 0; i < LevelData.SETItems.Length; i++) { string setstr = Path.Combine(syspath, "SET" + LevelData.SETName + LevelData.SETChars[i] + ".bin"); if (modpath != null) setstr = Path.Combine(modpath, setstr); // TODO: Consider simply blanking the SET file instead of deleting it. // Completely deleting it might be undesirable since Sonic's layout will be loaded // in place of the missing file. And where mods are concerned, you could have conflicts // with other mods if the file is deleted. if (File.Exists(setstr)) File.Delete(setstr); if (LevelData.SETItems[i].Count == 0) continue; List<byte> file = new List<byte>(LevelData.SETItems[i].Count*0x20 + 0x20); file.AddRange(BitConverter.GetBytes(LevelData.SETItems[i].Count)); file.Align(0x20); foreach (SETItem item in LevelData.SETItems[i]) file.AddRange(item.GetBytes()); File.WriteAllBytes(setstr, file.ToArray()); } } progress.StepProgress(); #endregion #region Saving CAM Items progress.Step = "CAM items..."; Application.DoEvents(); if (LevelData.CAMItems != null) { for (int i = 0; i < LevelData.CAMItems.Length; i++) { string camString = Path.Combine(syspath, "CAM" + LevelData.SETName + LevelData.SETChars[i] + ".bin"); if (modpath != null) camString = Path.Combine(modpath, camString); // TODO: Handle this differently. File stream? If the user is using a symbolic link for example, we defeat the purpose by deleting it. if (File.Exists(camString)) File.Delete(camString); if (LevelData.CAMItems[i].Count == 0) continue; List<byte> file = new List<byte>(LevelData.CAMItems[i].Count*0x40 + 0x40); // setting up file size and header file.AddRange(BitConverter.GetBytes(LevelData.CAMItems[i].Count)); file.Align(0x40); foreach (CAMItem item in LevelData.CAMItems[i]) // outputting individual components file.AddRange(item.GetBytes()); File.WriteAllBytes(camString, file.ToArray()); } } progress.StepProgress(); progress.SetTaskAndStep("Save complete!"); Application.DoEvents(); #endregion }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { #if !DEBUG try { #endif int steps = 10; if (d3ddevice == null) ++steps; toolStrip1.Enabled = false; // HACK: Fixes Twinkle Circuit's geometry lingering if loaded before Sky Chase. // I'm sure the real problem is somewhere below, but this is sort of an all around cleanup. if (isStageLoaded) LevelData.Clear(); isStageLoaded = false; using (ProgressDialog progress = new ProgressDialog("Loading stage: " + levelName, steps)) { IniLevelData level = ini.Levels[levelID]; string syspath = Path.Combine(Environment.CurrentDirectory, ini.SystemPath); string modpath = ini.ModPath; SA1LevelAct levelact = new SA1LevelAct(level.LevelID); LevelData.leveltexs = null; cam = new EditorCamera(EditorOptions.RenderDrawDistance); Invoke((Action<IWin32Window>)progress.Show, this); if (d3ddevice == null) { progress.SetTask("Initializing Direct3D..."); Invoke((Action)InitializeDirect3D); progress.StepProgress(); } progress.SetTaskAndStep("Loading level data:", "Geometry"); if (string.IsNullOrEmpty(level.LevelGeometry)) LevelData.geo = null; else { LevelData.geo = LandTable.LoadFromFile(level.LevelGeometry); LevelData.LevelItems = new List<LevelItem>(); for (int i = 0; i < LevelData.geo.COL.Count; i++) LevelData.LevelItems.Add(new LevelItem(LevelData.geo.COL[i], d3ddevice, i, selectedItems)); } progress.StepProgress(); progress.SetStep("Textures"); LevelData.TextureBitmaps = new Dictionary<string, BMPInfo[]>(); LevelData.Textures = new Dictionary<string, Texture[]>(); if (LevelData.geo != null && !string.IsNullOrEmpty(LevelData.geo.TextureFileName)) { BMPInfo[] TexBmps = TextureArchive.GetTextures(Path.Combine(syspath, LevelData.geo.TextureFileName) + ".PVM"); Texture[] texs = new Texture[TexBmps.Length]; for (int j = 0; j < TexBmps.Length; j++) texs[j] = new Texture(d3ddevice, TexBmps[j].Image, Usage.None, Pool.Managed); if (!LevelData.TextureBitmaps.ContainsKey(LevelData.geo.TextureFileName)) LevelData.TextureBitmaps.Add(LevelData.geo.TextureFileName, TexBmps); if (!LevelData.Textures.ContainsKey(LevelData.geo.TextureFileName)) LevelData.Textures.Add(LevelData.geo.TextureFileName, texs); LevelData.leveltexs = LevelData.geo.TextureFileName; } progress.StepProgress(); #region Start Positions progress.SetTaskAndStep("Setting up start positions..."); LevelData.StartPositions = new StartPosItem[LevelData.Characters.Length]; for (int i = 0; i < LevelData.StartPositions.Length; i++) { progress.SetStep(string.Format("{0}/{1}", (i + 1), LevelData.StartPositions.Length)); IniCharInfo character; if (i == 0 && levelact.Level == SA1LevelIDs.PerfectChaos) character = ini.Characters["SuperSonic"]; else character = ini.Characters[LevelData.Characters[i]]; Dictionary<SA1LevelAct, SA1StartPosInfo> posini = SA1StartPosList.Load(character.StartPositions); Vertex pos = new Vertex(); int rot = 0; if (posini.ContainsKey(levelact)) { pos = posini[levelact].Position; rot = posini[levelact].YRotation; } LevelData.StartPositions[i] = new StartPosItem(new ModelFile(character.Model).Model, character.Textures, character.Height, pos, rot, d3ddevice, selectedItems); LoadTextureList(character.TextureList, syspath); } progress.StepProgress(); #endregion #region Death Zones progress.SetTaskAndStep("Death Zones:", "Initializing..."); if (string.IsNullOrEmpty(level.DeathZones)) LevelData.DeathZones = null; else { LevelData.DeathZones = new List<DeathZoneItem>(); DeathZoneFlags[] dzini = DeathZoneFlagsList.Load(level.DeathZones); string path = Path.GetDirectoryName(level.DeathZones); for (int i = 0; i < dzini.Length; i++) { progress.SetStep(String.Format("Loading model {0}/{1}", (i + 1), dzini.Length)); LevelData.DeathZones.Add(new DeathZoneItem( new ModelFile(Path.Combine(path, i.ToString(System.Globalization.NumberFormatInfo.InvariantInfo) + ".sa1mdl")) .Model, dzini[i].Flags, d3ddevice, selectedItems)); } } progress.StepProgress(); #endregion #region Textures and Texture Lists progress.SetTaskAndStep("Loading textures for:"); progress.SetStep("Common objects"); // Loads common object textures (e.g OBJ_REGULAR) LoadTextureList(ini.ObjectTextureList, syspath); progress.SetTaskAndStep("Loading stage texture lists..."); // Loads the textures in the texture list for this stage (e.g BEACH01) foreach (string file in Directory.GetFiles(ini.LevelTextureLists)) { LevelTextureList texini = LevelTextureList.Load(file); if (texini.Level != levelact) continue; LoadTextureList(texini.TextureList, syspath); } progress.SetTaskAndStep("Loading textures for:", "Objects"); // Object texture list(s) LoadTextureList(level.ObjectTextureList, syspath); progress.SetStep("Stage"); // The stage textures... again? "Extra"? if (level.Textures != null && level.Textures.Length > 0) foreach (string tex in level.Textures) { LoadPVM(tex, syspath); if (string.IsNullOrEmpty(LevelData.leveltexs)) LevelData.leveltexs = tex; } progress.StepProgress(); #endregion #region Object Definitions / SET Layout progress.SetTaskAndStep("Loading Object Definitions:", "Parsing..."); LevelData.ObjDefs = new List<ObjectDefinition>(); Dictionary<string, ObjectData> objdefini = IniSerializer.Deserialize<Dictionary<string, ObjectData>>(ini.ObjectDefinitions); if (!string.IsNullOrEmpty(level.ObjectList) && File.Exists(level.ObjectList)) { List<ObjectData> objectErrors = new List<ObjectData>(); ObjectListEntry[] objlstini = ObjectList.Load(level.ObjectList, false); Directory.CreateDirectory("dllcache").Attributes |= FileAttributes.Hidden; for (int ID = 0; ID < objlstini.Length; ID++) { string codeaddr = objlstini[ID].CodeString; if (!objdefini.ContainsKey(codeaddr)) codeaddr = "0"; ObjectData defgroup = objdefini[codeaddr]; ObjectDefinition def; if (!string.IsNullOrEmpty(defgroup.CodeFile)) { progress.SetStep("Compiling: " + defgroup.CodeFile); // TODO: Split this out to a function #region Compile object code files string ty = defgroup.CodeType; string dllfile = Path.Combine("dllcache", ty + ".dll"); DateTime modDate = DateTime.MinValue; if (File.Exists(dllfile)) modDate = File.GetLastWriteTime(dllfile); string fp = defgroup.CodeFile.Replace('/', Path.DirectorySeparatorChar); if (modDate >= File.GetLastWriteTime(fp) && modDate > File.GetLastWriteTime(Application.ExecutablePath)) def = (ObjectDefinition) Activator.CreateInstance( Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, dllfile)) .GetType(ty)); else { string ext = Path.GetExtension(fp); CodeDomProvider pr = null; switch (ext.ToLowerInvariant()) { case ".cs": pr = new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; case ".vb": pr = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; } if (pr != null) { CompilerParameters para = new CompilerParameters(new string[] { "System.dll", "System.Core.dll", "System.Drawing.dll", Assembly.GetAssembly(typeof (Vector3)).Location, Assembly.GetAssembly(typeof (Texture)).Location, Assembly.GetAssembly(typeof (D3DX)).Location, Assembly.GetExecutingAssembly().Location, Assembly.GetAssembly(typeof (LandTable)).Location, Assembly.GetAssembly(typeof (EditorCamera)).Location, Assembly.GetAssembly(typeof (SA1LevelAct)).Location, Assembly.GetAssembly(typeof (ObjectDefinition)).Location }) { GenerateExecutable = false, GenerateInMemory = false, IncludeDebugInformation = true, OutputAssembly = Path.Combine(Environment.CurrentDirectory, dllfile) }; CompilerResults res = pr.CompileAssemblyFromFile(para, fp); if (res.Errors.HasErrors) { // TODO: Merge with existing object error handler. I add too many ToDos. string errors = null; foreach (CompilerError item in res.Errors) errors += String.Format("\n\n{0}, {1}: {2}", item.Line, item.Column, item.ErrorText); MessageBox.Show("Failed to compile object code file:\n" + defgroup.CodeFile + errors, "Object compilation failure", MessageBoxButtons.OK, MessageBoxIcon.Error); def = new DefaultObjectDefinition(); } else { def = (ObjectDefinition)Activator.CreateInstance(res.CompiledAssembly.GetType(ty)); } } else def = new DefaultObjectDefinition(); } #endregion } else { def = new DefaultObjectDefinition(); } LevelData.ObjDefs.Add(def); // The only reason .Model is checked for null is for objects that don't yet have any // models defined for them. It would be annoying seeing that error all the time! if (string.IsNullOrEmpty(defgroup.CodeFile) && !string.IsNullOrEmpty(defgroup.Model)) { progress.SetStep("Loading: " + defgroup.Model); // Otherwise, if the model file doesn't exist and/or no texture file is defined, // load the "default object" instead ("?"). if (!File.Exists(defgroup.Model) || string.IsNullOrEmpty(defgroup.Texture) || !LevelData.Textures.ContainsKey(defgroup.Texture)) { ObjectData error = new ObjectData { Name = defgroup.Name, Model = defgroup.Model, Texture = defgroup.Texture }; objectErrors.Add(error); defgroup.Model = null; } } def.Init(defgroup, objlstini[ID].Name, d3ddevice); def.SetInternalName(objlstini[ID].Name); } // Loading SET Layout progress.SetTaskAndStep("Loading SET items", "Initializing..."); if (LevelData.ObjDefs.Count > 0) { LevelData.SETName = level.SETName ?? level.LevelID; string setstr = Path.Combine(syspath, "SET" + LevelData.SETName + "{0}.bin"); LevelData.SETItems = new List<SETItem>[LevelData.SETChars.Length]; for (int i = 0; i < LevelData.SETChars.Length; i++) { List<SETItem> list = new List<SETItem>(); byte[] setfile = null; string formatted = string.Format(setstr, LevelData.SETChars[i]); if (modpath != null && File.Exists(Path.Combine(modpath, formatted))) setfile = File.ReadAllBytes(Path.Combine(modpath, formatted)); else if (File.Exists(formatted)) setfile = File.ReadAllBytes(formatted); if (setfile != null) { progress.SetTask("SET: " + formatted.Replace(Environment.CurrentDirectory, "")); int count = BitConverter.ToInt32(setfile, 0); int address = 0x20; for (int j = 0; j < count; j++) { progress.SetStep(string.Format("{0}/{1}", (j + 1), count)); SETItem ent = new SETItem(setfile, address, selectedItems); list.Add(ent); address += 0x20; } } LevelData.SETItems[i] = list; } } else { LevelData.SETItems = null; } // Checks if there have been any errors added to the error list and does its thing // This thing is a mess. If anyone can think of a cleaner way to do this, be my guest. if (objectErrors.Count > 0) { int count = objectErrors.Count; List<string> errorStrings = new List<string> { "The following objects failed to load:" }; foreach (ObjectData o in objectErrors) { bool texEmpty = string.IsNullOrEmpty(o.Texture); bool texExists = (!string.IsNullOrEmpty(o.Texture) && LevelData.Textures.ContainsKey(o.Texture)); errorStrings.Add(""); errorStrings.Add("Object:\t\t" + o.Name); errorStrings.Add("\tModel:"); errorStrings.Add("\t\tName:\t" + o.Model); errorStrings.Add("\t\tExists:\t" + File.Exists(o.Model)); errorStrings.Add("\tTexture:"); errorStrings.Add("\t\tName:\t" + ((texEmpty) ? "(N/A)" : o.Texture)); errorStrings.Add("\t\tExists:\t" + texExists); } // TODO: Proper logging. Who knows where this file may end up File.WriteAllLines("SADXLVL2.log", errorStrings.ToArray()); MessageBox.Show(count + ((count == 1) ? " object" : " objects") + " failed to load their model(s).\n" + "\nThe level will still display, but the objects in question will not display their proper models." + "\n\nPlease check the log for details.", "Error loading models", MessageBoxButtons.OK, MessageBoxIcon.Information); } } else { LevelData.SETItems = null; } progress.StepProgress(); #endregion #region CAM Layout progress.SetTaskAndStep("Loading CAM items", "Initializing..."); string camstr = Path.Combine(syspath, "CAM" + LevelData.SETName + "{0}.bin"); LevelData.CAMItems = new List<CAMItem>[LevelData.SETChars.Length]; for (int i = 0; i < LevelData.SETChars.Length; i++) { List<CAMItem> list = new List<CAMItem>(); byte[] camfile = null; string formatted = string.Format(camstr, LevelData.SETChars[i]); if (modpath != null && File.Exists(Path.Combine(modpath, formatted))) camfile = File.ReadAllBytes(Path.Combine(modpath, formatted)); else if (File.Exists(formatted)) camfile = File.ReadAllBytes(formatted); if (camfile != null) { progress.SetTask("CAM: " + formatted.Replace(Environment.CurrentDirectory, "")); int count = BitConverter.ToInt32(camfile, 0); int address = 0x40; for (int j = 0; j < count; j++) { progress.SetStep(string.Format("{0}/{1}", (j + 1), count)); CAMItem ent = new CAMItem(camfile, address, selectedItems); list.Add(ent); address += 0x40; } } LevelData.CAMItems[i] = list; } CAMItem.Init(d3ddevice); progress.StepProgress(); #endregion #region Loading Level Effects LevelData.leveleff = null; if (!string.IsNullOrEmpty(level.Effects)) { progress.SetTaskAndStep("Loading Level Effects..."); LevelDefinition def = null; string ty = "SADXObjectDefinitions.Level_Effects." + Path.GetFileNameWithoutExtension(level.Effects); string dllfile = Path.Combine("dllcache", ty + ".dll"); DateTime modDate = DateTime.MinValue; if (File.Exists(dllfile)) modDate = File.GetLastWriteTime(dllfile); string fp = level.Effects.Replace('/', Path.DirectorySeparatorChar); if (modDate >= File.GetLastWriteTime(fp) && modDate > File.GetLastWriteTime(Application.ExecutablePath)) { def = (LevelDefinition) Activator.CreateInstance( Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, dllfile)).GetType(ty)); } else { string ext = Path.GetExtension(fp); CodeDomProvider pr = null; switch (ext.ToLowerInvariant()) { case ".cs": pr = new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; case ".vb": pr = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); break; } if (pr != null) { CompilerParameters para = new CompilerParameters(new string[] { "System.dll", "System.Core.dll", "System.Drawing.dll", Assembly.GetAssembly(typeof (Vector3)).Location, Assembly.GetAssembly(typeof (Texture)).Location, Assembly.GetAssembly(typeof (D3DX)).Location, Assembly.GetExecutingAssembly().Location, Assembly.GetAssembly(typeof (LandTable)).Location, Assembly.GetAssembly(typeof (EditorCamera)).Location, Assembly.GetAssembly(typeof (SA1LevelAct)).Location, Assembly.GetAssembly(typeof (Item)).Location }) { GenerateExecutable = false, GenerateInMemory = false, IncludeDebugInformation = true, OutputAssembly = Path.Combine(Environment.CurrentDirectory, dllfile) }; CompilerResults res = pr.CompileAssemblyFromFile(para, fp); if (!res.Errors.HasErrors) def = (LevelDefinition)Activator.CreateInstance(res.CompiledAssembly.GetType(ty)); } } if (def != null) def.Init(level, levelact.Act, d3ddevice); LevelData.leveleff = def; } progress.StepProgress(); #endregion #region Loading Splines LevelData.LevelSplines = new List<SplineData>(); SplineData.Init(); if (!string.IsNullOrEmpty(ini.Paths)) { progress.SetTaskAndStep("Reticulating splines..."); String splineDirectory = Path.Combine(Path.Combine(Environment.CurrentDirectory, ini.Paths), levelact.ToString()); if (Directory.Exists(splineDirectory)) { List<string> pathFiles = new List<string>(); for (int i = 0; i < int.MaxValue; i++) { string path = string.Concat(splineDirectory, string.Format("/{0}.ini", i)); if (File.Exists(path)) { pathFiles.Add(path); } else break; } foreach (string pathFile in pathFiles) // looping through path files { SplineData newSpline = new SplineData(PathData.Load(pathFile), selectedItems); newSpline.RebuildMesh(d3ddevice); LevelData.LevelSplines.Add(newSpline); } } } progress.StepProgress(); #endregion #region Stage Lights progress.SetTaskAndStep("Loading lights..."); if ((stageLightList != null) && (stageLightList.Count > 0)) { List<SA1StageLightData> lightList = new List<SA1StageLightData>(); foreach (SA1StageLightData lightData in stageLightList) { if ((lightData.Level == levelact.Level) && (lightData.Act == levelact.Act)) lightList.Add(lightData); } if (lightList.Count > 0) { for (int i = 0; i < d3ddevice.Lights.Count; i++) // clear all default lights { d3ddevice.Lights[i].Enabled = false; } for (int i = 0; i < lightList.Count; i++) { SA1StageLightData lightData = lightList[i]; d3ddevice.Lights[i].Enabled = true; d3ddevice.Lights[i].Type = (lightData.UseDirection) ? LightType.Directional : LightType.Point; d3ddevice.Lights[i].Diffuse = lightData.RGB.ToColor(); d3ddevice.Lights[i].DiffuseColor = new ColorValue(lightData.RGB.X, lightData.RGB.Y, lightData.RGB.Z, 1.0f); d3ddevice.Lights[i].Ambient = lightData.AmbientRGB.ToColor(); d3ddevice.Lights[i].Specular = Color.Black; d3ddevice.Lights[i].Direction = lightData.Direction.ToVector3(); d3ddevice.Lights[i].Range = lightData.Dif; // guessing here } } else { MessageBox.Show("No lights were found for this stage. Using default lights instead.", "No lights found", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } progress.StepProgress(); #endregion transformGizmo = new TransformGizmo(); Invoke((Action)progress.Close); } #if !DEBUG } catch (Exception ex) { MessageBox.Show( ex.GetType().Name + ": " + ex.Message + "\nLog file has been saved to " + Path.Combine(Environment.CurrentDirectory, "SADXLVL2.log") + ".\nSend this to MainMemory on the Sonic Retro forums.", "SADXLVL2 Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); File.WriteAllText("SADXLVL2.log", ex.ToString()); initerror = true; } #endif }
private void exportOBJToolStripMenuItem_Click(object sender, EventArgs e) { SaveFileDialog a = new SaveFileDialog { DefaultExt = "obj", Filter = "OBJ Files|*.obj" }; if (a.ShowDialog() == DialogResult.OK) { using (StreamWriter objstream = new StreamWriter(a.FileName, false)) using (StreamWriter mtlstream = new StreamWriter(Path.ChangeExtension(a.FileName, "mtl"), false)) { #region Material Exporting string materialPrefix = LevelData.leveltexs; objstream.WriteLine("mtllib " + Path.GetFileNameWithoutExtension(a.FileName) + ".mtl"); int stepCount = LevelData.TextureBitmaps[LevelData.leveltexs].Length + LevelData.geo.COL.Count; if (LevelData.geo.Anim != null) stepCount += LevelData.geo.Anim.Count; ProgressDialog progress = new ProgressDialog("Exporting stage: " + levelName, stepCount, true, false); progress.Show(this); progress.SetTaskAndStep("Exporting..."); // This is admittedly not an accurate representation of the materials used in the model - HOWEVER, it makes the materials more managable in MAX // So we're doing it this way. In the future we should come back and add an option to do it this way or the original way. for (int i = 0; i < LevelData.TextureBitmaps[LevelData.leveltexs].Length; i++) { mtlstream.WriteLine("newmtl {0}_material_{1}", materialPrefix, i); mtlstream.WriteLine("Ka 1 1 1"); mtlstream.WriteLine("Kd 1 1 1"); mtlstream.WriteLine("Ks 0 0 0"); mtlstream.WriteLine("illum 1"); if (!string.IsNullOrEmpty(LevelData.leveltexs)) { mtlstream.WriteLine("Map_Kd " + LevelData.TextureBitmaps[LevelData.leveltexs][i].Name + ".png"); // save texture string mypath = Path.GetDirectoryName(a.FileName); BMPInfo item = LevelData.TextureBitmaps[LevelData.leveltexs][i]; item.Image.Save(Path.Combine(mypath, item.Name + ".png")); } progress.Step = String.Format("Texture {0}/{1}", i + 1, LevelData.TextureBitmaps[LevelData.leveltexs].Length); progress.StepProgress(); Application.DoEvents(); } #endregion int totalVerts = 0; int totalNorms = 0; int totalUVs = 0; bool errorFlag = false; for (int i = 0; i < LevelData.geo.COL.Count; i++) { Direct3D.Extensions.WriteModelAsObj(objstream, LevelData.geo.COL[i].Model, materialPrefix, new MatrixStack(), ref totalVerts, ref totalNorms, ref totalUVs, ref errorFlag); progress.Step = String.Format("Mesh {0}/{1}", i + 1, LevelData.geo.COL.Count); progress.StepProgress(); Application.DoEvents(); } if (LevelData.geo.Anim != null) { for (int i = 0; i < LevelData.geo.Anim.Count; i++) { Direct3D.Extensions.WriteModelAsObj(objstream, LevelData.geo.Anim[i].Model, materialPrefix, new MatrixStack(), ref totalVerts, ref totalNorms, ref totalUVs, ref errorFlag); progress.Step = String.Format("Animation {0}/{1}", i + 1, LevelData.geo.Anim.Count); progress.StepProgress(); Application.DoEvents(); } } if (errorFlag) { MessageBox.Show("Error(s) encountered during export. Inspect the output file for more details.", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } progress.SetTaskAndStep("Export complete!"); } } }