/// <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 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!"); } } }