Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Animation Export (DAE - Pokemon) #55

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
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
86 changes: 78 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,90 @@
# SPICA [![Build status](https://ci.appveyor.com/api/projects/status/ar1fyeo109v587xf/branch/master?svg=true)](https://ci.appveyor.com/project/gdkchan/spica/branch/master)
Experimental H3D tool for serializing/deserializing BCH.

Dependencies:

### Dependencies:
- .NET Framework 4.6.2
- OpenGL 3.3 at least
- OpenTK
- OpenTK.GLControl
- Both can be found on NuGet.
- _Note: The version of OpenTK.GLControl on NuGet is broken, so it's recommended to build it yourself from source and manually add a reference to the compiled library._
- OpenTK git can be found [here](https://github.com/opentk/opentk).

Both can be found on NuGet.

Note: The version of OpenTK.GLControl on NuGet is broken, so it's recommended to build it yourself from source and manually add a reference to the compiled library.
OpenTK git can be found [here](https://github.com/opentk/opentk).

You will need .NET Framework 4.6 and a GPU capable of OpenGL 3.3 at least.

### Linux/Mac Support
SPICA can be built on Linux/Mac using [Mono](https://www.mono-project.com/).
See `README.mono.md` for details.

**Windows build:**

### Windows build
To download the lastest automatic build for Windows, [Click Here](https://ci.appveyor.com/api/projects/gdkchan/spica/artifacts/spica_lastest.zip).


# Modder Notes _(multi-motion export for pokemon sun)_
_specifically pokemon fans, although possibly not limited to just that realm. DAE files are kind of a pain, but they are open source and easily manipulated. I needed a simple way to combine N animations into a single dae file. Spica already supported single animation exports, yet i hate blender, so prefer to create the game ready models and animations programatically._

### what you'll need
1. get a decrypted .3ds file _(possible called .cia)_
2. [visual studio 2017 community][vs2017]
3. [DotNet.3DS.Toolkit][3dstoolkit] _(i used 1.2.0)_
4. [PUYOTOOLS][puyotools] _(i built from source)_
5. [PK3DS][pk3ds] _(build 354 - others may work)_
6. SPICA
7. [UNITY3D][unity]

### Step 1. DotNet.3DS.Toolkit extract from rom
- use DotNet.3DS.Toolkit to extract the already decrpyted cia/.3ds file
- _(make sure outputs are placed in a diff directory)_

### Step 2. PK3DS unpack a GARC
- Open PK3DS and choose tools>Misc Tools> Un(pack)+BCLIM
- Drag the garc from a/0/9/4 in the first field while holding ctrl.
- When it's done, you'll have 0000.bin - 10422.bin

### Step 3. PUYOTOOLS batch decrypt all the .bins
- just use the prebuilt binary!
- or build yourself
- `git clone git@github.com:nickworonekin/puyotools.git`
- build in visual studio 2017
- run and decompress the entire directory output by the pk3ds tool
- _you now have 10k files with all the pkmn data in it an uncompressed for spica_

### Step 4A. SPICA CLI
- `git clone git@github.com:Wambosa/SPICA.git`
- build using visual studio 2017
- run
- `.\SPICA.CLI.exe -bin=C:/pkmn/094 -pokemon=charmander`

[![image.png](https://s15.postimg.cc/6edsj7rdn/image.png)](https://postimg.cc/image/aniildumv/)

### Step 4B. SPICA UI
- open spica
- select merge _(folder with plus on it)_
- choose the ~9 files corresponding to the pkmn you want _(ctrl+click)_
- [here is a model index][usum model index]
- export textures to `.../pokemon/tex`
- most _(not all)_ textures need to be mirrored in order to work in unity*
- hit save _(floppy disk)_ with selected animation(s) and model to store as .dae

**this version of the codebase will save all the animations as a single animation on export**

### Step 5. UNITY3D
- import textures first into unity,
- then import .dae model
- check out the animation tab


### bounty
[![poli.gif](https://s15.postimg.cc/697oxd7t7/poli.gif)](https://postimg.cc/image/mwz6zv2kn/)
[![poli.png](https://s15.postimg.cc/v2h8xw8sb/poli.png)](https://postimg.cc/image/b7v7brtkn/)



[usum model index]: https://gbatemp.net/threads/pokemon-sun-moon-pokemon-animations-textures-and-models.473906/
[alternative model index]: https://gbatemp.net/threads/sun-moon-pokemon-model-file-mapping-cheat-list-for-a-0-9-4-archives.478882/
[vs2017]: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15
[3dstoolkit]: https://github.com/evandixon/DotNet3dsToolkit/releases/download/1.2.0/DotNet.3DS.Toolkit.v1.2.0.zip
[puyotools]: https://github.com/nickworonekin/puyotools/releases/download/2.0.3/PuyoTools-2.0.3.zip
[pk3ds]: https://github.com/kwsch/pk3DS/releases/download/1.0.3/pk3DS.12-01-15.zip
[unity]: https://store.unity.com/
6 changes: 6 additions & 0 deletions SPICA.CLI/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
</configuration>
63 changes: 63 additions & 0 deletions SPICA.CLI/FileIO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.IO;
using SPICA.WinForms;
using SPICA.Formats.CtrH3D;
using SPICA.Formats.CtrH3D.Model;
using SPICA.Formats.Generic.COLLADA;
using SPICA.WinForms.Formats;

namespace SPICA.CLI
{
class FileIO
{
public static H3D Merge(string[] FileNames, H3D Scene = null)
{
if (Scene == null)
{
Scene = new H3D();
}

int OpenFiles = 0;

foreach (string FileName in FileNames)
{
H3DDict<H3DBone> Skeleton = null;

if (Scene.Models.Count > 0) Skeleton = Scene.Models[0].Skeleton;

H3D Data = FormatIdentifier.IdentifyAndOpen(FileName, Skeleton);

if (Data != null)
{
Scene.Merge(Data);
OpenFiles++;
}
}

if (OpenFiles == 0)
{
//todo: improve this error message by making the format discovery return some kind of report
Console.Write("Unsupported file format!", "Can't open file!");
}

return Scene;
}

public static void ExportDae(H3D Scene, string Filename, int[] SelectedAnimations)
{
int highPolyModel = 0;
new DAE(Scene, highPolyModel, SelectedAnimations).Save(Filename);
}

public static void ExportTextures(H3D Scene, string path)
{
TextureManager.Textures = Scene.Textures;
for (int i = 0; i < Scene.Textures.Count; i++)
{
string FileName = Path.Combine(path, $"{Scene.Textures[i].Name}.png");

TextureManager.GetTexture(i).Save(FileName);
}
}
}
}
18 changes: 18 additions & 0 deletions SPICA.CLI/MotionLexicon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;

namespace SPICA.CLI
{
class MotionLexicon
{
public static Dictionary<string, string> StandardMotion = new Dictionary<string, string>() {
{ "idle", "Motion_0"},
{ "attack-close", "Motion_6"},
{ "attack-range", "Motion_9"},
{ "hit", "Motion_13"},
{ "faint", "Motion_17"},
{ "celebrate", "Motion_10_1"},
{ "walk", "Motion_1_2"},
{ "run", "Motion_3_2"},
};
}
}
102 changes: 102 additions & 0 deletions SPICA.CLI/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.IO;
using System.Linq;
using System.Dynamic;
using Mono.Options;
using SPICA.Formats.CtrH3D;
using YamlDotNet.Serialization;
using System.Collections.Generic;


namespace SPICA.CLI
{
class Program
{
private static OptionSet options;

static void Main(string[] args)
{
string here = AppDomain.CurrentDomain.BaseDirectory;

Console.WriteLine("SPICA CLI");

//default args
string Pokemon = "bulbasaur";
string MapLocation = $"{here}/data/model_bin_map.yml";
string BinLocation = $"{here}/in";
string TextureLocation = $"{here}/tex";
string ModelLocation = $"{here}/out";

options = new OptionSet() {
{ "?|h|help", "show the help", _ => HelpText()},
{ "p=|pokemon=", "desired pokemon to dump", input => Pokemon = input},
{ "map=|map-file=", "", input => MapLocation = input},
{ "in=|bin=|bin-dir=", "", input => BinLocation = input},
{ "tex=|texture=|texture-out=", "", input => TextureLocation = input},
{ "model=|model-out", "", input => ModelLocation = input},
};

var errors = options.Parse(args);

if (errors.Count > 0) {
foreach (var err in errors) {
Console.Error.WriteLine("Unrecognized option {0}", err);
}
Environment.Exit(1);
}

Directory.CreateDirectory(TextureLocation);
Directory.CreateDirectory(ModelLocation);

Console.WriteLine($"Searching for: {Pokemon}");
string[] files = GetFileNames(Pokemon, MapLocation).Select(file => $"{BinLocation}/{file}").ToArray();

Console.Write($"Building Scene with files:\n{string.Join("\n", files)}\n");
H3D Scene = FileIO.Merge(files);

Console.WriteLine($"Exporting {Scene.Textures.Count} textures");
FileIO.ExportTextures(Scene, TextureLocation);

int[] motions = GetMotionIndices(Scene, MotionLexicon.StandardMotion.Values);

Console.WriteLine($"exporting {ModelLocation}/{Pokemon}.dae model with {motions.Length} motions");
FileIO.ExportDae(Scene, $"{ModelLocation}/{Pokemon}.dae", motions);

Console.WriteLine($"completed exports for: {Pokemon}");
}

public static int[] GetMotionIndices(H3D Scene, IEnumerable<string> MotionNames)
{
List<string> motionNames = new List<string>(MotionNames);
List<int> targets = new List<int>();

for (int i = 0; i < Scene.SkeletalAnimations.Count; i++)
{
if (motionNames.Contains(Scene.SkeletalAnimations[i].Name))
{
targets.Add(i);
}
}
return targets.ToArray();
}

private static string[] GetFileNames(string Pokemon, string MapLocation) {
var yaml = new DeserializerBuilder().Build();
dynamic expando = yaml.Deserialize<ExpandoObject>(File.ReadAllText(MapLocation));
var lookup = expando as IDictionary<string, Object>;

List<string> files = new List<string>();
foreach (var file in (lookup[Pokemon] as List<Object>))
files.Add(file.ToString());

return files.ToArray();
}

private static void HelpText() {
options.WriteOptionDescriptions(Console.Out);
Console.WriteLine("SPICA Command Line Interface");

Environment.Exit(1);
}
}
}
39 changes: 39 additions & 0 deletions SPICA.CLI/SPICA.CLI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>

<ItemGroup>
<None Remove="data\model_bin_map.yml" />
</ItemGroup>

<ItemGroup>
<Content Include="data\model_bin_map.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
<PackageReference Include="YamlDotNet" Version="6.1.1" />

<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />

</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SPICA.WinForms\SPICA.WinForms.csproj" />
<ProjectReference Include="..\SPICA\SPICA.csproj" />
</ItemGroup>

</Project>
Loading