Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
450 changes: 235 additions & 215 deletions OpenKh.Tools.Kh2MdlxEditor/Utils/MdlxEditorImporter.cs

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion OpenKh.Tools.Kh2MdlxEditor/ViewModels/Importer_VM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public bool ModelLoaded
public ObservableCollection<ImportMeshOptions> MeshOptionsList { get; set; }

public bool KeepShadow { get; set; }
public bool KeepSkeleton { get; set; }
public byte VertexLimitPerPacket { get; set; }
public byte MemoryLimitPerPacket { get; set; }

Expand All @@ -38,6 +39,7 @@ public Importer_VM(Main2_VM mainVM)
MeshOptionsList = new ObservableCollection<ImportMeshOptions>();

KeepShadow = false;
KeepSkeleton = true; // Default to true (keep original skeleton)
VertexLimitPerPacket = (byte)VifProcessor.VERTEX_LIMIT;
MemoryLimitPerPacket = (byte)VifProcessor.MEMORY_LIMIT;
}
Expand Down Expand Up @@ -115,11 +117,12 @@ public void ImportModel()
return;

MdlxEditorImporter.KEEP_ORIGINAL_SHADOW = KeepShadow;
MdlxEditorImporter.KEEP_ORIGINAL_SKELETON = KeepSkeleton;
VifProcessor.VERTEX_LIMIT = VertexLimitPerPacket;
VifProcessor.MEMORY_LIMIT = MemoryLimitPerPacket;

List<VifProcessor.MeshOptions> newMeshOptions = new List<VifProcessor.MeshOptions>();
foreach(ImportMeshOptions meshOptions in MeshOptionsList)
foreach (ImportMeshOptions meshOptions in MeshOptionsList)
{
newMeshOptions.Add(new VifProcessor.MeshOptions(meshOptions.HasColors, meshOptions.ApplyColors, meshOptions.HasNormals, meshOptions.ApplyNormals));
}
Expand Down
6 changes: 5 additions & 1 deletion OpenKh.Tools.Kh2MdlxEditor/ViewModels/Main2_VM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,16 @@ public void buildBarFile()
case Bar.EntryType.Model:
barEntry.Stream = new MemoryStream();
ModelFile.Write(barEntry.Stream);
barEntry.Stream.Position = 0;
break;
case Bar.EntryType.ModelTexture:
barEntry.Stream = new MemoryStream();
TextureFile.Write(barEntry.Stream);
barEntry.Stream.Position = 0;
break;
case Bar.EntryType.ModelCollision:
barEntry.Stream = CollisionFile.toStream();
barEntry.Stream.Position = 0;
break;
default:
break;
Expand All @@ -111,7 +114,8 @@ public void replaceModel(string filePath)

TextureFile = MdlxEditorImporter.createModelTexture(scene, filePath);

ModelFile = MdlxEditorImporter.replaceMeshModelSkeletal(scene, ModelFile, filePath);
// Pass CollisionFile to the importer so it can reset bones if needed
ModelFile = MdlxEditorImporter.replaceMeshModelSkeletal(scene, ModelFile, filePath, CollisionFile);
}
}
}
82 changes: 81 additions & 1 deletion OpenKh.Tools.Kh2MdlxEditor/ViewModels/TextureFile_VM.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,110 @@
using OpenKh.Kh2;
using OpenKh.Tools.Kh2MdlxEditor.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

namespace OpenKh.Tools.Kh2MdlxEditor.ViewModels
{
internal class TextureFile_VM
{
private Action<ModelTexture> _onTextureReplaced;

public ModelTexture textureData { get; set; }
public ObservableCollection<ModelTexture.Texture> textures { get; set; }

public TextureFile_VM() { }
public TextureFile_VM(ModelTexture textureFile)
public TextureFile_VM(ModelTexture textureFile, Action<ModelTexture> onTextureReplaced = null)
{
textureData = textureFile;
textures = new ObservableCollection<ModelTexture.Texture>(textureData.Images);
_onTextureReplaced = onTextureReplaced;
}

public void removeTextureAt(int index)
{
textures.RemoveAt(index);
textureData.Images.RemoveAt(index);
}

public void addTexture(string filename)
{
textures.Add(ImageUtils.pngToTexture(filename));
textureData.Images.Add(ImageUtils.pngToTexture(filename));
}

// Replace texture at specific index
public void replaceTextureAt(int index, string filename)
{
if (index < 0 || index >= textures.Count)
{
throw new ArgumentOutOfRangeException(nameof(index), "Invalid texture index");
}

// Convert PNG to Imgd format
Imgd newImgd = ImageUtils.pngToImgd(filename);

// Build new texture list with replacement
List<Imgd> imgdList = new List<Imgd>();
for (int i = 0; i < textureData.Images.Count; i++)
{
if (i == index)
{
// Use the new texture
imgdList.Add(newImgd);
}
else
{
// Convert existing texture back to Imgd
var existingTexture = textureData.Images[i];
Imgd existingImgd = new Imgd(
existingTexture.Size,
existingTexture.PixelFormat,
existingTexture.GetData(),
existingTexture.GetClut(),
false
);
imgdList.Add(existingImgd);
}
}

// Store the footer data if it exists
byte[] footerData = null;
if (textureData.TextureFooterData != null)
{
using (var memStream = new MemoryStream())
{
textureData.TextureFooterData.Write(memStream);
footerData = memStream.ToArray();
}
}

// Rebuild the ModelTexture with the new texture list
var build = new ModelTexture.Build
{
images = imgdList,
footerData = footerData
};

ModelTexture newModelTexture = new ModelTexture(build);

// Write and read back to ensure proper formatting
Stream tempStream = new MemoryStream();
newModelTexture.Write(tempStream);
tempStream.Position = 0;
textureData = ModelTexture.Read(tempStream);

// Notify parent (Main2_VM) that texture was replaced so it can update its reference
_onTextureReplaced?.Invoke(textureData);

// Update the observable collection for UI
textures.Clear();
foreach (var texture in textureData.Images)
{
textures.Add(texture);
}
}
}
}
11 changes: 7 additions & 4 deletions OpenKh.Tools.Kh2MdlxEditor/Views/Importer_Window.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
<!-- Loader -->
<Button Margin="10" Click="Button_LoadModel" Width="200">Select model to load</Button>

<!-- Shadow -->
<CheckBox Margin="5" IsChecked="{Binding Path=KeepShadow, Mode=TwoWay}">Keep shadow</CheckBox>
<!-- Keep options -->
<StackPanel Margin="5" Orientation="Horizontal">
<CheckBox Margin="0,0,10,0" IsChecked="{Binding Path=KeepShadow, Mode=TwoWay}">Keep shadow</CheckBox>
<CheckBox IsChecked="{Binding Path=KeepSkeleton, Mode=TwoWay}">Keep skeleton</CheckBox>
</StackPanel>

<!-- Limits -->
<StackPanel Margin="10" Orientation="Horizontal">
Expand Down Expand Up @@ -65,8 +68,8 @@
<!-- Import -->
<Button Margin="0 30 0 10" Height="30" Width="70" Click="Button_Import">Import</Button>
</StackPanel>

<Label Name="ErrorMessage"/>

</StackPanel>
</Window>
6 changes: 6 additions & 0 deletions OpenKh.Tools.Kh2MdlxEditor/Views/Importer_Window.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using OpenKh.Tools.Kh2MdlxEditor.Utils;
using OpenKh.Tools.Kh2MdlxEditor.ViewModels;
using System;
using System.Linq;
Expand Down Expand Up @@ -71,6 +72,11 @@ private void Button_Import(object sender, EventArgs e)
try
{
ErrorMessage.Content = "Loading...";

// Set the static flags based on checkbox states
MdlxEditorImporter.KEEP_ORIGINAL_SHADOW = importerVM.KeepShadow;
MdlxEditorImporter.KEEP_ORIGINAL_SKELETON = importerVM.KeepSkeleton;

importerVM.ImportModel();
ErrorMessage.Content = "Finished";
if (mainWindow != null)
Expand Down
13 changes: 10 additions & 3 deletions OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,16 @@ private void Side_ModelBones(object sender, EventArgs e)
{
contentFrame.Content = new ModelBones_Control(mainVM.ModelFile);
}
private void Side_Texture(object sender, EventArgs e)
{
contentFrame.Content = new TextureFile_Control(mainVM.TextureFile);
private void Side_Texture(object sender, EventArgs e)
{
LoadTextureControl();
}
private void LoadTextureControl()
{
if (mainVM != null && mainVM.TextureFile != null)
{
contentFrame.Content = new TextureFile_Control(mainVM.TextureFile, mainVM, LoadTextureControl);
}
}
private void Side_Collision(object sender, EventArgs e)
{
Expand Down
7 changes: 4 additions & 3 deletions OpenKh.Tools.Kh2MdlxEditor/Views/TextureFile_Control.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>

<!-- Texture loader -->
<DockPanel Grid.Column="0" Background="#222222">
<Label DockPanel.Dock="Top" Foreground="White" HorizontalAlignment="Center">Texture</Label>
<ContentControl Background="#2d2d2d" x:Name="contentFrameTexture"/>
</DockPanel>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />

<!-- Texture animation loader -->
<DockPanel Grid.Column="2" Background="#222222">
<Label DockPanel.Dock="Top" Foreground="White" HorizontalAlignment="Center">Texture Animation</Label>
Expand All @@ -32,14 +32,15 @@
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Texture list -->
<DockPanel Grid.Row="0" LastChildFill="True">
<Label DockPanel.Dock="Top" Foreground="White" HorizontalAlignment="Center">Textures</Label>
<ListView Name="LV_Textures" ItemsSource="{Binding textureData.Images}" DisplayMemberPath="Size">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Export as PNG" Click="Texture_Export"/>
<MenuItem Header="Replace Texture" Click="Texture_Replace"/>
<MenuItem Header="Remove texture" Click="Texture_Remove" Visibility="Collapsed"/>
<MenuItem Header="Add texture" Click="Texture_Add" Visibility="Collapsed"/>
</ContextMenu>
Expand Down
61 changes: 59 additions & 2 deletions OpenKh.Tools.Kh2MdlxEditor/Views/TextureFile_Control.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,40 @@ namespace OpenKh.Tools.Kh2MdlxEditor.Views
public partial class TextureFile_Control : UserControl
{
TextureFile_VM textureFileControlModel { get; set; }
private Main2_VM _mainVM;
private Action _refreshCallback;

public TextureFile_Control()
{
InitializeComponent();
}

public TextureFile_Control(ModelTexture textureFile)
{
InitializeComponent();
textureFileControlModel = new TextureFile_VM(textureFile);
reloadContents();
}

// Constructor that accepts Main2_VM to update texture reference
public TextureFile_Control(ModelTexture textureFile, Main2_VM mainVM, Action refreshCallback = null)
{
InitializeComponent();
_mainVM = mainVM;
_refreshCallback = refreshCallback;
textureFileControlModel = new TextureFile_VM(textureFile, (newTexture) => {
// Update the Main2_VM reference when texture is replaced
if (_mainVM != null)
{
_mainVM.TextureFile = newTexture;
}
});
reloadContents();
}

public void reloadContents()
{
DataContext = null; // Force refresh
DataContext = textureFileControlModel;

if (textureFileControlModel.textureData.Images != null && textureFileControlModel.textureData.Images.Count > 0)
Expand Down Expand Up @@ -66,9 +86,10 @@ public void ListViewItem_OpenTexAnim(object sender, MouseButtonEventArgs e)
}
contentFrameAnimation.Content = new TexAnim_Control(texAnimSelected, clutPalette);
}

public void Texture_Export(object sender, RoutedEventArgs e)
{
if(LV_Textures.SelectedItem != null)
if (LV_Textures.SelectedItem != null)
{
BitmapSource bitmapImage = (LV_Textures.SelectedItem as ModelTexture.Texture).GetBimapSource();

Expand All @@ -84,6 +105,41 @@ public void Texture_Export(object sender, RoutedEventArgs e)
}
}
}

// Replace texture feature
public void Texture_Replace(object sender, RoutedEventArgs e)
{
if (LV_Textures.SelectedItem != null)
{
int selectedIndex = LV_Textures.SelectedIndex;

System.Windows.Forms.OpenFileDialog ofd;
ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Title = "Select PNG image to replace texture";
ofd.Filter = "PNG files (*.png)|*.png";
ofd.ShowDialog();

if (ofd.FileName != "")
{
try
{
textureFileControlModel.replaceTextureAt(selectedIndex, ofd.FileName);

// Trigger the refresh callback to reload the entire control
_refreshCallback?.Invoke();

MessageBox.Show($"Texture replaced successfully!\nNew size: {textureFileControlModel.textureData.Images[selectedIndex].Size}",
"Success", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error replacing texture: {ex.Message}\n\nStack trace:\n{ex.StackTrace}",
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

// Works for the textures, but the textures are outputted from the actual data, so it only works visually on the tool
public void Texture_Remove(object sender, RoutedEventArgs e)
{
Expand All @@ -94,7 +150,7 @@ public void Texture_Remove(object sender, RoutedEventArgs e)
reloadContents();
}
}
// Works for the textures, but the textures are outputted from the actual data, so it only works visually on the tool

public void Texture_Add(object sender, RoutedEventArgs e)
{
System.Windows.Forms.OpenFileDialog sfd;
Expand All @@ -110,6 +166,7 @@ public void Texture_Add(object sender, RoutedEventArgs e)
}
}
}

public void Animation_Export(object sender, RoutedEventArgs e)
{
if (LV_Animations.SelectedItem != null)
Expand Down
Loading