1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
//
// This code was created by Jeff Molofee '99
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe
//
//=====================================================================
// Converted to C# and MonoMac by Kenneth J. Pouncey
// http://www.cocoa-mono.org
//
// Copyright (c) 2011 Kenneth J. Pouncey
//
//
// 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.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using MonoMac.Foundation;
using MonoMac.AppKit;
using MonoMac.CoreVideo;
using MonoMac.CoreGraphics;
using MonoMac.OpenGL;
namespace NeHeLesson17
{
public partial class MyOpenGLView : MonoMac.AppKit.NSView
{
NSOpenGLContext openGLContext;
NSOpenGLPixelFormat pixelFormat;
MainWindowController controller;
CVDisplayLink displayLink;
NSObject notificationProxy;
[Export("initWithFrame:")]
public MyOpenGLView (RectangleF frame) : this(frame, null)
{
}
public MyOpenGLView (RectangleF frame,NSOpenGLContext context) : base(frame)
{
var attribs = new object [] {
NSOpenGLPixelFormatAttribute.Accelerated,
NSOpenGLPixelFormatAttribute.NoRecovery,
NSOpenGLPixelFormatAttribute.DoubleBuffer,
NSOpenGLPixelFormatAttribute.ColorSize, 24,
NSOpenGLPixelFormatAttribute.DepthSize, 16 };
pixelFormat = new NSOpenGLPixelFormat (attribs);
if (pixelFormat == null)
Console.WriteLine ("No OpenGL pixel format");
// NSOpenGLView does not handle context sharing, so we draw to a custom NSView instead
openGLContext = new NSOpenGLContext (pixelFormat, context);
openGLContext.MakeCurrentContext ();
// Synchronize buffer swaps with vertical refresh rate
openGLContext.SwapInterval = true;
// Initialize our newly created view.
InitGL ();
SetupDisplayLink ();
// Look for changes in view size
// Note, -reshape will not be called automatically on size changes because NSView does not export it to override
notificationProxy = NSNotificationCenter.DefaultCenter.AddObserver (NSView.NSViewGlobalFrameDidChangeNotification, HandleReshape);
}
public override void DrawRect (RectangleF dirtyRect)
{
// Ignore if the display link is still running
if (!displayLink.IsRunning && controller != null)
DrawView ();
}
public override bool AcceptsFirstResponder ()
{
// We want this view to be able to receive key events
return true;
}
public override void LockFocus ()
{
base.LockFocus ();
if (openGLContext.View != this)
openGLContext.View = this;
}
public override void KeyDown (NSEvent theEvent)
{
controller.KeyDown (theEvent);
}
public override void MouseDown (NSEvent theEvent)
{
controller.MouseDown (theEvent);
}
// All Setup For OpenGL Goes Here
public bool InitGL ()
{
// Enables Smooth Shading
GL.ShadeModel (ShadingModel.Smooth);
// Set background color to black
GL.ClearColor(Color.Black);
// Setup Depth Testing
// Depth Buffer setup
GL.ClearDepth (1.0);
// Enables Depth testing
GL.Enable (EnableCap.DepthTest);
// The type of depth testing to do
GL.DepthFunc (DepthFunction.Lequal);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
// Really Nice Perspective Calculations
GL.Hint (HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.Enable (EnableCap.Texture2D);
return true;
}
private void DrawView ()
{
// This method will be called on both the main thread (through -drawRect:) and a secondary thread (through the display link rendering loop)
// Also, when resizing the view, -reshape is called on the main thread, but we may be drawing on a secondary thread
// Add a mutex around to avoid the threads accessing the context simultaneously
openGLContext.CGLContext.Lock ();
// Make sure we draw to the right context
openGLContext.MakeCurrentContext ();
// Delegate to the scene object for rendering
controller.Scene.DrawGLScene ();
openGLContext.FlushBuffer ();
openGLContext.CGLContext.Unlock ();
}
private void SetupDisplayLink ()
{
// Create a display link capable of being used with all active displays
displayLink = new CVDisplayLink ();
// Set the renderer output callback function
displayLink.SetOutputCallback (MyDisplayLinkOutputCallback);
// Set the display link for the current renderer
CGLContext cglContext = openGLContext.CGLContext;
CGLPixelFormat cglPixelFormat = PixelFormat.CGLPixelFormat;
displayLink.SetCurrentDisplay (cglContext, cglPixelFormat);
}
public CVReturn MyDisplayLinkOutputCallback (CVDisplayLink displayLink, ref CVTimeStamp inNow, ref CVTimeStamp inOutputTime, CVOptionFlags flagsIn, ref CVOptionFlags flagsOut)
{
CVReturn result = GetFrameForTime (inOutputTime);
return result;
}
private CVReturn GetFrameForTime (CVTimeStamp outputTime)
{
// There is no autorelease pool when this method is called because it will be called from a background thread
// It's important to create one or you will leak objects
using (NSAutoreleasePool pool = new NSAutoreleasePool ()) {
// Update the animation
DrawView ();
}
return CVReturn.Success;
}
public NSOpenGLContext OpenGLContext {
get { return openGLContext; }
}
public NSOpenGLPixelFormat PixelFormat {
get { return pixelFormat; }
}
public MainWindowController MainController {
set { controller = value; }
}
public void UpdateView ()
{
// This method will be called on the main thread when resizing, but we may be drawing on a secondary thread through the display link
// Add a mutex around to avoid the threads accessing the context simultaneously
openGLContext.CGLContext.Lock ();
// Delegate to the scene object to update for a change in the view size
controller.Scene.ResizeGLScene (Bounds);
openGLContext.Update ();
openGLContext.CGLContext.Unlock ();
}
private void HandleReshape (NSNotification note)
{
UpdateView ();
}
public void StartAnimation ()
{
if (displayLink != null && !displayLink.IsRunning)
displayLink.Start ();
}
public void StopAnimation ()
{
if (displayLink != null && displayLink.IsRunning)
displayLink.Stop ();
}
// Clean up the notifications
public void DeAllocate ()
{
displayLink.Stop ();
displayLink.SetOutputCallback (null);
NSNotificationCenter.DefaultCenter.RemoveObserver (notificationProxy);
}
[Export("toggleFullScreen:")]
public void toggleFullScreen (NSObject sender)
{
controller.toggleFullScreen (sender);
}
}
}
|