Skip to content
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
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

451 changes: 62 additions & 389 deletions .idea/workspace.xml

Large diffs are not rendered by default.

91 changes: 46 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,70 @@ Both directions have been tested and confirmed to work. A more clear tutorial on
Because of limitations of the klei animation format you cannot convert all Spriter projects to klei animations. The requiremets for a Spriter project to be converted to a klei animation are listed here. If you do not **exactly** follow these requirements in your Spriter project it **will not** convert to a klei animation properly and you will be sad and I will be unable to help.
* Name all of your image files with an underscore and a number at the end. For example if you have an image `blob.png` instead name it `blob_0.png`. The numbering for an individual sprite must start at 0. If you had two different versions of `blob` you would name them `blob_0.png` and `blob_1.png`. If you have separate sprites they each start at 0, for example `blob_0.png` and `head_0.png`. This is required for how the tool is currently written.
* You absolutely **must** enable snapping for your project. The klei animation format expects your keyframes to be in a specific interval. So if you do not enforce that your Spriter project snaps all keyframes to this interval it will be unable to be converted. To turn on snapping you will need to click the triple dots in the timeline panel for opening the settings and then making sure that the `Enable Snapping` option is checked. Additionally you should make all your animations at 33 ms interval to fit within the game's animation style as that is the interval used by klei's own animations for Oxygen Not Included.
![Image of timeline settings buttons](timeline_settings_buttons.png)
![Image of settings](timeline_settings_enable_snapping.png)
![Image of snapping enabled](snapping_enabled.png)
![Image of timeline settings buttons](imgs/timeline_settings_buttons.png)
![Image of settings](imgs/timeline_settings_enable_snapping.png)
![Image of snapping enabled](imgs/snapping_enabled.png)
* You absolutely **cannot** utilize Spriter's feature of setting a pivot point on individual sprites on individual keyframes. What is meant by this is: klei's animation format expects every sprite to have a specific consistent pivot point across the entire animation, i.e. the pivot point of a sprite never changes in a klei animation. As such if you were to change the pivot point of a sprite in your animation (do note that this ability is supported by Spriter which is why you must be so careful to avoid doing it) you simply cannot! This is because there is no way to convert a moving pivot point in Spriter to a consistent pivot in a klei animation. It is not mathematically possible to do so. What you want to do for setting the pivot properly is to set it once for every sprite by setting it in the palette window **before** dragging any of the sprites into your animation.
![Image of the palette window](pivot_correct_0.png)
![Image of the correct pivot setting](pivot_correct_1.png)
![Image of the palette window](imgs/pivot_correct_0.png)
![Image of the correct pivot setting](imgs/pivot_correct_1.png)
* Below is example of modifying the pivot on an individual sprite. This is an example of what you **should not** do. The red box shows the pivot location after manually moving it on an individual sprite. If you do not move the pivot on the sprite manually it will be a red circle instead of the grey circle.
![Image of wrong pivot workflow](pivot_incorrect_0.png)
![Image of wrong pivot workflow](imgs/pivot_incorrect_0.png)

## Usage Guide

### Requirements

You must have Java 9 or higher to use kparser. The standard consumer Java install (from java.org) is Java 8, meaning you cannot use that version.

### Extracting an SCML Project

1. Install uTinyRipper from [here](https://github.com/mafaca/UtinyRipper).
![Image of download button](tut_step_0.png)
![Image of download button](imgs/tut_step_0.png)
2. Extract uTinyRipper from the zip file
![Image of extraction](tut_step_1.png)
![Image of extraction](imgs/tut_step_1.png)
3. Run uTinyRipper from the exe file
![Image of exe](tut_step_2.png)
![Image of exe](imgs/tut_step_2.png)
4. With uTinyRipper running open your file system and navigate to your OxygenNotIncluded_Data folder. On Windows it should be located at "C:\Program Files (x86)\Steam\steamapps\common\OxygenNotIncluded\OxygenNotIncludedData". You are looking for "sharedassets0.assets", "sharedassets1.assets", and "sharedassets2.assets"
![Image of files](tut_step_3.png)
![Image of files](imgs/tut_step_3.png)
5. Now you will need to drag and drop "sharedassets0.assets" onto your uTinyRipper application window.
![Image of uTinyRipper application landing screen with drag-n-drop](tut_step_4.png)
![Image of uTinyRipper application landing screen with drag-n-drop](imgs/tut_step_4.png)
6. Now your uTinyRipper should look like this with a large export button in the middle. When you click this button it will prompt you to find a location to put the exported files in. I would recommend putting it in a new folder called "ONI-Exports" under your user's "Documents" directory.
![Image of uTinyRipper export button](tut_step_5.png)
![Image of uTinyRipper export button](imgs/tut_step_5.png)
7. After uTinyRipper has exported "sharedassets0.assets" you may go ahead and export the the other two "sharedassets" files or you can leave that for later and continue with the tutorial for now. You will need to bring up that directory in which your exported the ONI game files.
![Image of directory with exported files](tut_step_6.png)
![Image of directory with exported files](imgs/tut_step_6.png)
8. In the "Assets" folder of one of the "sharedassets" directories you will find two folders. These are "TextAsset" and "Texture2D". A Klei animation is composed of three files - 1st an image file that contains the raw picture data for the animation (this is considered a Texture2D), 2nd and 3rd are two binary files called the build and animation files (both are considered a TextAsset).
![Image of the TextAsset and Texture2D folders](tut_step_7.png)
![Image of the TextAsset and Texture2D folders](imgs/tut_step_7.png)
9. Now we can actually bring together all the files that make up a Klei animation. For this example we will look at the Klei animation called "airconditioner". In the game, this animation is used for the building called the "Thermo Regulator". The files you will need for the "airconditioner" animation are "Texture2D/airconditioner_0.png", "TextAsset/airconditioner_build.bytes", and "TextAsset/airconditioner_anim.bytes".
![Image of the texture file](tut_step_8a.png)
![Image of the binary files](tut_step_8b.png)
![Image of the texture file](imgs/tut_step_8a.png)
![Image of the binary files](imgs/tut_step_8b.png)
10. All of the files for the "airconditioner" animation together looks like this.
![Image of the directory of the files together](tut_step_9.png)
![Image of the directory of the files together](imgs/tut_step_9.png)
11. Now download the jar version of K-Parser from the releases section of this github repository. Click [here](https://github.com/daviscook477/kparser/releases) to download it. Put the jar somewhere useful. I recommend putting it near your folder containing the animation files.
![Image of the kparser jar](tut_step_10.png)
12. For this next step you will need to be using at least java 9. Run the jar file with the animation files as the parameters to create the scml file. Note the order of the parameters. They must be in this order: image, build, anim.
![Image of command to run](tut_step_11.png)
13. Now you will have the "scml" project file contained inside the new "scml" directory. Additional the original texture file will have been split into its component pieces.
![Image of the directory with the scml file](tut_step_12.png)
14. Next open the scml file in [Spriter](https://brashmonkey.com/). You can now examine and edit the Klei animation as a spriter project.
![Image of Spriter opening the scml file](tut_step_13.png)
![Image of the kparser jar](imgs/tut_step_10.png)
12. Run the jar file with the animation files as the parameters to create the scml file. The order of the parameters does *not* matter, but make sure you have all three.
```
$ java -jar --to-scml airconditioner/airconditioner_0.png airconditioner/airconditioner_build.bytes airconditioner/airconditioner_anim.bytes
OR
$ java -jar -S airconditioner/airconditioner_0.png airconditioner/airconditioner_build.bytes airconditioner/airconditioner_anim.bytes
```

You can specify an output directory with the `--output-dir/-o` flag. It defaults to `output/` at the current working directory.

![Image of command to run](imgs/tut_step_11.png)
13. Now you will have the "scml" project file contained inside the output directory. It has also been cut into sprites.
![Image of the directory with the scml file](imgs/tut_step_12.png)
14. Next open the scml file in [Spriter](https://brashmonkey.com/). You can now examine and edit the Klei animation as a Spriter project.
![Image of Spriter opening the scml file](imgs/tut_step_13.png)

### Compiling Klei Animation Files
1. Assuming you have K-Parser installed already (if not see step 11 of the scml extraction section of this tutorial) it is simple to run the compiler on the single scml file.
![Image of command to run](tut_step_14.png)
2. Now you will have created your three Klei animation files in the same directory as the scml project.
![Image of created files](tut_step_15.png)
3. The final step before you can use this new animation you have created in an ONI mod is to create an AssetBundle from these three files. This is the part that requires you to have full install of unity to do it. Unity versions 2018.3.0f2 and 2017.1.5f1 are confirmed to work. Any 2019 Unity version will fail so do not use that version of unity.
4. Create a new 3d unity project and look at the assets panel in the bottom. It will be empty.
![Image of assets panel](tut_step_16.png)
5. Follow this [Unity tutorial](https://docs.unity3d.com/Manual/AssetBundles-Workflow.html) starting at the **Build the AssetBundles** header to get your Unity setup to create asset bundles.
6. Once that is done you now should have your assets panel look like this with the "Editor" folder.
![Image of editor folder](tut_step_17.png)
7. Now copy your compiled Klei Animation files into the Unity Assets folder.
![Image of assets copied over](tut_step_18.png)
8. Next you must assign the files to an AssetBundle. Using the AssetBundle menu in the bottom right of the screen create a new AssetBundle with whatever name you want.
![Image of asset bundle menu location](tut_step_19.png)
9. This is what it will look like once you have assigned an AssetBundle to each of the files. Assign the three files to the **same** asset bundle.
![Image of asset bundle assigned](tut_step_20.png)
10. Finally right click to open the menu and then select "Build AssetBundles"
![Image of build assetbundles option](tut_step_21.png)
11. You AssetBundle will have been created in a new directory called "AssetBundles"
![Image of generated bundle](tut_step_22.png)
12. The last step is to copy the generated assetbundle file from the "AssetBundles" folder in your temporary Unity project to the "anims" folder of your mod. All animations in assetbundles that are placed in the "anims" folder will automatically be loaded as Klei animations in ONI that are available with an id that is the same as the name of the assetbundle file that you created.
![Image of anims folder](tut_step_23.png)
```asciidoc
~/my_scml_project_dir $ java -jar kparser.jar --to-kanim my_scml.scml
(or)
~/my_scml_project_dir $ java -jar kparser.jar -k my_scml.scml
```
This will generate KAnim files, by default under `output/` of the current working directory. You can specify an output directory with the `-o` option.

2. Now you will have created your three Klei animation files (the `.atlas` file is not needed for KAnim and can be safely deleted).
![Image of created files](imgs/new_build_results.png)
3. Put these files in `MOD_DIR/anim/assets/ANIMNAME/`, and they can be loaded by referencing `ANIMNAME_kanim` in your C# code.
Binary file added imgs/new_build_results.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
3 changes: 2 additions & 1 deletion kparse.iml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_9">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
Expand All @@ -11,6 +11,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: args4j:args4j:2.33" level="project" />
<orderEntry type="library" name="Maven: com.badlogicgames.gdx:gdx-tools:1.9.2" level="project" />
<orderEntry type="library" name="Maven: com.badlogicgames.gdx:gdx-backend-lwjgl:1.9.2" level="project" />
<orderEntry type="library" name="Maven: com.badlogicgames.gdx:gdx:1.9.2" level="project" />
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
</build>

<dependencies>
<dependency>
<groupId>args4j</groupId>
<artifactId>args4j</artifactId>
<version>2.33</version>
</dependency>
<dependency>
<groupId>com.badlogicgames.gdx</groupId>
<artifactId>gdx-tools</artifactId>
Expand Down
40 changes: 30 additions & 10 deletions src/main/java/KAnimConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,46 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

public class KAnimConverter {

public static void convert(String imgPath, String buildPath, String animPath) throws FileNotFoundException, IOException, ParserConfigurationException, TransformerException {
Reader reader = new Reader(new FileInputStream(buildPath),
new FileInputStream(animPath),
new FileInputStream(imgPath));
public static void convert(String imgPathStr, String buildPathStr, String animPathStr, String outputDir) throws FileNotFoundException, IOException, ParserConfigurationException, TransformerException {
var outputPath = Path.of(outputDir);
// Ensure output dirs exist
outputPath.toFile().mkdirs();

Utilities.PrintInfo(String.format("Outputting to %s", outputPath.toAbsolutePath().toString()));
Utilities.PrintInfo("Unpack started.");
var imgPath = Path.of(imgPathStr);
var build = Path.of(buildPathStr).toFile();
var anim = Path.of(animPathStr).toFile();

Reader reader = new Reader(new FileInputStream(build),
new FileInputStream(anim),
new FileInputStream(imgPath.toFile()));
Utilities.PrintInfo("Parsing build data.");
reader.parseBILDData();
File imgFile = new File(imgPath);
String scmlFolderPath = imgFile.getParent() + "\\scml\\";
new File(scmlFolderPath).mkdir();
reader.exportTextures(scmlFolderPath);
Utilities.PrintInfo("Exporting textures.");

reader.exportTextures(outputPath);
Utilities.PrintInfo("Parsing animation data.");
reader.parseANIMData();
Writer writer = new Writer();
writer.init(reader.BILDTable, reader.BILDData, reader.ANIMData, reader.ANIMHash);
String scmlPath = imgFile.getParent() + "\\scml\\" + imgFile.getName().substring(0, imgFile.getName().lastIndexOf('.')) + ".scml";
writer.save(scmlPath);

var filename = imgPath.getFileName().toString();
String scmlFileName = filename
.substring(0, filename.lastIndexOf('.')) + ".scml";
var outputFilePath = outputPath.resolve(scmlFileName);

Utilities.PrintInfo("Writing...");
writer.save(outputFilePath);

Utilities.PrintInfo("Done.");
}

}
Loading