From Eric Wing, new ogsimpleviewCocoa example.
This commit is contained in:
BIN
examples/osgsimpleviewerCocoa/English.lproj/InfoPlist.strings
Normal file
BIN
examples/osgsimpleviewerCocoa/English.lproj/InfoPlist.strings
Normal file
Binary file not shown.
28
examples/osgsimpleviewerCocoa/Info.plist
Normal file
28
examples/osgsimpleviewerCocoa/Info.plist
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openscenegraph.osgsimpleviewerCocoa</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
133
examples/osgsimpleviewerCocoa/SimpleViewerCocoa.h
Normal file
133
examples/osgsimpleviewerCocoa/SimpleViewerCocoa.h
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// SimpleViewerCocoa.h
|
||||
// osgsimpleviewerCocoa
|
||||
//
|
||||
// Created by Eric Wing on 11/12/06.
|
||||
// Copyright 2006. All rights reserved.
|
||||
//
|
||||
/* This is the class interface for a custom NSView that interfaces with an osgViewer.
|
||||
* Because Cocoa is written in Objective-C, but OSG is written in C++, we rely on
|
||||
* Objective-C++ to make the integration easy.
|
||||
*
|
||||
* One thing to remember is that any Objective-C files that include this header
|
||||
* must also be compiled as Objective-C++ because there are C++ constructs in
|
||||
* this file (such as namespaces) which the normal Objective-C compiler
|
||||
* won't understand. (The easy way to do this is rename the .m files to .mm.)
|
||||
*
|
||||
* In the event that you have a large, pre-existing code base written in
|
||||
* pure Objective-C and you find that the header include propagates to a
|
||||
* large number of your files, forcing you to mark many files to be compiled as
|
||||
* Objective-C++, and you find that you don't want to change these files because
|
||||
* they are shared with other pure Obj-C projects, you might consider further
|
||||
* wrapping the C++ code so there are only C or Obj-C constructs in
|
||||
* this header. There are several different techniques ranging from, wrapping
|
||||
* the C++ code in pure C interfaces, to simply using void pointers in this
|
||||
* file for any C++ pointers and casting appropriately in the implementation
|
||||
* file.
|
||||
*/
|
||||
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
// Just a forward declaration so I don't need the #include in the header.
|
||||
class SimpleViewer;
|
||||
}
|
||||
|
||||
// Subclass NSOpenGLView. We could subclass NSView instead, but NSOpenGLView is easy.
|
||||
@interface SimpleViewerCocoa : NSOpenGLView
|
||||
{
|
||||
// Note: In Objective-C++, if you use objects instead of pointers as
|
||||
// member instance variables, you MUST turn on "Call C++ Default Ctors/Dtors in Objective-C".
|
||||
// -fobjc-call-cxx-cdtors
|
||||
// This option makes sure constructors and destructors are run.
|
||||
// This option is only available for gcc 4.0+ (Mac OS X 10.4+)
|
||||
|
||||
// Is SimpleViewer supposed use ref_ptr? (Doesn't look like it to me.)
|
||||
// If so, remember ref_ptr is an object on the stack and the cdtors option must be activated.
|
||||
// We could also make simpleViewer an object instead of a pointer, but again, turn on the option.
|
||||
osgViewer::SimpleViewer* simpleViewer;
|
||||
|
||||
// This timer is used to trigger animation callbacks since everything is event driven.
|
||||
NSTimer* animationTimer;
|
||||
|
||||
// Flags to help track whether ctrl-clicking or option-clicking is being used
|
||||
BOOL isUsingCtrlClick;
|
||||
BOOL isUsingOptionClick;
|
||||
}
|
||||
|
||||
// My custom static method to create a basic pixel format
|
||||
+ (NSOpenGLPixelFormat*) basicPixelFormat;
|
||||
|
||||
// Official init methods
|
||||
- (id) initWithFrame:(NSRect)frame_rect pixelFormat:(NSOpenGLPixelFormat*)pixel_format;
|
||||
- (id) initWithCoder:(NSCoder*)the_coder;
|
||||
|
||||
// Private init helper methods
|
||||
- (void) commonInit;
|
||||
- (void) initOSGViewer;
|
||||
- (void) initAnimationTimer;
|
||||
|
||||
// Official/Special NSOpenGLView method that gets called for you to prepare your OpenGL state.
|
||||
- (void) prepareOpenGL;
|
||||
// Class dealloc method
|
||||
- (void) dealloc;
|
||||
|
||||
// Official mouse event methods
|
||||
- (void) mouseDown:(NSEvent*)the_event;
|
||||
- (void) mouseDragged:(NSEvent*)the_event;
|
||||
- (void) mouseUp:(NSEvent*)the_event;
|
||||
- (void) rightMouseDown:(NSEvent*)the_event;
|
||||
- (void) rightMouseDragged:(NSEvent*)the_event;
|
||||
- (void) rightMouseUp:(NSEvent*)the_event;
|
||||
- (void) otherMouseDown:(NSEvent*)the_event;
|
||||
- (void) otherMouseDragged:(NSEvent*)the_event;
|
||||
- (void) otherMouseUp:(NSEvent*)the_event;
|
||||
|
||||
// Private setter/getter methods to track ctrl/option-clicking
|
||||
- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click;
|
||||
- (BOOL) isUsingCtrlClick;
|
||||
- (void) setIsUsingOptionClick:(BOOL)is_using_option_click;
|
||||
- (BOOL) isUsingOptionClick;
|
||||
// Private helper methods to help deal with mouse events
|
||||
- (void) doLeftMouseButtonDown:(NSEvent*)the_event;
|
||||
- (void) doLeftMouseButtonUp:(NSEvent*)the_event;
|
||||
- (void) doRightMouseButtonDown:(NSEvent*)the_event;
|
||||
- (void) doRightMouseButtonUp:(NSEvent*)the_event;
|
||||
- (void) doMiddleMouseButtonDown:(NSEvent*)the_event;
|
||||
- (void) doExtraMouseButtonDown:(NSEvent*)the_event buttonNumber:(int)button_number;
|
||||
- (void) doMiddleMouseButtonUp:(NSEvent*)the_event;
|
||||
- (void) doExtraMouseButtonUp:(NSEvent*)the_event buttonNumber:(int)button_number;
|
||||
|
||||
// Official scrollWheel (scrollball) method
|
||||
- (void) scrollWheel:(NSEvent*)the_event;
|
||||
|
||||
// Official methods for keyboard events
|
||||
- (BOOL) acceptsFirstResponder;
|
||||
- (void) keyDown:(NSEvent*)the_event;
|
||||
- (void) keyUp:(NSEvent*)the_event;
|
||||
|
||||
// My custom method to handle timer callbacks
|
||||
- (void) animationCallback;
|
||||
|
||||
// Official methods for view stuff and drawing
|
||||
- (BOOL) isOpaque;
|
||||
- (void) resizeViewport;
|
||||
- (void) reshape;
|
||||
- (void) drawRect:(NSRect)the_rect;
|
||||
|
||||
// Official methods for drag and drop (view as target)
|
||||
- (unsigned int) draggingEntered:(id <NSDraggingInfo>)the_sender;
|
||||
- (void) draggingExited:(id <NSDraggingInfo>)the_sender;
|
||||
- (BOOL) prepareForDragOperation:(id <NSDraggingInfo>)the_sender;
|
||||
- (BOOL) performDragOperation:(id <NSDraggingInfo>)the_sender;
|
||||
- (void) concludeDragOperation:(id <NSDraggingInfo>)the_sender;
|
||||
|
||||
|
||||
// Examples of providing an action to connect to.
|
||||
- (IBAction) resetPosition:(id)the_sender;
|
||||
- (IBAction) takeBackgroundColorFrom:(id)the_sender;
|
||||
|
||||
@end
|
||||
|
||||
757
examples/osgsimpleviewerCocoa/SimpleViewerCocoa.mm
Normal file
757
examples/osgsimpleviewerCocoa/SimpleViewerCocoa.mm
Normal file
@@ -0,0 +1,757 @@
|
||||
//
|
||||
// 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
|
||||
14
examples/osgsimpleviewerCocoa/main.m
Normal file
14
examples/osgsimpleviewerCocoa/main.m
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// main.m
|
||||
// osgsimpleviewerCocoa
|
||||
//
|
||||
// Created by Eric Wing on 11/13/06.
|
||||
// Copyright __MyCompanyName__ 2006. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, (const char **) argv);
|
||||
}
|
||||
Reference in New Issue
Block a user