758 lines
27 KiB
Plaintext
758 lines
27 KiB
Plaintext
|
//
|
||
|
// SimpleViewerCocoa.mm
|
||
|
// osgsimpleviewerCocoa
|
||
|
//
|
||
|
// Created by Eric Wing on 11/12/06.
|
||
|
// Copyright 2006. All rights reserved.
|
||
|
//
|
||
|
/* This class demonstrates how to subclass NSOpenGLView to integrate with the
|
||
|
* osgViewer::SimpleViewer.
|
||
|
* This class demonstrates:
|
||
|
* Objective-C++
|
||
|
* How to subclass NSOpenGLView
|
||
|
* Specifying OpenGL pixel formats
|
||
|
* V-sync
|
||
|
* Timer based animation
|
||
|
* One button and multibutton mice.
|
||
|
* Scroll events (Mighty Mouse, Two-finger trackpads)
|
||
|
* Keyboard events
|
||
|
* Drag and drop (as target)
|
||
|
* Resolution Independent UI (maybe, not Leopard tested, only Tiger Quartz Debug)
|
||
|
* Target-Action (for other widgets to invoke actions on this view)
|
||
|
*
|
||
|
* Things not demonstrated by this view or application example (but would be interesting):
|
||
|
* Cocoa Bindings (highly recommended)
|
||
|
* Core Data (works great with Cocoa Bindings)
|
||
|
* Custom Interface Builder palette with Inspector configurable options
|
||
|
* Shared OpenGLContexts
|
||
|
* More PixelFormat options
|
||
|
* Fullscreen mode
|
||
|
* Low-level CGL access.
|
||
|
* Delegates for your view
|
||
|
* Multithreading
|
||
|
* Drag-and-drop as a source (similar to the target code)
|
||
|
* Copy, Cut, Paste (very similar to drag-and-drop)
|
||
|
* Creating (updating) images of the window for the minimized view in the Dock
|
||
|
* Printing support
|
||
|
* Non-view stuff (Application Delegates, models, controllers)
|
||
|
* Launching by double-clicking a model or drag-and-drop onto Application Icon (non-view)
|
||
|
* Launching via commandline with parameters (non-view)
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Coding conventions:
|
||
|
* My coding style is slightly different than what you normally see in Cocoa.
|
||
|
* So here is the cheat sheet:
|
||
|
* I hate Hungarian (Microsoft) notation. And prefixed underscore _variables
|
||
|
* are technically reserved by the compiler which is why I avoid them. So...
|
||
|
* Member variables use camelCase (Pascal/Java)
|
||
|
* Local variables use under_scores (Ada)
|
||
|
* For methods, I follow standard Cocoa conventions.
|
||
|
* I tend to keep * with the type (e.g. NSView* foo) instead of with the variable (NSView *foo).
|
||
|
* (I tend to think of the pointer as part of the type.)
|
||
|
* For Obj-C named parameters, I tend to keep the namedParameter and the value
|
||
|
* together instead of separated by spaces
|
||
|
* (e.g. [self initWithX:x_val yVal:y_val zVal:z_val].
|
||
|
* (When I was first learning Objective-C, this made it easier for me to
|
||
|
* figure out which things * were paired.)
|
||
|
*/
|
||
|
#import "SimpleViewerCocoa.h"
|
||
|
|
||
|
#include <osgViewer/SimpleViewer>
|
||
|
#include <osgGA/TrackballManipulator>
|
||
|
// Needed to explicitly typecast keys to the OSG type
|
||
|
#include <osgGA/GUIEventAdapter>
|
||
|
// Needed to load models
|
||
|
#include <osgDB/ReadFile>
|
||
|
|
||
|
// Used so I can change the background (clear) color
|
||
|
#include <osgUtil/SceneView>
|
||
|
#include <osg/Vec4>
|
||
|
|
||
|
// For debugging
|
||
|
//#include <osg/Notify>
|
||
|
|
||
|
// osgText is used only to announce that you can use drag-and-drop to load models.
|
||
|
// osgViewer itself does not have a dependency on osgText.
|
||
|
#include <osg/ref_ptr>
|
||
|
#include <osgText/Text>
|
||
|
#include <osg/Geode>
|
||
|
|
||
|
@implementation SimpleViewerCocoa
|
||
|
|
||
|
// My simple pixel format definition
|
||
|
+ (NSOpenGLPixelFormat*) basicPixelFormat
|
||
|
{
|
||
|
NSOpenGLPixelFormatAttribute pixel_attributes[] =
|
||
|
{
|
||
|
NSOpenGLPFAWindow,
|
||
|
NSOpenGLPFADoubleBuffer, // double buffered
|
||
|
NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32, // depth buffer size in bits
|
||
|
(NSOpenGLPixelFormatAttribute)nil
|
||
|
};
|
||
|
return [[[NSOpenGLPixelFormat alloc] initWithAttributes:pixel_attributes] autorelease];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// Init Stuff /////////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/* This is the designated initializer for an NSOpenGLView. However, since I'm
|
||
|
* using Interface Builder to help, initWithCoder: is the initializer that gets called.
|
||
|
* But for completeness, I implement this method here.
|
||
|
*/
|
||
|
- (id) initWithFrame:(NSRect)frame_rect pixelFormat:(NSOpenGLPixelFormat*)pixel_format
|
||
|
{
|
||
|
self = [super initWithFrame:frame_rect pixelFormat:pixel_format];
|
||
|
if(self)
|
||
|
{
|
||
|
[self commonInit];
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
/* Going through the IB palette, this initializer is calling instead of the designated initializer
|
||
|
* initWithFrame:pixelFormat:
|
||
|
* But for some reason, the pixel format set in IB selected seems to be either ignored or is missing
|
||
|
* a value I need. (The depth buffer looks too shallow to me and glErrors are triggered.)
|
||
|
* So I explicitly set the pixel format inside here (overriding the IB palette options).
|
||
|
* This probably should be investigated, but since IB is getting an overhaul for Leopard,
|
||
|
* I'll wait on this for now.
|
||
|
*/
|
||
|
- (id) initWithCoder:(NSCoder*)the_coder
|
||
|
{
|
||
|
self = [super initWithCoder:the_coder];
|
||
|
if(self)
|
||
|
{
|
||
|
NSOpenGLPixelFormat* pixel_format = [SimpleViewerCocoa basicPixelFormat];
|
||
|
[self setPixelFormat:pixel_format];
|
||
|
[self commonInit];
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
// My custom methods to centralize common init stuff
|
||
|
- (void) commonInit
|
||
|
{
|
||
|
isUsingCtrlClick = NO;
|
||
|
isUsingOptionClick = NO;
|
||
|
|
||
|
|
||
|
[self initOSGViewer];
|
||
|
[self initAnimationTimer];
|
||
|
|
||
|
// Register for Drag and Drop
|
||
|
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
|
||
|
}
|
||
|
|
||
|
// Allocate a SimpleViewer and do basic initialization. No assumption about having an
|
||
|
// a valid OpenGL context is made by this function.
|
||
|
- (void) initOSGViewer
|
||
|
{
|
||
|
// osg::setNotifyLevel( osg::DEBUG_FP );
|
||
|
simpleViewer = new osgViewer::SimpleViewer;
|
||
|
// Cocoa follows the same coordinate convention as OpenGL. osgViewer's default is inverted.
|
||
|
simpleViewer->getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
|
||
|
// Use a trackball manipulator...matches nicely with the Mighty Mouse Scrollball.
|
||
|
simpleViewer->setCameraManipulator(new osgGA::TrackballManipulator);
|
||
|
}
|
||
|
|
||
|
- (void) initAnimationTimer
|
||
|
{
|
||
|
// Cocoa is event driven, so by default, there is nothing to trigger redraws for animation.
|
||
|
// The easiest way to animate is to set a repeating NSTimer which triggers a redraw.
|
||
|
SEL the_selector;
|
||
|
NSMethodSignature* a_signature;
|
||
|
NSInvocation* an_invocation;
|
||
|
// animationCallback is my animation callback method
|
||
|
the_selector = @selector( animationCallback );
|
||
|
a_signature = [SimpleViewerCocoa instanceMethodSignatureForSelector:the_selector];
|
||
|
an_invocation = [NSInvocation invocationWithMethodSignature:a_signature] ;
|
||
|
[an_invocation setSelector:the_selector];
|
||
|
[an_invocation setTarget:self];
|
||
|
|
||
|
animationTimer = [NSTimer
|
||
|
scheduledTimerWithTimeInterval:1.0/60.0 // fps
|
||
|
invocation:an_invocation
|
||
|
repeats:YES];
|
||
|
[animationTimer retain];
|
||
|
|
||
|
// For single threaded apps like this one,
|
||
|
// Cocoa seems to block timers or events sometimes. This can be seen
|
||
|
// when I'm animating (via a timer) and you open an popup box or move a slider.
|
||
|
// Apparently, sheets and dialogs can also block (try printing).
|
||
|
// To work around this, Cocoa provides different run-loop modes. I need to
|
||
|
// specify the modes to avoid the blockage.
|
||
|
// NSDefaultRunLoopMode seems to be the default. I don't think I need to explicitly
|
||
|
// set this one, but just in case, I will set it anyway.
|
||
|
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSDefaultRunLoopMode];
|
||
|
// This seems to be the one for preventing blocking on other events (popup box, slider, etc)
|
||
|
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSEventTrackingRunLoopMode];
|
||
|
// This seems to be the one for dialogs.
|
||
|
[[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSModalPanelRunLoopMode];
|
||
|
}
|
||
|
|
||
|
- (void) dealloc
|
||
|
{
|
||
|
[animationTimer invalidate];
|
||
|
[animationTimer release];
|
||
|
delete simpleViewer;
|
||
|
[super dealloc];
|
||
|
}
|
||
|
|
||
|
/* NSOpenGLView defines this method to be called (only once) after the OpenGL
|
||
|
* context is created and made the current context. It is intended to be used to setup
|
||
|
* your initial OpenGL state. This seems like a good place to initialize the
|
||
|
* OSG stuff. This method exists in 10.3 and later. If you are running pre-10.3, you
|
||
|
* must manually call this method sometime after the OpenGL context is created and
|
||
|
* made current, or refactor this code.
|
||
|
*/
|
||
|
- (void) prepareOpenGL
|
||
|
{
|
||
|
[super prepareOpenGL];
|
||
|
|
||
|
// The NSOpenGLCPSwapInterval seems to be vsync. If 1, buffers are swapped with vertical refresh.
|
||
|
// If 0, flushBuffer will execute as soon as possible.
|
||
|
long swap_interval = 1 ;
|
||
|
[[self openGLContext] setValues:&swap_interval forParameter:NSOpenGLCPSwapInterval];
|
||
|
|
||
|
// This is also might be a good place to setup OpenGL state that OSG doesn't control.
|
||
|
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
|
||
|
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
||
|
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
||
|
|
||
|
// We need to tell the osgViewer what the viewport size is
|
||
|
[self resizeViewport];
|
||
|
|
||
|
|
||
|
// This is optional:
|
||
|
// This is to setup some default text in the OpenGL view so the
|
||
|
// user knows that they should drag and drop a model into the view.
|
||
|
osg::ref_ptr<osgText::Text> default_text = new osgText::Text;
|
||
|
default_text->setAlignment(osgText::Text::CENTER_CENTER);
|
||
|
default_text->setBackdropType(osgText::Text::OUTLINE);
|
||
|
default_text->setAxisAlignment(osgText::Text::XZ_PLANE);
|
||
|
default_text->setText("Drag-and-Drop\nyour .osg model here!");
|
||
|
osg::ref_ptr<osg::Geode> the_geode = new osg::Geode;
|
||
|
the_geode->addDrawable(default_text.get());
|
||
|
simpleViewer->setSceneData(the_geode.get());
|
||
|
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End Init Stuff /////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// Mouse Stuff ////////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
- (void) mouseDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// Because many Mac users have only a 1-button mouse, we should provide ways
|
||
|
// to access the button 2 and 3 actions of osgViewer.
|
||
|
// I will use the Ctrl modifer to represent right-clicking
|
||
|
// and Option modifier to represent middle clicking.
|
||
|
if([the_event modifierFlags] & NSControlKeyMask)
|
||
|
{
|
||
|
[self setIsUsingCtrlClick:YES];
|
||
|
[self doRightMouseButtonDown:the_event];
|
||
|
}
|
||
|
else if([the_event modifierFlags] & NSAlternateKeyMask)
|
||
|
{
|
||
|
[self setIsUsingOptionClick:YES];
|
||
|
[self doMiddleMouseButtonDown:the_event];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
[self doLeftMouseButtonDown:the_event];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void) mouseDragged:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) mouseUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// Because many Mac users have only a 1-button mouse, we should provide ways
|
||
|
// to access the button 2 and 3 actions of osgViewer.
|
||
|
// I will use the Ctrl modifer to represent right-clicking
|
||
|
// and Option modifier to represent middle clicking.
|
||
|
if([self isUsingCtrlClick] == YES)
|
||
|
{
|
||
|
[self setIsUsingCtrlClick:NO];
|
||
|
[self doRightMouseButtonUp:the_event];
|
||
|
}
|
||
|
if([self isUsingOptionClick] == YES)
|
||
|
{
|
||
|
[self setIsUsingOptionClick:NO];
|
||
|
[self doMiddleMouseButtonUp:the_event];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
[self doLeftMouseButtonUp:the_event];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void) rightMouseDown:(NSEvent*)the_event
|
||
|
{
|
||
|
[self doRightMouseButtonDown:the_event];
|
||
|
}
|
||
|
|
||
|
- (void) rightMouseDragged:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) rightMouseUp:(NSEvent*)the_event
|
||
|
{
|
||
|
[self doRightMouseButtonUp:the_event];
|
||
|
}
|
||
|
|
||
|
// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
|
||
|
- (void) otherMouseDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
// Button 0 is left
|
||
|
// Button 1 is right
|
||
|
// Button 2 is middle
|
||
|
// Button 3 keeps going
|
||
|
// osgViewer expects 1 for left, 3 for right, 2 for middle
|
||
|
// osgViewer has a reversed number mapping for right and middle compared to Cocoa
|
||
|
if([the_event buttonNumber] == 2)
|
||
|
{
|
||
|
[self doMiddleMouseButtonDown:the_event];
|
||
|
}
|
||
|
else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
|
||
|
{
|
||
|
[self doExtraMouseButtonDown:the_event buttonNumber:[the_event buttonNumber]];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void) otherMouseDragged:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
|
||
|
- (void) otherMouseUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// Button 0 is left
|
||
|
// Button 1 is right
|
||
|
// Button 2 is middle
|
||
|
// Button 3 keeps going
|
||
|
// osgViewer expects 1 for left, 3 for right, 2 for middle
|
||
|
// osgViewer has a reversed number mapping for right and middle compared to Cocoa
|
||
|
if([the_event buttonNumber] == 2)
|
||
|
{
|
||
|
[self doMiddleMouseButtonUp:the_event];
|
||
|
}
|
||
|
else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
|
||
|
{
|
||
|
// I don't think osgViewer does anything for these additional buttons,
|
||
|
// but just in case, pass them along. But as a Cocoa programmer, you might
|
||
|
// think about things you can do natively here instead of passing the buck.
|
||
|
} [self doExtraMouseButtonUp:the_event buttonNumber:[the_event buttonNumber]];
|
||
|
}
|
||
|
|
||
|
- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click
|
||
|
{
|
||
|
isUsingCtrlClick = is_using_ctrl_click;
|
||
|
}
|
||
|
|
||
|
- (BOOL) isUsingCtrlClick
|
||
|
{
|
||
|
return isUsingCtrlClick;
|
||
|
}
|
||
|
|
||
|
- (void) setIsUsingOptionClick:(BOOL)is_using_option_click
|
||
|
{
|
||
|
isUsingOptionClick = is_using_option_click;
|
||
|
}
|
||
|
|
||
|
- (BOOL) isUsingOptionClick
|
||
|
{
|
||
|
return isUsingOptionClick;
|
||
|
}
|
||
|
|
||
|
- (void) doLeftMouseButtonDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
if([the_event clickCount] == 1)
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 1);
|
||
|
}
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) doLeftMouseButtonUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 1);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) doRightMouseButtonDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
if([the_event clickCount] == 1)
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 3);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 3);
|
||
|
}
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
|
||
|
- (void) doRightMouseButtonUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 3);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) doMiddleMouseButtonDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
if([the_event clickCount] == 1)
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 2);
|
||
|
}
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) doExtraMouseButtonDown:(NSEvent*)the_event buttonNumber:(int)button_number
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
|
||
|
if([the_event clickCount] == 1)
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, button_number+1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, button_number+1);
|
||
|
}
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
|
||
|
- (void) doMiddleMouseButtonUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system. NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 2);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) doExtraMouseButtonUp:(NSEvent*)the_event buttonNumber:(int)button_number
|
||
|
{
|
||
|
// We must convert the mouse event locations from the window coordinate system to the
|
||
|
// local view coordinate system. NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, button_number+1);
|
||
|
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
// This is a job for Mighty Mouse!
|
||
|
// For the most fluid experience turn on 360 degree mode availble in 10.4.8+.
|
||
|
// With your Mighty Mouse plugged in,
|
||
|
// open 'Keyboard & Mouse' in 'System Preferences'.
|
||
|
// Select the 'Mouse' tab.
|
||
|
// Under 'Scrolling Options' select '360 degree'.
|
||
|
// That should improve diagonal scrolling.
|
||
|
// You should also be able to use 'two-finger scrolling' on newer laptops.
|
||
|
- (void) scrollWheel:(NSEvent*)the_event
|
||
|
{
|
||
|
// Unfortunately, it turns out mouseScroll2D doesn't actually do anything.
|
||
|
// The camera manipulators don't seem to implement any code that utilize the scroll values.
|
||
|
// This this call does nothing.
|
||
|
// simpleViewer->getEventQueue()->mouseScroll2D([the_event deltaX], [the_event deltaY]);
|
||
|
|
||
|
// With the absense of a useful mouseScroll2D API, we can manually simulate the desired effect.
|
||
|
NSPoint the_point = [the_event locationInWindow];
|
||
|
NSPoint converted_point = [self convertPoint:the_point fromView:nil];
|
||
|
simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1);
|
||
|
simpleViewer->getEventQueue()->mouseMotion(converted_point.x + -[the_event deltaX], converted_point.y + [the_event deltaY]);
|
||
|
simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x + -[the_event deltaX], converted_point.y + [the_event deltaY], 1);
|
||
|
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End Mouse Stuff ////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// Keyboard Stuff /////////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// Needed to accept keyboard events
|
||
|
- (BOOL) acceptsFirstResponder
|
||
|
{
|
||
|
return YES;
|
||
|
}
|
||
|
|
||
|
- (void) keyDown:(NSEvent*)the_event
|
||
|
{
|
||
|
// Do you want characters or charactersIgnoringModifiers?
|
||
|
NSString* event_characters = [the_event characters];
|
||
|
// NSString* event_characters = [the_event charactersIgnoringModifiers];
|
||
|
|
||
|
unichar unicode_character = [event_characters characterAtIndex:0];
|
||
|
// NSLog(@"unicode_character: %d", unicode_character);
|
||
|
simpleViewer->getEventQueue()->keyPress(static_cast<osgGA::GUIEventAdapter::KeySymbol>(unicode_character));
|
||
|
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
- (void) keyUp:(NSEvent*)the_event
|
||
|
{
|
||
|
// Do you want characters or charactersIgnoringModifiers?
|
||
|
NSString* event_characters = [the_event characters];
|
||
|
// NSString* event_characters = [the_event charactersIgnoringModifiers];
|
||
|
unichar unicode_character = [event_characters characterAtIndex:0];
|
||
|
simpleViewer->getEventQueue()->keyRelease(static_cast<osgGA::GUIEventAdapter::KeySymbol>(unicode_character));
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End Keyboard Stuff /////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// View and Draw Stuff ////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// This method is periodically called by my timer.
|
||
|
- (void) animationCallback
|
||
|
{
|
||
|
// Simply notify Cocoa that a drawRect needs to take place.
|
||
|
// Potential optimization is to query the OSG stuff to find out if a redraw is actually necessary.
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
// This is an optional optimization. This states you don't have a transparent view/window.
|
||
|
// Obviously don't use this or set it to NO if you intend for your view to be see-through.
|
||
|
- (BOOL) isOpaque
|
||
|
{
|
||
|
return YES;
|
||
|
}
|
||
|
|
||
|
// Resolution Independent UI is coming... (Tiger's Quartz Debug already has the tool.)
|
||
|
// We must think in 'point sizes', not pixel sizes, so a conversion is needed for OpenGL.
|
||
|
- (void) resizeViewport
|
||
|
{
|
||
|
NSSize size_in_points = [self bounds].size;
|
||
|
// This coordinate system conversion seems to make things work with Quartz Debug.
|
||
|
NSSize size_in_window_coordinates = [self convertSize:size_in_points toView:nil];
|
||
|
simpleViewer->getEventQueue()->windowResize(0, 0, size_in_window_coordinates.width, size_in_window_coordinates.height);
|
||
|
}
|
||
|
|
||
|
// For window resize
|
||
|
- (void) reshape
|
||
|
{
|
||
|
[super reshape];
|
||
|
[self resizeViewport];
|
||
|
}
|
||
|
|
||
|
// This is the code that actually draws.
|
||
|
// Remember you shouldn't call drawRect: directly and should use setNeedsDisplay:YES
|
||
|
// This is so the operating system can optimize when a draw is actually needed.
|
||
|
// (e.g. No sense drawing when the application is hidden.)
|
||
|
- (void) drawRect:(NSRect)the_rect
|
||
|
{
|
||
|
[[self openGLContext] makeCurrentContext];
|
||
|
simpleViewer->frame();
|
||
|
[[self openGLContext] flushBuffer];
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End View and Draw Stuff ////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// For drag and drop //////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
- (unsigned int) draggingEntered:(id <NSDraggingInfo>)the_sender
|
||
|
{
|
||
|
if([the_sender draggingSource] != self)
|
||
|
{
|
||
|
NSPasteboard* paste_board = [the_sender draggingPasteboard];
|
||
|
// I respond to filename types or URL types
|
||
|
NSArray* supported_types = [NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil];
|
||
|
// If any of the supported types are being dragged in, activate the copy operation
|
||
|
NSString* first_type = [paste_board availableTypeFromArray:supported_types];
|
||
|
if(first_type != nil)
|
||
|
{
|
||
|
return NSDragOperationCopy;
|
||
|
}
|
||
|
}
|
||
|
// Means we don't support this type
|
||
|
return NSDragOperationNone;
|
||
|
}
|
||
|
|
||
|
// We're not using this method, but here it is as an example.
|
||
|
- (void) draggingExited:(id <NSDraggingInfo>)the_sender
|
||
|
{
|
||
|
}
|
||
|
|
||
|
- (BOOL) prepareForDragOperation:(id <NSDraggingInfo>)the_sender
|
||
|
{
|
||
|
return YES;
|
||
|
}
|
||
|
|
||
|
- (BOOL) performDragOperation:(id <NSDraggingInfo>)the_sender
|
||
|
{
|
||
|
NSPasteboard* paste_board = [the_sender draggingPasteboard];
|
||
|
|
||
|
|
||
|
if([[paste_board types] containsObject:NSFilenamesPboardType])
|
||
|
{
|
||
|
NSArray* file_names = [paste_board propertyListForType:NSFilenamesPboardType];
|
||
|
// int number_of_files = [file_names count];
|
||
|
// Exercise for the reader: Try loading all files in the array
|
||
|
NSString* single_file = [file_names objectAtIndex:0];
|
||
|
osg::ref_ptr<osg::Node> loaded_model = osgDB::readNodeFile([single_file UTF8String]);
|
||
|
if(!loaded_model)
|
||
|
{
|
||
|
NSLog(@"File: %@ failed to load", single_file);
|
||
|
return NO;
|
||
|
}
|
||
|
simpleViewer->setSceneData(loaded_model.get());
|
||
|
return YES;
|
||
|
}
|
||
|
else if([[paste_board types] containsObject:NSURLPboardType])
|
||
|
{
|
||
|
NSURL* file_url = [NSURL URLFromPasteboard:paste_board];
|
||
|
// See if the URL is valid file path
|
||
|
if(![file_url isFileURL])
|
||
|
{
|
||
|
NSLog(@"URL: %@ needs to be a file for readNodeFile()", file_url);
|
||
|
return NO;
|
||
|
}
|
||
|
NSString* file_path = [file_url path];
|
||
|
osg::ref_ptr<osg::Node> loaded_model = osgDB::readNodeFile([file_path UTF8String]);
|
||
|
if(!loaded_model)
|
||
|
{
|
||
|
NSLog(@"URL: %@ failed to load, %@", file_url, file_path);
|
||
|
return NO;
|
||
|
}
|
||
|
simpleViewer->setSceneData(loaded_model.get());
|
||
|
return YES;
|
||
|
}
|
||
|
return NO;
|
||
|
}
|
||
|
|
||
|
// This method isn't really needed (I could move setNeedsDisplay up), but is here as an example
|
||
|
- (void) concludeDragOperation:(id <NSDraggingInfo>)the_sender
|
||
|
{
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End of drag and drop ///////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// IBAction examples /////////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// Connect a button to this to stop and reset the position.
|
||
|
- (IBAction) resetPosition:(id)the_sender
|
||
|
{
|
||
|
// osgGA::MatrixManipulator* camera_manipulator = simpleViewer->getCameraManipulator();
|
||
|
// This only resets the position
|
||
|
// camera_manipulator->home(0.0);
|
||
|
|
||
|
// There is no external API from SimpleViewer that I can see that will stop movement.
|
||
|
// So fake the 'spacebar' to stop things and reset.
|
||
|
simpleViewer->getEventQueue()->keyPress(static_cast<osgGA::GUIEventAdapter::KeySymbol>(0x20));
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
// Connect a NSColorWell to this to change color.
|
||
|
// A better way to do this is use Cocoa Bindings because it will automatically
|
||
|
// synchronize between your model and view, but since this demo lacks a model and controller
|
||
|
// aspect it wouldn't do much good.
|
||
|
- (IBAction) takeBackgroundColorFrom:(id)the_sender
|
||
|
{
|
||
|
NSColor* the_color = [the_sender color];
|
||
|
osgUtil::SceneView* scene_view = simpleViewer->getSceneView();
|
||
|
|
||
|
scene_view->setClearColor(
|
||
|
osg::Vec4(
|
||
|
[the_color redComponent],
|
||
|
[the_color greenComponent],
|
||
|
[the_color blueComponent],
|
||
|
[the_color alphaComponent]
|
||
|
)
|
||
|
);
|
||
|
[self setNeedsDisplay:YES];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////// End IBAction examples /////////////////////
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
@end
|