bullet3/examples/OpenGLWindow/SimpleOpenGL3App.cpp
erwincoumans ae8e83988b Add preliminary PhysX 4.0 backend for PyBullet
Add inverse dynamics / mass matrix code from DeepMimic, thanks to Xue Bin (Jason) Peng
Add example how to use stable PD control for humanoid with spherical joints (see humanoidMotionCapture.py)
Fix related to TinyRenderer object transforms not updating when using collision filtering
2019-01-22 21:08:37 -08:00

1188 lines
36 KiB
C++

#ifndef NO_OPENGL3
#include "SimpleOpenGL3App.h"
#include "ShapeData.h"
#ifdef BT_USE_EGL
#include "EGLOpenGLWindow.h"
#else
#endif //BT_USE_EGL
#ifdef B3_USE_GLFW
#include "GLFWOpenGLWindow.h"
#else
#ifdef __APPLE__
#include "MacOpenGLWindow.h"
#else
#ifdef _WIN32
#include "Win32OpenGLWindow.h"
#else
//let's cross the fingers it is Linux/X11
#include "X11OpenGLWindow.h"
#define BT_USE_X11 // for runtime backend selection, move to build?
#endif //_WIN32
#endif //__APPLE__
#endif //B3_USE_GLFW
#include <stdio.h>
#include "GLPrimitiveRenderer.h"
#include "GLInstancingRenderer.h"
#include "Bullet3Common/b3Vector3.h"
#include "Bullet3Common/b3Logging.h"
#include "fontstash.h"
#include "TwFonts.h"
#include "opengl_fontstashcallbacks.h"
#include <assert.h>
#include "GLRenderToTexture.h"
#include "Bullet3Common/b3Quaternion.h"
#include <string.h> //memset
#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#endif // _WIN32
struct SimpleInternalData
{
GLuint m_fontTextureId;
GLuint m_largeFontTextureId;
struct sth_stash* m_fontStash;
struct sth_stash* m_fontStash2;
RenderCallbacks* m_renderCallbacks;
RenderCallbacks* m_renderCallbacks2;
int m_droidRegular;
int m_droidRegular2;
int m_textureId;
const char* m_frameDumpPngFileName;
FILE* m_ffmpegFile;
GLRenderToTexture* m_renderTexture;
void* m_userPointer;
int m_upAxis; //y=1 or z=2 is supported
int m_customViewPortWidth;
int m_customViewPortHeight;
SimpleInternalData()
: m_fontTextureId(0),
m_largeFontTextureId(0),
m_fontStash(0),
m_fontStash2(0),
m_renderCallbacks(0),
m_renderCallbacks2(0),
m_droidRegular(0),
m_droidRegular2(0),
m_textureId(-1),
m_frameDumpPngFileName(0),
m_ffmpegFile(0),
m_renderTexture(0),
m_userPointer(0),
m_upAxis(1),
m_customViewPortWidth(-1),
m_customViewPortHeight(-1)
{
}
};
static SimpleOpenGL3App* gApp = 0;
static void SimpleResizeCallback(float widthf, float heightf)
{
int width = (int)widthf;
int height = (int)heightf;
if (gApp && gApp->m_instancingRenderer)
gApp->m_instancingRenderer->resize(width, height);
if (gApp && gApp->m_primRenderer)
gApp->m_primRenderer->setScreenSize(width, height);
}
static void SimpleKeyboardCallback(int key, int state)
{
if (key == B3G_ESCAPE && gApp && gApp->m_window)
{
gApp->m_window->setRequestExit();
}
else
{
//gApp->defaultKeyboardCallback(key,state);
}
}
void SimpleMouseButtonCallback(int button, int state, float x, float y)
{
gApp->defaultMouseButtonCallback(button, state, x, y);
}
void SimpleMouseMoveCallback(float x, float y)
{
gApp->defaultMouseMoveCallback(x, y);
}
void SimpleWheelCallback(float deltax, float deltay)
{
gApp->defaultWheelCallback(deltax, deltay);
}
static GLuint BindFont(const CTexFont* _Font)
{
GLuint TexID = 0;
glGenTextures(1, &TexID);
glBindTexture(GL_TEXTURE_2D, TexID);
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, _Font->m_TexWidth, _Font->m_TexHeight, 0, GL_RED, GL_UNSIGNED_BYTE, _Font->m_TexBytes);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
return TexID;
}
//static unsigned int s_indexData[INDEX_COUNT];
//static GLuint s_indexArrayObject, s_indexBuffer;
//static GLuint s_vertexArrayObject,s_vertexBuffer;
extern unsigned char OpenSansData[];
struct MyRenderCallbacks : public RenderCallbacks
{
GLInstancingRenderer* m_instancingRenderer;
b3AlignedObjectArray<unsigned char> m_rgbaTexture;
float m_color[4];
float m_worldPosition[3];
float m_worldOrientation[4];
int m_textureIndex;
MyRenderCallbacks(GLInstancingRenderer* instancingRenderer)
: m_instancingRenderer(instancingRenderer),
m_textureIndex(-1)
{
for (int i = 0; i < 4; i++)
{
m_color[i] = 1;
m_worldOrientation[i] = 0;
}
m_worldPosition[0] = 0;
m_worldPosition[1] = 0;
m_worldPosition[2] = 0;
m_worldOrientation[0] = 0;
m_worldOrientation[1] = 0;
m_worldOrientation[2] = 0;
m_worldOrientation[3] = 1;
}
virtual ~MyRenderCallbacks()
{
m_rgbaTexture.clear();
}
virtual void setWorldPosition(float pos[3])
{
for (int i = 0; i < 3; i++)
{
m_worldPosition[i] = pos[i];
}
}
virtual void setWorldOrientation(float orn[4])
{
for (int i = 0; i < 4; i++)
{
m_worldOrientation[i] = orn[i];
}
}
virtual void setColorRGBA(float color[4])
{
for (int i = 0; i < 4; i++)
{
m_color[i] = color[i];
}
}
virtual void updateTexture(sth_texture* texture, sth_glyph* glyph, int textureWidth, int textureHeight)
{
if (glyph)
{
m_rgbaTexture.resize(textureWidth * textureHeight * 3);
for (int i = 0; i < textureWidth * textureHeight; i++)
{
m_rgbaTexture[i * 3 + 0] = texture->m_texels[i];
m_rgbaTexture[i * 3 + 1] = texture->m_texels[i];
m_rgbaTexture[i * 3 + 2] = texture->m_texels[i];
}
bool flipPixelsY = false;
m_instancingRenderer->updateTexture(m_textureIndex, &m_rgbaTexture[0], flipPixelsY);
}
else
{
if (textureWidth && textureHeight)
{
texture->m_texels = (unsigned char*)malloc(textureWidth * textureHeight);
memset(texture->m_texels, 0, textureWidth * textureHeight);
if (m_textureIndex < 0)
{
m_rgbaTexture.resize(textureWidth * textureHeight * 3);
bool flipPixelsY = false;
m_textureIndex = m_instancingRenderer->registerTexture(&m_rgbaTexture[0], textureWidth, textureHeight, flipPixelsY);
int strideInBytes = 9 * sizeof(float);
int numVertices = sizeof(cube_vertices_textured) / strideInBytes;
int numIndices = sizeof(cube_indices) / sizeof(int);
float halfExtentsX = 1;
float halfExtentsY = 1;
float halfExtentsZ = 1;
float textureScaling = 4;
b3AlignedObjectArray<GfxVertexFormat1> verts;
verts.resize(numVertices);
for (int i = 0; i < numVertices; i++)
{
verts[i].x = halfExtentsX * cube_vertices_textured[i * 9];
verts[i].y = halfExtentsY * cube_vertices_textured[i * 9 + 1];
verts[i].z = halfExtentsZ * cube_vertices_textured[i * 9 + 2];
verts[i].w = cube_vertices_textured[i * 9 + 3];
verts[i].nx = cube_vertices_textured[i * 9 + 4];
verts[i].ny = cube_vertices_textured[i * 9 + 5];
verts[i].nz = cube_vertices_textured[i * 9 + 6];
verts[i].u = cube_vertices_textured[i * 9 + 7] * textureScaling;
verts[i].v = cube_vertices_textured[i * 9 + 8] * textureScaling;
}
int shapeId = m_instancingRenderer->registerShape(&verts[0].x, numVertices, cube_indices, numIndices, B3_GL_TRIANGLES, m_textureIndex);
b3Vector3 pos = b3MakeVector3(0, 0, 0);
b3Quaternion orn(0, 0, 0, 1);
b3Vector4 color = b3MakeVector4(1, 1, 1, 1);
b3Vector3 scaling = b3MakeVector3(.1, .1, .1);
//m_instancingRenderer->registerGraphicsInstance(shapeId, pos, orn, color, scaling);
m_instancingRenderer->writeTransforms();
}
else
{
b3Assert(0);
}
}
else
{
delete texture->m_texels;
texture->m_texels = 0;
//there is no m_instancingRenderer->freeTexture (yet), all textures are released at reset/deletion of the renderer
}
}
}
virtual void render(sth_texture* texture)
{
int index = 0;
float width = 1;
b3AlignedObjectArray<unsigned int> indices;
indices.resize(texture->nverts);
for (int i = 0; i < indices.size(); i++)
{
indices[i] = i;
}
m_instancingRenderer->drawTexturedTriangleMesh(m_worldPosition, m_worldOrientation, &texture->newverts[0].position.p[0], texture->nverts, &indices[0], indices.size(), m_color, m_textureIndex);
}
};
static void printGLString(const char* name, GLenum s)
{
const char* v = (const char*)glGetString(s);
printf("%s = %s\n", name, v);
}
bool sOpenGLVerbose = true;
SimpleOpenGL3App::SimpleOpenGL3App(const char* title, int width, int height, bool allowRetina, int windowType, int renderDevice, int maxNumObjectCapacity, int maxShapeCapacityInBytes)
{
gApp = this;
m_data = new SimpleInternalData;
if (windowType == 0)
{
m_window = new b3gDefaultOpenGLWindow();
}
else if (windowType == 1)
{
#ifdef BT_USE_X11
m_window = new X11OpenGLWindow();
#else
b3Warning("X11 requires Linux. Loading default window instead. \n");
m_window = new b3gDefaultOpenGLWindow();
#endif
}
else if (windowType == 2)
{
#ifdef BT_USE_EGL
m_window = new EGLOpenGLWindow();
#else
b3Warning("EGL window requires compilation with BT_USE_EGL.\n");
b3Warning("Loading default window instead. \n");
m_window = new b3gDefaultOpenGLWindow();
#endif
}
else
{
b3Warning("Unknown window type %d must be (0=default, 1=X11, 2=EGL).\n", windowType);
b3Warning("Loading default window instead. \n");
m_window = new b3gDefaultOpenGLWindow();
}
m_window->setAllowRetina(allowRetina);
b3gWindowConstructionInfo ci;
ci.m_title = title;
ci.m_width = width;
ci.m_height = height;
ci.m_renderDevice = renderDevice;
m_window->createWindow(ci);
m_window->setWindowTitle(title);
b3Assert(glGetError() == GL_NO_ERROR);
{
printGLString("Version", GL_VERSION);
printGLString("Vendor", GL_VENDOR);
printGLString("Renderer", GL_RENDERER);
}
glClearColor(m_backgroundColorRGB[0],
m_backgroundColorRGB[1],
m_backgroundColorRGB[2],
1.f);
m_window->startRendering();
width = m_window->getWidth();
height = m_window->getHeight();
b3Assert(glGetError() == GL_NO_ERROR);
//gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
#ifdef USE_GLEW
#ifndef __APPLE__
#ifndef _WIN32
#ifndef B3_USE_GLFW
//some Linux implementations need the 'glewExperimental' to be true
glewExperimental = GL_TRUE;
#endif //B3_USE_GLFW
#endif //_WIN32
#ifndef B3_USE_GLFW
if (glewInit() != GLEW_OK)
exit(1); // or handle the error in a nicer way
if (!GLEW_VERSION_2_1) // check that the machine supports the 2.1 API.
exit(1); // or handle the error in a nicer way
#endif //B3_USE_GLFW
#endif //__APPLE__
#endif //USE_GLEW
glGetError(); //don't remove this call, it is needed for Ubuntu
b3Assert(glGetError() == GL_NO_ERROR);
m_parameterInterface = 0;
b3Assert(glGetError() == GL_NO_ERROR);
m_instancingRenderer = new GLInstancingRenderer(maxNumObjectCapacity, maxShapeCapacityInBytes);
m_primRenderer = new GLPrimitiveRenderer(width, height);
m_renderer = m_instancingRenderer;
m_window->setResizeCallback(SimpleResizeCallback);
m_instancingRenderer->init();
m_instancingRenderer->resize(width, height);
m_primRenderer->setScreenSize(width, height);
b3Assert(glGetError() == GL_NO_ERROR);
m_instancingRenderer->InitShaders();
m_window->setMouseMoveCallback(SimpleMouseMoveCallback);
m_window->setMouseButtonCallback(SimpleMouseButtonCallback);
m_window->setKeyboardCallback(SimpleKeyboardCallback);
m_window->setWheelCallback(SimpleWheelCallback);
TwGenerateDefaultFonts();
m_data->m_fontTextureId = BindFont(g_DefaultNormalFont);
m_data->m_largeFontTextureId = BindFont(g_DefaultLargeFont);
{
m_data->m_renderCallbacks = new OpenGL2RenderCallbacks(m_primRenderer);
m_data->m_renderCallbacks2 = new MyRenderCallbacks(m_instancingRenderer);
m_data->m_fontStash2 = sth_create(512, 512, m_data->m_renderCallbacks2);
m_data->m_fontStash = sth_create(512, 512, m_data->m_renderCallbacks); //256,256);//,1024);//512,512);
b3Assert(glGetError() == GL_NO_ERROR);
if (!m_data->m_fontStash)
{
b3Warning("Could not create stash");
//fprintf(stderr, "Could not create stash.\n");
}
if (!m_data->m_fontStash2)
{
b3Warning("Could not create fontStash2");
}
unsigned char* data2 = OpenSansData;
unsigned char* data = (unsigned char*)data2;
if (!(m_data->m_droidRegular = sth_add_font_from_memory(m_data->m_fontStash, data)))
{
b3Warning("error!\n");
}
if (!(m_data->m_droidRegular2 = sth_add_font_from_memory(m_data->m_fontStash2, data)))
{
b3Warning("error!\n");
}
b3Assert(glGetError() == GL_NO_ERROR);
}
}
struct sth_stash* SimpleOpenGL3App::getFontStash()
{
return m_data->m_fontStash;
}
void SimpleOpenGL3App::drawText3D(const char* txt, float position[3], float orientation[4], float color[4], float size, int optionFlag)
{
B3_PROFILE("SimpleOpenGL3App::drawText3D");
float viewMat[16];
float projMat[16];
CommonCameraInterface* cam = m_instancingRenderer->getActiveCamera();
cam->getCameraViewMatrix(viewMat);
cam->getCameraProjectionMatrix(projMat);
float camPos[4];
cam->getCameraPosition(camPos);
//b3Vector3 cp= b3MakeVector3(camPos[0],camPos[2],camPos[1]);
//b3Vector3 p = b3MakeVector3(worldPosX,worldPosY,worldPosZ);
//float dist = (cp-p).length();
//float dv = 0;//dist/1000.f;
//
//printf("str = %s\n",unicodeText);
float dx = 0;
//int measureOnly=0;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
int viewport[4] = {0, 0, m_instancingRenderer->getScreenWidth(), m_instancingRenderer->getScreenHeight()};
float posX = position[0];
float posY = position[1];
float posZ = position[2];
float winx, winy, winz;
if (optionFlag & CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera)
{
if (!projectWorldCoordToScreen(position[0], position[1], position[2], viewMat, projMat, viewport, &winx, &winy, &winz))
{
return;
}
posX = winx;
posY = m_instancingRenderer->getScreenHeight() / 2 + (m_instancingRenderer->getScreenHeight() / 2) - winy;
posZ = 0.f;
}
if (optionFlag & CommonGraphicsApp::eDrawText3D_TrueType)
{
bool measureOnly = false;
float fontSize = 64; //512;//128;
if (optionFlag & CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera)
{
sth_draw_text(m_data->m_fontStash,
m_data->m_droidRegular, fontSize, posX, posY,
txt, &dx, this->m_instancingRenderer->getScreenWidth(), this->m_instancingRenderer->getScreenHeight(), measureOnly, m_window->getRetinaScale(), color);
sth_end_draw(m_data->m_fontStash);
sth_flush_draw(m_data->m_fontStash);
}
else
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_data->m_renderCallbacks2->setColorRGBA(color);
m_data->m_renderCallbacks2->setWorldPosition(position);
m_data->m_renderCallbacks2->setWorldOrientation(orientation);
sth_draw_text3D(m_data->m_fontStash2,
m_data->m_droidRegular2, fontSize, 0, 0, 0,
txt, &dx, size, color, 0);
sth_end_draw(m_data->m_fontStash2);
sth_flush_draw(m_data->m_fontStash2);
glDisable(GL_BLEND);
}
}
else
{
//float width = 0.f;
int pos = 0;
//float color[]={0.2f,0.2,0.2f,1.f};
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_data->m_largeFontTextureId);
//float width = r.x;
//float extraSpacing = 0.;
float startX = posX;
float startY = posY + g_DefaultLargeFont->m_CharHeight * size;
float z = position[2]; //2.f*winz-1.f;//*(far
if (optionFlag & CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera)
{
posX = winx;
posY = m_instancingRenderer->getScreenHeight() / 2 + (m_instancingRenderer->getScreenHeight() / 2) - winy;
z = 2.f * winz - 1.f;
startY = posY - g_DefaultLargeFont->m_CharHeight * size;
}
while (txt[pos])
{
int c = txt[pos];
//r.h = g_DefaultNormalFont->m_CharHeight;
//r.w = g_DefaultNormalFont->m_CharWidth[c]+extraSpacing;
float endX = startX + g_DefaultLargeFont->m_CharWidth[c] * size;
if (optionFlag & CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera)
{
endX = startX + g_DefaultLargeFont->m_CharWidth[c] * size;
}
float endY = posY;
//float currentColor[]={1.f,1.,0.2f,1.f};
// m_primRenderer->drawTexturedRect(startX, startY, endX, endY, currentColor,g_DefaultLargeFont->m_CharU0[c],g_DefaultLargeFont->m_CharV0[c],g_DefaultLargeFont->m_CharU1[c],g_DefaultLargeFont->m_CharV1[c]);
float u0 = g_DefaultLargeFont->m_CharU0[c];
float u1 = g_DefaultLargeFont->m_CharU1[c];
float v0 = g_DefaultLargeFont->m_CharV0[c];
float v1 = g_DefaultLargeFont->m_CharV1[c];
//float color[4] = {currentColor[0],currentColor[1],currentColor[2],currentColor[3]};
float x0 = startX;
float x1 = endX;
float y0 = startY;
float y1 = endY;
int screenWidth = m_instancingRenderer->getScreenWidth();
int screenHeight = m_instancingRenderer->getScreenHeight();
float identity[16] = {1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1};
if (optionFlag & CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera)
{
PrimVertex vertexData[4] = {
PrimVertex(PrimVec4(-1.f + 2.f * x0 / float(screenWidth), 1.f - 2.f * y0 / float(screenHeight), z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u0, v0)),
PrimVertex(PrimVec4(-1.f + 2.f * x0 / float(screenWidth), 1.f - 2.f * y1 / float(screenHeight), z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u0, v1)),
PrimVertex(PrimVec4(-1.f + 2.f * x1 / float(screenWidth), 1.f - 2.f * y1 / float(screenHeight), z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u1, v1)),
PrimVertex(PrimVec4(-1.f + 2.f * x1 / float(screenWidth), 1.f - 2.f * y0 / float(screenHeight), z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u1, v0))};
m_primRenderer->drawTexturedRect3D(vertexData[0], vertexData[1], vertexData[2], vertexData[3], identity, identity, false);
}
else
{
PrimVertex vertexData[4] = {
PrimVertex(PrimVec4(x0, y0, z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u0, v0)),
PrimVertex(PrimVec4(x0, y1, z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u0, v1)),
PrimVertex(PrimVec4(x1, y1, z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u1, v1)),
PrimVertex(PrimVec4(x1, y0, z, 1.f), PrimVec4(color[0], color[1], color[2], color[3]), PrimVec2(u1, v0))};
m_primRenderer->drawTexturedRect3D(vertexData[0], vertexData[1], vertexData[2], vertexData[3], viewMat, projMat, false);
}
//DrawTexturedRect(0,r,g_DefaultNormalFont->m_CharU0[c],g_DefaultNormalFont->m_CharV0[c],g_DefaultNormalFont->m_CharU1[c],g_DefaultNormalFont->m_CharV1[c]);
// DrawFilledRect(r);
startX = endX;
//startY = endY;
pos++;
}
glBindTexture(GL_TEXTURE_2D, 0);
}
glDisable(GL_BLEND);
}
void SimpleOpenGL3App::drawText3D(const char* txt, float worldPosX, float worldPosY, float worldPosZ, float size1)
{
float position[3] = {worldPosX, worldPosY, worldPosZ};
float orientation[4] = {0, 0, 0, 1};
float color[4] = {0, 0, 0, 1};
int optionFlags = CommonGraphicsApp::eDrawText3D_OrtogonalFaceCamera;
drawText3D(txt, position, orientation, color, size1, optionFlags);
}
void SimpleOpenGL3App::drawText(const char* txt, int posXi, int posYi, float size, float colorRGBA[4])
{
float posX = (float)posXi;
float posY = (float)posYi;
//
//printf("str = %s\n",unicodeText);
float dx = 0;
//int measureOnly=0;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (1) //m_useTrueTypeFont)
{
bool measureOnly = false;
float fontSize = 64 * size; //512;//128;
sth_draw_text(m_data->m_fontStash,
m_data->m_droidRegular, fontSize, posX, posY,
txt, &dx, this->m_instancingRenderer->getScreenWidth(),
this->m_instancingRenderer->getScreenHeight(),
measureOnly,
m_window->getRetinaScale(), colorRGBA);
sth_end_draw(m_data->m_fontStash);
sth_flush_draw(m_data->m_fontStash);
}
else
{
//float width = 0.f;
int pos = 0;
//float color[]={0.2f,0.2,0.2f,1.f};
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_data->m_largeFontTextureId);
//float width = r.x;
//float extraSpacing = 0.;
float startX = posX;
float startY = posY;
while (txt[pos])
{
int c = txt[pos];
//r.h = g_DefaultNormalFont->m_CharHeight;
//r.w = g_DefaultNormalFont->m_CharWidth[c]+extraSpacing;
float endX = startX + g_DefaultLargeFont->m_CharWidth[c];
float endY = startY + g_DefaultLargeFont->m_CharHeight;
float currentColor[] = {0.2f, 0.2, 0.2f, 1.f};
m_primRenderer->drawTexturedRect(startX, startY, endX, endY, currentColor, g_DefaultLargeFont->m_CharU0[c], g_DefaultLargeFont->m_CharV0[c], g_DefaultLargeFont->m_CharU1[c], g_DefaultLargeFont->m_CharV1[c]);
//DrawTexturedRect(0,r,g_DefaultNormalFont->m_CharU0[c],g_DefaultNormalFont->m_CharV0[c],g_DefaultNormalFont->m_CharU1[c],g_DefaultNormalFont->m_CharV1[c]);
// DrawFilledRect(r);
startX = endX;
//startY = endY;
pos++;
}
glBindTexture(GL_TEXTURE_2D, 0);
}
glDisable(GL_BLEND);
}
void SimpleOpenGL3App::drawTexturedRect(float x0, float y0, float x1, float y1, float color[4], float u0, float v0, float u1, float v1, int useRGBA)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_primRenderer->drawTexturedRect(x0, y0, x1, y1, color, u0, v0, u1, v1, useRGBA);
glDisable(GL_BLEND);
}
int SimpleOpenGL3App::registerCubeShape(float halfExtentsX, float halfExtentsY, float halfExtentsZ, int textureIndex, float textureScaling)
{
int strideInBytes = 9 * sizeof(float);
int numVertices = sizeof(cube_vertices_textured) / strideInBytes;
int numIndices = sizeof(cube_indices) / sizeof(int);
b3AlignedObjectArray<GfxVertexFormat1> verts;
verts.resize(numVertices);
for (int i = 0; i < numVertices; i++)
{
verts[i].x = halfExtentsX * cube_vertices_textured[i * 9];
verts[i].y = halfExtentsY * cube_vertices_textured[i * 9 + 1];
verts[i].z = halfExtentsZ * cube_vertices_textured[i * 9 + 2];
verts[i].w = cube_vertices_textured[i * 9 + 3];
verts[i].nx = cube_vertices_textured[i * 9 + 4];
verts[i].ny = cube_vertices_textured[i * 9 + 5];
verts[i].nz = cube_vertices_textured[i * 9 + 6];
verts[i].u = cube_vertices_textured[i * 9 + 7] * textureScaling;
verts[i].v = cube_vertices_textured[i * 9 + 8] * textureScaling;
}
int shapeId = m_instancingRenderer->registerShape(&verts[0].x, numVertices, cube_indices, numIndices, B3_GL_TRIANGLES, textureIndex);
return shapeId;
}
void SimpleOpenGL3App::registerGrid(int cells_x, int cells_z, float color0[4], float color1[4])
{
b3Vector3 cubeExtents = b3MakeVector3(0.5, 0.5, 0.5);
double halfHeight = 0.1;
cubeExtents[m_data->m_upAxis] = halfHeight;
int cubeId = registerCubeShape(cubeExtents[0], cubeExtents[1], cubeExtents[2]);
b3Quaternion orn(0, 0, 0, 1);
b3Vector3 center = b3MakeVector3(0, 0, 0, 1);
b3Vector3 scaling = b3MakeVector3(1, 1, 1, 1);
for (int i = 0; i < cells_x; i++)
{
for (int j = 0; j < cells_z; j++)
{
float* color = 0;
if ((i + j) % 2 == 0)
{
color = (float*)color0;
}
else
{
color = (float*)color1;
}
if (this->m_data->m_upAxis == 1)
{
center = b3MakeVector3((i + 0.5f) - cells_x * 0.5f, -halfHeight, (j + 0.5f) - cells_z * 0.5f);
}
else
{
center = b3MakeVector3((i + 0.5f) - cells_x * 0.5f, (j + 0.5f) - cells_z * 0.5f, -halfHeight);
}
m_instancingRenderer->registerGraphicsInstance(cubeId, center, orn, color, scaling);
}
}
}
int SimpleOpenGL3App::registerGraphicsUnitSphereShape(EnumSphereLevelOfDetail lod, int textureId)
{
int red = 255;
int green = 0;
int blue = 128;
if (textureId<0)
{
if (m_data->m_textureId < 0)
{
int texWidth = 1024;
int texHeight = 1024;
b3AlignedObjectArray<unsigned char> texels;
texels.resize(texWidth * texHeight * 3);
for (int i = 0; i < texWidth * texHeight * 3; i++)
texels[i] = 255;
for (int i = 0; i < texWidth; i++)
{
for (int j = 0; j < texHeight; j++)
{
int a = i < texWidth / 2 ? 1 : 0;
int b = j < texWidth / 2 ? 1 : 0;
if (a == b)
{
texels[(i + j * texWidth) * 3 + 0] = red;
texels[(i + j * texWidth) * 3 + 1] = green;
texels[(i + j * texWidth) * 3 + 2] = blue;
// texels[(i+j*texWidth)*4+3] = 255;
}
/*else
{
texels[i*3+0+j*texWidth] = 255;
texels[i*3+1+j*texWidth] = 255;
texels[i*3+2+j*texWidth] = 255;
}
*/
}
}
m_data->m_textureId = m_instancingRenderer->registerTexture(&texels[0], texWidth, texHeight);
}
textureId = m_data->m_textureId;
}
int strideInBytes = 9 * sizeof(float);
int graphicsShapeIndex = -1;
switch (lod)
{
case SPHERE_LOD_POINT_SPRITE:
{
int numVertices = sizeof(point_sphere_vertices) / strideInBytes;
int numIndices = sizeof(point_sphere_indices) / sizeof(int);
graphicsShapeIndex = m_instancingRenderer->registerShape(&point_sphere_vertices[0], numVertices, point_sphere_indices, numIndices, B3_GL_POINTS, textureId);
break;
}
case SPHERE_LOD_LOW:
{
int numVertices = sizeof(low_sphere_vertices) / strideInBytes;
int numIndices = sizeof(low_sphere_indices) / sizeof(int);
graphicsShapeIndex = m_instancingRenderer->registerShape(&low_sphere_vertices[0], numVertices, low_sphere_indices, numIndices, B3_GL_TRIANGLES, textureId);
break;
}
case SPHERE_LOD_MEDIUM:
{
int numVertices = sizeof(textured_detailed_sphere_vertices) / strideInBytes;
int numIndices = sizeof(textured_detailed_sphere_indices) / sizeof(int);
graphicsShapeIndex = m_instancingRenderer->registerShape(&textured_detailed_sphere_vertices[0], numVertices, textured_detailed_sphere_indices, numIndices, B3_GL_TRIANGLES, textureId);
break;
}
case SPHERE_LOD_HIGH:
default:
{
int numVertices = sizeof(textured_detailed_sphere_vertices) / strideInBytes;
int numIndices = sizeof(textured_detailed_sphere_indices) / sizeof(int);
graphicsShapeIndex = m_instancingRenderer->registerShape(&textured_detailed_sphere_vertices[0], numVertices, textured_detailed_sphere_indices, numIndices, B3_GL_TRIANGLES, textureId);
break;
}
};
return graphicsShapeIndex;
}
void SimpleOpenGL3App::drawGrid(DrawGridData data)
{
int gridSize = data.gridSize;
float upOffset = data.upOffset;
int upAxis = data.upAxis;
float gridColor[4];
gridColor[0] = data.gridColor[0];
gridColor[1] = data.gridColor[1];
gridColor[2] = data.gridColor[2];
gridColor[3] = data.gridColor[3];
int sideAxis = -1;
int forwardAxis = -1;
switch (upAxis)
{
case 1:
forwardAxis = 2;
sideAxis = 0;
break;
case 2:
forwardAxis = 1;
sideAxis = 0;
break;
default:
b3Assert(0);
};
//b3Vector3 gridColor = b3MakeVector3(0.5,0.5,0.5);
b3AlignedObjectArray<unsigned int> indices;
b3AlignedObjectArray<b3Vector3> vertices;
int lineIndex = 0;
for (int i = -gridSize; i <= gridSize; i++)
{
{
b3Assert(glGetError() == GL_NO_ERROR);
b3Vector3 from = b3MakeVector3(0, 0, 0);
from[sideAxis] = float(i);
from[upAxis] = upOffset;
from[forwardAxis] = float(-gridSize);
b3Vector3 to = b3MakeVector3(0, 0, 0);
to[sideAxis] = float(i);
to[upAxis] = upOffset;
to[forwardAxis] = float(gridSize);
vertices.push_back(from);
indices.push_back(lineIndex++);
vertices.push_back(to);
indices.push_back(lineIndex++);
// m_instancingRenderer->drawLine(from,to,gridColor);
}
b3Assert(glGetError() == GL_NO_ERROR);
{
b3Assert(glGetError() == GL_NO_ERROR);
b3Vector3 from = b3MakeVector3(0, 0, 0);
from[sideAxis] = float(-gridSize);
from[upAxis] = upOffset;
from[forwardAxis] = float(i);
b3Vector3 to = b3MakeVector3(0, 0, 0);
to[sideAxis] = float(gridSize);
to[upAxis] = upOffset;
to[forwardAxis] = float(i);
vertices.push_back(from);
indices.push_back(lineIndex++);
vertices.push_back(to);
indices.push_back(lineIndex++);
// m_instancingRenderer->drawLine(from,to,gridColor);
}
}
m_instancingRenderer->drawLines(&vertices[0].x,
gridColor,
vertices.size(), sizeof(b3Vector3), &indices[0], indices.size(), 1);
m_instancingRenderer->drawLine(b3MakeVector3(0, 0, 0), b3MakeVector3(1, 0, 0), b3MakeVector3(1, 0, 0), 3);
m_instancingRenderer->drawLine(b3MakeVector3(0, 0, 0), b3MakeVector3(0, 1, 0), b3MakeVector3(0, 1, 0), 3);
m_instancingRenderer->drawLine(b3MakeVector3(0, 0, 0), b3MakeVector3(0, 0, 1), b3MakeVector3(0, 0, 1), 3);
// void GLInstancingRenderer::drawPoints(const float* positions, const float color[4], int numPoints, int pointStrideInBytes, float pointDrawSize)
//we don't use drawPoints because all points would have the same color
// b3Vector3 points[3] = { b3MakeVector3(1, 0, 0), b3MakeVector3(0, 1, 0), b3MakeVector3(0, 0, 1) };
// m_instancingRenderer->drawPoints(&points[0].x, b3MakeVector3(1, 0, 0), 3, sizeof(b3Vector3), 6);
m_instancingRenderer->drawPoint(b3MakeVector3(1, 0, 0), b3MakeVector3(1, 0, 0), 6);
m_instancingRenderer->drawPoint(b3MakeVector3(0, 1, 0), b3MakeVector3(0, 1, 0), 6);
m_instancingRenderer->drawPoint(b3MakeVector3(0, 0, 1), b3MakeVector3(0, 0, 1), 6);
}
void SimpleOpenGL3App::setBackgroundColor(float red, float green, float blue)
{
CommonGraphicsApp::setBackgroundColor(red, green, blue);
glClearColor(m_backgroundColorRGB[0], m_backgroundColorRGB[1], m_backgroundColorRGB[2], 1.f);
}
SimpleOpenGL3App::~SimpleOpenGL3App()
{
delete m_instancingRenderer;
delete m_primRenderer;
sth_delete(m_data->m_fontStash);
delete m_data->m_renderCallbacks;
sth_delete(m_data->m_fontStash2);
delete m_data->m_renderCallbacks2;
TwDeleteDefaultFonts();
m_window->closeWindow();
delete m_window;
delete m_data;
}
void SimpleOpenGL3App::setViewport(int width, int height)
{
m_data->m_customViewPortWidth = width;
m_data->m_customViewPortHeight = height;
if (width >= 0)
{
glViewport(0, 0, width, height);
}
else
{
glViewport(0, 0, m_window->getRetinaScale() * m_instancingRenderer->getScreenWidth(), m_window->getRetinaScale() * m_instancingRenderer->getScreenHeight());
}
}
void SimpleOpenGL3App::getScreenPixels(unsigned char* rgbaBuffer, int bufferSizeInBytes, float* depthBuffer, int depthBufferSizeInBytes)
{
int width = m_data->m_customViewPortWidth >= 0 ? m_data->m_customViewPortWidth : (int)m_window->getRetinaScale() * m_instancingRenderer->getScreenWidth();
int height = m_data->m_customViewPortHeight >= 0 ? m_data->m_customViewPortHeight : (int)m_window->getRetinaScale() * m_instancingRenderer->getScreenHeight();
b3Assert((width * height * 4) == bufferSizeInBytes);
if ((width * height * 4) == bufferSizeInBytes)
{
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgbaBuffer);
int glstat;
glstat = glGetError();
b3Assert(glstat == GL_NO_ERROR);
}
b3Assert((width * height * sizeof(float)) == depthBufferSizeInBytes);
if ((width * height * sizeof(float)) == depthBufferSizeInBytes)
{
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, depthBuffer);
int glstat;
glstat = glGetError();
b3Assert(glstat == GL_NO_ERROR);
}
}
//#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image/stb_image_write.h"
static void writeTextureToFile(int textureWidth, int textureHeight, const char* fileName, FILE* ffmpegVideo)
{
int numComponents = 4;
//glPixelStorei(GL_PACK_ALIGNMENT,1);
b3Assert(glGetError() == GL_NO_ERROR);
//glReadBuffer(GL_BACK);//COLOR_ATTACHMENT0);
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);
assert(glGetError() == GL_NO_ERROR);
for (int j = 0; j < textureHeight; j++)
{
for (int i = 0; i < textureWidth; i++)
{
pixels[(j * textureWidth + i) * numComponents] = char(orgPixels[(j * textureWidth + i) * numComponents] * 255.f);
pixels[(j * textureWidth + i) * numComponents + 1] = char(orgPixels[(j * textureWidth + i) * numComponents + 1] * 255.f);
pixels[(j * textureWidth + i) * numComponents + 2] = char(orgPixels[(j * textureWidth + i) * numComponents + 2] * 255.f);
pixels[(j * textureWidth + i) * numComponents + 3] = char(orgPixels[(j * textureWidth + i) * numComponents + 3] * 255.f);
}
}
if (ffmpegVideo)
{
fwrite(pixels, textureWidth * textureHeight * numComponents, 1, ffmpegVideo);
//fwrite(pixels, 100,1,ffmpegVideo);//textureWidth*textureHeight*numComponents, 1, ffmpegVideo);
}
else
{
if (1)
{
//swap the pixels
unsigned char tmp;
for (int j = 0; j < textureHeight / 2; j++)
{
for (int i = 0; i < textureWidth; i++)
{
for (int c = 0; c < numComponents; c++)
{
tmp = pixels[(j * textureWidth + i) * numComponents + c];
pixels[(j * textureWidth + i) * numComponents + c] =
pixels[((textureHeight - j - 1) * textureWidth + i) * numComponents + c];
pixels[((textureHeight - j - 1) * textureWidth + i) * numComponents + c] = tmp;
}
}
}
}
stbi_write_png(fileName, textureWidth, textureHeight, numComponents, pixels, textureWidth * numComponents);
}
free(pixels);
free(orgPixels);
}
void SimpleOpenGL3App::swapBuffer()
{
if (m_data->m_frameDumpPngFileName)
{
int width = (int)m_window->getRetinaScale() * m_instancingRenderer->getScreenWidth();
int height = (int)m_window->getRetinaScale() * this->m_instancingRenderer->getScreenHeight();
writeTextureToFile(width,
height, 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->endRendering();
m_window->startRendering();
}
// see also http://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/
void SimpleOpenGL3App::dumpFramesToVideo(const char* mp4FileName)
{
if (mp4FileName)
{
int width = (int)m_window->getRetinaScale() * m_instancingRenderer->getScreenWidth();
int height = (int)m_window->getRetinaScale() * m_instancingRenderer->getScreenHeight();
char cmd[8192];
#ifdef _WIN32
sprintf(cmd,
"ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s %dx%d -i - "
"-threads 0 -y -b:v 50000k -c:v libx264 -preset slow -crf 22 -an -pix_fmt yuv420p -vf vflip %s",
width, height, mp4FileName);
//sprintf(cmd, "ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s %dx%d -i - "
// "-y -crf 0 -b:v 1500000 -an -vcodec h264 -vf vflip %s", width, height, mp4FileName);
#else
sprintf(cmd,
"ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s %dx%d -i - "
"-threads 0 -y -b 50000k -c:v libx264 -preset slow -crf 22 -an -pix_fmt yuv420p -vf vflip %s",
width, height, mp4FileName);
#endif
//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);
}
if (mp4FileName)
{
m_data->m_ffmpegFile = popen(cmd, "w");
m_data->m_frameDumpPngFileName = mp4FileName;
}
}
else
{
if (m_data->m_ffmpegFile)
{
fflush(m_data->m_ffmpegFile);
pclose(m_data->m_ffmpegFile);
m_data->m_frameDumpPngFileName = 0;
}
m_data->m_ffmpegFile = 0;
}
}
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_window->getRetinaScale(), m_instancingRenderer->getScreenHeight() * m_window->getRetinaScale(), 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_LINEAR_MIPMAP_LINEAR);
//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() * m_window->getRetinaScale(), this->m_instancingRenderer->getScreenHeight() * m_window->getRetinaScale(), renderTextureId, RENDERTEXTURE_COLOR);
}
m_data->m_renderTexture->enable();
}
void SimpleOpenGL3App::setUpAxis(int axis)
{
b3Assert((axis == 1) || (axis == 2)); //only Y or Z is supported at the moment
m_data->m_upAxis = axis;
}
int SimpleOpenGL3App::getUpAxis() const
{
return m_data->m_upAxis;
}
#endif //#ifndef NO_OPENGL3