Implement DAT exporter for Moho
This commit is contained in:
parent
6f0e3ef50b
commit
fef283de43
|
@ -360,6 +360,8 @@ target_link_libraries(rhubarb-core
|
||||||
|
|
||||||
# ... rhubarb-exporters
|
# ... rhubarb-exporters
|
||||||
add_library(rhubarb-exporters
|
add_library(rhubarb-exporters
|
||||||
|
src/exporters/DatExporter.cpp
|
||||||
|
src/exporters/DatExporter.h
|
||||||
src/exporters/Exporter.h
|
src/exporters/Exporter.h
|
||||||
src/exporters/exporterTools.cpp
|
src/exporters/exporterTools.cpp
|
||||||
src/exporters/exporterTools.h
|
src/exporters/exporterTools.h
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "DatExporter.h"
|
||||||
|
#include "animation/targetShapeSet.h"
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
using std::chrono::duration;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
DatExporter::DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair) :
|
||||||
|
frameRate(frameRate),
|
||||||
|
convertToPrestonBlair(convertToPrestonBlair),
|
||||||
|
prestonBlairShapeNames {
|
||||||
|
{ Shape::A, "MBP" },
|
||||||
|
{ Shape::B, "etc" },
|
||||||
|
{ Shape::C, "E" },
|
||||||
|
{ Shape::D, "AI" },
|
||||||
|
{ Shape::E, "O" },
|
||||||
|
{ Shape::F, "U" },
|
||||||
|
{ Shape::G, "FV" },
|
||||||
|
{ Shape::H, "L" },
|
||||||
|
{ Shape::X, "rest" },
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Animation works with a fixed frame rate of 100.
|
||||||
|
// Downsampling to much less than 25 fps may result in dropped frames.
|
||||||
|
// Upsampling to more than 100 fps doesn't make sense.
|
||||||
|
const double minFrameRate = 24.0;
|
||||||
|
const double maxFrameRate = 100.0;
|
||||||
|
|
||||||
|
if (frameRate < minFrameRate || frameRate > maxFrameRate) {
|
||||||
|
throw std::runtime_error(fmt::format("Frame rate must be between {} and {} fps.", minFrameRate, maxFrameRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertToPrestonBlair) {
|
||||||
|
for (Shape shape : targetShapeSet) {
|
||||||
|
if (prestonBlairShapeNames.find(shape) == prestonBlairShapeNames.end()) {
|
||||||
|
throw std::runtime_error(fmt::format("Mouth shape {} cannot be converted to Preston Blair shape names."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatExporter::exportAnimation(const ExporterInput& input, std::ostream& outputStream) {
|
||||||
|
outputStream << "MohoSwitch1" << "\n";
|
||||||
|
|
||||||
|
// Output shapes with start times
|
||||||
|
int lastFrameNumber = 0;
|
||||||
|
for (auto& timedShape : input.animation) {
|
||||||
|
const int frameNumber = toFrameNumber(timedShape.getStart());
|
||||||
|
if (frameNumber == lastFrameNumber) continue;
|
||||||
|
|
||||||
|
const string shapeName = toString(timedShape.getValue());
|
||||||
|
outputStream << frameNumber << " " << shapeName << "\n";
|
||||||
|
lastFrameNumber = frameNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output closed mouth with end time
|
||||||
|
int frameNumber = toFrameNumber(input.animation.getRange().getEnd());
|
||||||
|
if (frameNumber == lastFrameNumber) ++frameNumber;
|
||||||
|
const string shapeName = toString(convertToTargetShapeSet(Shape::X, input.targetShapeSet));
|
||||||
|
outputStream << frameNumber << " " << shapeName << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
string DatExporter::toString(Shape shape) const {
|
||||||
|
return convertToPrestonBlair
|
||||||
|
? prestonBlairShapeNames.at(shape)
|
||||||
|
: boost::lexical_cast<std::string>(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DatExporter::toFrameNumber(centiseconds time) const {
|
||||||
|
return 1 + static_cast<int>(frameRate * duration_cast<duration<double>>(time).count());
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Exporter.h"
|
||||||
|
#include "core/Shape.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Exporter for Moho's switch data file format
|
||||||
|
class DatExporter : public Exporter {
|
||||||
|
public:
|
||||||
|
DatExporter(const ShapeSet& targetShapeSet, double frameRate, bool convertToPrestonBlair);
|
||||||
|
void exportAnimation(const ExporterInput& input, std::ostream& outputStream) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int toFrameNumber(centiseconds time) const;
|
||||||
|
std::string toString(Shape shape) const;
|
||||||
|
|
||||||
|
double frameRate;
|
||||||
|
bool convertToPrestonBlair;
|
||||||
|
std::map<Shape, std::string> prestonBlairShapeNames;
|
||||||
|
};
|
|
@ -13,6 +13,7 @@ string ExportFormatConverter::getTypeName() {
|
||||||
|
|
||||||
EnumConverter<ExportFormat>::member_data ExportFormatConverter::getMemberData() {
|
EnumConverter<ExportFormat>::member_data ExportFormatConverter::getMemberData() {
|
||||||
return member_data {
|
return member_data {
|
||||||
|
{ ExportFormat::Dat, "dat" },
|
||||||
{ ExportFormat::Tsv, "tsv" },
|
{ ExportFormat::Tsv, "tsv" },
|
||||||
{ ExportFormat::Xml, "xml" },
|
{ ExportFormat::Xml, "xml" },
|
||||||
{ ExportFormat::Json, "json" }
|
{ ExportFormat::Json, "json" }
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "tools/EnumConverter.h"
|
#include "tools/EnumConverter.h"
|
||||||
|
|
||||||
enum class ExportFormat {
|
enum class ExportFormat {
|
||||||
|
Dat,
|
||||||
Tsv,
|
Tsv,
|
||||||
Xml,
|
Xml,
|
||||||
Json
|
Json
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "tools/textFiles.h"
|
#include "tools/textFiles.h"
|
||||||
#include "lib/rhubarbLib.h"
|
#include "lib/rhubarbLib.h"
|
||||||
#include "ExportFormat.h"
|
#include "ExportFormat.h"
|
||||||
|
#include "exporters/DatExporter.h"
|
||||||
#include "exporters/TsvExporter.h"
|
#include "exporters/TsvExporter.h"
|
||||||
#include "exporters/XmlExporter.h"
|
#include "exporters/XmlExporter.h"
|
||||||
#include "exporters/JsonExporter.h"
|
#include "exporters/JsonExporter.h"
|
||||||
|
@ -82,8 +83,15 @@ unique_ptr<Recognizer> createRecognizer(RecognizerType recognizerType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unique_ptr<Exporter> createExporter(ExportFormat exportFormat) {
|
unique_ptr<Exporter> createExporter(
|
||||||
|
ExportFormat exportFormat,
|
||||||
|
const ShapeSet& targetShapeSet,
|
||||||
|
double datFrameRate,
|
||||||
|
bool datUsePrestonBlair
|
||||||
|
) {
|
||||||
switch (exportFormat) {
|
switch (exportFormat) {
|
||||||
|
case ExportFormat::Dat:
|
||||||
|
return make_unique<DatExporter>(targetShapeSet, datFrameRate, datUsePrestonBlair);
|
||||||
case ExportFormat::Tsv:
|
case ExportFormat::Tsv:
|
||||||
return make_unique<TsvExporter>();
|
return make_unique<TsvExporter>();
|
||||||
case ExportFormat::Xml:
|
case ExportFormat::Xml:
|
||||||
|
@ -172,6 +180,16 @@ int main(int platformArgc, char* platformArgv[]) {
|
||||||
false, string(), "string", cmd
|
false, string(), "string", cmd
|
||||||
);
|
);
|
||||||
|
|
||||||
|
tclap::SwitchArg datUsePrestonBlair(
|
||||||
|
"", "datUsePrestonBlair", "Only for dat exporter: uses the Preston Blair mouth shape names.",
|
||||||
|
cmd, false
|
||||||
|
);
|
||||||
|
|
||||||
|
tclap::ValueArg<double> datFrameRate(
|
||||||
|
"", "datFrameRate", "Only for dat exporter: the desired frame rate.",
|
||||||
|
false, 24.0, "number", cmd
|
||||||
|
);
|
||||||
|
|
||||||
auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
|
auto exportFormats = vector<ExportFormat>(ExportFormatConverter::get().getValues());
|
||||||
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
|
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
|
||||||
tclap::ValueArg<ExportFormat> exportFormat(
|
tclap::ValueArg<ExportFormat> exportFormat(
|
||||||
|
@ -222,6 +240,13 @@ int main(int platformArgc, char* platformArgv[]) {
|
||||||
path inputFilePath(inputFileName.getValue());
|
path inputFilePath(inputFileName.getValue());
|
||||||
ShapeSet targetShapeSet = getTargetShapeSet(extendedShapes.getValue());
|
ShapeSet targetShapeSet = getTargetShapeSet(extendedShapes.getValue());
|
||||||
|
|
||||||
|
unique_ptr<Exporter> exporter = createExporter(
|
||||||
|
exportFormat.getValue(),
|
||||||
|
targetShapeSet,
|
||||||
|
datFrameRate.getValue(),
|
||||||
|
datUsePrestonBlair.getValue()
|
||||||
|
);
|
||||||
|
|
||||||
logging::log(StartEntry(inputFilePath));
|
logging::log(StartEntry(inputFilePath));
|
||||||
logging::debugFormat("Command line: {}",
|
logging::debugFormat("Command line: {}",
|
||||||
join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " "));
|
join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " "));
|
||||||
|
@ -246,7 +271,6 @@ int main(int platformArgc, char* platformArgv[]) {
|
||||||
logging::info("Done animating.");
|
logging::info("Done animating.");
|
||||||
|
|
||||||
// Export animation
|
// Export animation
|
||||||
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
|
|
||||||
optional<boost::filesystem::ofstream> outputFile;
|
optional<boost::filesystem::ofstream> outputFile;
|
||||||
if (outputFileName.isSet()) {
|
if (outputFileName.isSet()) {
|
||||||
outputFile = boost::in_place(outputFileName.getValue());
|
outputFile = boost::in_place(outputFileName.getValue());
|
||||||
|
|
Loading…
Reference in New Issue