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
|
||||
add_library(rhubarb-exporters
|
||||
src/exporters/DatExporter.cpp
|
||||
src/exporters/DatExporter.h
|
||||
src/exporters/Exporter.h
|
||||
src/exporters/exporterTools.cpp
|
||||
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() {
|
||||
return member_data {
|
||||
{ ExportFormat::Dat, "dat" },
|
||||
{ ExportFormat::Tsv, "tsv" },
|
||||
{ ExportFormat::Xml, "xml" },
|
||||
{ ExportFormat::Json, "json" }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "tools/EnumConverter.h"
|
||||
|
||||
enum class ExportFormat {
|
||||
Dat,
|
||||
Tsv,
|
||||
Xml,
|
||||
Json
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue