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,95 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011 Steven Lovegrove
*
* 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>
#include <pangolin/display/display.h>
#include <pangolin/display/device/PangolinNSApplication.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
# define NSAnyEventMask NSEventMaskAny
#endif
////////////////////////////////////////////////////////////////////
// PangolinNSApplication
////////////////////////////////////////////////////////////////////
@implementation PangolinNSApplication
+ (void)run_pre
{
[[NSNotificationCenter defaultCenter]
postNotificationName:NSApplicationWillFinishLaunchingNotification
object:NSApp];
[[NSNotificationCenter defaultCenter]
postNotificationName:NSApplicationDidFinishLaunchingNotification
object:NSApp];
}
+ (void)run_step
{
NSEvent *event;
do{
event = [NSApp
nextEventMatchingMask:NSAnyEventMask
untilDate:nil
// untilDate: [NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
[NSApp updateWindows];
}while(event != nil);
}
@end
////////////////////////////////////////////////////////////////////
// PangolinWindowDelegate
////////////////////////////////////////////////////////////////////
@implementation PangolinWindowDelegate
- (BOOL)windowShouldClose:(id)sender {
PANGOLIN_UNUSED(sender);
pangolin::Quit();
return YES;
}
@end
////////////////////////////////////////////////////////////////////
// PangolinAppDelegate
////////////////////////////////////////////////////////////////////
@implementation PangolinAppDelegate
- (void)dealloc
{
[super dealloc];
}
@end

View File

@@ -0,0 +1,325 @@
#include <pangolin/platform.h>
#include <pangolin/gl/glinclude.h>
#include <pangolin/display/device/PangolinNSGLView.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/handler/handler_enums.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
# define NSDeviceIndependentModifierFlagsMask NSEventModifierFlagDeviceIndependentFlagsMask
# define NSShiftKeyMask NSEventModifierFlagShift
# define NSControlKeyMask NSEventModifierFlagControl
# define NSAlternateKeyMask NSEventModifierFlagOption
# define NSCommandKeyMask NSEventModifierFlagCommand
# define NSFunctionKeyMask NSEventModifierFlagFunction
#endif
namespace pangolin
{
extern __thread PangolinGl* context;
}
////////////////////////////////////////////////////////////////////
// Input maps
////////////////////////////////////////////////////////////////////
inline
int mapMouseButton(int osx_button )
{
const int map[] = {0, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10};
return map[osx_button];
}
inline
int mapKeymap(int osx_key)
{
if(osx_key == NSUpArrowFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_UP;
else if(osx_key == NSDownArrowFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_DOWN;
else if(osx_key == NSLeftArrowFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_LEFT;
else if(osx_key == NSRightArrowFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_RIGHT;
else if(osx_key == NSPageUpFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_PAGE_UP;
else if(osx_key == NSPageDownFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_PAGE_DOWN;
else if(osx_key == NSHomeFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_HOME;
else if(osx_key == NSEndFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_END;
else if(osx_key == NSInsertFunctionKey)
return pangolin::PANGO_SPECIAL + pangolin::PANGO_KEY_INSERT;
else if(osx_key == NSDeleteCharacter )
return NSBackspaceCharacter;
else if(osx_key == NSDeleteFunctionKey)
return NSDeleteCharacter;
else {
return osx_key;
}
}
////////////////////////////////////////////////////////////////////
// PangolinNSGLView
////////////////////////////////////////////////////////////////////
@implementation PangolinNSGLView
-(id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format
{
self = [super initWithFrame:frameRect pixelFormat:format];
context = pangolin::context;
return(self);
}
- (void)prepareOpenGL
{
[super prepareOpenGL];
}
-(void)reshape
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if ( [self wantsBestResolutionOpenGLSurface] && [ self.window respondsToSelector:@selector(backingScaleFactor) ] )
backing_scale = [self.window backingScaleFactor];
else
#endif
backing_scale = 1.0;
pangolin::process::Resize(self.bounds.size.width * backing_scale, self.bounds.size.height * backing_scale);
[[self openGLContext] update];
}
-(BOOL)acceptsFirstResponder
{
return(YES);
}
-(BOOL)becomeFirstResponder
{
return(YES);
}
-(BOOL)resignFirstResponder
{
return(YES);
}
-(BOOL)isFlipped
{
return(YES);
}
-(NSPoint)_Location:(NSEvent *)theEvent
{
NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil];
location.x *= backing_scale;
location.y *= backing_scale;
return location;
}
////////////////////////////////////////////////////////////////////
// Keyboard
////////////////////////////////////////////////////////////////////
-(void)keyDown:(NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
NSString *str = [theEvent characters];
int len = (int)[str length];
for(int i = 0; i < len; i++)
{
const int osx_key = [str characterAtIndex:i];
pangolin::process::Keyboard(mapKeymap(osx_key), location.x, location.y);
}
}
-(void)keyUp:(NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
NSString *str = [theEvent characters];
int len = (int)[str length];
for(int i = 0; i < len; i++)
{
const int osx_key = [str characterAtIndex:i];
pangolin::process::KeyboardUp(mapKeymap(osx_key), location.x, location.y);
}
}
- (void)flagsChanged:(NSEvent *)event
{
unsigned int flags = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if(flags&NSShiftKeyMask) {
context->mouse_state |= pangolin::KeyModifierShift;
}else{
context->mouse_state &= ~pangolin::KeyModifierShift;
}
if(flags&NSControlKeyMask) {
context->mouse_state |= pangolin::KeyModifierCtrl;
}else{
context->mouse_state &= ~pangolin::KeyModifierCtrl;
}
if(flags&NSAlternateKeyMask) {
context->mouse_state |= pangolin::KeyModifierAlt;
}else{
context->mouse_state &= ~pangolin::KeyModifierAlt;
}
if(flags&NSCommandKeyMask) {
context->mouse_state |= pangolin::KeyModifierCmd;
}else{
context->mouse_state &= ~pangolin::KeyModifierCmd;
}
if(flags&NSFunctionKeyMask) {
context->mouse_state |= pangolin::KeyModifierFnc;
}else{
context->mouse_state &= ~pangolin::KeyModifierFnc;
}
}
////////////////////////////////////////////////////////////////////
// Mouse Input
////////////////////////////////////////////////////////////////////
-(void)mouseDownCommon:(NSEvent *)theEvent
{
const int button = (int)[theEvent buttonNumber];
const NSPoint location = [self _Location: theEvent];
pangolin::process::Mouse(mapMouseButton(button), 0, location.x, location.y);
}
-(void)mouseUpCommon:(NSEvent *)theEvent
{
const int button = (int)[theEvent buttonNumber];
const NSPoint location = [self _Location: theEvent];
pangolin::process::Mouse(mapMouseButton(button), 1, location.x, location.y);
}
- (void)mouseDraggedCommon: (NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
pangolin::process::MouseMotion(location.x, location.y);
// pangolin::process::SubpixMotion(location.x, location.y, 1.0, 0.0, 0.0, 0.0);
}
-(void)mouseDown:(NSEvent *)theEvent
{
[self mouseDownCommon:theEvent];
}
-(void)mouseUp:(NSEvent *)theEvent
{
[self mouseUpCommon:theEvent];
}
- (void)mouseDragged: (NSEvent *)theEvent
{
[self mouseDraggedCommon:theEvent];
}
-(void)rightMouseDown:(NSEvent *)theEvent
{
[self mouseDownCommon:theEvent];
}
-(void)rightMouseUp:(NSEvent *)theEvent
{
[self mouseUpCommon:theEvent];
}
- (void)rightMouseDragged: (NSEvent *)theEvent
{
[self mouseDraggedCommon:theEvent];
}
-(void)otherMouseDown:(NSEvent *)theEvent
{
[self mouseDownCommon:theEvent];
}
-(void)otherMouseUp:(NSEvent *)theEvent
{
[self mouseUpCommon:theEvent];
}
- (void)otherMouseDragged: (NSEvent *)theEvent
{
[self mouseDraggedCommon:theEvent];
}
- (void)mouseMoved: (NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
// pangolin::process::PassiveMouseMotion(location.x, location.y);
pangolin::process::SubpixMotion(location.x, location.y, 0.0, 0.0, 0.0, 0.0);
}
- (void)scrollWheel:(NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
float dx, dy;
if([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
dx = theEvent.scrollingDeltaX; dy = theEvent.scrollingDeltaY;
} else {
dx = theEvent.deltaX; dy = theEvent.deltaY;
}
if(dx != 0.0f || dy != 0.0f) {
pangolin::process::SpecialInput(
pangolin::InputSpecialScroll,
location.x, context->base.v.h - location.y,
dx, dy,
0, 0
);
}
}
- (void)magnifyWithEvent: (NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
const float dm = theEvent.magnification;
if(dm != 0.0f) {
pangolin::process::SpecialInput(
pangolin::InputSpecialZoom,
location.x, context->base.v.h - location.y,
dm, 0.0f, 0.0f, 0.0f
);
}
}
- (void)rotateWithEvent: (NSEvent *)theEvent
{
const NSPoint location = [self _Location: theEvent];
const float dr = theEvent.rotation;
if(dr != 0.0f) {
pangolin::process::SpecialInput(
pangolin::InputSpecialRotate,
location.x, context->base.v.h - location.y,
dr, 0.0f, 0.0f, 0.0f
);
}
}
- (void)mouseEntered: (NSEvent *)theEvent
{
PANGOLIN_UNUSED(theEvent);
}
- (void)mouseExited: (NSEvent *)theEvent
{
PANGOLIN_UNUSED(theEvent);
}
-(void)dealloc
{
[super dealloc];
}
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
#include <pangolin/display/display_internal.h>
#include <pangolin/factory/factory_registry.h>
#include <EGL/egl.h>
namespace pangolin {
extern __thread PangolinGl* context;
namespace headless {
class EGLDisplayHL {
public:
EGLDisplayHL(const int width, const int height);
~EGLDisplayHL();
void swap();
void makeCurrent();
void removeCurrent();
private:
EGLSurface egl_surface;
EGLContext egl_context;
EGLDisplay egl_display;
static constexpr EGLint attribs[] = {
EGL_SURFACE_TYPE , EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE , EGL_OPENGL_BIT,
EGL_RED_SIZE , 8,
EGL_GREEN_SIZE , 8,
EGL_BLUE_SIZE , 8,
EGL_ALPHA_SIZE , 8,
EGL_DEPTH_SIZE , 24,
EGL_STENCIL_SIZE , 8,
EGL_NONE
};
};
constexpr EGLint EGLDisplayHL::attribs[];
struct HeadlessWindow : public PangolinGl {
HeadlessWindow(const int width, const int height);
~HeadlessWindow() override;
void ToggleFullscreen() override;
void Move(const int x, const int y) override;
void Resize(const unsigned int w, const unsigned int h) override;
void MakeCurrent() override;
void RemoveCurrent() override;
void SwapBuffers() override;
void ProcessEvents() override;
EGLDisplayHL display;
};
EGLDisplayHL::EGLDisplayHL(const int width, const int height) {
egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(!egl_display) {
std::cerr << "Failed to open EGL display" << std::endl;
}
EGLint major, minor;
if(eglInitialize(egl_display, &major, &minor)==EGL_FALSE) {
std::cerr << "EGL init failed" << std::endl;
}
if(eglBindAPI(EGL_OPENGL_API)==EGL_FALSE) {
std::cerr << "EGL bind failed" << std::endl;
}
EGLint count;
eglGetConfigs(egl_display, nullptr, 0, &count);
std::vector<EGLConfig> egl_configs(count);
EGLint numConfigs;
eglChooseConfig(egl_display, attribs, egl_configs.data(), count, &numConfigs);
egl_context = eglCreateContext(egl_display, egl_configs[0], EGL_NO_CONTEXT, nullptr);
const EGLint pbufferAttribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_NONE,
};
egl_surface = eglCreatePbufferSurface(egl_display, egl_configs[0], pbufferAttribs);
if (egl_surface == EGL_NO_SURFACE) {
std::cerr << "Cannot create EGL surface" << std::endl;
}
}
EGLDisplayHL::~EGLDisplayHL() {
if(egl_context) eglDestroyContext(egl_display, egl_context);
if(egl_surface) eglDestroySurface(egl_display, egl_surface);
if(egl_display) eglTerminate(egl_display);
}
void EGLDisplayHL::swap() {
eglSwapBuffers(egl_display, egl_surface);
}
void EGLDisplayHL::makeCurrent() {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
}
void EGLDisplayHL::removeCurrent() {
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
HeadlessWindow::HeadlessWindow(const int w, const int h) : display(w, h) {
windowed_size[0] = w;
windowed_size[1] = h;
}
HeadlessWindow::~HeadlessWindow() { }
void HeadlessWindow::MakeCurrent() {
display.makeCurrent();
context = this;
}
void HeadlessWindow::RemoveCurrent() {
display.removeCurrent();
}
void HeadlessWindow::ToggleFullscreen() { }
void HeadlessWindow::Move(const int /*x*/, const int /*y*/) { }
void HeadlessWindow::Resize(const unsigned int /*w*/, const unsigned int /*h*/) { }
void HeadlessWindow::ProcessEvents() { }
void HeadlessWindow::SwapBuffers() {
display.swap();
MakeCurrent();
}
} // namespace headless
PANGOLIN_REGISTER_FACTORY(NoneWindow) {
struct HeadlessWindowFactory : public FactoryInterface<WindowInterface> {
std::unique_ptr<WindowInterface> Open(const Uri& uri) override {
return std::unique_ptr<WindowInterface>(new headless::HeadlessWindow(uri.Get<int>("w", 640), uri.Get<int>("h", 480)));
}
virtual ~HeadlessWindowFactory() { }
};
auto factory = std::make_shared<HeadlessWindowFactory>();
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 1, "none");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 1, "nogui");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 1, "headless");
}
} // namespace pangolin

View File

@@ -0,0 +1,249 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011-2018 Steven Lovegrove, Andrey Mnatsakanov
*
* 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.
*/
// Silence all the OSX GL deprecation messages.
#define GL_SILENCE_DEPRECATION
#include <pangolin/factory/factory_registry.h>
#include <pangolin/platform.h>
#include <pangolin/gl/glinclude.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/display/device/OsxWindow.h>
#include <pangolin/display/device/PangolinNSGLView.h>
#include <pangolin/display/device/PangolinNSApplication.h>
#include <memory>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
# define NSFullScreenWindowMask NSWindowStyleMaskFullScreen
# define NSTitledWindowMask NSWindowStyleMaskTitled
# define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable
# define NSResizableWindowMask NSWindowStyleMaskResizable
# define NSClosableWindowMask NSWindowStyleMaskClosable
#endif
// Hack to fix window focus issue
// http://www.miscdebris.net/blog/2010/03/30/solution-for-my-mac-os-x-gui-program-doesnt-get-focus-if-its-outside-an-application-bundle/
extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); }
inline void FixOsxFocus()
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ProcessSerialNumber psn;
GetCurrentProcess( &psn );
CPSEnableForegroundOperation( &psn );
SetFrontProcess( &psn );
#pragma clang diagnostic pop
}
namespace pangolin
{
extern __thread PangolinGl* context;
std::unique_ptr<WindowInterface> CreateOsxWindowAndBind(std::string window_title, int w, int h, const bool is_highres)
{
OsxWindow* win = new OsxWindow(window_title, w, h, is_highres);
return std::unique_ptr<WindowInterface>(win);
}
OsxWindow::OsxWindow(
const std::string& title, int width, int height, bool USE_RETINA
) {
context = this;
PangolinGl::is_double_buffered = true;
PangolinGl::windowed_size[0] = width;
PangolinGl::windowed_size[1] = height;
// // These are important I think!
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// [pool release];
///////////////////////////////////////////////////////////////////////
// Make sure Application is initialised correctly.
// This can be run repeatedly.
[NSApplication sharedApplication];
PangolinAppDelegate *delegate = [[PangolinAppDelegate alloc] init];
[NSApp setDelegate:delegate];
[NSApp setPresentationOptions:NSFullScreenWindowMask];
[PangolinNSApplication run_pre];
[PangolinNSApplication run_step];
///////////////////////////////////////////////////////////////////////
// Create Window
NSRect viewRect = NSMakeRect( 0.0, 0.0, width, height );
_window = [[NSWindow alloc] initWithContentRect:viewRect styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:YES];
[_window setTitle:[NSString stringWithUTF8String:title.c_str()]];
[_window setOpaque:YES];
[_window makeKeyAndOrderFront:NSApp];
[_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
PangolinWindowDelegate *windelegate = [[PangolinWindowDelegate alloc] init];
[_window setDelegate:windelegate];
///////////////////////////////////////////////////////////////////////
// Setup Menu
// NSMenu *mainMenuBar;
// NSMenu *appMenu;
// NSMenuItem *menuItem;
// mainMenuBar = [[NSMenu alloc] init];
// appMenu = [[NSMenu alloc] initWithTitle:@"Pangolin Application"];
// menuItem = [appMenu addItemWithTitle:@"Quit Pangolin Application" action:@selector(terminate:) keyEquivalent:@"q"];
// [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
// menuItem = [[NSMenuItem alloc] init];
// [menuItem setSubmenu:appMenu];
// [mainMenuBar addItem:menuItem];
// //[NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu];
// [appMenu release];
// [NSApp setMainMenu:mainMenuBar];
///////////////////////////////////////////////////////////////////////
// Create OpenGL View for Window
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 32,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
0
};
NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
view = [[PangolinNSGLView alloc] initWithFrame:_window.frame pixelFormat:format];
[format release];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
if( USE_RETINA && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6)
[view setWantsBestResolutionOpenGLSurface:YES];
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
[_window setContentView:view];
[PangolinNSApplication run_step];
glewInit();
FixOsxFocus();
}
OsxWindow::~OsxWindow()
{
// Not sure how to deallocate...
}
void OsxWindow::StartFullScreen()
{
if(!is_fullscreen) {
[_window toggleFullScreen:nil];
is_fullscreen = true;
}
}
void OsxWindow::StopFullScreen()
{
if(is_fullscreen) {
[_window toggleFullScreen:nil];
is_fullscreen = false;
}
}
void OsxWindow::ToggleFullscreen()
{
[_window toggleFullScreen:nil];
PangolinGl::is_fullscreen = !PangolinGl::is_fullscreen;
}
void OsxWindow::Move(int x, int y)
{
[_window setFrame:CGRectMake(x, y, [_window frame].size.width,
[_window frame].size.height) display:NO];
}
void OsxWindow::Resize(unsigned int w, unsigned int h)
{
[_window setFrame:CGRectMake([_window frame].origin.x,
[_window frame].origin.y, w, h) display:NO];
}
void OsxWindow::MakeCurrent()
{
[[view openGLContext] makeCurrentContext];
context = this;
}
void OsxWindow::RemoveCurrent()
{
[NSOpenGLContext clearCurrentContext];
}
void OsxWindow::SwapBuffers()
{
[[view openGLContext] flushBuffer];
// [[view openGLContext] update];
// [view setNeedsDisplay:YES];
}
void OsxWindow::ProcessEvents()
{
[PangolinNSApplication run_step];
}
PANGOLIN_REGISTER_FACTORY(OsxWindow)
{
struct OsxWindowFactory : public FactoryInterface<WindowInterface> {
std::unique_ptr<WindowInterface> Open(const Uri& uri) override {
const std::string window_title = uri.Get<std::string>("window_title", "window");
const int w = uri.Get<int>("w", 640);
const int h = uri.Get<int>("h", 480);
const bool is_highres = uri.Get<bool>(PARAM_HIGHRES, false);
return std::unique_ptr<WindowInterface>(CreateOsxWindowAndBind(window_title, w, h, is_highres));
}
};
auto factory = std::make_shared<OsxWindowFactory>();
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 10, "cocoa");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 100, "default");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,599 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011-2018 Steven Lovegrove, Andrey Mnatsakanov
*
* 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/factory/factory_registry.h>
#include <pangolin/platform.h>
#include <pangolin/gl/glinclude.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/display/device/WinWindow.h>
#include <memory>
#define CheckWGLDieOnError() pangolin::_CheckWLDieOnError( __FILE__, __LINE__ );
namespace pangolin {
inline void _CheckWLDieOnError( const char *sFile, const int nLine )
{
DWORD errorCode = GetLastError();
if(errorCode!=0) {
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf, 0, NULL);
// MessageBox( NULL, (LPCTSTR)lpMsgBuf, ("Error "+std::to_string(errorCode)).c_str(), MB_OK | MB_ICONINFORMATION );
pango_print_error("Error %i: %s", errorCode, (char *)lpMsgBuf);
pango_print_error("In: %s, line %d\n", sFile, nLine);
// exit(EXIT_FAILURE);
}
}
}
namespace pangolin
{
const char *className = "Pangolin";
extern __thread PangolinGl* context;
////////////////////////////////////////////////////////////////////////
// Utils
////////////////////////////////////////////////////////////////////////
unsigned char GetPangoKey(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case VK_F1: return PANGO_SPECIAL + PANGO_KEY_F1;
case VK_F2: return PANGO_SPECIAL + PANGO_KEY_F2;
case VK_F3: return PANGO_SPECIAL + PANGO_KEY_F3;
case VK_F4: return PANGO_SPECIAL + PANGO_KEY_F4;
case VK_F5: return PANGO_SPECIAL + PANGO_KEY_F5;
case VK_F6: return PANGO_SPECIAL + PANGO_KEY_F6;
case VK_F7: return PANGO_SPECIAL + PANGO_KEY_F7;
case VK_F8: return PANGO_SPECIAL + PANGO_KEY_F8;
case VK_F9: return PANGO_SPECIAL + PANGO_KEY_F9;
case VK_F10: return PANGO_SPECIAL + PANGO_KEY_F10;
case VK_F11: return PANGO_SPECIAL + PANGO_KEY_F11;
case VK_F12: return PANGO_SPECIAL + PANGO_KEY_F12;
case VK_LEFT: return PANGO_SPECIAL + PANGO_KEY_LEFT;
case VK_UP: return PANGO_SPECIAL + PANGO_KEY_UP;
case VK_RIGHT: return PANGO_SPECIAL + PANGO_KEY_RIGHT;
case VK_DOWN: return PANGO_SPECIAL + PANGO_KEY_DOWN;
case VK_HOME: return PANGO_SPECIAL + PANGO_KEY_HOME;
case VK_END: return PANGO_SPECIAL + PANGO_KEY_END;
case VK_INSERT: return PANGO_SPECIAL + PANGO_KEY_INSERT;
case VK_DELETE: return 127;
default:
const int lBufferSize = 2;
WCHAR lBuffer[lBufferSize];
BYTE State[256];
GetKeyboardState(State);
const UINT scanCode = (lParam >> 8) & 0xFFFFFF00;
if( ToUnicode((UINT)wParam, scanCode, State, lBuffer, lBufferSize, 0) >=1 ) {
return (unsigned char)lBuffer[0];
}
}
return 0;
}
int GetMouseModifierKey(WPARAM wParam)
{
//maps windows key modifier to glutGetModifiers values
int gluKeyModVal = 0;
if (wParam & MK_SHIFT) gluKeyModVal += 1;
if (wParam & MK_CONTROL) gluKeyModVal += 2;
if (HIBYTE(GetKeyState(VK_MENU))) gluKeyModVal += 4;
return gluKeyModVal << 4;
}
////////////////////////////////////////////////////////////////////////
// WinWindow Implementation
////////////////////////////////////////////////////////////////////////
void WinWindow::SetupPixelFormat(HDC hDC)
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), /* size */
1, /* version */
PFD_SUPPORT_OPENGL |
PFD_DRAW_TO_WINDOW |
PFD_DOUBLEBUFFER, /* support double-buffering */
PFD_TYPE_RGBA, /* color type */
24, /* prefered color depth */
0, 0, 0, 0, 0, 0, /* color bits (ignored) */
8, /* alpha bits */
0, /* alpha shift (ignored) */
0, /* no accumulation buffer */
0, 0, 0, 0, /* accum bits (ignored) */
32, /* depth buffer */
0, /* no stencil buffer */
0, /* no auxiliary buffers */
PFD_MAIN_PLANE, /* main layer */
0, /* reserved */
0, 0, 0, /* no layer, visible, damage masks */
};
int pixelFormat;
pixelFormat = ChoosePixelFormat(hDC, &pfd);
if (pixelFormat == 0) {
MessageBoxA(WindowFromDC(hDC), "ChoosePixelFormat failed.", "Error", MB_ICONERROR | MB_OK);
CheckWGLDieOnError();
exit(1);
}
if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) {
MessageBoxA(WindowFromDC(hDC), "SetPixelFormat failed.", "Error", MB_ICONERROR | MB_OK);
CheckWGLDieOnError();
exit(1);
}
}
void WinWindow::SetupPalette(HDC hDC)
{
int pixelFormat = GetPixelFormat(hDC);
if(!pixelFormat) {
std::cerr << "GetPixelFormat() failed" << std::endl;
CheckWGLDieOnError();
}
PIXELFORMATDESCRIPTOR pfd;
LOGPALETTE* pPal;
int paletteSize;
if(!DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
std::cerr << "DescribePixelFormat() failed" << std::endl;
CheckWGLDieOnError();
}
if (pfd.dwFlags & PFD_NEED_PALETTE) {
paletteSize = 1 << pfd.cColorBits;
}
else {
return;
}
pPal = (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
pPal->palVersion = 0x300;
pPal->palNumEntries = paletteSize;
/* build a simple RGB color palette */
{
int redMask = (1 << pfd.cRedBits) - 1;
int greenMask = (1 << pfd.cGreenBits) - 1;
int blueMask = (1 << pfd.cBlueBits) - 1;
int i;
for (i = 0; i<paletteSize; ++i) {
pPal->palPalEntry[i].peRed =
(((i >> pfd.cRedShift) & redMask) * 255) / redMask;
pPal->palPalEntry[i].peGreen =
(((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
pPal->palPalEntry[i].peBlue =
(((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask;
pPal->palPalEntry[i].peFlags = 0;
}
}
hPalette = CreatePalette(pPal);
free(pPal);
if (hPalette) {
SelectPalette(hDC, hPalette, FALSE);
RealizePalette(hDC);
}
else {
std::cerr << "CreatePalette() failed" << std::endl;
}
}
WinWindow::WinWindow(
const std::string& window_title, int width, int height
) : hWnd(0)
{
const HMODULE hCurrentInst = GetModuleHandleA(nullptr);
if(hCurrentInst==NULL) {
std::cerr << "GetModuleHandle() failed" << std::endl;
CheckWGLDieOnError();
}
RegisterThisClass(hCurrentInst);
HWND thishwnd = CreateWindowA(
className, window_title.c_str(),
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, width, height,
NULL, NULL, hCurrentInst, this);
if(thishwnd==NULL) {
std::cerr << "CreateWindow() failed" << std::endl;
CheckWGLDieOnError();
}
if( thishwnd != hWnd ) {
throw std::runtime_error("Pangolin Window Creation Failed.");
}
// Gets the size of the window, excluding the top bar
RECT cRect;
GetClientRect(thishwnd, &cRect);
PangolinGl::windowed_size[0] = cRect.right;
PangolinGl::windowed_size[1] = cRect.bottom;
// Display Window
ShowWindow(hWnd, SW_SHOW);
PangolinGl::is_double_buffered = true;
}
WinWindow::~WinWindow()
{
if(!DestroyWindow(hWnd)) {
std::cerr << "DestroyWindow() failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::RegisterThisClass(HMODULE hCurrentInst)
{
WNDCLASSA wndClass;
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WinWindow::WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hCurrentInst;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = className;
if(!RegisterClassA(&wndClass)) {
std::cerr << "RegisterClass() failed" << std::endl;
CheckWGLDieOnError();
}
}
LRESULT APIENTRY
WinWindow::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WinWindow* self = 0;
if (uMsg == WM_NCCREATE) {
auto lpcs = reinterpret_cast<LPCREATESTRUCTA>(lParam);
self = reinterpret_cast<WinWindow*>(lpcs->lpCreateParams);
if(self) {
self->hWnd = hwnd;
SetWindowLongPtrA(hwnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(self));
}
} else {
self = reinterpret_cast<WinWindow*>(GetWindowLongPtrA(hwnd, GWLP_USERDATA));
}
if (self) {
return self->HandleWinMessages(uMsg, wParam, lParam);
} else {
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
}
LRESULT WinWindow::HandleWinMessages(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CREATE:
/* initialize OpenGL rendering */
hDC = GetDC(hWnd);
if(hDC==NULL) {
std::cerr << "WM_CREATE GetDC() failed" << std::endl;
}
SetupPixelFormat(hDC);
SetupPalette(hDC);
hGLRC = wglCreateContext(hDC);
if(!hGLRC) {
std::cerr << "WM_CREATE wglCreateContext() failed" << std::endl;
CheckWGLDieOnError();
}
if(!wglMakeCurrent(hDC, hGLRC)) {
std::cerr << "WM_CREATE wglMakeCurrent() failed" << std::endl;
CheckWGLDieOnError();
}
return 0;
case WM_DESTROY:
/* finish OpenGL rendering */
if (hGLRC) {
if(!wglMakeCurrent(NULL, NULL)) {
std::cerr << "WM_DESTROY wglMakeCurrent() failed" << std::endl;
CheckWGLDieOnError();
}
if(!wglDeleteContext(hGLRC)) {
std::cerr << "WM_DESTROY wglDeleteContext() failed" << std::endl;
CheckWGLDieOnError();
}
}
if (hPalette) {
DeleteObject(hPalette);
}
ReleaseDC(hWnd, hDC);
PostQuitMessage(0);
return 0;
case WM_SIZE:
/* track window size changes */
if (context == this) {
process::Resize((int)LOWORD(lParam), (int)HIWORD(lParam));
}
return 0;
case WM_PALETTECHANGED:
/* realize palette if this is *not* the current window */
if (hGLRC && hPalette && (HWND)wParam != hWnd) {
if(!UnrealizeObject(hPalette)) {
std::cerr << "WM_PALETTECHANGED UnrealizeObject() failed" << std::endl;
}
if(!SelectPalette(hDC, hPalette, FALSE)) {
std::cerr << "WM_PALETTECHANGED SelectPalette() failed" << std::endl;
}
if(RealizePalette(hDC)==GDI_ERROR) {
std::cerr << "WM_PALETTECHANGED RealizePalette() failed" << std::endl;
}
//redraw();
break;
}
break;
case WM_QUERYNEWPALETTE:
/* realize palette if this is the current window */
if (hGLRC && hPalette) {
if(!UnrealizeObject(hPalette)) {
std::cerr << "WM_QUERYNEWPALETTE UnrealizeObject() failed" << std::endl;
}
if(!SelectPalette(hDC, hPalette, FALSE)) {
std::cerr << "WM_QUERYNEWPALETTE SelectPalette() failed" << std::endl;
}
if(RealizePalette(hDC)==GDI_ERROR) {
std::cerr << "WM_QUERYNEWPALETTE RealizePalette() failed" << std::endl;
}
//redraw();
return TRUE;
}
break;
case WM_PAINT:
{
//PAINTSTRUCT ps;
//BeginPaint(hWnd, &ps);
//if (hGLRC) {
// redraw();
//}
//EndPaint(hWnd, &ps);
//return 0;
}
break;
case WM_KEYDOWN:
{
unsigned char key = GetPangoKey(wParam, lParam);
if(key>0) process::Keyboard(key, 1, 1);
return 0;
}
case WM_KEYUP:
{
unsigned char key = GetPangoKey(wParam, lParam);
if (key>0) process::KeyboardUp(key, 1, 1);
return 0;
}
case WM_LBUTTONDOWN:
process::Mouse(0 | GetMouseModifierKey(wParam), 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_MBUTTONDOWN:
process::Mouse(1 | GetMouseModifierKey(wParam), 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_RBUTTONDOWN:
process::Mouse(2 | GetMouseModifierKey(wParam), 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_LBUTTONUP:
process::Mouse(0 | GetMouseModifierKey(wParam), 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_MBUTTONUP:
process::Mouse(1 | GetMouseModifierKey(wParam), 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_RBUTTONUP:
process::Mouse(2 | GetMouseModifierKey(wParam), 1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_MOUSEMOVE:
if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) ) {
process::MouseMotion(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
} else{
process::PassiveMouseMotion(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
return 0;
case WM_MOUSEWHEEL:
process::Scroll(0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / 5.0f );
return 0;
case WM_MOUSEHWHEEL:
process::Scroll(GET_WHEEL_DELTA_WPARAM(wParam) / 5.0f, 0.0f);
return 0;
default:
break;
}
return DefWindowProcA(hWnd, message, wParam, lParam);
}
void WinWindow::StartFullScreen() {
LONG dwExStyle = GetWindowLongA(hWnd, GWL_EXSTYLE)
& ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
if(dwExStyle==0) {
std::cerr << "GetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
LONG dwStyle = GetWindowLongA(hWnd, GWL_STYLE)
& ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
if(dwStyle==0) {
std::cerr << "GetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(!SetWindowLongA(hWnd, GWL_EXSTYLE, dwExStyle)) {
std::cerr << "SetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(!SetWindowLongA(hWnd, GWL_STYLE, dwStyle)) {
std::cerr << "SetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
GLint prev[2];
std::memcpy(prev, context->windowed_size, sizeof(prev));
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
std::memcpy(context->windowed_size, prev, sizeof(prev));
}
void WinWindow::StopFullScreen() {
ChangeDisplaySettings(NULL, 0);
ShowCursor(TRUE);
LONG dwExStyle = GetWindowLongA(hWnd, GWL_EXSTYLE) | WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
LONG dwStyle = GetWindowLongA(hWnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW;
if(dwExStyle==0) {
std::cerr << "GetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(dwStyle==0) {
std::cerr << "GetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(!SetWindowLongA(hWnd, GWL_EXSTYLE, dwExStyle)) {
std::cerr << "SetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(!SetWindowLongA(hWnd, GWL_STYLE, dwStyle)) {
std::cerr << "SetWindowLongA() failed" << std::endl;
CheckWGLDieOnError();
}
if(!SetWindowPos(hWnd, HWND_TOP, 0, 0, context->windowed_size[0], context->windowed_size[1], SWP_FRAMECHANGED)) {
std::cerr << "SetWindowPos() failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::ToggleFullscreen()
{
if(!context->is_fullscreen) {
StartFullScreen();
context->is_fullscreen = true;
}else{
StopFullScreen();
context->is_fullscreen = false;
}
}
void WinWindow::Move(int x, int y)
{
if( !SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE) ) {
std::cerr << "WinWindow::Move failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::Resize(unsigned int w, unsigned int h)
{
if( !SetWindowPos(hWnd, 0, 0, 0, w, h, SWP_NOMOVE) ) {
std::cerr << "WinWindow::Resize failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::MakeCurrent()
{
if(wglMakeCurrent(hDC, hGLRC)==FALSE) {
std::cerr << "wglMakeCurrent() failed" << std::endl;
CheckWGLDieOnError();
}
// Setup threadlocal context as this
context = this;
RECT rect;
if(!GetWindowRect(hWnd, &rect)) {
std::cerr << "GetWindowRect() failed" << std::endl;
CheckWGLDieOnError();
}
Resize(rect.right - rect.left, rect.bottom - rect.top);
}
void WinWindow::RemoveCurrent()
{
if(wglMakeCurrent(NULL, NULL)==0) {
std::cerr << "wglMakeCurrent() failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::SwapBuffers()
{
if(!::SwapBuffers(hDC)) {
std::cerr << "SwapBuffers() failed" << std::endl;
CheckWGLDieOnError();
}
}
void WinWindow::ProcessEvents()
{
MSG msg;
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
pangolin::Quit();
break;
}
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
std::unique_ptr<WindowInterface> CreateWinWindowAndBind(std::string window_title, int w, int h)
{
WinWindow* win = new WinWindow(window_title, w, h);
return std::unique_ptr<WindowInterface>(win);
}
PANGOLIN_REGISTER_FACTORY(WinWindow)
{
struct WinWindowFactory : public FactoryInterface<WindowInterface> {
std::unique_ptr<WindowInterface> Open(const Uri& uri) override {
const std::string window_title = uri.Get<std::string>("window_title", "window");
const int w = uri.Get<int>("w", 640);
const int h = uri.Get<int>("h", 480);
return std::unique_ptr<WindowInterface>(CreateWinWindowAndBind(window_title, w, h));
}
};
auto factory = std::make_shared<WinWindowFactory>();
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 10, "winapi");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 100, "default");
}
}

View File

@@ -0,0 +1,530 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011-2018 Steven Lovegrove, Andrey Mnatsakanov
*
* 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.
*/
// Code based on public domain sample at
// https://www.opengl.org/wiki/Tutorial:_OpenGL_3.0_Context_Creation_%28GLX%29
#include <pangolin/factory/factory_registry.h>
#include <pangolin/platform.h>
#include <pangolin/gl/glinclude.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/display/window.h>
#include <pangolin/display/device/X11Window.h>
#include <mutex>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <GL/glx.h>
namespace pangolin
{
extern __thread PangolinGl* context;
std::mutex window_mutex;
std::weak_ptr<X11GlContext> global_gl_context;
const long EVENT_MASKS = ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|ButtonMotionMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|FocusChangeMask;
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(::Display*, ::GLXFBConfig, ::GLXContext, Bool, const int*);
// Adapted from: http://www.opengl.org/resources/features/OGLextensions/
bool isExtensionSupported(const char *extList, const char *extension)
{
/* Extension names should not have spaces. */
const char* where = strchr(extension, ' ');
if (where || *extension == '\0') {
return false;
}
for(const char* start=extList;;) {
where = strstr(start, extension);
if (!where) {
break;
}
const char *terminator = where + strlen(extension);
if ( where == start || *(where - 1) == ' ' ) {
if ( *terminator == ' ' || *terminator == '\0' ) {
return true;
}
}
start = terminator;
}
return false;
}
::GLXFBConfig ChooseFrameBuffer(
::Display *display, bool glx_doublebuffer,
int glx_sample_buffers, int glx_samples
) {
// Desired attributes
int visual_attribs[] =
{
GLX_X_RENDERABLE , True,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
GLX_RENDER_TYPE , GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_ALPHA_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_STENCIL_SIZE , 8,
GLX_DOUBLEBUFFER , glx_doublebuffer ? True : False,
None
};
int fbcount;
GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount);
if (!fbc) {
throw std::runtime_error("Pangolin X11: Unable to retrieve framebuffer options");
}
int best_fbc = -1;
int worst_fbc = -1;
int best_num_samp = -1;
int worst_num_samp = 999;
// Enumerate framebuffer options, storing the best and worst that match our attribs
for (int i=0; i<fbcount; ++i)
{
XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
if ( vi )
{
int samp_buf, samples;
glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES , &samples );
// Filter for the best available.
if ( samples > best_num_samp ) {
best_fbc = i;
best_num_samp = samples;
}
// Filter lowest settings which match minimum user requirement.
if ( samp_buf >= glx_sample_buffers && samples >= glx_samples && samples < worst_num_samp ) {
worst_fbc = i;
worst_num_samp = samples;
}
}
XFree( vi );
}
// Select the minimum suitable option. The 'best' is often too slow.
int chosen_fbc_id = worst_fbc;
// If minimum requested isn't available, return the best that is.
if(chosen_fbc_id < 0) {
pango_print_warn("Framebuffer with requested attributes not available. Using available framebuffer. You may see visual artifacts.");
chosen_fbc_id = best_fbc;
}
::GLXFBConfig chosenFbc = fbc[ chosen_fbc_id ];
XFree( fbc );
return chosenFbc;
}
static bool ctxErrorOccurred = false;
static int ctxErrorHandler( ::Display * /*dpy*/, ::XErrorEvent * ev )
{
const int buffer_size = 10240;
char buffer[buffer_size];
XGetErrorText(ev->display, ev->error_code, buffer, buffer_size );
pango_print_error("X11 Error: %s\n", buffer);
ctxErrorOccurred = true;
return 0;
}
GLXContext CreateGlContext(::Display *display, ::GLXFBConfig chosenFbc, GLXContext share_context = 0)
{
int glx_major, glx_minor;
if ( !glXQueryVersion( display, &glx_major, &glx_minor ) ||
( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
{
throw std::runtime_error("Pangolin X11: Invalid GLX version. Require GLX >= 1.3");
}
GLXContext new_ctx;
// Get the default screen's GLX extension list
const char *glxExts = glXQueryExtensionsString( display, DefaultScreen( display ) );
glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
(glXCreateContextAttribsARBProc) glXGetProcAddressARB(
(const GLubyte *) "glXCreateContextAttribsARB"
);
// Install an X error handler so the application won't exit if GL 3.0
// context allocation fails. Handler is global and shared across all threads.
ctxErrorOccurred = false;
int (*oldHandler)(::Display*, ::XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler);
if ( isExtensionSupported( glxExts, "GLX_ARB_create_context" ) && glXCreateContextAttribsARB )
{
int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
//GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
None
};
new_ctx = glXCreateContextAttribsARB( display, chosenFbc, share_context, True, context_attribs );
// Sync to ensure any errors generated are processed.
XSync( display, False );
if ( ctxErrorOccurred || !new_ctx ) {
ctxErrorOccurred = false;
// Fall back to old-style 2.x context. Implementations will return the newest
// context version compatible with OpenGL versions less than version 3.0.
context_attribs[1] = 1; // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
context_attribs[3] = 0; // GLX_CONTEXT_MINOR_VERSION_ARB = 0
new_ctx = glXCreateContextAttribsARB( display, chosenFbc, share_context, True, context_attribs );
}
} else {
// Fallback to GLX 1.3 Context
new_ctx = glXCreateNewContext( display, chosenFbc, GLX_RGBA_TYPE, share_context, True );
}
// Sync to ensure any errors generated are processed.
XSync( display, False );
// Restore the original error handler
XSetErrorHandler( oldHandler );
if ( ctxErrorOccurred || !new_ctx ) {
throw std::runtime_error("Pangolin X11: Failed to create an OpenGL context");
}
// Verifying that context is a direct context
if ( ! glXIsDirect ( display, new_ctx ) ) {
pango_print_warn("Pangolin X11: Indirect GLX rendering context obtained\n");
}
return new_ctx;
}
X11GlContext::X11GlContext(std::shared_ptr<X11Display>& d, ::GLXFBConfig chosenFbc, std::shared_ptr<X11GlContext> shared_context)
: display(d), shared_context(shared_context)
{
// prevent chained sharing
while(shared_context && shared_context->shared_context) {
shared_context = shared_context->shared_context;
}
// Contexts can't be shared across different displays.
if(shared_context && shared_context->display != d) {
shared_context.reset();
}
glcontext = CreateGlContext(display->display, chosenFbc, shared_context ? shared_context->glcontext : 0);
}
X11GlContext::~X11GlContext()
{
glXDestroyContext( display->display, glcontext );
}
X11Window::X11Window(
const std::string& title, int width, int height,
std::shared_ptr<X11Display>& display, ::GLXFBConfig chosenFbc
) : display(display), glcontext(0), win(0), cmap(0)
{
PangolinGl::windowed_size[0] = width;
PangolinGl::windowed_size[1] = height;
// Get a visual
XVisualInfo *vi = glXGetVisualFromFBConfig( display->display, chosenFbc );
// Create colourmap
XSetWindowAttributes swa;
swa.background_pixmap = None;
swa.border_pixel = 0;
swa.event_mask = StructureNotifyMask;
swa.colormap = cmap = XCreateColormap( display->display,
RootWindow( display->display, vi->screen ),
vi->visual, AllocNone );
// Create window
win = XCreateWindow( display->display, RootWindow( display->display, vi->screen ),
0, 0, width, height, 0, vi->depth, InputOutput,
vi->visual,
CWBorderPixel|CWColormap|CWEventMask, &swa );
XFree( vi );
if ( !win ) {
throw std::runtime_error("Pangolin X11: Failed to create window." );
}
XStoreName( display->display, win, title.c_str() );
XMapWindow( display->display, win );
// Request to be notified of these events
XSelectInput(display->display, win, EVENT_MASKS );
delete_message = XInternAtom(display->display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display->display, win, &delete_message, 1);
}
X11Window::~X11Window()
{
glXMakeCurrent( display->display, 0, 0 );
XDestroyWindow( display->display, win );
XFreeColormap( display->display, cmap );
}
void X11Window::MakeCurrent(GLXContext ctx)
{
glXMakeCurrent( display->display, win, ctx );
context = this;
}
void X11Window::MakeCurrent()
{
MakeCurrent(glcontext ? glcontext->glcontext : global_gl_context.lock()->glcontext);
}
void X11Window::RemoveCurrent()
{
glXMakeCurrent(display->display, 0, nullptr);
}
void X11Window::ToggleFullscreen()
{
const Atom _NET_WM_STATE_FULLSCREEN = XInternAtom(display->display, "_NET_WM_STATE_FULLSCREEN", True);
const Atom _NET_WM_STATE = XInternAtom(display->display, "_NET_WM_STATE", True);
XEvent e;
e.xclient.type = ClientMessage;
e.xclient.window = win;
e.xclient.message_type = _NET_WM_STATE;
e.xclient.format = 32;
e.xclient.data.l[0] = 2; // Toggle
e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
e.xclient.data.l[2] = 0;
e.xclient.data.l[3] = 1;
e.xclient.data.l[4] = 0;
XSendEvent(display->display, DefaultRootWindow(display->display), False, SubstructureRedirectMask | SubstructureNotifyMask, &e);
XMoveResizeWindow(display->display, win, 0, 0, windowed_size[0], windowed_size[1]);
}
void X11Window::Move(int x, int y)
{
XMoveWindow(display->display, win, x, y);
}
void X11Window::Resize(unsigned int w, unsigned int h)
{
XResizeWindow(display->display, win, w, h);
}
void X11Window::ProcessEvents()
{
XEvent ev;
while(!pangolin::ShouldQuit() && XPending(display->display) > 0)
{
XNextEvent(display->display, &ev);
switch(ev.type){
case ConfigureNotify:
pangolin::process::Resize(ev.xconfigure.width, ev.xconfigure.height);
break;
case ClientMessage:
// We've only registered to receive WM_DELETE_WINDOW, so no further checks needed.
pangolin::Quit();
break;
case ButtonPress:
case ButtonRelease:
{
const int button = ev.xbutton.button-1;
pangolin::process::Mouse(
button,
ev.xbutton.type == ButtonRelease,
ev.xbutton.x, ev.xbutton.y
);
break;
}
case FocusOut:
pangolin::context->mouse_state = 0;
break;
case MotionNotify:
if(ev.xmotion.state & (Button1Mask|Button2Mask|Button3Mask) ) {
pangolin::process::MouseMotion(ev.xmotion.x, ev.xmotion.y);
}else{
pangolin::process::PassiveMouseMotion(ev.xmotion.x, ev.xmotion.y);
}
break;
case KeyPress:
case KeyRelease:
int key;
char ch;
KeySym sym;
if( XLookupString(&ev.xkey,&ch,1,&sym,0) == 0) {
switch (sym) {
case XK_F1: key = PANGO_SPECIAL + PANGO_KEY_F1 ; break;
case XK_F2: key = PANGO_SPECIAL + PANGO_KEY_F2 ; break;
case XK_F3: key = PANGO_SPECIAL + PANGO_KEY_F3 ; break;
case XK_F4: key = PANGO_SPECIAL + PANGO_KEY_F4 ; break;
case XK_F5: key = PANGO_SPECIAL + PANGO_KEY_F5 ; break;
case XK_F6: key = PANGO_SPECIAL + PANGO_KEY_F6 ; break;
case XK_F7: key = PANGO_SPECIAL + PANGO_KEY_F7 ; break;
case XK_F8: key = PANGO_SPECIAL + PANGO_KEY_F8 ; break;
case XK_F9: key = PANGO_SPECIAL + PANGO_KEY_F9 ; break;
case XK_F10: key = PANGO_SPECIAL + PANGO_KEY_F10 ; break;
case XK_F11: key = PANGO_SPECIAL + PANGO_KEY_F11 ; break;
case XK_F12: key = PANGO_SPECIAL + PANGO_KEY_F12 ; break;
case XK_Left: key = PANGO_SPECIAL + PANGO_KEY_LEFT ; break;
case XK_Up: key = PANGO_SPECIAL + PANGO_KEY_UP ; break;
case XK_Right: key = PANGO_SPECIAL + PANGO_KEY_RIGHT ; break;
case XK_Down: key = PANGO_SPECIAL + PANGO_KEY_DOWN ; break;
case XK_Page_Up: key = PANGO_SPECIAL + PANGO_KEY_PAGE_UP ; break;
case XK_Page_Down: key = PANGO_SPECIAL + PANGO_KEY_PAGE_DOWN ; break;
case XK_Home: key = PANGO_SPECIAL + PANGO_KEY_HOME ; break;
case XK_End: key = PANGO_SPECIAL + PANGO_KEY_END ; break;
case XK_Insert: key = PANGO_SPECIAL + PANGO_KEY_INSERT ; break;
case XK_Shift_L:
case XK_Shift_R:
key = -1;
if(ev.type==KeyPress) {
pangolin::context->mouse_state |= pangolin::KeyModifierShift;
}else{
pangolin::context->mouse_state &= ~pangolin::KeyModifierShift;
}
break;
case XK_Control_L:
case XK_Control_R:
key = -1;
if(ev.type==KeyPress) {
pangolin::context->mouse_state |= pangolin::KeyModifierCtrl;
}else{
pangolin::context->mouse_state &= ~pangolin::KeyModifierCtrl;
}
break;
case XK_Alt_L:
case XK_Alt_R:
key = -1;
if(ev.type==KeyPress) {
pangolin::context->mouse_state |= pangolin::KeyModifierAlt;
}else{
pangolin::context->mouse_state &= ~pangolin::KeyModifierAlt;
}
break;
case XK_Super_L:
case XK_Super_R:
key = -1;
if(ev.type==KeyPress) {
pangolin::context->mouse_state |= pangolin::KeyModifierCmd;
}else{
pangolin::context->mouse_state &= ~pangolin::KeyModifierCmd;
}
break;
default: key = -1; break;
}
}else{
key = ch;
}
if(key >=0) {
if(ev.type == KeyPress) {
pangolin::process::Keyboard(key, ev.xkey.x, ev.xkey.y);
}else{
pangolin::process::KeyboardUp(key, ev.xkey.x, ev.xkey.y);
}
}
break;
}
}
}
void X11Window::SwapBuffers() {
glXSwapBuffers(display->display, win);
}
std::unique_ptr<WindowInterface> CreateX11WindowAndBind(const std::string& window_title, const int w, const int h, const std::string& display_name, const bool double_buffered, const int sample_buffers, const int samples)
{
std::shared_ptr<X11Display> newdisplay = std::make_shared<X11Display>(display_name.empty() ? NULL : display_name.c_str() );
if (!newdisplay) {
throw std::runtime_error("Pangolin X11: Failed to open X display");
}
::GLXFBConfig newfbc = ChooseFrameBuffer(newdisplay->display, double_buffered, sample_buffers, samples);
window_mutex.lock();
std::shared_ptr<X11GlContext> newglcontext = std::make_shared<X11GlContext>(
newdisplay, newfbc, global_gl_context.lock()
);
if(!global_gl_context.lock()) {
global_gl_context = newglcontext;
}
window_mutex.unlock();
X11Window* win = new X11Window(window_title, w, h, newdisplay, newfbc);
win->glcontext = newglcontext;
win->is_double_buffered = double_buffered;
return std::unique_ptr<WindowInterface>(win);
}
PANGOLIN_REGISTER_FACTORY(X11Window)
{
struct X11WindowFactory : public FactoryInterface<WindowInterface> {
std::unique_ptr<WindowInterface> Open(const Uri& uri) override {
const std::string window_title = uri.Get<std::string>("window_title", "window");
const int w = uri.Get<int>("w", 640);
const int h = uri.Get<int>("h", 480);
const std::string display_name = uri.Get<std::string>("display_name", "");
const bool double_buffered = uri.Get<bool>("double_buffered", true);
const int sample_buffers = uri.Get<int>("sample_buffers", 1);
const int samples = uri.Get<int>("samples", 1);
return std::unique_ptr<WindowInterface>(CreateX11WindowAndBind(window_title, w, h, display_name, double_buffered, sample_buffers, samples));
}
};
auto factory = std::make_shared<X11WindowFactory>();
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 10, "x11");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 10, "linux");
FactoryRegistry<WindowInterface>::I().RegisterFactory(factory, 100, "default");
}
}

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();
}
}

View File

@@ -0,0 +1,223 @@
#include <pangolin/display/image_view.h>
#include <pangolin/image/image_utils.h>
#include <pangolin/image/image_convert.h>
namespace pangolin
{
ImageView::ImageView()
: offset_scale(0.0f, 1.0f), lastPressed(false), mouseReleased(false), mousePressed(false), overlayRender(true)
{
SetHandler(this);
}
ImageView::~ImageView()
{
}
void ImageView::Render()
{
LoadPending();
glPushAttrib(GL_DEPTH_BITS);
glDisable(GL_DEPTH_TEST);
Activate();
this->UpdateView();
this->glSetViewOrtho();
if(tex.IsValid())
{
if(offset_scale.first != 0.0 || offset_scale.second != 1.0)
{
pangolin::GlSlUtilities::OffsetAndScale(offset_scale.first, offset_scale.second);
}
else
{
glColor4f(1, 1, 1, 1);
}
this->glRenderTexture(tex);
pangolin::GlSlUtilities::UseNone();
}
if(overlayRender)
{
this->glRenderOverlay();
}
if(extern_draw_function)
{
extern_draw_function(*this);
}
glPopAttrib();
}
void ImageView::Mouse(View& view, pangolin::MouseButton button, int x, int y, bool pressed, int button_state)
{
ImageViewHandler::Mouse(view, button, x, y, pressed, button_state);
mouseReleased = (!pressed && lastPressed);
mousePressed = lastPressed = (pressed && button == pangolin::MouseButtonLeft);
}
void ImageView::Keyboard(View& view, unsigned char key, int x, int y, bool pressed)
{
if(key == 'a')
{
if(!tex.IsValid())
{
std::cerr << "ImageViewHandler does not contain valid texture." << std::endl;
return;
}
// compute scale
const bool have_selection = std::isfinite(GetSelection().Area()) && std::abs(GetSelection().Area()) >= 4;
const pangolin::XYRangef froi = have_selection ? GetSelection() : GetViewToRender();
// Download texture so that we can take min / max
pangolin::TypedImage img;
tex.Download(img);
offset_scale = pangolin::GetOffsetScale(img, pangolin::Round(froi), img.fmt);
}
else if(key == 'b')
{
if(!tex.IsValid())
{
std::cerr << "ImageViewHandler does not contain valid texture." << std::endl;
return;
}
// compute scale
const bool have_selection = std::isfinite(GetSelection().Area()) && std::abs(GetSelection().Area()) >= 4;
const pangolin::XYRangef froi = have_selection ? GetSelection() : GetViewToRender();
// Download texture so that we can take min / max
pangolin::TypedImage img;
tex.Download(img);
std::pair<float, float> mm = pangolin::GetMinMax(img, pangolin::Round(froi), img.fmt);
printf("Min / Max in Region: %f / %f\n", mm.first, mm.second);
}
else
{
pangolin::ImageViewHandler::Keyboard(view, key, x, y, pressed);
}
}
pangolin::GlTexture& ImageView::Tex() {
return tex;
}
ImageView& ImageView::SetImage(void* ptr, size_t w, size_t h, size_t pitch, pangolin::GlPixFormat img_fmt, bool delayed_upload )
{
const size_t pix_bytes =
pangolin::GlFormatChannels(img_fmt.glformat) * pangolin::GlDataTypeBytes(img_fmt.gltype);
const bool convert_first = (img_fmt.gltype == GL_DOUBLE);
if(delayed_upload || !pangolin::GetBoundWindow() || IsDevicePtr(ptr) || convert_first )
{
texlock.lock();
if(!convert_first) {
img_to_load = ManagedImage<unsigned char>(w,h,w*pix_bytes);
PitchedCopy((char*)img_to_load.ptr, img_to_load.pitch, (char*)ptr, pitch, w * pix_bytes, h);
img_fmt_to_load = img_fmt;
}else if(img_fmt.gltype == GL_DOUBLE) {
Image<double> double_image( (double*)ptr, w, h, pitch);
img_to_load.OwnAndReinterpret(ImageConvert<float>(double_image));
img_fmt_to_load = GlPixFormat::FromType<float>();
}else{
pango_print_warn("TextureView: Unable to display image.\n");
}
texlock.unlock();
return *this;
}
PANGO_ASSERT(pitch % pix_bytes == 0);
const size_t stride = pitch / pix_bytes;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
// Initialise if it didn't already exist or the size was too small
if(!tex.tid || tex.width != (int)w || tex.height != (int)h ||
tex.internal_format != img_fmt.scalable_internal_format)
{
fmt = img_fmt;
SetDimensions(w, h);
SetAspect((float)w / (float)h);
tex.Reinitialise(w, h, img_fmt.scalable_internal_format, true, 0, img_fmt.glformat, img_fmt.gltype, ptr);
}
else
{
tex.Upload(ptr, img_fmt.glformat, img_fmt.gltype);
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
return *this;
}
ImageView& ImageView::SetImage(const pangolin::Image<unsigned char>& img, const pangolin::GlPixFormat& glfmt, bool delayed_upload )
{
return SetImage(img.ptr, img.w, img.h, img.pitch, glfmt, delayed_upload);
}
ImageView& ImageView::SetImage(const pangolin::TypedImage& img, bool delayed_upload )
{
return SetImage(img.ptr, img.w, img.h, img.pitch, pangolin::GlPixFormat(img.fmt), delayed_upload);
}
ImageView& ImageView::SetImage(const pangolin::GlTexture& texture)
{
// Initialise if it didn't already exist or the size was too small
if(!tex.tid || tex.width != texture.width || tex.height != texture.height ||
tex.internal_format != texture.internal_format)
{
SetDimensions(texture.width, texture.height);
SetAspect((float)texture.width / (float)texture.height);
tex.Reinitialise(texture.width, texture.height, texture.internal_format, true);
}
glCopyImageSubData(
texture.tid, GL_TEXTURE_2D, 0, 0, 0, 0, tex.tid, GL_TEXTURE_2D, 0, 0, 0, 0, tex.width, tex.height, 1);
return *this;
}
void ImageView::LoadPending()
{
if(img_to_load.ptr)
{
// Scoped lock
texlock.lock();
SetImage(img_to_load, img_fmt_to_load, false);
img_to_load.Deallocate();
texlock.unlock();
}
}
ImageView& ImageView::Clear()
{
tex.Delete();
return *this;
}
std::pair<float, float>& ImageView::GetOffsetScale() {
return offset_scale;
}
bool ImageView::MouseReleased() const {
return mouseReleased;
}
bool ImageView::MousePressed() const {
return mousePressed;
}
void ImageView::SetRenderOverlay(const bool& val) {
overlayRender = val;
}
}

View File

@@ -0,0 +1,707 @@
/* 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/display/opengl_render_state.h>
#include <pangolin/gl/glinclude.h>
#include <stdexcept>
namespace pangolin
{
inline void glLoadMatrix(const float* m) { glLoadMatrixf(m); }
inline void glMultMatrix(const float* m) { glMultMatrixf(m); }
#ifndef HAVE_GLES
inline void glLoadMatrix(const double* m) { glLoadMatrixd(m); }
inline void glMultMatrix(const double* m) { glMultMatrixd(m); }
#endif
OpenGlMatrix OpenGlMatrix::Translate(GLprecision x, GLprecision y, GLprecision z)
{
OpenGlMatrix mat;
mat.SetIdentity();
mat(0, 3) = x;
mat(1, 3) = y;
mat(2, 3) = z;
return mat;
}
OpenGlMatrix OpenGlMatrix::Scale(GLprecision x, GLprecision y, GLprecision z)
{
OpenGlMatrix mat;
mat.SetIdentity();
mat(0, 0) = x;
mat(1, 1) = y;
mat(2, 2) = z;
return mat;
}
OpenGlMatrix OpenGlMatrix::RotateX(GLprecision theta_rad)
{
OpenGlMatrix mat;
mat.SetIdentity();
const GLprecision costh = cos(theta_rad);
const GLprecision sinth = sin(theta_rad);
mat(1, 1) = costh;
mat(1, 2) = -sinth;
mat(2, 1) = sinth;
mat(2, 2) = costh;
return mat;
}
OpenGlMatrix OpenGlMatrix::RotateY(GLprecision theta_rad)
{
OpenGlMatrix mat;
mat.SetIdentity();
const GLprecision costh = cos(theta_rad);
const GLprecision sinth = sin(theta_rad);
mat(0, 0) = costh;
mat(0, 2) = sinth;
mat(2, 0) = -sinth;
mat(2, 2) = costh;
return mat;
}
OpenGlMatrix OpenGlMatrix::RotateZ(GLprecision theta_rad)
{
OpenGlMatrix mat;
mat.SetIdentity();
const GLprecision costh = cos(theta_rad);
const GLprecision sinth = sin(theta_rad);
mat(0, 0) = costh;
mat(0, 1) = -sinth;
mat(1, 0) = sinth;
mat(1, 1) = costh;
return mat;
}
void OpenGlMatrix::Load() const
{
glLoadMatrix(m);
}
void OpenGlMatrix::Multiply() const
{
glMultMatrix(m);
}
void OpenGlMatrix::SetIdentity()
{
m[0] = 1.0f; m[1] = 0.0f; m[2] = 0.0f; m[3] = 0.0f;
m[4] = 0.0f; m[5] = 1.0f; m[6] = 0.0f; m[7] = 0.0f;
m[8] = 0.0f; m[9] = 0.0f; m[10] = 1.0f; m[11] = 0.0f;
m[12] = 0.0f; m[13] = 0.0f; m[14] = 0.0f; m[15] = 1.0f;
}
OpenGlMatrix OpenGlMatrix::Transpose() const
{
OpenGlMatrix trans;
trans.m[0] = m[0]; trans.m[4] = m[1]; trans.m[8] = m[2]; trans.m[12] = m[3];
trans.m[1] = m[4]; trans.m[5] = m[5]; trans.m[9] = m[6]; trans.m[13] = m[7];
trans.m[2] = m[8]; trans.m[6] = m[9]; trans.m[10] = m[10]; trans.m[14] = m[11];
trans.m[3] = m[12]; trans.m[7] = m[13]; trans.m[11] = m[14]; trans.m[15] = m[15];
return trans;
}
OpenGlMatrix OpenGlMatrix::Inverse() const
{
OpenGlMatrix inv;
inv.m[0] = m[0]; inv.m[4] = m[1]; inv.m[8] = m[2]; inv.m[12] = -(m[0]*m[12] + m[1]*m[13] + m[2]*m[14]);
inv.m[1] = m[4]; inv.m[5] = m[5]; inv.m[9] = m[6]; inv.m[13] = -(m[4]*m[12] + m[5]*m[13] + m[6]*m[14]);
inv.m[2] = m[8]; inv.m[6] = m[9]; inv.m[10] = m[10]; inv.m[14] = -(m[8]*m[12] + m[9]*m[13] + m[10]*m[14]);
inv.m[3] = 0; inv.m[7] = 0; inv.m[11] = 0; inv.m[15] = 1;
return inv;
}
std::ostream& operator<<(std::ostream& os, const OpenGlMatrix& mat)
{
for(int r=0; r< 4; ++r) {
for(int c=0; c<4; ++c) {
std::cout << mat.m[4*c+r] << '\t';
}
std::cout << std::endl;
}
return os;
}
void OpenGlRenderState::Apply() const
{
glMatrixMode(GL_PROJECTION);
projection[0].Load();
// Leave in MODEVIEW mode
glMatrixMode(GL_MODELVIEW);
modelview.Load();
if(follow) {
T_cw.Multiply();
}
}
OpenGlMatrix& OpenGlRenderState::GetProjectionMatrix(unsigned int view)
{
if( projection.size() <= view ) {
projection.resize(view+1);
}
return projection[view];
}
OpenGlMatrix OpenGlRenderState::GetProjectionMatrix(unsigned int view) const
{
if( projection.size() <= view ) {
return IdentityMatrix();
}
return projection[view];
}
OpenGlMatrix& OpenGlRenderState::GetViewOffset(unsigned int view)
{
if( modelview_premult.size() <= view ) {
modelview_premult.resize(view+1);
}
return modelview_premult[view];
}
OpenGlMatrix OpenGlRenderState::GetViewOffset(unsigned int view) const
{
if( modelview_premult.size() <= view ) {
return IdentityMatrix();
}
return modelview_premult[view];
}
void OpenGlRenderState::ApplyNView(int view) const
{
glMatrixMode(GL_PROJECTION);
projection[view].Load();
// Leave in MODEVIEW mode
glMatrixMode(GL_MODELVIEW);
OpenGlMatrix m = GetModelViewMatrix(view);
m.Load();
if(follow) {
T_cw.Multiply();
}
}
OpenGlRenderState::OpenGlRenderState()
: modelview(IdentityMatrix()), follow(false)
{
projection.push_back( IdentityMatrix() );
}
OpenGlRenderState::OpenGlRenderState(const OpenGlMatrix& projection_matrix)
: modelview(IdentityMatrix()), follow(false)
{
projection.push_back( projection_matrix );
}
OpenGlRenderState::OpenGlRenderState(const OpenGlMatrix& projection_matrix, const OpenGlMatrix& modelview_matrx)
: modelview(modelview_matrx), follow(false)
{
projection.push_back( projection_matrix );
}
void OpenGlRenderState::ApplyIdentity()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
OpenGlRenderState& OpenGlRenderState::SetProjectionMatrix(OpenGlMatrix m)
{
projection[0] = m;
return *this;
}
OpenGlRenderState& OpenGlRenderState::SetModelViewMatrix(OpenGlMatrix m)
{
modelview = m;
return *this;
}
OpenGlRenderState& OpenGlRenderState::Set(OpenGlMatrixSpec m)
{
switch (m.type) {
case GlProjectionStack:
projection[0] = m;
break;
case GlModelViewStack:
modelview = m;
break;
default:
throw std::runtime_error("Unexpected matrix type");
break;
}
return *this;
}
OpenGlMatrix operator*(const OpenGlMatrix& lhs, const OpenGlMatrix& rhs)
{
OpenGlMatrix ret;
pangolin::MatMul<4,4,4>(ret.m, lhs.m, rhs.m);
return ret;
}
OpenGlMatrix& OpenGlRenderState::GetProjectionMatrix()
{
// guarenteed to have at least one projection matrix element
return projection[0];
}
OpenGlMatrix OpenGlRenderState::GetProjectionMatrix() const
{
// guarenteed to have at least one projection matrix element
return projection[0];
}
OpenGlMatrix& OpenGlRenderState::GetModelViewMatrix()
{
return modelview;
}
OpenGlMatrix OpenGlRenderState::GetModelViewMatrix() const
{
return modelview;
}
OpenGlMatrix OpenGlRenderState::GetModelViewMatrix(int i) const
{
return modelview_premult[i] * modelview;
}
OpenGlMatrix OpenGlRenderState::GetProjectionModelViewMatrix() const
{
return GetProjectionMatrix() * GetModelViewMatrix();
}
OpenGlMatrix OpenGlRenderState::GetProjectiveTextureMatrix() const
{
return OpenGlMatrix::Translate(0.5,0.5,0.5) * OpenGlMatrix::Scale(0.5,0.5,0.5) * GetProjectionModelViewMatrix();
}
void OpenGlRenderState::EnableProjectiveTexturing() const
{
#ifndef HAVE_GLES
const pangolin::OpenGlMatrix projmattrans = GetProjectiveTextureMatrix().Transpose();
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
glTexGendv(GL_S, GL_EYE_PLANE, projmattrans.m);
glTexGendv(GL_T, GL_EYE_PLANE, projmattrans.m+4);
glTexGendv(GL_R, GL_EYE_PLANE, projmattrans.m+8);
glTexGendv(GL_Q, GL_EYE_PLANE, projmattrans.m+12);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
#endif
}
void OpenGlRenderState::DisableProjectiveTexturing() const
{
#ifndef HAVE_GLES
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);
#endif
}
void OpenGlRenderState::Follow(const OpenGlMatrix& T_wc, bool follow)
{
this->T_cw = T_wc.Inverse();
if(follow != this->follow) {
if(follow) {
const OpenGlMatrix T_vc = GetModelViewMatrix() * T_wc;
SetModelViewMatrix(T_vc);
this->follow = true;
}else{
Unfollow();
}
}
}
void OpenGlRenderState::Unfollow()
{
const OpenGlMatrix T_vw = GetModelViewMatrix() * T_cw;
SetModelViewMatrix(T_vw);
this->follow = false;
}
// Use OpenGl's default frame of reference
OpenGlMatrixSpec ProjectionMatrix(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
return ProjectionMatrixRUB_BottomLeft(w,h,fu,fv,u0,v0,zNear,zFar);
}
OpenGlMatrixSpec ProjectionMatrixOrthographic(GLprecision l, GLprecision r, GLprecision b, GLprecision t, GLprecision n, GLprecision f )
{
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
P.m[0] = 2/(r-l);
P.m[1] = 0;
P.m[2] = 0;
P.m[3] = 0;
P.m[4] = 0;
P.m[5] = 2/(t-b);
P.m[6] = 0;
P.m[7] = 0;
P.m[8] = 0;
P.m[9] = 0;
P.m[10] = -2/(f-n);
P.m[11] = 0;
P.m[12] = -(r+l)/(r-l);
P.m[13] = -(t+b)/(t-b);
P.m[14] = -(f+n)/(f-n);
P.m[15] = 1;
return P;
}
// Camera Axis:
// X - Right, Y - Up, Z - Back
// Image Origin:
// Bottom Left
// Caution: Principal point defined with respect to image origin (0,0) at
// top left of top-left pixel (not center, and in different frame
// of reference to projection function image)
OpenGlMatrixSpec ProjectionMatrixRUB_BottomLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision L = +(u0) * zNear / -fu;
const GLprecision T = +(v0) * zNear / fv;
const GLprecision R = -(w-u0) * zNear / -fu;
const GLprecision B = -(h-v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+2] = -(zFar +zNear) / (zFar - zNear);
P.m[2*4+0] = (R+L)/(R-L);
P.m[2*4+1] = (T+B)/(T-B);
P.m[2*4+3] = -1.0;
P.m[3*4+2] = -(2*zFar*zNear)/(zFar-zNear);
return P;
}
// Camera Axis:
// X - Right, Y - Up, Z - Back
// Image Origin:
// Top Left
// Caution: Principal point defined with respect to image origin (0,0) at
// top left of top-left pixel (not center, and in different frame
// of reference to projection function image)
OpenGlMatrixSpec ProjectionMatrixRUB_TopLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision L = +(u0) * zNear / -fu;
const GLprecision R = -(w-u0) * zNear / -fu;
const GLprecision T = -(h-v0) * zNear / fv;
const GLprecision B = +(v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+2] = -(zFar +zNear) / (zFar - zNear);
P.m[2*4+0] = (R+L)/(R-L);
P.m[2*4+1] = (T+B)/(T-B);
P.m[2*4+3] = -1.0;
P.m[3*4+2] = -(2*zFar*zNear)/(zFar-zNear);
return P;
}
// Camera Axis:
// X - Right, Y - Down, Z - Forward
// Image Origin:
// Top Left
// Pricipal point specified with image origin (0,0) at top left of top-left pixel (not center)
OpenGlMatrixSpec ProjectionMatrixRDF_TopLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision L = -(u0) * zNear / fu;
const GLprecision R = +(w-u0) * zNear / fu;
const GLprecision T = -(v0) * zNear / fv;
const GLprecision B = +(h-v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+0] = (R+L)/(L-R);
P.m[2*4+1] = (T+B)/(B-T);
P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
P.m[2*4+3] = 1.0;
P.m[3*4+2] = (2*zFar*zNear)/(zNear - zFar);
return P;
}
// Camera Axis:
// X - Right, Y - Down, Z - Forward
// Image Origin:
// Top Right
// Pricipal point specified with image origin (0,0) at top right of top-right pixel (not center)
OpenGlMatrixSpec ProjectionMatrixRDF_TopRight(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision L = +(w-u0) * zNear / fu;
const GLprecision R = -(u0) * zNear / fu;
const GLprecision T = -(v0) * zNear / fv;
const GLprecision B = +(h-v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+0] = (R+L)/(L-R);
P.m[2*4+1] = (T+B)/(B-T);
P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
P.m[2*4+3] = 1.0;
P.m[3*4+2] = (2*zFar*zNear)/(zNear - zFar);
return P;
}
// Camera Axis:
// X - Right, Y - Down, Z - Forward
// Image Origin:
// Bottom Left
// Pricipal point specified with image origin (0,0) at top left of top-left pixel (not center)
OpenGlMatrixSpec ProjectionMatrixRDF_BottomLeft(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision L = -(u0) * zNear / fu;
const GLprecision R = +(w-u0) * zNear / fu;
const GLprecision B = -(v0) * zNear / fv;
const GLprecision T = +(h-v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+0] = (R+L)/(L-R);
P.m[2*4+1] = (T+B)/(B-T);
P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
P.m[2*4+3] = 1.0;
P.m[3*4+2] = (2*zFar*zNear)/(zNear - zFar);
return P;
}
// Camera Axis:
// X - Right, Y - Down, Z - Forward
// Image Origin:
// Bottom Right
// Pricipal point specified with image origin (0,0) at top right of top-right pixel (not center)
OpenGlMatrixSpec ProjectionMatrixRDF_BottomRight(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar )
{
// http://www.songho.ca/opengl/gl_projectionmatrix.html
const GLprecision R = -(u0) * zNear / fu;
const GLprecision L = +(w-u0) * zNear / fu;
const GLprecision B = -(v0) * zNear / fv;
const GLprecision T = +(h-v0) * zNear / fv;
OpenGlMatrixSpec P;
P.type = GlProjectionStack;
std::fill_n(P.m,4*4,0);
P.m[0*4+0] = 2 * zNear / (R-L);
P.m[1*4+1] = 2 * zNear / (T-B);
P.m[2*4+0] = (R+L)/(L-R);
P.m[2*4+1] = (T+B)/(B-T);
P.m[2*4+2] = (zFar +zNear) / (zFar - zNear);
P.m[2*4+3] = 1.0;
P.m[3*4+2] = (2*zFar*zNear)/(zNear - zFar);
return P;
}
OpenGlMatrix ModelViewLookAtRUB(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz)
{
OpenGlMatrix mat;
GLprecision* m = mat.m;
const GLprecision u_o[3] = {ux,uy,uz};
GLprecision x[3], y[3];
GLprecision z[] = {ex - lx, ey - ly, ez - lz};
Normalise<3>(z);
CrossProduct(x,u_o,z);
CrossProduct(y,z,x);
// Normalize x, y
const GLprecision lenx = Length<3>(x);
const GLprecision leny = Length<3>(y);
if( lenx > 0 && leny > 0) {
for(size_t r = 0; r < 3; ++r ) {
x[r] /= lenx;
y[r] /= leny;
}
#define M(row,col) m[col*4+row]
M(0,0) = x[0];
M(0,1) = x[1];
M(0,2) = x[2];
M(1,0) = y[0];
M(1,1) = y[1];
M(1,2) = y[2];
M(2,0) = z[0];
M(2,1) = z[1];
M(2,2) = z[2];
M(3,0) = 0.0;
M(3,1) = 0.0;
M(3,2) = 0.0;
M(0,3) = -(M(0,0)*ex + M(0,1)*ey + M(0,2)*ez);
M(1,3) = -(M(1,0)*ex + M(1,1)*ey + M(1,2)*ez);
M(2,3) = -(M(2,0)*ex + M(2,1)*ey + M(2,2)*ez);
M(3,3) = 1.0;
#undef M
return mat;
}else{
throw std::invalid_argument("'Look' and 'up' vectors cannot be parallel when calling ModelViewLookAt.");
}
}
OpenGlMatrix ModelViewLookAtRDF(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz)
{
OpenGlMatrix mat;
GLprecision* m = mat.m;
const GLprecision u_o[3] = {ux,uy,uz};
GLprecision x[3], y[3];
GLprecision z[] = {lx - ex, ly - ey, lz - ez};
Normalise<3>(z);
CrossProduct(x,z,u_o);
CrossProduct(y,z,x);
// Normalize x, y
const GLprecision lenx = Length<3>(x);
const GLprecision leny = Length<3>(y);
if( lenx > 0 && leny > 0) {
for(size_t r = 0; r < 3; ++r ) {
x[r] /= lenx;
y[r] /= leny;
}
#define M(row,col) m[col*4+row]
M(0,0) = x[0];
M(0,1) = x[1];
M(0,2) = x[2];
M(1,0) = y[0];
M(1,1) = y[1];
M(1,2) = y[2];
M(2,0) = z[0];
M(2,1) = z[1];
M(2,2) = z[2];
M(3,0) = 0.0;
M(3,1) = 0.0;
M(3,2) = 0.0;
M(0,3) = -(M(0,0)*ex + M(0,1)*ey + M(0,2)*ez);
M(1,3) = -(M(1,0)*ex + M(1,1)*ey + M(1,2)*ez);
M(2,3) = -(M(2,0)*ex + M(2,1)*ey + M(2,2)*ez);
M(3,3) = 1.0;
#undef M
return mat;
}else{
throw std::invalid_argument("'Look' and 'up' vectors cannot be parallel when calling ModelViewLookAt.");
}
}
OpenGlMatrix ModelViewLookAt(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, GLprecision ux, GLprecision uy, GLprecision uz)
{
return ModelViewLookAtRUB(ex,ey,ez,lx,ly,lz,ux,uy,uz);
}
OpenGlMatrix ModelViewLookAt(GLprecision ex, GLprecision ey, GLprecision ez, GLprecision lx, GLprecision ly, GLprecision lz, AxisDirection up)
{
const GLprecision* u = AxisDirectionVector[up];
return ModelViewLookAtRUB(ex,ey,ez,lx,ly,lz,u[0],u[1],u[2]);
}
OpenGlMatrix IdentityMatrix()
{
OpenGlMatrix P;
std::fill_n(P.m,4*4,0);
for( int i=0; i<4; ++i ) P.m[i*4+i] = 1;
return P;
}
OpenGlMatrixSpec IdentityMatrix(OpenGlStack type)
{
OpenGlMatrixSpec P;
P.type = type;
std::fill_n(P.m,4*4,0);
for( int i=0; i<4; ++i ) P.m[i*4+i] = 1;
return P;
}
OpenGlMatrixSpec negIdentityMatrix(OpenGlStack type)
{
OpenGlMatrixSpec P;
P.type = type;
std::fill_n(P.m,4*4,0);
for( int i=0; i<4; ++i ) P.m[i*4+i] = -1;
P.m[3*4+3] =1;
return P;
}
}

583
thirdparty/Pangolin/src/display/view.cpp vendored Normal file
View File

@@ -0,0 +1,583 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011 Steven Lovegrove
*
* 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/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/display/opengl_render_state.h>
#include <pangolin/display/view.h>
#include <pangolin/display/viewport.h>
#include <pangolin/gl/gl.h>
#include <pangolin/platform.h>
#include <stdexcept>
namespace pangolin
{
// Pointer to context defined in display.cpp
extern __thread PangolinGl* context;
const int panal_v_margin = 6;
int AttachAbs( int low, int high, Attach a)
{
if( a.unit == Pixel ) return low + (int)a.p;
if( a.unit == ReversePixel ) return high - (int)a.p;
return (int)(low + a.p * (high - low));
}
double AspectAreaWithinTarget(double target, double test)
{
if( test < target )
return test / target;
else
return target / test;
}
void SaveViewFromFbo(std::string prefix, View& view, float scale)
{
PANGOLIN_UNUSED(prefix);
#ifndef HAVE_GLES
const Viewport orig = view.v;
view.v.l = 0;
view.v.b = 0;
view.v.w = (int)(view.v.w * scale);
view.v.h = (int)(view.v.h * scale);
const int w = view.v.w;
const int h = view.v.h;
float origLineWidth;
glGetFloatv(GL_LINE_WIDTH, &origLineWidth);
glLineWidth(origLineWidth * scale);
float origPointSize;
glGetFloatv(GL_POINT_SIZE, &origPointSize);
glPointSize(origPointSize * scale);
// Create FBO
GlTexture color(w,h);
GlRenderBuffer depth(w,h);
GlFramebuffer fbo(color, depth);
// Render into FBO
fbo.Bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
view.Render();
glFlush();
#ifdef HAVE_PNG
const PixelFormat fmt = PixelFormatFromString("RGBA32");
TypedImage buffer(w, h, fmt );
glReadBuffer(GL_BACK);
glPixelStorei(GL_PACK_ALIGNMENT, 1); // TODO: Avoid this?
glReadPixels(0,0,w,h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.ptr );
SaveImage(buffer, fmt, prefix + ".png", false);
#endif // HAVE_PNG
// unbind FBO
fbo.Unbind();
// restore viewport / line width
view.v = orig;
glLineWidth(origLineWidth);
glPointSize(origPointSize);
#endif // HAVE_GLES
}
void View::Resize(const Viewport& p)
{
// Compute Bounds based on specification
v.l = AttachAbs(p.l,p.r(),left);
v.b = AttachAbs(p.b,p.t(),bottom);
int r = AttachAbs(p.l,p.r(),right);
int t = AttachAbs(p.b,p.t(),top);
// Make sure left and right, top and bottom are correct order
if( t < v.b ) std::swap(t,v.b);
if( r < v.l ) std::swap(r,v.l);
v.w = r - v.l;
v.h = t - v.b;
vp = v;
// Adjust based on aspect requirements
if( aspect != 0 )
{
const float current_aspect = (float)v.w / (float)v.h;
if( aspect > 0 )
{
// Fit to space
if( current_aspect < aspect )
{
//Adjust height
const int nh = (int)(v.w / aspect);
v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
v.h = nh;
}else if( current_aspect > aspect )
{
//Adjust width
const int nw = (int)(v.h * aspect);
v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
v.w = nw;
}
}else{
// Overfit
double true_aspect = -aspect;
if( current_aspect < true_aspect )
{
//Adjust width
const int nw = (int)(v.h * true_aspect);
v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
v.w = nw;
}else if( current_aspect > true_aspect )
{
//Adjust height
const int nh = (int)(v.w / true_aspect);
v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
v.h = nh;
}
}
}
ResizeChildren();
}
inline int zcompare(const View* lhs, const View* rhs)
{
return lhs->zorder < rhs->zorder;
}
void View::ResizeChildren()
{
if( layout == LayoutOverlay )
{
// Sort children into z-order
std::sort(views.begin(), views.end(), zcompare);
for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv ) {
(*iv)->Resize(v);
}
}else if( layout == LayoutVertical )
{
// Allocate space incrementally
Viewport space = v.Inset(panal_v_margin);
int num_children = 0;
for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
{
if (!(*iv)->show) continue;
num_children++;
if(scroll_offset >= num_children ) {
(*iv)->scroll_show = false;
}else{
(*iv)->scroll_show = true;
(*iv)->Resize(space);
space.h = (*iv)->v.b - panal_v_margin - space.b;
}
}
}else if(layout == LayoutHorizontal )
{
// Allocate space incrementally
const int margin = 8;
Viewport space = v.Inset(margin);
for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
{
(*iv)->Resize(space);
space.w = (*iv)->v.l + margin + space.l;
}
}else if(layout == LayoutEqualVertical )
{
// Allocate vertical space equally
const size_t visiblechildren = NumVisibleChildren();
const float height = (float)v.h / (float)visiblechildren;
for( size_t i=0; i < visiblechildren; ++i) {
Viewport space(v.l, (GLint)(v.b+(visiblechildren-1-i)*height), v.w, (GLint)(height) );
VisibleChild(i).Resize(space);
}
}else if(layout == LayoutEqualHorizontal )
{
// Allocate vertical space equally
const size_t visiblechildren = NumVisibleChildren();
const float width = (float)v.w / (float)visiblechildren;
for( size_t i=0; i < visiblechildren; ++i) {
Viewport space( (GLint)(v.l+i*width), v.b, (GLint)width, v.h);
VisibleChild(i).Resize(space);
}
}else if(layout == LayoutEqual )
{
const size_t visiblechildren = NumVisibleChildren();
// TODO: Make this neater, and make fewer assumptions!
if( visiblechildren > 0 )
{
// This containers aspect
const double this_a = std::fabs(v.aspect());
// Use first child with fixed aspect for all children
double child_a = std::fabs(VisibleChild(0).aspect);
for(size_t i=1; (child_a==0) && i < visiblechildren; ++i ) {
child_a = std::fabs(VisibleChild(i).aspect);
}
if(child_a == 0) {
child_a = 1;
}
double a = visiblechildren*child_a;
double area = AspectAreaWithinTarget(this_a, a);
size_t cols = visiblechildren-1;
for(; cols > 0; --cols)
{
const size_t rows = visiblechildren / cols + (visiblechildren % cols == 0 ? 0 : 1);
const double na = cols * child_a / rows;
const double new_area = visiblechildren*AspectAreaWithinTarget(this_a,na)/(rows*cols);
if( new_area <= area )
break;
area = new_area;
a = na;
}
cols++;
const size_t rows = visiblechildren / cols + (visiblechildren % cols == 0 ? 0 : 1);
size_t cw, ch;
if( a > this_a )
{
cw = v.w / cols;
ch = (int)(cw / child_a); //v.h / rows;
}else{
ch = v.h / rows;
cw = (int)(ch * child_a);
}
for(size_t i=0; i< visiblechildren; ++i )
{
size_t c = i % cols;
size_t r = i / cols;
Viewport space( GLint(v.l + c*cw), GLint(v.t() - (r+1)*ch), GLint(cw), GLint(ch) );
VisibleChild(i).Resize(space);
}
}
}
}
void View::Render()
{
if(extern_draw_function && show && scroll_show) {
extern_draw_function(*this);
}
RenderChildren();
}
void View::RenderChildren()
{
for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
{
if((*iv)->show && (*iv)->scroll_show) (*iv)->Render();
}
}
void View::Activate() const
{
v.Activate();
}
void View::ActivateAndScissor() const
{
vp.Scissor();
v.Activate();
}
void View::ActivateScissorAndClear() const
{
vp.Scissor();
v.Activate();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void View::Activate(const OpenGlRenderState& state ) const
{
v.Activate();
state.Apply();
}
void View::ActivateAndScissor(const OpenGlRenderState& state) const
{
vp.Scissor();
v.Activate();
state.Apply();
}
void View::ActivateScissorAndClear(const OpenGlRenderState& state ) const
{
vp.Scissor();
v.Activate();
state.Apply();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void View::ActivatePixelOrthographic() const
{
v.ActivatePixelOrthographic();
}
void View::ActivateIdentity() const
{
v.ActivateIdentity();
}
GLfloat View::GetClosestDepth(int x, int y, int radius) const
{
// TODO: Get to work on android
#ifdef _MSVC_
// MSVC Requires fixed sized arrays on stack
radius = 5;
const int zl = (5*2+1);
#else
const int zl = (radius*2+1);
#endif
const int zsize = zl*zl;
GLfloat zs[zsize];
#ifndef HAVE_GLES
glReadBuffer(GL_FRONT);
glReadPixels(x-radius,y-radius,zl,zl,GL_DEPTH_COMPONENT,GL_FLOAT,zs);
#else
std::fill(zs,zs+zsize, 0.8);
#endif
const GLfloat mindepth = *(std::min_element(zs,zs+zsize));
return mindepth;
}
void View::GetObjectCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const
{
const GLint viewport[4] = {v.l,v.b,v.w,v.h};
const OpenGlMatrix proj = cam_state.GetProjectionMatrix();
const OpenGlMatrix mv = cam_state.GetModelViewMatrix();
glUnProject(winx, winy, winzdepth, mv.m, proj.m, viewport, &x, &y, &z);
}
void View::GetCamCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const
{
v.GetCamCoordinates(cam_state, winx, winy, winzdepth, x, y, z);
}
View& View::SetFocus()
{
context->activeDisplay = this;
return *this;
}
bool View::HasFocus() const
{
return context->activeDisplay == this;
}
View& View::SetBounds(Attach bottom, Attach top, Attach left, Attach right)
{
this->left = left;
this->top = top;
this->right = right;
this->bottom = bottom;
context->base.ResizeChildren();
return *this;
}
View& View::SetBounds(Attach bottom, Attach top, Attach left, Attach right, bool keep_aspect)
{
aspect = keep_aspect ? v.aspect() : 0;
SetBounds(top,bottom,left,right);
return *this;
}
View& View::SetBounds(Attach bottom, Attach top, Attach left, Attach right, double aspect)
{
this->aspect = aspect;
SetBounds(top,bottom,left,right);
return *this;
}
View& View::SetAspect(double aspect)
{
this->aspect = aspect;
context->base.ResizeChildren();
return *this;
}
View& View::SetLock(Lock horizontal, Lock vertical )
{
vlock = vertical;
hlock = horizontal;
return *this;
}
View& View::SetLayout(Layout l)
{
layout = l;
return *this;
}
View& View::AddDisplay(View& child)
{
// detach child from any other view, and add to this
std::vector<View*>::iterator f = std::find(
context->base.views.begin(), context->base.views.end(), &child
);
if( f != context->base.views.end() )
context->base.views.erase(f);
views.push_back(&child);
context->base.ResizeChildren();
return *this;
}
View& View::Show(bool show)
{
this->show = show;
context->base.ResizeChildren();
return *this;
}
void View::ToggleShow()
{
Show(!show);
}
bool View::IsShown() const
{
return show;
}
Viewport View::GetBounds() const
{
return Viewport( std::max(v.l, vp.l), std::max(v.b, vp.b), std::min(v.w, vp.w), std::min(v.h, vp.h) );
}
void View::SaveOnRender(const std::string& filename_prefix)
{
const Viewport tosave = this->v.Intersect(this->vp);
context->screen_capture.push(std::pair<std::string,Viewport>(filename_prefix,tosave ) );
}
void View::RecordOnRender(const std::string& record_uri)
{
PANGOLIN_UNUSED(record_uri);
#ifdef BUILD_PANGOLIN_VIDEO
if(!context->recorder.IsOpen()) {
Viewport area = GetBounds();
context->record_view = this;
try{
context->recorder.Open(record_uri);
std::vector<StreamInfo> streams;
const PixelFormat fmt = PixelFormatFromString("RGB24");
streams.push_back( StreamInfo(fmt, area.w, area.h, area.w * fmt.bpp / 8) );
context->recorder.SetStreams(streams);
}catch(const std::exception& e) {
pango_print_error("Unable to open VideoRecorder:\n\t%s\n", e.what());
}
}else{
context->recorder.Close();
}
#else
std::cerr << "Error: Video Support hasn't been built into this library." << std::endl;
#endif // BUILD_PANGOLIN_VIDEO
}
void View::SaveRenderNow(const std::string& filename_prefix, float scale)
{
SaveViewFromFbo(filename_prefix, *this, scale);
}
View& View::operator[](size_t i)
{
return *views[i];
}
size_t View::NumChildren() const
{
return views.size();
}
size_t View::NumVisibleChildren() const
{
int numvis = 0;
for(std::vector<View*>::const_iterator i=views.begin(); i!=views.end(); ++i)
{
if((*i)->show) {
numvis++;
}
}
return numvis;
}
View& View::VisibleChild(size_t i)
{
size_t numvis = 0;
for(size_t v=0; v < views.size(); ++v ) {
if(views[v]->show) {
if( i == numvis ) {
return *views[v];
}
numvis++;
}
}
// Shouldn't get here
throw std::out_of_range("No such child.");
}
View* View::FindChild(int x, int y)
{
// Find in reverse order to mirror draw order
for( std::vector<View*>::const_reverse_iterator i = views.rbegin(); i != views.rend(); ++i )
if( (*i)->show && (*i)->GetBounds().Contains(x,y) )
return (*i);
return 0;
}
View& View::SetHandler(Handler* h)
{
handler = h;
return *this;
}
View& View::SetDrawFunction(const std::function<void(View&)>& drawFunc)
{
extern_draw_function = drawFunc;
return *this;
}
}

View File

@@ -0,0 +1,113 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2013 Steven Lovegrove
*
* 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/display/viewport.h>
#include <algorithm>
#include <pangolin/utils/simple_math.h>
namespace pangolin {
void Viewport::Activate() const
{
glViewport(l,b,w,h);
}
void Viewport::Scissor() const
{
glEnable(GL_SCISSOR_TEST);
glScissor(l,b,w,h);
}
void Viewport::ActivateAndScissor() const
{
glViewport(l,b,w,h);
glEnable(GL_SCISSOR_TEST);
glScissor(l,b,w,h);
}
void Viewport::DisableScissor()
{
glDisable(GL_SCISSOR_TEST);
}
bool Viewport::Contains(int x, int y) const
{
return l <= x && x < (l+w) && b <= y && y < (b+h);
}
void Viewport::ActivatePixelOrthographic() const
{
Activate();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, w-0.5, -0.5, h-0.5, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Viewport::ActivateIdentity() const
{
Activate();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Viewport Viewport::Inset(int i) const
{
return Viewport(l+i, b+i, w-2*i, h-2*i);
}
Viewport Viewport::Inset(int horiz, int vert) const
{
return Viewport(l+horiz, b+vert, w-horiz, h-vert);
}
Viewport Viewport::Intersect(const Viewport& vp) const
{
GLint nl = std::max(l,vp.l);
GLint nr = std::min(r(),vp.r());
GLint nb = std::max(b,vp.b);
GLint nt = std::min(t(),vp.t());
return Viewport(nl,nb, nr-nl, nt-nb);
}
void Viewport::GetCamCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const
{
const GLint viewport[4] = {l, b, w, h};
const OpenGlMatrix proj = cam_state.GetProjectionMatrix();
#ifndef HAVE_GLES
glUnProject(winx, winy, winzdepth, Identity4d, proj.m, viewport, &x, &y, &z);
#else
glUnProject(winx, winy, winzdepth, Identity4f, proj.m, viewport, &x, &y, &z);
#endif
}
}

View File

@@ -0,0 +1,680 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011 Steven Lovegrove
*
* 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/display/widgets/widgets.h>
#include <pangolin/display/display.h>
#include <pangolin/display/display_internal.h>
#include <pangolin/gl/gldraw.h>
#include <pangolin/var/varextra.h>
#include <pangolin/utils/file_utils.h>
#include <thread>
#include <mutex>
#include <iostream>
#include <iomanip>
using namespace std;
namespace pangolin
{
// Pointer to context defined in display.cpp
extern __thread PangolinGl* context;
const static GLfloat colour_s1[4] = {0.2f, 0.2f, 0.2f, 1.0f};
const static GLfloat colour_s2[4] = {0.6f, 0.6f, 0.6f, 1.0f};
const static GLfloat colour_bg[4] = {0.9f, 0.9f, 0.9f, 1.0f};
const static GLfloat colour_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f};
const static GLfloat colour_tx[4] = {0.0f, 0.0f, 0.0f, 1.0f};
const static GLfloat colour_dn[4] = {1.0f, 0.7f, 0.7f, 1.0f};
static inline GlFont& font()
{
return GlFont::I();
}
static inline int cb_height()
{
return (int)(font().Height() * 1.0);
}
static inline int tab_h()
{
return (int)(font().Height() * 1.4);
}
std::mutex display_mutex;
template<typename T>
void GuiVarChanged( Var<T>& var)
{
VarState::I().FlagVarChanged();
var.Meta().gui_changed = true;
for(std::vector<GuiVarChangedCallback>::iterator igvc = VarState::I().gui_var_changed_callbacks.begin(); igvc != VarState::I().gui_var_changed_callbacks.end(); ++igvc) {
if( StartsWith(var.Meta().full_name, igvc->filter) ) {
igvc->fn( igvc->data, var.Meta().full_name, var.Ref() );
}
}
}
void glRect(Viewport v)
{
GLfloat vs[] = { (float)v.l,(float)v.b,
(float)v.l,(float)v.t(),
(float)v.r(),(float)v.t(),
(float)v.r(),(float)v.b };
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vs);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
}
void glRect(Viewport v, int inset)
{
glRect(v.Inset(inset));
}
void DrawShadowRect(Viewport& v)
{
glColor4fv(colour_s2);
glDrawRectPerimeter((GLfloat)v.l, (GLfloat)v.b, (GLfloat)v.r(), (GLfloat)v.t());
}
void DrawShadowRect(Viewport& v, bool pushed)
{
const GLfloat* c1 = pushed ? colour_s1 : colour_s2;
const GLfloat* c2 = pushed ? colour_s2 : colour_s1;
GLfloat vs[] = { (float)v.l,(float)v.b,
(float)v.l,(float)v.t(),
(float)v.r(),(float)v.t(),
(float)v.r(),(float)v.b,
(float)v.l,(float)v.b };
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vs);
glColor4fv(c1);
glDrawArrays(GL_LINE_STRIP, 0, 3);
glColor4fv(c2);
glDrawArrays(GL_LINE_STRIP, 2, 3);
glDisableClientState(GL_VERTEX_ARRAY);
}
Panel::Panel()
{
handler = &StaticHandlerScroll;
layout = LayoutVertical;
}
Panel::Panel(const std::string& auto_register_var_prefix)
{
handler = &StaticHandlerScroll;
layout = LayoutVertical;
RegisterNewVarCallback(&Panel::AddVariable,(void*)this,auto_register_var_prefix);
ProcessHistoricCallbacks(&Panel::AddVariable,(void*)this,auto_register_var_prefix);
}
void Panel::AddVariable(void* data, const std::string& name, VarValueGeneric& var, bool /*brand_new*/)
{
Panel* thisptr = (Panel*)data;
const string& title = var.Meta().friendly;
display_mutex.lock();
ViewMap::iterator pnl = context->named_managed_views.find(name);
// Only add if a widget by the same name doesn't
// already exist
if( pnl == context->named_managed_views.end() )
{
View* nv = NULL;
if( !strcmp(var.TypeId(), typeid(bool).name()) ) {
nv = (var.Meta().flags & META_FLAG_TOGGLE) ? (View*)new Checkbox(title,var) : (View*)new Button(title,var);
} else if (!strcmp(var.TypeId(), typeid(double).name()) ||
!strcmp(var.TypeId(), typeid(float).name()) ||
!strcmp(var.TypeId(), typeid(int).name()) ||
!strcmp(var.TypeId(), typeid(unsigned int).name()))
{
nv = new Slider(title, var);
} else if (!strcmp(var.TypeId(), typeid(std::function<void(void)>).name() ) ) {
nv = (View*)new FunctionButton(title, var);
}else{
nv = new TextInput(title,var);
}
if(nv) {
context->named_managed_views[name] = nv;
thisptr->views.push_back( nv );
thisptr->ResizeChildren();
}
}
display_mutex.unlock();
}
void Panel::Render()
{
#ifndef HAVE_GLES
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_SCISSOR_BIT | GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
#endif
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
DisplayBase().ActivatePixelOrthographic();
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_LINE_SMOOTH);
glDisable( GL_COLOR_MATERIAL );
glLineWidth(1.0);
glColor4fv(colour_bg);
glRect(v);
DrawShadowRect(v);
RenderChildren();
#ifndef HAVE_GLES
glPopAttrib();
#else
glEnable(GL_LINE_SMOOTH);
glEnable(GL_DEPTH_TEST);
#endif
}
void Panel::ResizeChildren()
{
View::ResizeChildren();
}
View& CreatePanel(const std::string& name)
{
if(context->named_managed_views.find(name) != context->named_managed_views.end()) {
throw std::runtime_error("Panel already registered with this name.");
}
Panel * p = new Panel(name);
context->named_managed_views[name] = p;
context->base.views.push_back(p);
return *p;
}
Button::Button(string title, VarValueGeneric& tv)
: Widget<bool>(title,tv), down(false)
{
top = 1.0; bottom = Attach::Pix(-tab_h());
left = 0.0; right = 1.0;
hlock = LockLeft;
vlock = LockBottom;
gltext = font().Text(title);
}
void Button::Mouse(View&, MouseButton button, int /*x*/, int /*y*/, bool pressed, int /*mouse_state*/)
{
if(button == MouseButtonLeft )
{
down = pressed;
if( !pressed ) {
var->Set(!var->Get());
GuiVarChanged(*this);
}
}
}
void Button::Render()
{
glColor4fv(colour_fg );
glRect(v);
glColor4fv(colour_tx);
gltext.DrawWindow(raster[0],raster[1]-down);
DrawShadowRect(v, down);
}
void Button::ResizeChildren()
{
raster[0] = floor(v.l + (v.w-gltext.Width())/2.0f);
raster[1] = floor(v.b + (v.h-gltext.Height())/2.0f);
}
FunctionButton::FunctionButton(string title, VarValueGeneric& tv)
: Widget<std::function<void(void)> >(title, tv), down(false)
{
top = 1.0; bottom = Attach::Pix(-tab_h());
left = 0.0; right = 1.0;
hlock = LockLeft;
vlock = LockBottom;
gltext = font().Text(title);
}
void FunctionButton::Mouse(View&, MouseButton button, int /*x*/, int /*y*/, bool pressed, int /*mouse_state*/)
{
if (button == MouseButtonLeft)
{
down = pressed;
if (!pressed) {
var->Get()();
GuiVarChanged(*this);
}
}
}
void FunctionButton::Render()
{
glColor4fv(colour_fg);
glRect(v);
glColor4fv(colour_tx);
gltext.DrawWindow(raster[0],raster[1]-down);
DrawShadowRect(v, down);
}
void FunctionButton::ResizeChildren()
{
raster[0] = v.l + (v.w - gltext.Width()) / 2.0f;
raster[1] = v.b + (v.h - gltext.Height()) / 2.0f;
}
Checkbox::Checkbox(std::string title, VarValueGeneric& tv)
: Widget<bool>(title,tv)
{
top = 1.0; bottom = Attach::Pix(-tab_h());
left = 0.0; right = 1.0;
hlock = LockLeft;
vlock = LockBottom;
handler = this;
gltext = font().Text(title);
}
void Checkbox::Mouse(View&, MouseButton button, int /*x*/, int /*y*/, bool pressed, int /*mouse_state*/)
{
if( button == MouseButtonLeft && pressed ) {
var->Set(!var->Get());
GuiVarChanged(*this);
}
}
void Checkbox::ResizeChildren()
{
raster[0] = v.l + cb_height() + 4.0f;
raster[1] = v.b + (v.h-gltext.Height())/2.0f;
const int h = v.h;
const int t = (int)((h-cb_height()) / 2.0f);
vcb = Viewport(v.l,v.b+t,cb_height(),cb_height());
}
void Checkbox::Render()
{
const bool val = var->Get();
if( val )
{
glColor4fv(colour_dn);
glRect(vcb);
}
glColor4fv(colour_tx);
gltext.DrawWindow(raster[0],raster[1]);
DrawShadowRect(vcb, val);
}
inline bool IsIntegral(const char* typeidname)
{
// TODO: There must be a better way of doing this...
return !strcmp(typeidname, typeid(char).name()) ||
!strcmp(typeidname, typeid(short).name()) ||
!strcmp(typeidname, typeid(int).name()) ||
!strcmp(typeidname, typeid(long).name()) ||
!strcmp(typeidname, typeid(unsigned char).name()) ||
!strcmp(typeidname, typeid(unsigned short).name()) ||
!strcmp(typeidname, typeid(unsigned int).name()) ||
!strcmp(typeidname, typeid(unsigned long).name());
}
Slider::Slider(std::string title, VarValueGeneric& tv)
: Widget<double>(title+":", tv), lock_bounds(true)
{
top = 1.0; bottom = Attach::Pix(-tab_h());
left = 0.0; right = 1.0;
hlock = LockLeft;
vlock = LockBottom;
handler = this;
logscale = (int)tv.Meta().logscale;
gltext = font().Text(title);
is_integral_type = IsIntegral(tv.TypeId());
}
void Slider::Keyboard(View&, unsigned char key, int /*x*/, int /*y*/, bool pressed)
{
if( pressed && var->Meta().range[0] < var->Meta().range[1] )
{
double val = !logscale ? var->Get() : log(var->Get());
if(key=='-' || key=='_' || key=='=' || key=='+') {
double inc = var->Meta().increment;
if (key == '-') inc *= -1.0;
if (key == '_') inc *= -0.1;
if (key == '+') inc *= 0.1;
const double newval = max(var->Meta().range[0], min(var->Meta().range[1], val + inc));
var->Set( logscale ? exp(newval) : newval );
}else if(key == 'r'){
Reset();
}else{
return;
}
GuiVarChanged(*this);
}
}
void Slider::Mouse(View& view, MouseButton button, int x, int y, bool pressed, int mouse_state)
{
if(pressed)
{
// Wheel
if( button == MouseWheelUp || button == MouseWheelDown )
{
// Change scale around current value
const double frac = max(0.0,min(1.0,(double)(x - v.l)/(double)v.w));
double val = frac * (var->Meta().range[1] - var->Meta().range[0]) + var->Meta().range[0];
if (logscale)
{
if (val<=0)
val = std::numeric_limits<double>::min();
else
val = log(val);
}
const double scale = (button == MouseWheelUp ? 1.2 : 1.0 / 1.2 );
var->Meta().range[1] = val + (var->Meta().range[1] - val)*scale;
var->Meta().range[0] = val - (val - var->Meta().range[0])*scale;
}else{
lock_bounds = (button == MouseButtonLeft);
MouseMotion(view,x,y,mouse_state);
}
}else{
if(!lock_bounds)
{
double val = !logscale ? var->Get() : log(var->Get());
var->Meta().range[0] = min(var->Meta().range[0], val);
var->Meta().range[1] = max(var->Meta().range[1], val);
}
}
}
void Slider::MouseMotion(View&, int x, int /*y*/, int /*mouse_state*/)
{
if( var->Meta().range[0] != var->Meta().range[1] )
{
const double range = (var->Meta().range[1] - var->Meta().range[0]);
const double frac = (double)(x - v.l)/(double)v.w;
double val;
if( lock_bounds )
{
const double bfrac = max(0.0,min(1.0,frac));
val = bfrac * range + var->Meta().range[0] ;
}else{
val = frac * range + var->Meta().range[0];
}
if (logscale) {
val = exp(val);
}
if( is_integral_type ) {
val = std::round(val);
}
var->Set(val);
GuiVarChanged(*this);
}
}
void Slider::ResizeChildren()
{
raster[0] = v.l + 2.0f;
raster[1] = v.b + (v.h-gltext.Height())/2.0f;
}
void Slider::Render()
{
const double val = var->Get();
if( var->Meta().range[0] != var->Meta().range[1] )
{
double rval = val;
if (logscale)
{
rval = log(val);
}
glColor4fv(colour_fg);
glRect(v);
glColor4fv(colour_dn);
const double norm_val = max(0.0,min(1.0,(rval - var->Meta().range[0]) / (var->Meta().range[1] - var->Meta().range[0])));
glRect(Viewport(v.l,v.b, (int)(v.w*norm_val),v.h));
DrawShadowRect(v);
}
glColor4fv(colour_tx);
if(gltext.Text() != var->Meta().friendly) {
gltext = font().Text(var->Meta().friendly);
}
gltext.DrawWindow(raster[0], raster[1]);
std::ostringstream oss;
oss << setprecision(4) << val;
string str = oss.str();
GlText glval = font().Text(str);
const float l = glval.Width() + 2.0f;
glval.DrawWindow( v.l + v.w - l, raster[1] );
}
TextInput::TextInput(std::string title, VarValueGeneric& tv)
: Widget<std::string>(title+":", tv), can_edit(!(tv.Meta().flags & META_FLAG_READONLY)), do_edit(false)
{
top = 1.0; bottom = Attach::Pix(-tab_h());
left = 0.0; right = 1.0;
hlock = LockLeft;
vlock = LockBottom;
handler = this;
sel[0] = -1;
sel[1] = -1;
gltext = font().Text(title);
}
void TextInput::Keyboard(View&, unsigned char key, int /*x*/, int /*y*/, bool pressed)
{
if(can_edit && pressed && do_edit)
{
const bool selection = sel[1] > sel[0] && sel[0] >= 0;
if(key == 13)
{
var->Set(edit);
GuiVarChanged(*this);
do_edit = false;
sel[0] = sel[1] = -1;
}else if(key == 8) {
// backspace
if(selection)
{
edit = edit.substr(0,sel[0]) + edit.substr(sel[1],edit.length()-sel[1]);
sel[1] = sel[0];
}else{
if(sel[0] >0)
{
edit = edit.substr(0,sel[0]-1) + edit.substr(sel[0],edit.length()-sel[0]);
sel[0]--;
sel[1]--;
}
}
}else if(key == 127){
// delete
if(selection)
{
edit = edit.substr(0,sel[0]) + edit.substr(sel[1],edit.length()-sel[1]);
sel[1] = sel[0];
}else{
if(sel[0] < (int)edit.length())
{
edit = edit.substr(0,sel[0]) + edit.substr(sel[0]+1,edit.length()-sel[0]+1);
}
}
}else if(key == 230){
// right
sel[0] = min((int)edit.length(),sel[0]+1);
sel[1] = sel[0];
}else if(key == 228){
// left
sel[0] = max(0,sel[0]-1);
sel[1] = sel[0];
}else if(key == 234){
// home
sel[0] = sel[1] = 0;
}else if(key == 235){
// end
sel[0] = sel[1] = (int)edit.length();
}else if(key < PANGO_SPECIAL){
edit = edit.substr(0,sel[0]).append(1,key) + edit.substr(sel[1],edit.length()-sel[1]);
sel[1] = sel[0];
sel[0]++;
sel[1]++;
}
}
}
void TextInput::Mouse(View& /*view*/, MouseButton button, int x, int /*y*/, bool pressed, int /*mouse_state*/)
{
if(can_edit && button != MouseWheelUp && button != MouseWheelDown )
{
if(do_edit)
{
const int sl = (int)gledit.Width() + 2;
const int rl = v.l + v.w - sl;
int ep = (int)edit.length();
if( x < rl )
{
ep = 0;
}else{
for( unsigned i=0; i<edit.length(); ++i )
{
const int tl = (int)(rl + font().Text(edit.substr(0,i)).Width());
if(x < tl+2)
{
ep = i;
break;
}
}
}
if(pressed)
{
sel[0] = sel[1] = ep;
}else{
sel[1] = ep;
}
if(sel[0] > sel[1])
std::swap(sel[0],sel[1]);
}else{
do_edit = !pressed;
sel[0] = 0;
sel[1] = (int)edit.length();
}
}
}
void TextInput::MouseMotion(View&, int x, int /*y*/, int /*mouse_state*/)
{
if(can_edit && do_edit)
{
const int sl = (int)gledit.Width() + 2;
const int rl = v.l + v.w - sl;
int ep = (int)edit.length();
if( x < rl )
{
ep = 0;
}else{
for( unsigned i=0; i<edit.length(); ++i )
{
const int tl = (int)(rl + font().Text(edit.substr(0,i)).Width());
if(x < tl+2)
{
ep = i;
break;
}
}
}
sel[1] = ep;
}
}
void TextInput::ResizeChildren()
{
raster[0] = v.l + 2.0f;
raster[1] = v.b + (v.h-gltext.Height()) / 2.0f;
}
void TextInput::Render()
{
if(!do_edit) edit = var->Get();
gledit = font().Text(edit);
glColor4fv(colour_fg);
if(can_edit) glRect(v);
const int sl = (int)gledit.Width() + 2;
const int rl = v.l + v.w - sl;
if( do_edit && sel[0] >= 0)
{
const int tl = (int)(rl + font().Text(edit.substr(0,sel[0])).Width());
const int tr = (int)(rl + font().Text(edit.substr(0,sel[1])).Width());
glColor4fv(colour_dn);
glRect(Viewport(tl,v.b,tr-tl,v.h));
}
glColor4fv(colour_tx);
gltext.DrawWindow(raster[0], raster[1]);
gledit.DrawWindow((GLfloat)(rl), raster[1]);
if(can_edit) DrawShadowRect(v);
}
}

View File

@@ -0,0 +1,42 @@
/* This file is part of the Pangolin Project.
* http://github.com/stevenlovegrove/Pangolin
*
* Copyright (c) 2011-2017 Steven Lovegrove, Andrey Mnatsakanov
*
* 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/display/window.h>
#include <pangolin/factory/factory_registry.h>
namespace pangolin
{
template<>
FactoryRegistry<WindowInterface>& FactoryRegistry<WindowInterface>::I()
{
// Singleton instance
static FactoryRegistry instance;
return instance;
}
}