#pragma once #include "Zip.h" #include "Atlas.h" #include "conversion.h" #include "Artboard.h" #include "ofEasyCam.h" #include "ofMain.h" #include "ofQuaternion.h" #include "ofTrueTypeFont.h" #include "ofxVariableLab.h" #include "ofxProfiler.h" #include #ifdef TARGET_EMSCRIPTEN #include #include #include #include #endif using namespace msdfgen; // TODO: better antialias // possibly just draw a bigger fbo? // or do it properly: // https://github.com/emscripten-core/emscripten/issues/7898 // // TODO: fix linux build namespace VariableEditor { struct AppSettings { std::array backgroundColor = {212 / 255.0, 212 / 255.0, 212 / 255.0, 1}; // check data/appSettings.json string tmpExportDir = "data/export"; string tmpImportDir = "data/import"; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AppSettings, backgroundColor, tmpExportDir); }; class ZipProjectSaver : public ofThread { public: void setup(string projectName, string projectJsonString){ this->projectName = projectName; this->projectJsonString = projectJsonString; this->userFontsPath = "/idbfs/fonts"; this->timestamp = ofGetTimestampString(); this->filename = projectName + "_project_" + timestamp + ".zip"; } void update(){ if(freshDownload.load()){ emscripten_browser_file::download(filename.c_str(), "application/zip", buffer, buffer_size); freshDownload.store(false); } } void threadedFunction(){ Zip zip(filename.c_str()); ofDisableDataPath(); { char buffy[projectJsonString.length()]; projectJsonString.copy(buffy, projectJsonString.length()); zip.addBuffer("project.json", buffy, projectJsonString.length()); } ofDirectory userFonts(userFontsPath); userFonts.sort(); userFonts.allowExt("ttf"); userFonts.allowExt("otf"); userFonts.listDir(); for(int i = 0; i < userFonts.size(); i++){ std::string fontFilename = userFonts.getName(i); std::string fontFilepath = userFontsPath + "/" + fontFilename; ofFile file = userFonts.getFile(i); ofStringReplace(fontFilepath, "/idbfs/", "idbfs/"); if(of::filesystem::exists(fontFilepath)){ //cout << "huuurrayy " << fontFilepath << " exists" << endl; }else{ cout << "ofApp::downloadProject() trying to load " << fontFilepath << " but it does not exist." << endl; } file.open(fontFilepath); ofBuffer buffy = file.readToBuffer(); zip.addBuffer(fontFilename, buffy.getData(), buffy.size()); } buffer = NULL; buffer_size = 0; zip.getOutputBuffer(&buffer, buffer_size); zip.close(); ofEnableDataPath(); freshDownload.store(true); } void exit(){ free(buffer); } string projectName; string projectJsonString; string userFontsPath; string timestamp; string filename; char * buffer; size_t buffer_size; std::atomic freshDownload{false}; std::atomic percent{0}; }; class ZipSaver : public ofThread { public: void setup(string projectName, string framePath, vector recordedFrameNames){ this->projectName = projectName; this->framePath = framePath; this->recordedFrameNames = recordedFrameNames; this->timestamp = ofGetTimestampString(); this->filename = projectName + "_frames_" + timestamp + ".zip"; } void update(){ if(freshDownload.load()){ EM_ASM({ document.getElementById('export_progress_task').innerHTML = 'rendering'; let innerHTML = "|"; for(let i = 0; i < 100; i++){ innerHTML += "-"; } innerHTML += "|"; let progress = document.getElementById("export_progress"); progress.innerHTML = innerHTML; let progress_task = document.getElementById("export_progress_task"); progress_task.innerHTML = "idle"; }); emscripten_browser_file::download(filename.c_str(), "application/zip", buffer, buffer_size); freshDownload.store(false); }else if(isThreadRunning()){ setProgress(percent.load()); } } void setProgress(int percent){ EM_ASM_INT({ let percent = $0; document.getElementById('export_progress_task').innerHTML = 'rendering'; let innerHTML = "|"; const niceText = "zip "; for(let i = 0; i < 100; i++){ if(i < percent){ innerHTML += niceText[i % niceText.length]; }else{ innerHTML += "-"; } } innerHTML += "|"; let progress = document.getElementById("export_progress"); progress.innerHTML = innerHTML; let progress_task = document.getElementById("export_progress_task"); progress_task.innerHTML = "creating zip file"; }, percent); } void threadedFunction(){ Zip zip(filename.c_str()); ofDisableDataPath(); int total = recordedFrameNames.size(); int i = 0; for(const std::string & f: recordedFrameNames){ std::string filepath = framePath + "/" + f + ".png"; if(of::filesystem::exists(filepath)){ //cout << "huuurrayy " << filepath << " exists" << endl; }else{ cout << "ofApp::downloadFramesAsZip() trying to load " << filepath << " but it does not exist." << endl; } ofImage image; image.setUseTexture(false); image.load(filepath); ofBuffer buffer; ofSaveImage(image.getPixels(), buffer, OF_IMAGE_FORMAT_PNG); zip.addBuffer(f + ".png", buffer.getData(), buffer.size()); percent.store((float(i) / float(total)) * 100.0f); i++; } buffer = NULL; buffer_size = 0; zip.getOutputBuffer(&buffer, buffer_size); zip.close(); ofEnableDataPath(); freshDownload.store(true); } void exit(){ free(buffer); } string projectName; string framePath; string timestamp; string filename; std::vector recordedFrameNames; char * buffer; size_t buffer_size; std::atomic freshDownload{false}; std::atomic percent{0}; }; class ZipUnpacker : public ofThread { public: void setup(){ this->timestamp = ofGetTimestampString(); } void update(){ if(freshUpload.load()){ freshUpload.store(false); }else if(isThreadRunning()){ //setProgress(percent.load()); } } void setProgress(int percent){ EM_ASM_INT({ let percent = $0; document.getElementById('export_progress_task').innerHTML = 'uploading and unpacking'; let innerHTML = "|"; const niceText = "zip "; for(let i = 0; i < 100; i++){ if(i < percent){ innerHTML += niceText[i % niceText.length]; }else{ innerHTML += "-"; } } innerHTML += "|"; let progress = document.getElementById("import_progress"); progress.innerHTML = innerHTML; let progress_task = document.getElementById("import_progress_task"); progress_task.innerHTML = "creating zip file"; }, percent); } void threadedFunction(){ ofDisableDataPath(); } void exit(){ free(buffer); } string timestamp; char * buffer; size_t buffer_size; std::atomic freshUpload{false}; std::atomic percent{0}; }; class ofApp : public ofBaseApp { public: void setup() override; void update() override; void draw() override; void drawGrid(); void setPlaying(bool playing); void keyPressed(int key) override; void keyReleased(int key) override; void mouseMoved(int x, int y) override; void mouseDragged(int x, int y, int button) override; void mousePressed(int x, int y, int button) override; void mouseReleased(int x, int y, int button) override; void mouseEntered(int x, int y) override; void mouseExited(int x, int y) override; void mouseScrolled(ofMouseEventArgs & mouse) override; void mouseScrolled(int x, int y, float scrollX, float scrollY) override; void windowResized(int w, int h) override; void dragEvent(ofDragInfo dragInfo) override; void gotMessage(ofMessage msg) override; void saveFrame(const string & filename, const ofFbo & _fbo); #ifdef TARGET_EMSCRIPTEN void downloadFrame(const string & filename); void downloadFramesAsZip(const string & projectName = "project"); static void downloadImage(const string & filename, const ofImage & image); static void downloadFboAsImage(const string & filename, const ofFbo & _fbo); static void downloadPixelsAsImage(const string & filename, const ofPixels & image); static void downloadJson(const string & filename, const nlohmann::json & json); void downloadProject(const string & projectName, const string & projectJsonString); #endif void exit() override; ofxVariableLab::LayerComposition layerComposition; ofCamera cam; ofFboSettings fboSettings; ofFbo fbo; ofEasyCam observer; bool observing = false; bool playing = true; bool rendering = false; bool renderNextFrame = false; int guessedFrameCount = 30; double timelineLength_seconds = 0; int currentFrame = 0; double theatrePosition = 0; double timeScale = 1.0; ofImage image42; ofImage image420; AppSettings settings; ofTrueTypeFont ttf; Artboard artboard; std::set inputPressed; // EXPORTER std::vector recordedFrameNames; ZipSaver zipSaver; ZipProjectSaver projectZipSaver; }; }