This commit is contained in:
Ivan
2022-04-05 11:42:28 +03:00
commit 6dc0eb0fcf
5565 changed files with 1200500 additions and 0 deletions

View File

@@ -0,0 +1,660 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011 Steven Lovegrove, Richard Newcombe
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <pangolin/platform.h>
#ifdef HAVE_PYTHON
#include <pangolin/python/pyinterpreter.h>
#include <pangolin/console/ConsoleView.h>
#endif // HAVE_PYTHON
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <mutex>
#include <cstdlib>
#include <pangolin/factory/factory_registry.h>
#include <pangolin/window_frameworks.h>
#include <pangolin/gl/glinclude.h>
#include <pangolin/gl/gldraw.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/handler/handler.h>
#include <pangolin/utils/simple_math.h>
#include <pangolin/utils/timer.h>
#include <pangolin/utils/type_convert.h>
#include <pangolin/image/image_io.h>
#ifdef BUILD_PANGOLIN_VARS
#include <pangolin/var/var.h>
#endif
namespace pangolin
{
#ifdef BUILD_PANGOLIN_VIDEO
// Forward declaration.
void SaveFramebuffer(VideoOutput& video, const Viewport& v);
#endif // BUILD_PANGOLIN_VIDEO
const char* PARAM_DISPLAYNAME = "DISPLAYNAME";
const char* PARAM_DOUBLEBUFFER = "DOUBLEBUFFER";
const char* PARAM_SAMPLE_BUFFERS = "SAMPLE_BUFFERS";
const char* PARAM_SAMPLES = "SAMPLES";
const char* PARAM_HIGHRES = "HIGHRES";
typedef std::map<std::string,std::shared_ptr<PangolinGl> > ContextMap;
// Map of active contexts
ContextMap contexts;
std::recursive_mutex contexts_mutex;
bool one_time_window_frameworks_init = false;
// Context active for current thread
__thread PangolinGl* context = 0;
PangolinGl::PangolinGl()
: user_app(0), is_high_res(false), quit(false), mouse_state(0),activeDisplay(0)
#ifdef BUILD_PANGOLIN_VIDEO
, record_view(0)
#endif
#ifdef HAVE_PYTHON
, console_view(0)
#endif
{
}
PangolinGl::~PangolinGl()
{
// Free displays owned by named_managed_views
for(ViewMap::iterator iv = named_managed_views.begin(); iv != named_managed_views.end(); ++iv) {
delete iv->second;
}
named_managed_views.clear();
}
PangolinGl* GetCurrentContext()
{
return context;
}
PangolinGl *FindContext(const std::string& name)
{
contexts_mutex.lock();
ContextMap::iterator ic = contexts.find(name);
PangolinGl* context = (ic == contexts.end()) ? 0 : ic->second.get();
contexts_mutex.unlock();
return context;
}
WindowInterface& CreateWindowAndBind(std::string window_title, int w, int h, const Params& params)
{
std::unique_lock<std::recursive_mutex> l(contexts_mutex);
if(!one_time_window_frameworks_init) {
one_time_window_frameworks_init = LoadBuiltInWindowFrameworks();
}
pangolin::Uri win_uri;
if(const char* extra_params = std::getenv("PANGOLIN_WINDOW_URI"))
{
// Take any defaults from the environment
win_uri = pangolin::ParseUri(extra_params);
}else{
// Otherwise revert to 'default' scheme.
win_uri.scheme = "default";
}
// Allow params to override
win_uri.scheme = params.Get("scheme", win_uri.scheme);
// Override with anything the program specified
win_uri.params.insert(std::end(win_uri.params), std::begin(params.params), std::end(params.params));
win_uri.Set("w", w);
win_uri.Set("h", h);
win_uri.Set("window_title", window_title);
std::unique_ptr<WindowInterface> window = FactoryRegistry<WindowInterface>::I().Open(win_uri);
// We're expecting not only a WindowInterface, but a PangolinGl.
if(!window || !dynamic_cast<PangolinGl*>(window.get())) {
throw WindowExceptionNoKnownHandler(win_uri.scheme);
}
std::shared_ptr<PangolinGl> context(dynamic_cast<PangolinGl*>(window.release()));
RegisterNewContext(window_title, context );
// is_high_res will alter default font size and a few other gui elements.
context->is_high_res = win_uri.Get(PARAM_HIGHRES,false);
context->MakeCurrent();
context->ProcessEvents();
glewInit();
return *context;
}
// Assumption: unique lock is held on contexts_mutex for multi-threaded operation
void RegisterNewContext(const std::string& name, std::shared_ptr<PangolinGl> newcontext)
{
// Set defaults
newcontext->base.left = 0.0;
newcontext->base.bottom = 0.0;
newcontext->base.top = 1.0;
newcontext->base.right = 1.0;
newcontext->base.aspect = 0;
newcontext->base.handler = &StaticHandler;
newcontext->is_fullscreen = false;
// Create and add
if( contexts.find(name) != contexts.end() ) {
throw std::runtime_error("Context already exists.");
}
contexts[name] = newcontext;
// Process the following as if this context is now current.
PangolinGl *oldContext = context;
context = newcontext.get();
process::Resize(
newcontext->windowed_size[0],
newcontext->windowed_size[1]
);
// Default key bindings can be overridden
RegisterKeyPressCallback(PANGO_KEY_ESCAPE, Quit );
RegisterKeyPressCallback('\t', ToggleFullscreen );
RegisterKeyPressCallback('`', ToggleConsole );
context = oldContext;
}
WindowInterface* GetBoundWindow()
{
return context;
}
void DestroyWindow(const std::string& name)
{
contexts_mutex.lock();
ContextMap::iterator ic = contexts.find(name);
PangolinGl *context_to_destroy = (ic == contexts.end()) ? 0 : ic->second.get();
if (context_to_destroy == context) {
context = nullptr;
}
size_t erased = contexts.erase(name);
if(erased == 0) {
pango_print_warn("Context '%s' doesn't exist for deletion.\n", name.c_str());
}
contexts_mutex.unlock();
}
WindowInterface& BindToContext(std::string name)
{
std::unique_lock<std::recursive_mutex> l(contexts_mutex);
// N.B. context is modified prior to invoking MakeCurrent so that
// state management callbacks (such as Resize()) can be correctly
// processed.
PangolinGl *context_to_bind = FindContext(name);
if( !context_to_bind )
{
std::shared_ptr<PangolinGl> newcontext(new PangolinGl());
RegisterNewContext(name, newcontext);
return *(newcontext.get());
}else{
context_to_bind->MakeCurrent();
return *context_to_bind;
}
}
void Quit()
{
context->quit = true;
}
void QuitAll()
{
for(auto nc : contexts) {
nc.second->quit = true;
}
}
bool ShouldQuit()
{
return !context || context->quit;
}
bool HadInput()
{
if( context->had_input > 0 )
{
--context->had_input;
return true;
}
return false;
}
bool HasResized()
{
if( context->has_resized > 0 )
{
--context->has_resized;
return true;
}
return false;
}
void StartFullScreen() {
if(!context->is_fullscreen) {
context->ToggleFullscreen();
context->is_fullscreen = true;
}
}
void StopFullScreen() {
if(context->is_fullscreen) {
context->ToggleFullscreen();
context->is_fullscreen = false;
}
}
void SetFullscreen(bool fullscreen)
{
if(fullscreen) {
StartFullScreen();
}else{
StopFullScreen();
}
}
void RenderViews()
{
Viewport::DisableScissor();
DisplayBase().Render();
}
void RenderRecordGraphic(const Viewport& v)
{
const float r = 7;
v.ActivatePixelOrthographic();
glRecordGraphic(v.w-2*r, v.h-2*r, r);
}
void PostRender()
{
while(context->screen_capture.size()) {
std::pair<std::string,Viewport> fv = context->screen_capture.front();
context->screen_capture.pop();
SaveFramebuffer(fv.first, fv.second);
}
#ifdef BUILD_PANGOLIN_VIDEO
if(context->recorder.IsOpen()) {
SaveFramebuffer(context->recorder, context->record_view->GetBounds() );
RenderRecordGraphic(context->record_view->GetBounds());
}
#endif // BUILD_PANGOLIN_VIDEO
// Disable scissor each frame
Viewport::DisableScissor();
}
void FinishFrame()
{
RenderViews();
PostRender();
context->SwapBuffers();
context->ProcessEvents();
}
View& DisplayBase()
{
return context->base;
}
View& CreateDisplay()
{
int iguid = rand();
std::stringstream ssguid;
ssguid << iguid;
return Display(ssguid.str());
}
void ToggleConsole()
{
#ifdef HAVE_PYTHON
if( !context->console_view) {
// Create console and let the pangolin context take ownership
context->console_view = new ConsoleView(new PyInterpreter());
context->named_managed_views["pangolin_console"] = context->console_view;
context->console_view->SetFocus();
context->console_view->zorder = std::numeric_limits<int>::max();
DisplayBase().AddDisplay(*context->console_view);
}else{
context->console_view->ToggleShow();
if(context->console_view->IsShown()) {
context->console_view->SetFocus();
}
}
#endif
}
void ToggleFullscreen()
{
SetFullscreen(!context->is_fullscreen);
}
View& Display(const std::string& name)
{
// Get / Create View
ViewMap::iterator vi = context->named_managed_views.find(name);
if( vi != context->named_managed_views.end() )
{
return *(vi->second);
}else{
View * v = new View();
context->named_managed_views[name] = v;
v->handler = &StaticHandler;
context->base.views.push_back(v);
return *v;
}
}
void RegisterKeyPressCallback(int key, std::function<void(void)> func)
{
context->keypress_hooks[key] = func;
}
void SaveWindowOnRender(std::string prefix)
{
context->screen_capture.push(std::pair<std::string,Viewport>(prefix, context->base.v) );
}
void SaveFramebuffer(std::string prefix, const Viewport& v)
{
PANGOLIN_UNUSED(prefix);
PANGOLIN_UNUSED(v);
#ifndef HAVE_GLES
#ifdef HAVE_PNG
PixelFormat fmt = PixelFormatFromString("RGBA32");
TypedImage buffer(v.w, v.h, fmt );
glReadBuffer(GL_BACK);
glPixelStorei(GL_PACK_ALIGNMENT, 1); // TODO: Avoid this?
glReadPixels(v.l, v.b, v.w, v.h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.ptr );
SaveImage(buffer, fmt, prefix + ".png", false);
#endif // HAVE_PNG
#endif // HAVE_GLES
}
#ifdef BUILD_PANGOLIN_VIDEO
void SaveFramebuffer(VideoOutput& video, const Viewport& v)
{
#ifndef HAVE_GLES
const StreamInfo& si = video.Streams()[0];
if(video.Streams().size()==0 || (int)si.Width() != v.w || (int)si.Height() != v.h) {
video.Close();
return;
}
static basetime last_time = TimeNow();
const basetime time_now = TimeNow();
last_time = time_now;
static std::vector<unsigned char> img;
img.resize(v.w*v.h*4);
glReadBuffer(GL_BACK);
glPixelStorei(GL_PACK_ALIGNMENT, 1); // TODO: Avoid this?
glReadPixels(v.l, v.b, v.w, v.h, GL_RGB, GL_UNSIGNED_BYTE, &img[0] );
video.WriteStreams(&img[0]);
#endif // HAVE_GLES
}
#endif // BUILD_PANGOLIN_VIDEO
namespace process
{
float last_x = 0;
float last_y = 0;
void Keyboard( unsigned char key, int x, int y)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
#ifdef HAVE_APPLE_OPENGL_FRAMEWORK
// Switch backspace and delete for OSX!
if(key== '\b') {
key = 127;
}else if(key == 127) {
key = '\b';
}
#endif
context->had_input = context->is_double_buffered ? 2 : 1;
// Check if global key hook exists
const KeyhookMap::iterator hook = context->keypress_hooks.find(key);
#ifdef HAVE_PYTHON
// Console receives all input when it is open
if( context->console_view && context->console_view->IsShown() ) {
context->console_view->Keyboard(*(context->console_view),key,x,y,true);
}else
#endif
if(hook != context->keypress_hooks.end() ) {
hook->second();
} else if(context->activeDisplay && context->activeDisplay->handler) {
context->activeDisplay->handler->Keyboard(*(context->activeDisplay),key,x,y,true);
}
}
void KeyboardUp(unsigned char key, int x, int y)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
if(context->activeDisplay && context->activeDisplay->handler)
{
context->activeDisplay->handler->Keyboard(*(context->activeDisplay),key,x,y,false);
}
}
void SpecialFunc(int key, int x, int y)
{
Keyboard(key+128,x,y);
}
void SpecialFuncUp(int key, int x, int y)
{
KeyboardUp(key+128,x,y);
}
void Mouse( int button_raw, int state, int x, int y)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
last_x = (float)x;
last_y = (float)y;
const MouseButton button = (MouseButton)(1 << (button_raw & 0xf) );
const bool pressed = (state == 0);
context->had_input = context->is_double_buffered ? 2 : 1;
const bool fresh_input = ( (context->mouse_state & 7) == 0);
if( pressed ) {
context->mouse_state |= (button&7);
}else{
context->mouse_state &= ~(button&7);
}
#if defined(_WIN_)
context->mouse_state &= 0x0000ffff;
context->mouse_state |= (button_raw >> 4) << 16;
#endif
if(fresh_input) {
context->base.handler->Mouse(context->base,button,x,y,pressed,context->mouse_state);
}else if(context->activeDisplay && context->activeDisplay->handler) {
context->activeDisplay->handler->Mouse(*(context->activeDisplay),button,x,y,pressed,context->mouse_state);
}
}
void MouseMotion( int x, int y)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
last_x = (float)x;
last_y = (float)y;
context->had_input = context->is_double_buffered ? 2 : 1;
if( context->activeDisplay)
{
if( context->activeDisplay->handler )
context->activeDisplay->handler->MouseMotion(*(context->activeDisplay),x,y,context->mouse_state);
}else{
context->base.handler->MouseMotion(context->base,x,y,context->mouse_state);
}
}
void PassiveMouseMotion(int x, int y)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
context->base.handler->PassiveMouseMotion(context->base,x,y,context->mouse_state);
last_x = (float)x;
last_y = (float)y;
}
void Display()
{
// No implementation
}
void Resize( int width, int height )
{
if( !context->is_fullscreen )
{
context->windowed_size[0] = width;
context->windowed_size[1] = height;
}
// TODO: Fancy display managers seem to cause this to mess up?
context->had_input = 20; //context->is_double_buffered ? 2 : 1;
context->has_resized = 20; //context->is_double_buffered ? 2 : 1;
Viewport win(0,0,width,height);
context->base.Resize(win);
}
void SpecialInput(InputSpecial inType, float x, float y, float p1, float p2, float p3, float p4)
{
// Assume coords already match OpenGl Window Coords
context->had_input = context->is_double_buffered ? 2 : 1;
const bool fresh_input = (context->mouse_state == 0);
if(fresh_input) {
context->base.handler->Special(context->base,inType,x,y,p1,p2,p3,p4,context->mouse_state);
}else if(context->activeDisplay && context->activeDisplay->handler) {
context->activeDisplay->handler->Special(*(context->activeDisplay),inType,x,y,p1,p2,p3,p4,context->mouse_state);
}
}
void Scroll(float x, float y)
{
SpecialInput(InputSpecialScroll, last_x, last_y, x, y, 0, 0);
}
void Zoom(float m)
{
SpecialInput(InputSpecialZoom, last_x, last_y, m, 0, 0, 0);
}
void Rotate(float r)
{
SpecialInput(InputSpecialRotate, last_x, last_y, r, 0, 0, 0);
}
void SubpixMotion(float x, float y, float pressure, float rotation, float tiltx, float tilty)
{
// Force coords to match OpenGl Window Coords
y = context->base.v.h - y;
SpecialInput(InputSpecialTablet, x, y, pressure, rotation, tiltx, tilty);
}
}
void DrawTextureToViewport(GLuint texid)
{
OpenGlRenderState::ApplyIdentity();
glBindTexture(GL_TEXTURE_2D, texid);
glEnable(GL_TEXTURE_2D);
GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 };
glVertexPointer(2, GL_FLOAT, 0, sq_vert);
glEnableClientState(GL_VERTEX_ARRAY);
GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 };
glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
ToggleViewFunctor::ToggleViewFunctor(View& view)
: view(view)
{
}
ToggleViewFunctor::ToggleViewFunctor(const std::string& name)
: view(Display(name))
{
}
void ToggleViewFunctor::operator()()
{
view.ToggleShow();
}
}