From 0c39cda57b4124ba185b3bd18b1b84dd4776684d Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Tue, 29 Jul 2014 16:58:22 -0700 Subject: [PATCH] enable png and mp4 output in SimpleOpenGL3App, see Demos3/SimpleOpenGL3 use commandline parameter --png_file="pngname" or --mp4_file="video.mp4" Thanks to http://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/ --- Demos3/SimpleOpenGL3/main.cpp | 38 ++++-- btgui/OpenGLWindow/SimpleOpenGL3App.cpp | 149 ++++++++++++++++++++++++ btgui/OpenGLWindow/SimpleOpenGL3App.h | 3 + build3/premake4.lua | 2 +- 4 files changed, 182 insertions(+), 10 deletions(-) diff --git a/Demos3/SimpleOpenGL3/main.cpp b/Demos3/SimpleOpenGL3/main.cpp index 91faf454a..530d2b9a5 100644 --- a/Demos3/SimpleOpenGL3/main.cpp +++ b/Demos3/SimpleOpenGL3/main.cpp @@ -1,13 +1,18 @@ -#include "../../btgui/OpenGLWindow/SimpleOpenGL3App.h" +#include "OpenGLWindow/SimpleOpenGL3App.h" #include "Bullet3Common/b3Vector3.h" +#include "Bullet3Common/b3CommandLineArgs.h" #include "assert.h" #include +char* gVideoFileName = 0; +char* gPngFileName = 0; + int main(int argc, char* argv[]) { - + b3CommandLineArgs myArgs(argc,argv); + float dt = 1./120.f; - + SimpleOpenGL3App* app = new SimpleOpenGL3App("SimpleOpenGL3App",1024,768); app->m_instancingRenderer->setCameraDistance(13); app->m_instancingRenderer->setCameraPitch(0); @@ -15,25 +20,40 @@ int main(int argc, char* argv[]) GLint err = glGetError(); assert(err==GL_NO_ERROR); - + + myArgs.GetCmdLineArgument("mp4_file",gVideoFileName); + if (gVideoFileName) + app->dumpFramesToVideo(gVideoFileName); + + myArgs.GetCmdLineArgument("png_file",gPngFileName); + char fileName[1024]; + do { + static int frameCount = 0; + frameCount++; + if (gPngFileName) + { + printf("gPngFileName=%s\n",gPngFileName); + + sprintf(fileName,"%s%d.png",gPngFileName,frameCount++); + app->dumpNextFrameToPng(fileName); + } + GLint err = glGetError(); assert(err==GL_NO_ERROR); app->m_instancingRenderer->init(); app->m_instancingRenderer->updateCamera(); - + app->drawGrid(); char bla[1024]; - static int frameCount = 0; - frameCount++; sprintf(bla,"Simple test frame %d", frameCount); - + app->drawText(bla,10,10); app->swapBuffer(); } while (!app->m_window->requestedExit()); - + delete app; return 0; } diff --git a/btgui/OpenGLWindow/SimpleOpenGL3App.cpp b/btgui/OpenGLWindow/SimpleOpenGL3App.cpp index 0033419e6..265b9958b 100644 --- a/btgui/OpenGLWindow/SimpleOpenGL3App.cpp +++ b/btgui/OpenGLWindow/SimpleOpenGL3App.cpp @@ -13,6 +13,7 @@ #include "OpenGLWindow/X11OpenGLWindow.h" #endif //_WIN32 #endif//__APPLE__ +#include #include "OpenGLWindow/GLPrimitiveRenderer.h" #include "OpenGLWindow/GLInstancingRenderer.h" @@ -24,6 +25,12 @@ #include "OpenGLWindow/TwFonts.h" #include "OpenGLTrueTypeFont/opengl_fontstashcallbacks.h" #include +#include "GLRenderToTexture.h" + +#ifdef _WIN32 + #define popen _popen + #define pclose _pclose +#endif // _WIN32 struct SimpleInternalData { @@ -31,6 +38,9 @@ struct SimpleInternalData struct sth_stash* m_fontStash; OpenGL2RenderCallbacks* m_renderCallbacks; int m_droidRegular; + const char* m_frameDumpPngFileName; + FILE* m_ffmpegFile; + GLRenderToTexture* m_renderTexture; }; @@ -70,6 +80,10 @@ SimpleOpenGL3App::SimpleOpenGL3App( const char* title, int width,int height) { gApp = this; m_data = new SimpleInternalData; + m_data->m_frameDumpPngFileName = 0; + m_data->m_renderTexture = 0; + m_data->m_ffmpegFile = 0; + m_window = new b3gDefaultOpenGLWindow(); b3gWindowConstructionInfo ci; ci.m_title = title; @@ -404,8 +418,143 @@ SimpleOpenGL3App::~SimpleOpenGL3App() delete m_data ; } +//#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "OpenGLTrueTypeFont/stb_image_write.h" +static void writeTextureToFile(int textureWidth, int textureHeight, const char* fileName, FILE* ffmpegVideo) +{ + int numComponents = 4; + //glPixelStorei(GL_PACK_ALIGNMENT,1); + GLuint err=glGetError(); + assert(err==GL_NO_ERROR); + //glReadBuffer(GL_BACK);//COLOR_ATTACHMENT0); + err=glGetError(); + assert(err==GL_NO_ERROR); + float* orgPixels = (float*)malloc(textureWidth*textureHeight*numComponents*4); + glReadPixels(0,0,textureWidth, textureHeight, GL_RGBA, GL_FLOAT, orgPixels); + //it is useful to have the actual float values for debugging purposes + + //convert float->char + char* pixels = (char*)malloc(textureWidth*textureHeight*numComponents); + err=glGetError(); + assert(err==GL_NO_ERROR); + + for (int j=0;jendRendering(); + if (m_data->m_frameDumpPngFileName) + { + writeTextureToFile(m_instancingRenderer->getScreenWidth(), + this->m_instancingRenderer->getScreenHeight(),m_data->m_frameDumpPngFileName, + m_data->m_ffmpegFile); + //m_data->m_renderTexture->disable(); + //if (m_data->m_ffmpegFile==0) + //{ + // m_data->m_frameDumpPngFileName = 0; + //} + } m_window->startRendering(); } +// see also http://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/ +void SimpleOpenGL3App::dumpFramesToVideo(const char* mp4FileName) +{ + int width = m_instancingRenderer->getScreenWidth(); + int height = m_instancingRenderer->getScreenHeight(); + char cmd[8192]; + + sprintf(cmd,"ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s %dx%d -i - " + "-threads 0 -y -crf 0 -b 50000k -vf vflip %s",width,height,mp4FileName); + + // sprintf(cmd,"ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s %dx%d -i - " + // "-threads 0 -preset fast -y -crf 21 -vf vflip %s",width,height,mp4FileName); + + if (m_data->m_ffmpegFile) + { + pclose(m_data->m_ffmpegFile); + } + m_data->m_ffmpegFile = popen(cmd, "w"); + + m_data->m_frameDumpPngFileName = mp4FileName; +} +void SimpleOpenGL3App::dumpNextFrameToPng(const char* filename) +{ + + // open pipe to ffmpeg's stdin in binary write mode + + m_data->m_frameDumpPngFileName = filename; + +//you could use m_renderTexture to allow to render at higher resolutions, such as 4k or so + /*if (!m_data->m_renderTexture) + { + m_data->m_renderTexture = new GLRenderToTexture(); + GLuint renderTextureId; + glGenTextures(1, &renderTextureId); + + // "Bind" the newly created texture : all future texture functions will modify this texture + glBindTexture(GL_TEXTURE_2D, renderTextureId); + + // Give an empty image to OpenGL ( the last "0" ) + //glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, g_OpenGLWidth,g_OpenGLHeight, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0); + //glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA32F, g_OpenGLWidth,g_OpenGLHeight, 0,GL_RGBA, GL_FLOAT, 0); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA32F, + m_instancingRenderer->getScreenWidth(),m_instancingRenderer->getScreenHeight() + , 0,GL_RGBA, GL_FLOAT, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + m_data->m_renderTexture->init(m_instancingRenderer->getScreenWidth(),this->m_instancingRenderer->getScreenHeight(),renderTextureId, RENDERTEXTURE_COLOR); + } + + bool result = m_data->m_renderTexture->enable(); +*/ +} diff --git a/btgui/OpenGLWindow/SimpleOpenGL3App.h b/btgui/OpenGLWindow/SimpleOpenGL3App.h index 0ebf8ed4b..9bfa64e2c 100644 --- a/btgui/OpenGLWindow/SimpleOpenGL3App.h +++ b/btgui/OpenGLWindow/SimpleOpenGL3App.h @@ -38,6 +38,9 @@ struct SimpleOpenGL3App int registerCubeShape(float halfExtentsX=1.f,float halfExtentsY=1.f, float halfExtentsZ = 1.f); int registerGraphicsSphereShape(float radius, bool usePointSprites=true, int largeSphereThreshold=100, int mediumSphereThreshold=10); + void dumpNextFrameToPng(const char* pngFilename); + void dumpFramesToVideo(const char* mp4Filename); + void drawGrid(DrawGridData data=DrawGridData()); void swapBuffer(); void drawText( const char* txt, int posX, int posY); diff --git a/build3/premake4.lua b/build3/premake4.lua index 9d17c5e3c..4f5d5be50 100644 --- a/build3/premake4.lua +++ b/build3/premake4.lua @@ -116,7 +116,7 @@ if findOpenGL() then include "../btgui/OpenGLWindow" -- include "../Demos3/ImplicitCloth" --- include "../Demos3/SimpleOpenGL3" + include "../Demos3/SimpleOpenGL3" include "../btgui/lua-5.2.3" include "../test/lua"