Implement DAT exporter for Moho

This commit is contained in:
Daniel Wolf 2019-06-03 20:42:34 +02:00
parent 6f0e3ef50b
commit fef283de43
6 changed files with 123 additions and 2 deletions

View File

@ -360,6 +360,8 @@ target_link_libraries(rhubarb-core
# ... rhubarb-exporters
add_library(rhubarb-exporters
src/exporters/DatExporter.cpp
src/exporters/DatExporter.h
src/exporters/Exporter.h
src/exporters/exporterTools.cpp
src/exporters/exporterTools.h

View File

@ -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());
}

View File

@ -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;
};

View File

@ -13,6 +13,7 @@ string ExportFormatConverter::getTypeName() {
EnumConverter<ExportFormat>::member_data ExportFormatConverter::getMemberData() {
return member_data {
{ ExportFormat::Dat, "dat" },
{ ExportFormat::Tsv, "tsv" },
{ ExportFormat::Xml, "xml" },
{ ExportFormat::Json, "json" }

View File

@ -3,6 +3,7 @@
#include "tools/EnumConverter.h"
enum class ExportFormat {
Dat,
Tsv,
Xml,
Json

View File

@ -18,6 +18,7 @@
#include "tools/textFiles.h"
#include "lib/rhubarbLib.h"
#include "ExportFormat.h"
#include "exporters/DatExporter.h"
#include "exporters/TsvExporter.h"
#include "exporters/XmlExporter.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) {
case ExportFormat::Dat:
return make_unique<DatExporter>(targetShapeSet, datFrameRate, datUsePrestonBlair);
case ExportFormat::Tsv:
return make_unique<TsvExporter>();
case ExportFormat::Xml:
@ -172,6 +180,16 @@ int main(int platformArgc, char* platformArgv[]) {
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());
tclap::ValuesConstraint<ExportFormat> exportFormatConstraint(exportFormats);
tclap::ValueArg<ExportFormat> exportFormat(
@ -222,6 +240,13 @@ int main(int platformArgc, char* platformArgv[]) {
path inputFilePath(inputFileName.getValue());
ShapeSet targetShapeSet = getTargetShapeSet(extendedShapes.getValue());
unique_ptr<Exporter> exporter = createExporter(
exportFormat.getValue(),
targetShapeSet,
datFrameRate.getValue(),
datUsePrestonBlair.getValue()
);
logging::log(StartEntry(inputFilePath));
logging::debugFormat("Command line: {}",
join(args | transformed([](string arg) { return fmt::format("\"{}\"", arg); }), " "));
@ -246,7 +271,6 @@ int main(int platformArgc, char* platformArgv[]) {
logging::info("Done animating.");
// Export animation
unique_ptr<Exporter> exporter = createExporter(exportFormat.getValue());
optional<boost::filesystem::ofstream> outputFile;
if (outputFileName.isSet()) {
outputFile = boost::in_place(outputFileName.getValue());