diff options
author | Joseph Eagar <joeedh@gmail.com> | 2009-10-24 09:43:27 +0400 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2009-10-24 09:43:27 +0400 |
commit | 672c8926d7801fcc4b45e9dff75022e1ff9d240b (patch) | |
tree | 33dfc4620f52af667b4c251d0a940de86118dcc7 | |
parent | 8f788c64db587e74079d812dbcea40c2bd4b91ff (diff) | |
parent | 6ffb79107c9509f01019c8bf9af2caf0348ca330 (diff) |
files missing from recent merge
23 files changed, 6961 insertions, 1 deletions
diff --git a/intern/ghost/intern/GHOST_DisplayManagerCocoa.h b/intern/ghost/intern/GHOST_DisplayManagerCocoa.h new file mode 100644 index 00000000000..0ce5e08b4f6 --- /dev/null +++ b/intern/ghost/intern/GHOST_DisplayManagerCocoa.h @@ -0,0 +1,106 @@ +/** + * $Id: GHOST_DisplayManagerCocoa.h 23603 2009-10-02 07:20:33Z damien78 $ + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +/** + * @file GHOST_DisplayManagerCocoa.h + * Declaration of GHOST_DisplayManagerCocoa class. + */ + +#ifndef _GHOST_DISPLAY_MANAGER_COCOA_H_ +#define _GHOST_DISPLAY_MANAGER_COCOA_H_ + +#ifndef __APPLE__ +#error Apple only! +#endif // __APPLE__ + +#include "GHOST_DisplayManager.h" + +/** + * Manages system displays (Mac OSX/Cocoa implementation). + * @see GHOST_DisplayManager + * @author Maarten Gribnau + * @date September 21, 2001 + */ +class GHOST_DisplayManagerCocoa : public GHOST_DisplayManager +{ +public: + /** + * Constructor. + */ + GHOST_DisplayManagerCocoa(void); + + /** + * Returns the number of display devices on this system. + * @param numDisplays The number of displays on this system. + * @return Indication of success. + */ + virtual GHOST_TSuccess getNumDisplays(GHOST_TUns8& numDisplays) const; + + /** + * Returns the number of display settings for this display device. + * @param display The index of the display to query with 0 <= display < getNumDisplays(). + * @param setting The number of settings of the display device with this index. + * @return Indication of success. + */ + virtual GHOST_TSuccess getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const; + + /** + * Returns the current setting for this display device. + * @param display The index of the display to query with 0 <= display < getNumDisplays(). + * @param index The setting index to be returned. + * @param setting The setting of the display device with this index. + * @return Indication of success. + */ + virtual GHOST_TSuccess getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const; + + /** + * Returns the current setting for this display device. + * @param display The index of the display to query with 0 <= display < getNumDisplays(). + * @param setting The current setting of the display device with this index. + * @return Indication of success. + */ + virtual GHOST_TSuccess getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const; + + /** + * Changes the current setting for this display device. + * @param display The index of the display to query with 0 <= display < getNumDisplays(). + * @param setting The current setting of the display device with this index. + * @return Indication of success. + */ + virtual GHOST_TSuccess setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting); + +protected: + //Do not cache values as OS X supports screen hot plug + /** Cached number of displays. */ + //CGDisplayCount m_numDisplays; + /** Cached display id's for each display. */ + //CGDirectDisplayID* m_displayIDs; +}; + + +#endif // _GHOST_DISPLAY_MANAGER_COCOA_H_ + diff --git a/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm new file mode 100644 index 00000000000..5be49851bcb --- /dev/null +++ b/intern/ghost/intern/GHOST_DisplayManagerCocoa.mm @@ -0,0 +1,168 @@ +/**
+ * $Id: GHOST_DisplayManagerCocoa.mm 23603 2009-10-02 07:20:33Z damien78 $
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Maarten Gribnau 09/2001
+ Damien Plisson 10/2009
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <Cocoa/Cocoa.h>
+
+#include "GHOST_DisplayManagerCocoa.h"
+#include "GHOST_Debug.h"
+
+// We do not support multiple monitors at the moment
+
+
+GHOST_DisplayManagerCocoa::GHOST_DisplayManagerCocoa(void)
+{
+}
+
+
+GHOST_TSuccess GHOST_DisplayManagerCocoa::getNumDisplays(GHOST_TUns8& numDisplays) const
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ numDisplays = (GHOST_TUns8) [[NSScreen screens] count];
+
+ [pool drain];
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_DisplayManagerCocoa::getNumDisplaySettings(GHOST_TUns8 display, GHOST_TInt32& numSettings) const
+{
+ GHOST_ASSERT((display==kMainDisplay), "GHOST_DisplayManagerCocoa::getNumDisplaySettings(): only main display is supported");
+
+ numSettings = (GHOST_TInt32)3; //Width, Height, BitsPerPixel
+
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_DisplayManagerCocoa::getDisplaySetting(GHOST_TUns8 display, GHOST_TInt32 index, GHOST_DisplaySetting& setting) const
+{
+ //Note that only current display setting is available
+ NSScreen *askedDisplay;
+
+ GHOST_ASSERT((display==kMainDisplay), "GHOST_DisplayManagerCocoa::getDisplaySetting(): only main display is supported");
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (display == kMainDisplay) //Screen #0 may not be the main one
+ askedDisplay = [NSScreen mainScreen];
+ else
+ askedDisplay = [[NSScreen screens] objectAtIndex:display];
+
+ if(askedDisplay == nil) {
+ [pool drain];
+ return GHOST_kFailure;
+ }
+
+ NSRect frame = [askedDisplay visibleFrame];
+ setting.xPixels = frame.size.width;
+ setting.yPixels = frame.size.height;
+
+ setting.bpp = NSBitsPerPixelFromDepth([askedDisplay depth]);
+
+ setting.frequency = 0; //No more CRT display...
+
+#ifdef GHOST_DEBUG
+ printf("display mode: width=%d, height=%d, bpp=%d, frequency=%d\n", setting.xPixels, setting.yPixels, setting.bpp, setting.frequency);
+#endif // GHOST_DEBUG
+
+ [pool drain];
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_DisplayManagerCocoa::getCurrentDisplaySetting(GHOST_TUns8 display, GHOST_DisplaySetting& setting) const
+{
+ NSScreen *askedDisplay;
+
+ GHOST_ASSERT((display==kMainDisplay), "GHOST_DisplayManagerCocoa::getCurrentDisplaySetting(): only main display is supported");
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (display == kMainDisplay) //Screen #0 may not be the main one
+ askedDisplay = [NSScreen mainScreen];
+ else
+ askedDisplay = [[NSScreen screens] objectAtIndex:display];
+
+ if(askedDisplay == nil) {
+ [pool drain];
+ return GHOST_kFailure;
+ }
+
+ NSRect frame = [askedDisplay visibleFrame];
+ setting.xPixels = frame.size.width;
+ setting.yPixels = frame.size.height;
+
+ setting.bpp = NSBitsPerPixelFromDepth([askedDisplay depth]);
+
+ setting.frequency = 0; //No more CRT display...
+
+#ifdef GHOST_DEBUG
+ printf("current display mode: width=%d, height=%d, bpp=%d, frequency=%d\n", setting.xPixels, setting.yPixels, setting.bpp, setting.frequency);
+#endif // GHOST_DEBUG
+
+ [pool drain];
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(GHOST_TUns8 display, const GHOST_DisplaySetting& setting)
+{
+ GHOST_ASSERT((display==kMainDisplay), "GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(): only main display is supported");
+
+#ifdef GHOST_DEBUG
+ printf("GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(): requested settings:\n");
+ printf(" setting.xPixels=%d\n", setting.xPixels);
+ printf(" setting.yPixels=%d\n", setting.yPixels);
+ printf(" setting.bpp=%d\n", setting.bpp);
+ printf(" setting.frequency=%d\n", setting.frequency);
+#endif // GHOST_DEBUG
+
+ //Display configuration is no more available in 10.6
+
+/* CFDictionaryRef displayModeValues = ::CGDisplayBestModeForParametersAndRefreshRate(
+ m_displayIDs[display],
+ (size_t)setting.bpp,
+ (size_t)setting.xPixels,
+ (size_t)setting.yPixels,
+ (CGRefreshRate)setting.frequency,
+ NULL);*/
+
+#ifdef GHOST_DEBUG
+ printf("GHOST_DisplayManagerCocoa::setCurrentDisplaySetting(): switching to:\n");
+ printf(" setting.xPixels=%d\n", getValue(displayModeValues, kCGDisplayWidth));
+ printf(" setting.yPixels=%d\n", getValue(displayModeValues, kCGDisplayHeight));
+ printf(" setting.bpp=%d\n", getValue(displayModeValues, kCGDisplayBitsPerPixel));
+ printf(" setting.frequency=%d\n", getValue(displayModeValues, kCGDisplayRefreshRate));
+#endif // GHOST_DEBUG
+
+ //CGDisplayErr err = ::CGDisplaySwitchToMode(m_displayIDs[display], displayModeValues);
+
+ return /*err == CGDisplayNoErr ? GHOST_kSuccess :*/ GHOST_kFailure;
+}
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h new file mode 100644 index 00000000000..002089e4185 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -0,0 +1,273 @@ +/** + * $Id: GHOST_SystemCocoa.h 23737 2009-10-09 12:48:28Z damien78 $ + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Maarten Gribnau 05/2001 + * Damien Plisson 09/2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ +/** + * @file GHOST_SystemCocoa.h + * Declaration of GHOST_SystemCocoa class. + */ + +#ifndef _GHOST_SYSTEM_COCOA_H_ +#define _GHOST_SYSTEM_COCOA_H_ + +#ifndef __APPLE__ +#error Apple OSX only! +#endif // __APPLE__ + +//#define __CARBONSOUND__ + + +#include "GHOST_System.h" + +class GHOST_EventCursor; +class GHOST_EventKey; +class GHOST_EventWindow; +class GHOST_WindowCocoa; + + +class GHOST_SystemCocoa : public GHOST_System { +public: + /** + * Constructor. + */ + GHOST_SystemCocoa(); + + /** + * Destructor. + */ + ~GHOST_SystemCocoa(); + + /*************************************************************************************** + ** Time(r) functionality + ***************************************************************************************/ + + /** + * Returns the system time. + * Returns the number of milliseconds since the start of the system process. + * Based on ANSI clock() routine. + * @return The number of milliseconds. + */ + virtual GHOST_TUns64 getMilliSeconds() const; + + /*************************************************************************************** + ** Display/window management functionality + ***************************************************************************************/ + + /** + * Returns the number of displays on this system. + * @return The number of displays. + */ + virtual GHOST_TUns8 getNumDisplays() const; + + /** + * Returns the dimensions of the main display on this system. + * @return The dimension of the main display. + */ + virtual void getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const; + + /** + * Create a new window. + * The new window is added to the list of windows managed. + * Never explicitly delete the window, use disposeWindow() instead. + * @param title The name of the window (displayed in the title bar of the window if the OS supports it). + * @param left The coordinate of the left edge of the window. + * @param top The coordinate of the top edge of the window. + * @param width The width the window. + * @param height The height the window. + * @param state The state of the window when opened. + * @param type The type of drawing context installed in this window. + * @param parentWindow Parent (embedder) window + * @return The new window (or 0 if creation failed). + */ + virtual GHOST_IWindow* createWindow( + const STR_String& title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + const bool stereoVisual, + const GHOST_TEmbedderWindowID parentWindow = 0 + ); + + virtual GHOST_TSuccess beginFullScreen( + const GHOST_DisplaySetting& setting, + GHOST_IWindow** window, + const bool stereoVisual + ); + + virtual GHOST_TSuccess endFullScreen( void ); + + /*************************************************************************************** + ** Event management functionality + ***************************************************************************************/ + + /** + * Gets events from the system and stores them in the queue. + * @param waitForEvent Flag to wait for an event (or return immediately). + * @return Indication of the presence of events. + */ + virtual bool processEvents(bool waitForEvent); + + /** + * Handle User request to quit, from Menu bar Quit, and Cmd+Q + * Display alert panel if changes performed since last save + */ + GHOST_TUns8 handleQuitRequest(); + + /*************************************************************************************** + ** Cursor management functionality + ***************************************************************************************/ + + /** + * Returns the current location of the cursor (location in screen coordinates) + * @param x The x-coordinate of the cursor. + * @param y The y-coordinate of the cursor. + * @return Indication of success. + */ + virtual GHOST_TSuccess getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const; + + /** + * Updates the location of the cursor (location in screen coordinates). + * @param x The x-coordinate of the cursor. + * @param y The y-coordinate of the cursor. + * @return Indication of success. + */ + virtual GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const; + + /*************************************************************************************** + ** Access to mouse button and keyboard states. + ***************************************************************************************/ + + /** + * Returns the state of all modifier keys. + * @param keys The state of all modifier keys (true == pressed). + * @return Indication of success. + */ + virtual GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys& keys) const; + + /** + * Returns the state of the mouse buttons (ouside the message queue). + * @param buttons The state of the buttons. + * @return Indication of success. + */ + virtual GHOST_TSuccess getButtons(GHOST_Buttons& buttons) const; + + /** + * Returns Clipboard data + * @param selection Indicate which buffer to return + * @return Returns the selected buffer + */ + virtual GHOST_TUns8* getClipboard(bool selection) const; + + /** + * Puts buffer to system clipboard + * @param buffer The buffer to be copied + * @param selection Indicates which buffer to copy too, only used on X11 + */ + virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const; + + /** + * Handles a window event. Called by GHOST_WindowCocoa window delegate + * @param eventPtr An NSEvent pointer (casted to void* to enable compilation in standard C++) + * @return Indication whether the event was handled. + */ + GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window); + +protected: + /** + * Initializes the system. + * For now, it justs registers the window class (WNDCLASS). + * @return A success value. + */ + virtual GHOST_TSuccess init(); + + /** + * Handles a tablet event. + * @param eventPtr An NSEvent pointer (casted to void* to enable compilation in standard C++) + * @param eventType The type of the event. It needs to be passed separately as it can be either directly in the event type, or as a subtype if combined with a mouse button event + * @return Indication whether the event was handled. + */ + GHOST_TSuccess handleTabletEvent(void *eventPtr, short eventType); + + /** + * Handles a mouse event. + * @param eventPtr An NSEvent pointer (casted to void* to enable compilation in standard C++) + * @return Indication whether the event was handled. + */ + GHOST_TSuccess handleMouseEvent(void *eventPtr); + + /** + * Handles a key event. + * @param eventPtr An NSEvent pointer (casted to void* to enable compilation in standard C++) + * @return Indication whether the event was handled. + */ + GHOST_TSuccess handleKeyEvent(void *eventPtr); + + /** + * Handles all basic Mac application stuff for a mouse down event. + * @param eventPtr An NSEvent pointer (casted to void* to enable compilation in standard C++) + * @return Indication whether the event was handled. + */ + // bool handleMouseDown(void *eventPtr); + + /** + * Handles a Mac menu command. + * @param menuResult A Mac menu/item identifier. + * @return Indication whether the event was handled. + */ + // bool handleMenuCommand(GHOST_TInt32 menuResult); + + /* callback for blender generated events */ +// static OSStatus blendEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData); + + + /** + * Callback for Mac Timer tasks that expire. + * @param tmTask Pointer to the timer task that expired. + */ + //static void s_timerCallback(TMTaskPtr tmTask); + + /** Event handler reference. */ + //EventHandlerRef m_handler; + + /** Start time at initialization. */ + GHOST_TUns64 m_start_time; + + /** Mouse buttons state */ + GHOST_TUns32 m_pressedMouseButtons; + + /** State of the modifiers. */ + GHOST_TUns32 m_modifierMask; + + /** Ignores window size messages (when window is dragged). */ + bool m_ignoreWindowSizedMessages; +}; + +#endif // _GHOST_SYSTEM_COCOA_H_ + diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm new file mode 100644 index 00000000000..b924adeebde --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -0,0 +1,1396 @@ +/**
+ * $Id: GHOST_SystemCocoa.mm 23854 2009-10-15 08:27:31Z damien78 $
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Maarten Gribnau 05/2001
+ * Damien Plisson 09/2009
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include "GHOST_SystemCocoa.h"
+
+#include "GHOST_DisplayManagerCocoa.h"
+#include "GHOST_EventKey.h"
+#include "GHOST_EventButton.h"
+#include "GHOST_EventCursor.h"
+#include "GHOST_EventWheel.h"
+#include "GHOST_EventNDOF.h"
+
+#include "GHOST_TimerManager.h"
+#include "GHOST_TimerTask.h"
+#include "GHOST_WindowManager.h"
+#include "GHOST_WindowCocoa.h"
+#include "GHOST_NDOFManager.h"
+#include "AssertMacros.h"
+
+#pragma mark KeyMap, mouse converters
+
+
+/* Keycodes from Carbon include file */
+/*
+ * Summary:
+ * Virtual keycodes
+ *
+ * Discussion:
+ * These constants are the virtual keycodes defined originally in
+ * Inside Mac Volume V, pg. V-191. They identify physical keys on a
+ * keyboard. Those constants with "ANSI" in the name are labeled
+ * according to the key position on an ANSI-standard US keyboard.
+ * For example, kVK_ANSI_A indicates the virtual keycode for the key
+ * with the letter 'A' in the US keyboard layout. Other keyboard
+ * layouts may have the 'A' key label on a different physical key;
+ * in this case, pressing 'A' will generate a different virtual
+ * keycode.
+ */
+enum {
+ kVK_ANSI_A = 0x00,
+ kVK_ANSI_S = 0x01,
+ kVK_ANSI_D = 0x02,
+ kVK_ANSI_F = 0x03,
+ kVK_ANSI_H = 0x04,
+ kVK_ANSI_G = 0x05,
+ kVK_ANSI_Z = 0x06,
+ kVK_ANSI_X = 0x07,
+ kVK_ANSI_C = 0x08,
+ kVK_ANSI_V = 0x09,
+ kVK_ANSI_B = 0x0B,
+ kVK_ANSI_Q = 0x0C,
+ kVK_ANSI_W = 0x0D,
+ kVK_ANSI_E = 0x0E,
+ kVK_ANSI_R = 0x0F,
+ kVK_ANSI_Y = 0x10,
+ kVK_ANSI_T = 0x11,
+ kVK_ANSI_1 = 0x12,
+ kVK_ANSI_2 = 0x13,
+ kVK_ANSI_3 = 0x14,
+ kVK_ANSI_4 = 0x15,
+ kVK_ANSI_6 = 0x16,
+ kVK_ANSI_5 = 0x17,
+ kVK_ANSI_Equal = 0x18,
+ kVK_ANSI_9 = 0x19,
+ kVK_ANSI_7 = 0x1A,
+ kVK_ANSI_Minus = 0x1B,
+ kVK_ANSI_8 = 0x1C,
+ kVK_ANSI_0 = 0x1D,
+ kVK_ANSI_RightBracket = 0x1E,
+ kVK_ANSI_O = 0x1F,
+ kVK_ANSI_U = 0x20,
+ kVK_ANSI_LeftBracket = 0x21,
+ kVK_ANSI_I = 0x22,
+ kVK_ANSI_P = 0x23,
+ kVK_ANSI_L = 0x25,
+ kVK_ANSI_J = 0x26,
+ kVK_ANSI_Quote = 0x27,
+ kVK_ANSI_K = 0x28,
+ kVK_ANSI_Semicolon = 0x29,
+ kVK_ANSI_Backslash = 0x2A,
+ kVK_ANSI_Comma = 0x2B,
+ kVK_ANSI_Slash = 0x2C,
+ kVK_ANSI_N = 0x2D,
+ kVK_ANSI_M = 0x2E,
+ kVK_ANSI_Period = 0x2F,
+ kVK_ANSI_Grave = 0x32,
+ kVK_ANSI_KeypadDecimal = 0x41,
+ kVK_ANSI_KeypadMultiply = 0x43,
+ kVK_ANSI_KeypadPlus = 0x45,
+ kVK_ANSI_KeypadClear = 0x47,
+ kVK_ANSI_KeypadDivide = 0x4B,
+ kVK_ANSI_KeypadEnter = 0x4C,
+ kVK_ANSI_KeypadMinus = 0x4E,
+ kVK_ANSI_KeypadEquals = 0x51,
+ kVK_ANSI_Keypad0 = 0x52,
+ kVK_ANSI_Keypad1 = 0x53,
+ kVK_ANSI_Keypad2 = 0x54,
+ kVK_ANSI_Keypad3 = 0x55,
+ kVK_ANSI_Keypad4 = 0x56,
+ kVK_ANSI_Keypad5 = 0x57,
+ kVK_ANSI_Keypad6 = 0x58,
+ kVK_ANSI_Keypad7 = 0x59,
+ kVK_ANSI_Keypad8 = 0x5B,
+ kVK_ANSI_Keypad9 = 0x5C
+};
+
+/* keycodes for keys that are independent of keyboard layout*/
+enum {
+ kVK_Return = 0x24,
+ kVK_Tab = 0x30,
+ kVK_Space = 0x31,
+ kVK_Delete = 0x33,
+ kVK_Escape = 0x35,
+ kVK_Command = 0x37,
+ kVK_Shift = 0x38,
+ kVK_CapsLock = 0x39,
+ kVK_Option = 0x3A,
+ kVK_Control = 0x3B,
+ kVK_RightShift = 0x3C,
+ kVK_RightOption = 0x3D,
+ kVK_RightControl = 0x3E,
+ kVK_Function = 0x3F,
+ kVK_F17 = 0x40,
+ kVK_VolumeUp = 0x48,
+ kVK_VolumeDown = 0x49,
+ kVK_Mute = 0x4A,
+ kVK_F18 = 0x4F,
+ kVK_F19 = 0x50,
+ kVK_F20 = 0x5A,
+ kVK_F5 = 0x60,
+ kVK_F6 = 0x61,
+ kVK_F7 = 0x62,
+ kVK_F3 = 0x63,
+ kVK_F8 = 0x64,
+ kVK_F9 = 0x65,
+ kVK_F11 = 0x67,
+ kVK_F13 = 0x69,
+ kVK_F16 = 0x6A,
+ kVK_F14 = 0x6B,
+ kVK_F10 = 0x6D,
+ kVK_F12 = 0x6F,
+ kVK_F15 = 0x71,
+ kVK_Help = 0x72,
+ kVK_Home = 0x73,
+ kVK_PageUp = 0x74,
+ kVK_ForwardDelete = 0x75,
+ kVK_F4 = 0x76,
+ kVK_End = 0x77,
+ kVK_F2 = 0x78,
+ kVK_PageDown = 0x79,
+ kVK_F1 = 0x7A,
+ kVK_LeftArrow = 0x7B,
+ kVK_RightArrow = 0x7C,
+ kVK_DownArrow = 0x7D,
+ kVK_UpArrow = 0x7E
+};
+
+/* ISO keyboards only*/
+enum {
+ kVK_ISO_Section = 0x0A
+};
+
+/* JIS keyboards only*/
+enum {
+ kVK_JIS_Yen = 0x5D,
+ kVK_JIS_Underscore = 0x5E,
+ kVK_JIS_KeypadComma = 0x5F,
+ kVK_JIS_Eisu = 0x66,
+ kVK_JIS_Kana = 0x68
+};
+
+
+static GHOST_TButtonMask convertButton(int button)
+{
+ switch (button) {
+ case 0:
+ return GHOST_kButtonMaskLeft;
+ case 1:
+ return GHOST_kButtonMaskRight;
+ case 2:
+ return GHOST_kButtonMaskMiddle;
+ case 3:
+ return GHOST_kButtonMaskButton4;
+ case 4:
+ return GHOST_kButtonMaskButton5;
+ default:
+ return GHOST_kButtonMaskLeft;
+ }
+}
+
+/**
+ * Converts Mac rawkey codes (same for Cocoa & Carbon)
+ * into GHOST key codes
+ * @param rawCode The raw physical key code
+ * @param recvChar the character ignoring modifiers (except for shift)
+ * @return Ghost key code
+ */
+static GHOST_TKey convertKey(int rawCode, unichar recvChar)
+{
+
+ //printf("\nrecvchar %c 0x%x",recvChar,recvChar);
+ switch (rawCode) {
+ /*Physical keycodes not used due to map changes in int'l keyboards
+ case kVK_ANSI_A: return GHOST_kKeyA;
+ case kVK_ANSI_B: return GHOST_kKeyB;
+ case kVK_ANSI_C: return GHOST_kKeyC;
+ case kVK_ANSI_D: return GHOST_kKeyD;
+ case kVK_ANSI_E: return GHOST_kKeyE;
+ case kVK_ANSI_F: return GHOST_kKeyF;
+ case kVK_ANSI_G: return GHOST_kKeyG;
+ case kVK_ANSI_H: return GHOST_kKeyH;
+ case kVK_ANSI_I: return GHOST_kKeyI;
+ case kVK_ANSI_J: return GHOST_kKeyJ;
+ case kVK_ANSI_K: return GHOST_kKeyK;
+ case kVK_ANSI_L: return GHOST_kKeyL;
+ case kVK_ANSI_M: return GHOST_kKeyM;
+ case kVK_ANSI_N: return GHOST_kKeyN;
+ case kVK_ANSI_O: return GHOST_kKeyO;
+ case kVK_ANSI_P: return GHOST_kKeyP;
+ case kVK_ANSI_Q: return GHOST_kKeyQ;
+ case kVK_ANSI_R: return GHOST_kKeyR;
+ case kVK_ANSI_S: return GHOST_kKeyS;
+ case kVK_ANSI_T: return GHOST_kKeyT;
+ case kVK_ANSI_U: return GHOST_kKeyU;
+ case kVK_ANSI_V: return GHOST_kKeyV;
+ case kVK_ANSI_W: return GHOST_kKeyW;
+ case kVK_ANSI_X: return GHOST_kKeyX;
+ case kVK_ANSI_Y: return GHOST_kKeyY;
+ case kVK_ANSI_Z: return GHOST_kKeyZ;*/
+
+ /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/
+ case kVK_ISO_Section: return GHOST_kKeyUnknown;
+ case kVK_ANSI_1: return GHOST_kKey1;
+ case kVK_ANSI_2: return GHOST_kKey2;
+ case kVK_ANSI_3: return GHOST_kKey3;
+ case kVK_ANSI_4: return GHOST_kKey4;
+ case kVK_ANSI_5: return GHOST_kKey5;
+ case kVK_ANSI_6: return GHOST_kKey6;
+ case kVK_ANSI_7: return GHOST_kKey7;
+ case kVK_ANSI_8: return GHOST_kKey8;
+ case kVK_ANSI_9: return GHOST_kKey9;
+ case kVK_ANSI_0: return GHOST_kKey0;
+
+ case kVK_ANSI_Keypad0: return GHOST_kKeyNumpad0;
+ case kVK_ANSI_Keypad1: return GHOST_kKeyNumpad1;
+ case kVK_ANSI_Keypad2: return GHOST_kKeyNumpad2;
+ case kVK_ANSI_Keypad3: return GHOST_kKeyNumpad3;
+ case kVK_ANSI_Keypad4: return GHOST_kKeyNumpad4;
+ case kVK_ANSI_Keypad5: return GHOST_kKeyNumpad5;
+ case kVK_ANSI_Keypad6: return GHOST_kKeyNumpad6;
+ case kVK_ANSI_Keypad7: return GHOST_kKeyNumpad7;
+ case kVK_ANSI_Keypad8: return GHOST_kKeyNumpad8;
+ case kVK_ANSI_Keypad9: return GHOST_kKeyNumpad9;
+ case kVK_ANSI_KeypadDecimal: return GHOST_kKeyNumpadPeriod;
+ case kVK_ANSI_KeypadEnter: return GHOST_kKeyNumpadEnter;
+ case kVK_ANSI_KeypadPlus: return GHOST_kKeyNumpadPlus;
+ case kVK_ANSI_KeypadMinus: return GHOST_kKeyNumpadMinus;
+ case kVK_ANSI_KeypadMultiply: return GHOST_kKeyNumpadAsterisk;
+ case kVK_ANSI_KeypadDivide: return GHOST_kKeyNumpadSlash;
+ case kVK_ANSI_KeypadClear: return GHOST_kKeyUnknown;
+
+ case kVK_F1: return GHOST_kKeyF1;
+ case kVK_F2: return GHOST_kKeyF2;
+ case kVK_F3: return GHOST_kKeyF3;
+ case kVK_F4: return GHOST_kKeyF4;
+ case kVK_F5: return GHOST_kKeyF5;
+ case kVK_F6: return GHOST_kKeyF6;
+ case kVK_F7: return GHOST_kKeyF7;
+ case kVK_F8: return GHOST_kKeyF8;
+ case kVK_F9: return GHOST_kKeyF9;
+ case kVK_F10: return GHOST_kKeyF10;
+ case kVK_F11: return GHOST_kKeyF11;
+ case kVK_F12: return GHOST_kKeyF12;
+ case kVK_F13: return GHOST_kKeyF13;
+ case kVK_F14: return GHOST_kKeyF14;
+ case kVK_F15: return GHOST_kKeyF15;
+ case kVK_F16: return GHOST_kKeyF16;
+ case kVK_F17: return GHOST_kKeyF17;
+ case kVK_F18: return GHOST_kKeyF18;
+ case kVK_F19: return GHOST_kKeyF19;
+ case kVK_F20: return GHOST_kKeyF20;
+
+ case kVK_UpArrow: return GHOST_kKeyUpArrow;
+ case kVK_DownArrow: return GHOST_kKeyDownArrow;
+ case kVK_LeftArrow: return GHOST_kKeyLeftArrow;
+ case kVK_RightArrow: return GHOST_kKeyRightArrow;
+
+ case kVK_Return: return GHOST_kKeyEnter;
+ case kVK_Delete: return GHOST_kKeyBackSpace;
+ case kVK_ForwardDelete: return GHOST_kKeyDelete;
+ case kVK_Escape: return GHOST_kKeyEsc;
+ case kVK_Tab: return GHOST_kKeyTab;
+ case kVK_Space: return GHOST_kKeySpace;
+
+ case kVK_Home: return GHOST_kKeyHome;
+ case kVK_End: return GHOST_kKeyEnd;
+ case kVK_PageUp: return GHOST_kKeyUpPage;
+ case kVK_PageDown: return GHOST_kKeyDownPage;
+
+ /*case kVK_ANSI_Minus: return GHOST_kKeyMinus;
+ case kVK_ANSI_Equal: return GHOST_kKeyEqual;
+ case kVK_ANSI_Comma: return GHOST_kKeyComma;
+ case kVK_ANSI_Period: return GHOST_kKeyPeriod;
+ case kVK_ANSI_Slash: return GHOST_kKeySlash;
+ case kVK_ANSI_Semicolon: return GHOST_kKeySemicolon;
+ case kVK_ANSI_Quote: return GHOST_kKeyQuote;
+ case kVK_ANSI_Backslash: return GHOST_kKeyBackslash;
+ case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket;
+ case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket;
+ case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave;*/
+
+ case kVK_VolumeUp:
+ case kVK_VolumeDown:
+ case kVK_Mute:
+ return GHOST_kKeyUnknown;
+
+ default:
+ /*Then detect on character value for "remappable" keys in int'l keyboards*/
+ if ((recvChar >= 'A') && (recvChar <= 'Z')) {
+ return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);
+ } else if ((recvChar >= 'a') && (recvChar <= 'z')) {
+ return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);
+ } else
+ switch (recvChar) {
+ case '-': return GHOST_kKeyMinus;
+ case '=': return GHOST_kKeyEqual;
+ case ',': return GHOST_kKeyComma;
+ case '.': return GHOST_kKeyPeriod;
+ case '/': return GHOST_kKeySlash;
+ case ';': return GHOST_kKeySemicolon;
+ case '\'': return GHOST_kKeyQuote;
+ case '\\': return GHOST_kKeyBackslash;
+ case '[': return GHOST_kKeyLeftBracket;
+ case ']': return GHOST_kKeyRightBracket;
+ case '`': return GHOST_kKeyAccentGrave;
+ default:
+ return GHOST_kKeyUnknown;
+ }
+ }
+ return GHOST_kKeyUnknown;
+}
+
+/* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
+ * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
+ * I am not sure how international this works...
+ * For cross-platform convention, we'll use the Latin ascii set instead.
+ * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
+ *
+ */
+static unsigned char convertRomanToLatin(unsigned char ascii)
+{
+
+ if(ascii<128) return ascii;
+
+ switch(ascii) {
+ case 128: return 142;
+ case 129: return 143;
+ case 130: return 128;
+ case 131: return 201;
+ case 132: return 209;
+ case 133: return 214;
+ case 134: return 220;
+ case 135: return 225;
+ case 136: return 224;
+ case 137: return 226;
+ case 138: return 228;
+ case 139: return 227;
+ case 140: return 229;
+ case 141: return 231;
+ case 142: return 233;
+ case 143: return 232;
+ case 144: return 234;
+ case 145: return 235;
+ case 146: return 237;
+ case 147: return 236;
+ case 148: return 238;
+ case 149: return 239;
+ case 150: return 241;
+ case 151: return 243;
+ case 152: return 242;
+ case 153: return 244;
+ case 154: return 246;
+ case 155: return 245;
+ case 156: return 250;
+ case 157: return 249;
+ case 158: return 251;
+ case 159: return 252;
+ case 160: return 0;
+ case 161: return 176;
+ case 162: return 162;
+ case 163: return 163;
+ case 164: return 167;
+ case 165: return 183;
+ case 166: return 182;
+ case 167: return 223;
+ case 168: return 174;
+ case 169: return 169;
+ case 170: return 174;
+ case 171: return 180;
+ case 172: return 168;
+ case 173: return 0;
+ case 174: return 198;
+ case 175: return 216;
+ case 176: return 0;
+ case 177: return 177;
+ case 178: return 0;
+ case 179: return 0;
+ case 180: return 165;
+ case 181: return 181;
+ case 182: return 0;
+ case 183: return 0;
+ case 184: return 215;
+ case 185: return 0;
+ case 186: return 0;
+ case 187: return 170;
+ case 188: return 186;
+ case 189: return 0;
+ case 190: return 230;
+ case 191: return 248;
+ case 192: return 191;
+ case 193: return 161;
+ case 194: return 172;
+ case 195: return 0;
+ case 196: return 0;
+ case 197: return 0;
+ case 198: return 0;
+ case 199: return 171;
+ case 200: return 187;
+ case 201: return 201;
+ case 202: return 0;
+ case 203: return 192;
+ case 204: return 195;
+ case 205: return 213;
+ case 206: return 0;
+ case 207: return 0;
+ case 208: return 0;
+ case 209: return 0;
+ case 210: return 0;
+
+ case 214: return 247;
+
+ case 229: return 194;
+ case 230: return 202;
+ case 231: return 193;
+ case 232: return 203;
+ case 233: return 200;
+ case 234: return 205;
+ case 235: return 206;
+ case 236: return 207;
+ case 237: return 204;
+ case 238: return 211;
+ case 239: return 212;
+ case 240: return 0;
+ case 241: return 210;
+ case 242: return 218;
+ case 243: return 219;
+ case 244: return 217;
+ case 245: return 0;
+ case 246: return 0;
+ case 247: return 0;
+ case 248: return 0;
+ case 249: return 0;
+ case 250: return 0;
+
+
+ default: return 0;
+ }
+
+}
+
+#define FIRSTFILEBUFLG 512
+static bool g_hasFirstFile = false;
+static char g_firstFileBuf[512];
+
+//TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
+extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) {
+ if (g_hasFirstFile) {
+ strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
+ buf[FIRSTFILEBUFLG - 1] = '\0';
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+#pragma mark Cocoa objects
+
+/**
+ * CocoaAppDelegate
+ * ObjC object to capture applicationShouldTerminate, and send quit event
+ **/
+@interface CocoaAppDelegate : NSObject {
+ GHOST_SystemCocoa *systemCocoa;
+}
+-(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+- (void)applicationWillTerminate:(NSNotification *)aNotification;
+@end
+
+@implementation CocoaAppDelegate : NSObject
+-(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
+{
+ systemCocoa = sysCocoa;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
+ //Note that Cmd+Q is already handled by keyhandler
+ if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
+ return NSTerminateCancel;//NSTerminateNow;
+ else
+ return NSTerminateCancel;
+}
+
+// To avoid cancelling a log off process, we must use Cocoa termination process
+// And this function is the only chance to perform clean up
+// So WM_exit needs to be called directly, as the event loop will never run before termination
+- (void)applicationWillTerminate:(NSNotification *)aNotification
+{
+ /*G.afbreek = 0; //Let Cocoa perform the termination at the end
+ WM_exit(C);*/
+}
+@end
+
+
+
+#pragma mark initialization/finalization
+
+
+GHOST_SystemCocoa::GHOST_SystemCocoa()
+{
+ m_modifierMask =0;
+ m_pressedMouseButtons =0;
+ m_displayManager = new GHOST_DisplayManagerCocoa ();
+ GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
+ m_displayManager->initialize();
+
+ //NSEvent timeStamp is given in system uptime, state start date is boot time
+ //FIXME : replace by Cocoa equivalent
+ int mib[2];
+ struct timeval boottime;
+ size_t len;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ len = sizeof(struct timeval);
+
+ sysctl(mib, 2, &boottime, &len, NULL, 0);
+ m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
+
+ m_ignoreWindowSizedMessages = false;
+}
+
+GHOST_SystemCocoa::~GHOST_SystemCocoa()
+{
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::init()
+{
+
+ GHOST_TSuccess success = GHOST_System::init();
+ if (success) {
+ //ProcessSerialNumber psn;
+
+ //Carbon stuff to move window & menu to foreground
+ /*if (!GetCurrentProcess(&psn)) {
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ SetFrontProcess(&psn);
+ }*/
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if (NSApp == nil) {
+ [NSApplication sharedApplication];
+
+ if ([NSApp mainMenu] == nil) {
+ NSMenu *mainMenubar = [[NSMenu alloc] init];
+ NSMenuItem *menuItem;
+ NSMenu *windowMenu;
+ NSMenu *appMenu;
+
+ //Create the application menu
+ appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
+
+ [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+ [appMenu addItem:[NSMenuItem separatorItem]];
+
+ menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
+
+ menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
+
+ [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
+ [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
+
+ menuItem = [[NSMenuItem alloc] init];
+ [menuItem setSubmenu:appMenu];
+
+ [mainMenubar addItem:menuItem];
+ [menuItem release];
+ [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
+ [appMenu release];
+
+ //Create the window menu
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
+
+ [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
+
+ menuItem = [[NSMenuItem alloc] init];
+ [menuItem setSubmenu:windowMenu];
+
+ [mainMenubar addItem:menuItem];
+ [menuItem release];
+
+ [NSApp setMainMenu:mainMenubar];
+ [NSApp setWindowsMenu:windowMenu];
+ [windowMenu release];
+ }
+ [NSApp finishLaunching];
+ }
+ if ([NSApp delegate] == nil) {
+ CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
+ [appDelegate setSystemCocoa:this];
+ [NSApp setDelegate:appDelegate];
+ }
+
+ [pool drain];
+ }
+ return success;
+}
+
+
+#pragma mark window management
+
+GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
+{
+ //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
+ int mib[2];
+ struct timeval boottime;
+ size_t len;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ len = sizeof(struct timeval);
+
+ sysctl(mib, 2, &boottime, &len, NULL, 0);
+
+ return ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
+}
+
+
+GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
+{
+ //Note that OS X supports monitor hot plug
+ // We do not support multiple monitors at the moment
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ GHOST_TUns8 count = [[NSScreen screens] count];
+
+ [pool drain];
+ return count;
+}
+
+
+void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ //Get visible frame, that is frame excluding dock and top menu bar
+ NSRect frame = [[NSScreen mainScreen] visibleFrame];
+
+ //Returns max window contents (excluding title bar...)
+ NSRect contentRect = [NSWindow contentRectForFrameRect:frame
+ styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
+
+ width = contentRect.size.width;
+ height = contentRect.size.height;
+
+ [pool drain];
+}
+
+
+GHOST_IWindow* GHOST_SystemCocoa::createWindow(
+ const STR_String& title,
+ GHOST_TInt32 left,
+ GHOST_TInt32 top,
+ GHOST_TUns32 width,
+ GHOST_TUns32 height,
+ GHOST_TWindowState state,
+ GHOST_TDrawingContextType type,
+ bool stereoVisual,
+ const GHOST_TEmbedderWindowID parentWindow
+)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ GHOST_IWindow* window = 0;
+
+ //Get the available rect for including window contents
+ NSRect frame = [[NSScreen mainScreen] visibleFrame];
+ NSRect contentRect = [NSWindow contentRectForFrameRect:frame
+ styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
+
+ //Ensures window top left is inside this available rect
+ left = left > contentRect.origin.x ? left : contentRect.origin.x;
+ top = top > contentRect.origin.y ? top : contentRect.origin.y;
+
+ window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type);
+
+ if (window) {
+ if (window->getValid()) {
+ // Store the pointer to the window
+ GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
+ m_windowManager->addWindow(window);
+ m_windowManager->setActiveWindow(window);
+ //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
+ pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
+ pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
+
+ }
+ else {
+ GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
+ delete window;
+ window = 0;
+ }
+ }
+ else {
+ GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
+ }
+ [pool drain];
+ return window;
+}
+
+GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
+{
+ GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
+
+ *window = currentWindow;
+
+ return currentWindow->setState(GHOST_kWindowStateFullScreen);
+}
+
+GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
+{
+ GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
+
+ return currentWindow->setState(GHOST_kWindowStateNormal);
+}
+
+
+
+
+GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
+{
+ NSPoint mouseLoc = [NSEvent mouseLocation];
+
+ // Returns the mouse location in screen coordinates
+ x = (GHOST_TInt32)mouseLoc.x;
+ y = (GHOST_TInt32)mouseLoc.y;
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
+{
+ float xf=(float)x, yf=(float)y;
+
+ //Quartz Display Services uses the old coordinates (top left origin)
+ yf = [[NSScreen mainScreen] frame].size.height -yf;
+
+ //CGAssociateMouseAndMouseCursorPosition(false);
+ CGWarpMouseCursorPosition(CGPointMake(xf, yf));
+ //CGAssociateMouseAndMouseCursorPosition(true);
+
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
+{
+ unsigned int modifiers = [[NSApp currentEvent] modifierFlags];
+ //Direct query to modifierFlags can be used in 10.6
+
+ keys.set(GHOST_kModifierKeyCommand, (modifiers & NSCommandKeyMask) ? true : false);
+ keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & NSAlternateKeyMask) ? true : false);
+ keys.set(GHOST_kModifierKeyLeftShift, (modifiers & NSShiftKeyMask) ? true : false);
+ keys.set(GHOST_kModifierKeyLeftControl, (modifiers & NSControlKeyMask) ? true : false);
+
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
+{
+ buttons.clear();
+ buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
+ buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
+ buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
+ buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
+ buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
+ return GHOST_kSuccess;
+}
+
+
+
+#pragma mark Event handlers
+
+/**
+ * The event queue polling function
+ */
+bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
+{
+ bool anyProcessed = false;
+ NSEvent *event;
+
+ // SetMouseCoalescingEnabled(false, NULL);
+ //TODO : implement timer ??
+
+ /*do {
+ GHOST_TimerManager* timerMgr = getTimerManager();
+
+ if (waitForEvent) {
+ GHOST_TUns64 next = timerMgr->nextFireTime();
+ double timeOut;
+
+ if (next == GHOST_kFireTimeNever) {
+ timeOut = kEventDurationForever;
+ } else {
+ timeOut = (double)(next - getMilliSeconds())/1000.0;
+ if (timeOut < 0.0)
+ timeOut = 0.0;
+ }
+
+ ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
+ }
+
+ if (timerMgr->fireTimers(getMilliSeconds())) {
+ anyProcessed = true;
+ }
+
+ if (getFullScreen()) {
+ // Check if the full-screen window is dirty
+ GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
+ if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
+ anyProcessed = true;
+ }
+ }*/
+
+ do {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event==nil) {
+ [pool drain];
+ break;
+ }
+
+ anyProcessed = true;
+
+ switch ([event type]) {
+ case NSKeyDown:
+ case NSKeyUp:
+ case NSFlagsChanged:
+ handleKeyEvent(event);
+
+ /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */
+ /* if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
+ [NSApp sendEvent:event];
+ }*/
+ break;
+
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSMouseMoved:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSScrollWheel:
+ case NSOtherMouseDown:
+ case NSOtherMouseUp:
+ case NSOtherMouseDragged:
+ handleMouseEvent(event);
+ break;
+
+ case NSTabletPoint:
+ case NSTabletProximity:
+ handleTabletEvent(event,[event type]);
+ break;
+
+ /* Trackpad features, will need OS X 10.6 for implementation
+ case NSEventTypeGesture:
+ case NSEventTypeMagnify:
+ case NSEventTypeSwipe:
+ case NSEventTypeRotate:
+ case NSEventTypeBeginGesture:
+ case NSEventTypeEndGesture:
+ break; */
+
+ /*Unused events
+ NSMouseEntered = 8,
+ NSMouseExited = 9,
+ NSAppKitDefined = 13,
+ NSSystemDefined = 14,
+ NSApplicationDefined = 15,
+ NSPeriodic = 16,
+ NSCursorUpdate = 17,*/
+
+ default:
+ break;
+ }
+ //Resend event to NSApp to ensure Mac wide events are handled
+ [NSApp sendEvent:event];
+ [pool drain];
+ } while (event!= nil);
+ //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
+
+
+
+ return anyProcessed;
+}
+
+//Note: called from NSWindow delegate
+GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
+{
+ if (!validWindow(window)) {
+ return GHOST_kFailure;
+ }
+ switch(eventType)
+ {
+ case GHOST_kEventWindowClose:
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
+ break;
+ case GHOST_kEventWindowActivate:
+ m_windowManager->setActiveWindow(window);
+ window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
+ break;
+ case GHOST_kEventWindowDeactivate:
+ m_windowManager->setWindowInactive(window);
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
+ break;
+ case GHOST_kEventWindowUpdate:
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
+ break;
+ case GHOST_kEventWindowSize:
+ if (!m_ignoreWindowSizedMessages)
+ {
+ window->updateDrawingContext();
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
+ }
+ break;
+ default:
+ return GHOST_kFailure;
+ break;
+ }
+ return GHOST_kSuccess;
+}
+
+GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
+{
+ //Check open windows if some changes are not saved
+ if (m_windowManager->getAnyModifiedState())
+ {
+ int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved. Do you really want to quit ?",
+ @"Cancel", @"Quit anyway", nil);
+ if (shouldQuit == NSAlertAlternateReturn)
+ {
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
+ return GHOST_kExitNow;
+ }
+ }
+ else {
+ pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
+ return GHOST_kExitNow;
+ }
+
+ return GHOST_kExitCancel;
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
+{
+ NSEvent *event = (NSEvent *)eventPtr;
+ GHOST_IWindow* window = m_windowManager->getActiveWindow();
+ GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
+
+ switch (eventType) {
+ case NSTabletPoint:
+ ct.Pressure = [event tangentialPressure];
+ ct.Xtilt = [event tilt].x;
+ ct.Ytilt = [event tilt].y;
+ break;
+
+ case NSTabletProximity:
+ ct.Pressure = 0;
+ ct.Xtilt = 0;
+ ct.Ytilt = 0;
+ if ([event isEnteringProximity])
+ {
+ //pointer is entering tablet area proximity
+ switch ([event pointingDeviceType]) {
+ case NSPenPointingDevice:
+ ct.Active = GHOST_kTabletModeStylus;
+ break;
+ case NSEraserPointingDevice:
+ ct.Active = GHOST_kTabletModeEraser;
+ break;
+ case NSCursorPointingDevice:
+ case NSUnknownPointingDevice:
+ default:
+ ct.Active = GHOST_kTabletModeNone;
+ break;
+ }
+ } else {
+ // pointer is leaving - return to mouse
+ ct.Active = GHOST_kTabletModeNone;
+ }
+ break;
+
+ default:
+ GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
+ return GHOST_kFailure;
+ break;
+ }
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
+{
+ NSEvent *event = (NSEvent *)eventPtr;
+ GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
+
+ if (!window) {
+ return GHOST_kFailure;
+ }
+
+ switch ([event type])
+ {
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
+ //Handle tablet events combined with mouse events
+ switch ([event subtype]) {
+ case NX_SUBTYPE_TABLET_POINT:
+ handleTabletEvent(eventPtr, NSTabletPoint);
+ break;
+ case NX_SUBTYPE_TABLET_PROXIMITY:
+ handleTabletEvent(eventPtr, NSTabletProximity);
+ break;
+ default:
+ //No tablet event included : do nothing
+ break;
+ }
+ break;
+
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
+ //Handle tablet events combined with mouse events
+ switch ([event subtype]) {
+ case NX_SUBTYPE_TABLET_POINT:
+ handleTabletEvent(eventPtr, NSTabletPoint);
+ break;
+ case NX_SUBTYPE_TABLET_PROXIMITY:
+ handleTabletEvent(eventPtr, NSTabletProximity);
+ break;
+ default:
+ //No tablet event included : do nothing
+ break;
+ }
+ break;
+
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ //Handle tablet events combined with mouse events
+ switch ([event subtype]) {
+ case NX_SUBTYPE_TABLET_POINT:
+ handleTabletEvent(eventPtr, NSTabletPoint);
+ break;
+ case NX_SUBTYPE_TABLET_PROXIMITY:
+ handleTabletEvent(eventPtr, NSTabletProximity);
+ break;
+ default:
+ //No tablet event included : do nothing
+ break;
+ }
+ case NSMouseMoved:
+ {
+ if(window->getCursorWarp()) {
+ GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
+
+ window->getCursorWarpPos(x_warp, y_warp);
+
+ window->getCursorWarpAccum(x_accum, y_accum);
+ x_accum += [event deltaX];
+ y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
+ window->setCursorWarpAccum(x_accum, y_accum);
+
+ pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
+ }
+ else { //Normal cursor operation: send mouse position in window
+ NSPoint mousePos = [event locationInWindow];
+ pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
+ window->setCursorWarpAccum(0, 0); //Mouse motion occured between two cursor warps, so we can reset the delta counter
+ }
+ break;
+ }
+
+ case NSScrollWheel:
+ {
+ GHOST_TInt32 delta;
+
+ double deltaF = [event deltaY];
+ if (deltaF == 0.0) break; //discard trackpad delta=0 events
+
+ delta = deltaF > 0.0 ? 1 : -1;
+ pushEvent(new GHOST_EventWheel([event timestamp], window, delta));
+ }
+ break;
+
+ default:
+ return GHOST_kFailure;
+ break;
+ }
+
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
+{
+ NSEvent *event = (NSEvent *)eventPtr;
+ GHOST_IWindow* window = m_windowManager->getActiveWindow();
+ unsigned int modifiers;
+ NSString *characters;
+ GHOST_TKey keyCode;
+ unsigned char ascii;
+
+ /* Can happen, very rarely - seems to only be when command-H makes
+ * the window go away and we still get an HKey up.
+ */
+ if (!window) {
+ printf("\nW failure");
+ return GHOST_kFailure;
+ }
+
+ switch ([event type]) {
+ case NSKeyDown:
+ case NSKeyUp:
+ characters = [event characters];
+ if ([characters length]) { //Check for dead keys
+ keyCode = convertKey([event keyCode],
+ [[event charactersIgnoringModifiers] characterAtIndex:0]);
+ ascii= convertRomanToLatin((char)[characters characterAtIndex:0]);
+ } else {
+ keyCode = convertKey([event keyCode],0);
+ ascii= 0;
+ }
+
+
+ if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
+ break; //Cmd-Q is directly handled by Cocoa
+
+ if ([event type] == NSKeyDown) {
+ pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyDown, window, keyCode, ascii) );
+ //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);
+ } else {
+ pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyUp, window, keyCode, ascii) );
+ }
+ break;
+
+ case NSFlagsChanged:
+ modifiers = [event modifierFlags];
+ if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
+ pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
+ }
+ if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
+ pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
+ }
+ if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
+ pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
+ }
+ if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
+ pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
+ }
+
+ m_modifierMask = modifiers;
+ break;
+
+ default:
+ return GHOST_kFailure;
+ break;
+ }
+
+ return GHOST_kSuccess;
+}
+
+
+
+#pragma mark Clipboard get/set
+
+GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
+{
+ GHOST_TUns8 * temp_buff;
+ size_t pastedTextSize;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
+
+ if (pasteBoard == nil) {
+ [pool drain];
+ return NULL;
+ }
+
+ NSArray *supportedTypes =
+ [NSArray arrayWithObjects: @"public.utf8-plain-text", nil];
+
+ NSString *bestType = [[NSPasteboard generalPasteboard]
+ availableTypeFromArray:supportedTypes];
+
+ if (bestType == nil) {
+ [pool drain];
+ return NULL;
+ }
+
+ NSString * textPasted = [pasteBoard stringForType:@"public.utf8-plain-text"];
+
+ if (textPasted == nil) {
+ [pool drain];
+ return NULL;
+ }
+
+ pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+
+ temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1);
+
+ if (temp_buff == NULL) {
+ [pool drain];
+ return NULL;
+ }
+
+ strncpy((char*)temp_buff, [textPasted UTF8String], pastedTextSize);
+
+ temp_buff[pastedTextSize] = '\0';
+
+ [pool drain];
+
+ if(temp_buff) {
+ return temp_buff;
+ } else {
+ return NULL;
+ }
+}
+
+void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
+{
+ NSString *textToCopy;
+
+ if(selection) {return;} // for copying the selection, used on X11
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
+
+ if (pasteBoard == nil) {
+ [pool drain];
+ return;
+ }
+
+ NSArray *supportedTypes = [NSArray arrayWithObject:@"public.utf8-plain-text"];
+
+ [pasteBoard declareTypes:supportedTypes owner:nil];
+
+ textToCopy = [NSString stringWithUTF8String:buffer];
+
+ [pasteBoard setString:textToCopy forType:@"public.utf8-plain-text"];
+
+ [pool drain];
+}
+
+#pragma mark Carbon stuff to remove
+
+#ifdef WITH_CARBON
+
+
+OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
+{
+ //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
+
+ return noErr;
+}
+
+OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
+{
+ //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
+ AEDescList docs;
+ SInt32 ndocs;
+ OSErr err;
+
+ err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
+ if (err != noErr) return err;
+
+ err = AECountItems(&docs, &ndocs);
+ if (err==noErr) {
+ int i;
+
+ for (i=0; i<ndocs; i++) {
+ FSSpec fss;
+ AEKeyword kwd;
+ DescType actType;
+ Size actSize;
+
+ err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
+ if (err!=noErr)
+ break;
+
+ if (i==0) {
+ FSRef fsref;
+
+ if (FSpMakeFSRef(&fss, &fsref)!=noErr)
+ break;
+ if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
+ break;
+
+ g_hasFirstFile = true;
+ }
+ }
+ }
+
+ AEDisposeDesc(&docs);
+
+ return err;
+}
+
+OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
+{
+ //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
+
+ return noErr;
+}
+
+OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
+{
+ GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
+
+ sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
+
+ return noErr;
+}
+#endif
\ No newline at end of file diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h new file mode 100644 index 00000000000..4037ebafc64 --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -0,0 +1,295 @@ +/** + * $Id: GHOST_WindowCocoa.h 23789 2009-10-12 16:51:36Z damien78 $ + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +/** + * @file GHOST_WindowCocoa.h + * Declaration of GHOST_WindowCocoa class. + */ + +#ifndef _GHOST_WINDOW_COCOA_H_ +#define _GHOST_WINDOW_COCOA_H_ + +#ifndef __APPLE__ +#error Apple OSX only! +#endif // __APPLE__ + +#include "GHOST_Window.h" +#include "STR_String.h" + +class GHOST_SystemCocoa; + +/** + * Window on Mac OSX/Cocoa. + * Carbon windows have a size widget in the lower right corner of the window. + * To force it to be visible, the height of the client rectangle is reduced so + * that applications do not draw in that area. GHOST will manage that area + * which is called the gutter. + * When OpenGL contexts are active, GHOST will use AGL_BUFFER_RECT to prevent + * OpenGL drawing outside the reduced client rectangle. + * @author Maarten Gribnau + * @date May 23, 2001 + */ +class GHOST_WindowCocoa : public GHOST_Window { +public: + /** + * Constructor. + * Creates a new window and opens it. + * To check if the window was created properly, use the getValid() method. + * @param systemCocoa The associated system class to forward events to + * @param title The text shown in the title bar of the window. + * @param left The coordinate of the left edge of the window. + * @param top The coordinate of the top edge of the window. + * @param width The width the window. + * @param height The height the window. + * @param state The state the window is initially opened with. + * @param type The type of drawing context installed in this window. + * @param stereoVisual Stereo visual for quad buffered stereo. + */ + GHOST_WindowCocoa( + GHOST_SystemCocoa *systemCocoa, + const STR_String& title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type = GHOST_kDrawingContextTypeNone, + const bool stereoVisual = false + ); + + /** + * Destructor. + * Closes the window and disposes resources allocated. + */ + virtual ~GHOST_WindowCocoa(); + + /** + * Returns indication as to whether the window is valid. + * @return The validity of the window. + */ + virtual bool getValid() const; + + /** + * Sets the title displayed in the title bar. + * @param title The title to display in the title bar. + */ + virtual void setTitle(const STR_String& title); + + /** + * Returns the title displayed in the title bar. + * @param title The title displayed in the title bar. + */ + virtual void getTitle(STR_String& title) const; + + /** + * Returns the window rectangle dimensions. + * The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen. + * @param bounds The bounding rectangle of the window. + */ + virtual void getWindowBounds(GHOST_Rect& bounds) const; + + /** + * Returns the client rectangle dimensions. + * The left and top members of the rectangle are always zero. + * @param bounds The bounding rectangle of the cleient area of the window. + */ + virtual void getClientBounds(GHOST_Rect& bounds) const; + + /** + * Resizes client rectangle width. + * @param width The new width of the client area of the window. + */ + virtual GHOST_TSuccess setClientWidth(GHOST_TUns32 width); + + /** + * Resizes client rectangle height. + * @param height The new height of the client area of the window. + */ + virtual GHOST_TSuccess setClientHeight(GHOST_TUns32 height); + + /** + * Resizes client rectangle. + * @param width The new width of the client area of the window. + * @param height The new height of the client area of the window. + */ + virtual GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height); + + /** + * Returns the state of the window (normal, minimized, maximized). + * @return The state of the window. + */ + virtual GHOST_TWindowState getState() const; + + /** + * Sets the window "modified" status, indicating unsaved changes + * @param isUnsavedChanges Unsaved changes or not + * @return Indication of success. + */ + virtual GHOST_TSuccess setModifiedState(bool isUnsavedChanges); + + /** + * Converts a point in screen coordinates to client rectangle coordinates + * @param inX The x-coordinate on the screen. + * @param inY The y-coordinate on the screen. + * @param outX The x-coordinate in the client rectangle. + * @param outY The y-coordinate in the client rectangle. + */ + virtual void screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + + /** + * Converts a point in screen coordinates to client rectangle coordinates + * @param inX The x-coordinate in the client rectangle. + * @param inY The y-coordinate in the client rectangle. + * @param outX The x-coordinate on the screen. + * @param outY The y-coordinate on the screen. + */ + virtual void clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const; + + /** + * Sets the state of the window (normal, minimized, maximized). + * @param state The state of the window. + * @return Indication of success. + */ + virtual GHOST_TSuccess setState(GHOST_TWindowState state); + + /** + * Sets the order of the window (bottom, top). + * @param order The order of the window. + * @return Indication of success. + */ + virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order); + + /** + * Swaps front and back buffers of a window. + * @return A boolean success indicator. + */ + virtual GHOST_TSuccess swapBuffers(); + + /** + * Updates the drawing context of this window. Needed + * whenever the window is changed. + * @return Indication of success. + */ + GHOST_TSuccess updateDrawingContext(); + + /** + * Activates the drawing context of this window. + * @return A boolean success indicator. + */ + virtual GHOST_TSuccess activateDrawingContext(); + + virtual void loadCursor(bool visible, GHOST_TStandardCursor cursor) const; + + + const GHOST_TabletData* GetTabletData() + { return &m_tablet; } + + GHOST_TabletData& GetCocoaTabletData() + { return m_tablet; } +protected: + /** + * Tries to install a rendering context in this window. + * @param type The type of rendering context installed. + * @return Indication as to whether installation has succeeded. + */ + virtual GHOST_TSuccess installDrawingContext(GHOST_TDrawingContextType type); + + /** + * Removes the current drawing context. + * @return Indication as to whether removal has succeeded. + */ + virtual GHOST_TSuccess removeDrawingContext(); + + /** + * Invalidates the contents of this window. + * @return Indication of success. + */ + virtual GHOST_TSuccess invalidate(); + + /** + * Sets the cursor visibility on the window using + * native window system calls. + */ + virtual GHOST_TSuccess setWindowCursorVisibility(bool visible); + + /** + * Sets the cursor warp accumulator. Overriden for workaround due to Cocoa next event after cursor set giving delta values non zero + */ + inline virtual bool setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y); + + /** + * Sets the cursor grab on the window using + * native window system calls. + * @param warp Only used when grab is enabled, hides the mouse and allows gragging outside the screen. + */ + virtual GHOST_TSuccess setWindowCursorGrab(bool grab, bool warp, bool restore); + + /** + * Sets the cursor shape on the window using + * native window system calls. + */ + virtual GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape); + + /** + * Sets the cursor shape on the window using + * native window system calls. + */ + virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, + int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color); + + virtual GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY); + + /** The window containing the OpenGL view */ + NSWindow *m_window; + + /** The openGL view */ + NSOpenGLView *m_openGLView; + + /** The opgnGL drawing context */ + NSOpenGLContext *m_openGLContext; + + /** The mother SystemCocoa class to send events */ + GHOST_SystemCocoa *m_systemCocoa; + + /** The first created OpenGL context (for sharing display lists) */ + static NSOpenGLContext *s_firstOpenGLcontext; + + NSCursor* m_customCursor; + + GHOST_TabletData m_tablet; + + /** + * The width/height of the size rectangle in the lower right corner of a + * Mac/Carbon window. This is also the height of the gutter area. + */ +#ifdef GHOST_DRAW_CARBON_GUTTER + static const GHOST_TInt32 s_sizeRectSize; +#endif // GHOST_DRAW_CARBON_GUTTER +}; + +#endif // _GHOST_WINDOW_COCOA_H_ + diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm new file mode 100644 index 00000000000..a2b146c1e33 --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -0,0 +1,1028 @@ +/**
+ * $Id: GHOST_WindowCocoa.mm 23873 2009-10-15 20:09:50Z damien78 $
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Maarten Gribnau 05/2001
+ Damien Plisson 10/2009
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <Cocoa/Cocoa.h>
+
+#ifndef MAC_OS_X_VERSION_10_6
+//Use of the SetSystemUIMode function (64bit compatible)
+#include <Carbon/Carbon.h>
+#endif
+
+#include "GHOST_WindowCocoa.h"
+#include "GHOST_SystemCocoa.h"
+#include "GHOST_Debug.h"
+
+
+// Pixel Format Attributes for the windowed NSOpenGLContext
+static NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[] =
+{
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFAAccelerated,
+ //NSOpenGLPFAAllowOfflineRenderers, // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
+ NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute) 32,
+ (NSOpenGLPixelFormatAttribute) 0
+};
+
+#pragma mark Cocoa window delegate object
+
+@interface CocoaWindowDelegate : NSObject
+{
+ GHOST_SystemCocoa *systemCocoa;
+ GHOST_WindowCocoa *associatedWindow;
+}
+
+- (void)setSystemAndWindowCocoa:(const GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
+- (void)windowWillClose:(NSNotification *)notification;
+- (void)windowDidBecomeKey:(NSNotification *)notification;
+- (void)windowDidResignKey:(NSNotification *)notification;
+- (void)windowDidUpdate:(NSNotification *)notification;
+- (void)windowDidResize:(NSNotification *)notification;
+@end
+
+@implementation CocoaWindowDelegate : NSObject
+- (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
+{
+ systemCocoa = sysCocoa;
+ associatedWindow = winCocoa;
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification
+{
+ systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
+}
+
+- (void)windowDidResignKey:(NSNotification *)notification
+{
+ //The window is no more key when its own view becomes fullscreen
+ //but ghost doesn't know the view/window difference, so hide this fact
+ if (associatedWindow->getState() != GHOST_kWindowStateFullScreen)
+ systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
+}
+
+- (void)windowDidUpdate:(NSNotification *)notification
+{
+ systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+ systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
+}
+@end
+
+#pragma mark NSWindow subclass
+//We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
+@interface CocoaWindow: NSWindow
+{
+
+}
+-(BOOL)canBecomeKeyWindow;
+
+@end
+@implementation CocoaWindow
+
+-(BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
+@end
+
+
+
+#pragma mark NSOpenGLView subclass
+//We need to subclass it in order to give Cocoa the feeling key events are trapped
+@interface CocoaOpenGLView : NSOpenGLView
+{
+
+}
+@end
+@implementation CocoaOpenGLView
+
+- (BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
+//The trick to prevent Cocoa from complaining (beeping)
+- (void)keyDown:(NSEvent *)theEvent
+{}
+
+- (BOOL)isOpaque
+{
+ return YES;
+}
+
+@end
+
+
+#pragma mark initialization / finalization
+
+NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
+
+GHOST_WindowCocoa::GHOST_WindowCocoa(
+ GHOST_SystemCocoa *systemCocoa,
+ const STR_String& title,
+ GHOST_TInt32 left,
+ GHOST_TInt32 top,
+ GHOST_TUns32 width,
+ GHOST_TUns32 height,
+ GHOST_TWindowState state,
+ GHOST_TDrawingContextType type,
+ const bool stereoVisual
+) :
+ GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone),
+ m_customCursor(0)
+{
+ m_systemCocoa = systemCocoa;
+ m_fullScreen = false;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+
+ //Creates the window
+ NSRect rect;
+
+ rect.origin.x = left;
+ rect.origin.y = top;
+ rect.size.width = width;
+ rect.size.height = height;
+
+ m_window = [[CocoaWindow alloc] initWithContentRect:rect
+ styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
+ backing:NSBackingStoreBuffered defer:NO];
+ if (m_window == nil) {
+ [pool drain];
+ return;
+ }
+
+ setTitle(title);
+
+
+ //Creates the OpenGL View inside the window
+ NSOpenGLPixelFormat *pixelFormat =
+ [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
+
+ m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
+ pixelFormat:pixelFormat];
+
+ [pixelFormat release];
+
+ m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
+
+ [m_window setContentView:m_openGLView];
+ [m_window setInitialFirstResponder:m_openGLView];
+
+ [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
+
+ [m_window makeKeyAndOrderFront:nil];
+
+ setDrawingContextType(type);
+ updateDrawingContext();
+ activateDrawingContext();
+
+ m_tablet.Active = GHOST_kTabletModeNone;
+
+ CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
+ [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
+ [m_window setDelegate:windowDelegate];
+
+ [m_window setAcceptsMouseMovedEvents:YES];
+
+ if (state == GHOST_kWindowStateFullScreen)
+ setState(GHOST_kWindowStateFullScreen);
+
+ [pool drain];
+}
+
+
+GHOST_WindowCocoa::~GHOST_WindowCocoa()
+{
+ if (m_customCursor) delete m_customCursor;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLView release];
+
+ if (m_window) {
+ [m_window close];
+ [[m_window delegate] release];
+ [m_window release];
+ m_window = nil;
+ }
+
+ //Check for other blender opened windows and make the frontmost key
+ NSArray *windowsList = [NSApp orderedWindows];
+ if ([windowsList count]) {
+ [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
+ }
+ [pool drain];
+}
+
+#pragma mark accessors
+
+bool GHOST_WindowCocoa::getValid() const
+{
+ bool valid;
+ if (!m_fullScreen) {
+ valid = (m_window != 0); //&& ::IsValidWindowPtr(m_windowRef);
+ }
+ else {
+ valid = true;
+ }
+ return valid;
+}
+
+
+void GHOST_WindowCocoa::setTitle(const STR_String& title)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
+
+ //Set associated file if applicable
+ if ([windowTitle hasPrefix:@"Blender"])
+ {
+ NSRange fileStrRange;
+ NSString *associatedFileName;
+ int len;
+
+ fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
+ len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
+
+ if (len >0)
+ {
+ fileStrRange.length = len;
+ associatedFileName = [windowTitle substringWithRange:fileStrRange];
+ @try {
+ [m_window setRepresentedFilename:associatedFileName];
+ }
+ @catch (NSException * e) {
+ printf("\nInvalid file path given in window title");
+ }
+ [m_window setTitle:[associatedFileName lastPathComponent]];
+ }
+ else {
+ [m_window setTitle:windowTitle];
+ [m_window setRepresentedFilename:@""];
+ }
+
+ } else {
+ [m_window setTitle:windowTitle];
+ [m_window setRepresentedFilename:@""];
+ }
+
+
+ [windowTitle release];
+ [pool drain];
+}
+
+
+void GHOST_WindowCocoa::getTitle(STR_String& title) const
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *windowTitle = [m_window title];
+
+ if (windowTitle != nil) {
+ title = [windowTitle UTF8String];
+ }
+
+ [pool drain];
+}
+
+
+void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
+{
+ NSRect rect;
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSRect screenSize = [[m_window screen] visibleFrame];
+
+ rect = [m_window frame];
+
+ bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
+ bounds.m_l = rect.origin.x -screenSize.origin.x;
+ bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
+ bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
+
+ [pool drain];
+}
+
+
+void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
+{
+ NSRect rect;
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (!m_fullScreen)
+ {
+ NSRect screenSize = [[m_window screen] visibleFrame];
+
+ //Max window contents as screen size (excluding title bar...)
+ NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
+ styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
+
+ rect = [m_window contentRectForFrameRect:[m_window frame]];
+
+ bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
+ bounds.m_l = rect.origin.x -contentRect.origin.x;
+ bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
+ bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
+ }
+ else {
+ NSRect screenSize = [[m_window screen] frame];
+
+ bounds.m_b = screenSize.origin.y + screenSize.size.height;
+ bounds.m_l = screenSize.origin.x;
+ bounds.m_r = screenSize.origin.x + screenSize.size.width;
+ bounds.m_t = screenSize.origin.y;
+ }
+ [pool drain];
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
+ GHOST_Rect cBnds, wBnds;
+ getClientBounds(cBnds);
+ if (((GHOST_TUns32)cBnds.getWidth()) != width) {
+ NSSize size;
+ size.width=width;
+ size.height=cBnds.getHeight();
+ [m_window setContentSize:size];
+ }
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
+ GHOST_Rect cBnds, wBnds;
+ getClientBounds(cBnds);
+ if (((GHOST_TUns32)cBnds.getHeight()) != height) {
+ NSSize size;
+ size.width=cBnds.getWidth();
+ size.height=height;
+ [m_window setContentSize:size];
+ }
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
+ GHOST_Rect cBnds, wBnds;
+ getClientBounds(cBnds);
+ if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
+ (((GHOST_TUns32)cBnds.getHeight()) != height)) {
+ NSSize size;
+ size.width=width;
+ size.height=height;
+ [m_window setContentSize:size];
+ }
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TWindowState GHOST_WindowCocoa::getState() const
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ GHOST_TWindowState state;
+ if (m_fullScreen) {
+ state = GHOST_kWindowStateFullScreen;
+ }
+ else if ([m_window isMiniaturized]) {
+ state = GHOST_kWindowStateMinimized;
+ }
+ else if ([m_window isZoomed]) {
+ state = GHOST_kWindowStateMaximized;
+ }
+ else {
+ state = GHOST_kWindowStateNormal;
+ }
+ [pool drain];
+ return state;
+}
+
+
+void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
+
+ NSPoint screenCoord;
+ NSPoint baseCoord;
+
+ screenCoord.x = inX;
+ screenCoord.y = inY;
+
+ baseCoord = [m_window convertScreenToBase:screenCoord];
+
+ outX = baseCoord.x;
+ outY = baseCoord.y;
+}
+
+
+void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
+
+ NSPoint screenCoord;
+ NSPoint baseCoord;
+
+ baseCoord.x = inX;
+ baseCoord.y = inY;
+
+ screenCoord = [m_window convertBaseToScreen:baseCoord];
+
+ outX = screenCoord.x;
+ outY = screenCoord.y;
+}
+
+/**
+ * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
+ * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
+ * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
+ */
+GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
+ switch (state) {
+ case GHOST_kWindowStateMinimized:
+ [m_window miniaturize:nil];
+ break;
+ case GHOST_kWindowStateMaximized:
+ [m_window zoom:nil];
+ break;
+
+ case GHOST_kWindowStateFullScreen:
+ if (!m_fullScreen)
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ //This status change needs to be done before Cocoa call to enter fullscreen mode
+ //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
+ m_fullScreen = true;
+
+#ifdef MAC_OS_X_VERSION_10_6
+ //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
+ //Hide menu & dock if needed
+ if ([[m_window screen] isEqual:[NSScreen mainScreen]])
+ {
+ [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
+ }
+ //Make window borderless and enlarge it
+ [m_window setStyleMask:NSBorderlessWindowMask];
+ [m_window setFrame:[[m_window screen] frame] display:YES];
+#else
+ //With 10.5, we need to create a new window to change its style to borderless
+ //Hide menu & dock if needed
+ if ([[m_window screen] isEqual:[NSScreen mainScreen]])
+ {
+ //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
+ //One of the very few 64bit compatible Carbon function
+ SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
+ }
+ //Create a fullscreen borderless window
+ CocoaWindow *tmpWindow = [[CocoaWindow alloc]
+ initWithContentRect:[[m_window screen] frame]
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:YES];
+ //Copy current window parameters
+ [tmpWindow setTitle:[m_window title]];
+ [tmpWindow setRepresentedFilename:[m_window representedFilename]];
+ [tmpWindow setReleasedWhenClosed:NO];
+ [tmpWindow setAcceptsMouseMovedEvents:YES];
+ [tmpWindow setDelegate:[m_window delegate]];
+
+ //Assign the openGL view to the new window
+ [tmpWindow setContentView:m_openGLView];
+
+ //Show the new window
+ [tmpWindow makeKeyAndOrderFront:nil];
+ //Close and release old window
+ [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
+ [m_window close];
+ [m_window release];
+ m_window = tmpWindow;
+#endif
+
+ //Tell WM of view new size
+ m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
+
+ [pool drain];
+ }
+ break;
+ case GHOST_kWindowStateNormal:
+ default:
+ if (m_fullScreen)
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ m_fullScreen = false;
+
+ //Exit fullscreen
+#ifdef MAC_OS_X_VERSION_10_6
+ //Show again menu & dock if needed
+ if ([[m_window screen] isEqual:[NSScreen mainScreen]])
+ {
+ [NSApp setPresentationOptions:NSApplicationPresentationDefault];
+ }
+ //Make window normal and resize it
+ [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
+ [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
+#else
+ //With 10.5, we need to create a new window to change its style to borderless
+ //Show menu & dock if needed
+ if ([[m_window screen] isEqual:[NSScreen mainScreen]])
+ {
+ //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
+ SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
+ }
+ //Create a fullscreen borderless window
+ CocoaWindow *tmpWindow = [[CocoaWindow alloc]
+ initWithContentRect:[[m_window screen] frame]
+ styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
+ backing:NSBackingStoreBuffered
+ defer:YES];
+ //Copy current window parameters
+ [tmpWindow setTitle:[m_window title]];
+ [tmpWindow setRepresentedFilename:[m_window representedFilename]];
+ [tmpWindow setReleasedWhenClosed:NO];
+ [tmpWindow setAcceptsMouseMovedEvents:YES];
+ [tmpWindow setDelegate:[m_window delegate]];
+
+ //Assign the openGL view to the new window
+ [tmpWindow setContentView:m_openGLView];
+
+ //Show the new window
+ [tmpWindow makeKeyAndOrderFront:nil];
+ //Close and release old window
+ [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
+ [m_window close];
+ [m_window release];
+ m_window = tmpWindow;
+#endif
+
+ //Tell WM of view new size
+ m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
+
+ [pool drain];
+ }
+ else if ([m_window isMiniaturized])
+ [m_window deminiaturize:nil];
+ else if ([m_window isZoomed])
+ [m_window zoom:nil];
+ break;
+ }
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [m_window setDocumentEdited:isUnsavedChanges];
+
+ [pool drain];
+ return GHOST_Window::setModifiedState(isUnsavedChanges);
+}
+
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
+ if (order == GHOST_kWindowOrderTop) {
+ [m_window orderFront:nil];
+ }
+ else {
+ [m_window orderBack:nil];
+ }
+ return GHOST_kSuccess;
+}
+
+#pragma mark Drawing context
+
+/*#define WAIT_FOR_VSYNC 1*/
+
+GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
+{
+ if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext flushBuffer];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ }
+ return GHOST_kFailure;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
+{
+ if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext update];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ }
+ return GHOST_kFailure;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
+{
+ if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
+ if (m_openGLContext != nil) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLContext makeCurrentContext];
+ [pool drain];
+ return GHOST_kSuccess;
+ }
+ }
+ return GHOST_kFailure;
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
+{
+ GHOST_TSuccess success = GHOST_kFailure;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSOpenGLPixelFormat *pixelFormat;
+ NSOpenGLContext *tmpOpenGLContext;
+
+ switch (type) {
+ case GHOST_kDrawingContextTypeOpenGL:
+ if (!getValid()) break;
+
+ pixelFormat = [m_openGLView pixelFormat];
+ tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
+ shareContext:s_firstOpenGLcontext];
+ if (tmpOpenGLContext == nil) {
+ success = GHOST_kFailure;
+ break;
+ }
+
+ if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
+#ifdef WAIT_FOR_VSYNC
+ /* wait for vsync, to avoid tearing artifacts */
+ [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
+#endif
+ [m_openGLView setOpenGLContext:tmpOpenGLContext];
+ [tmpOpenGLContext setView:m_openGLView];
+
+ m_openGLContext = tmpOpenGLContext;
+ break;
+
+ case GHOST_kDrawingContextTypeNone:
+ success = GHOST_kSuccess;
+ break;
+
+ default:
+ break;
+ }
+ [pool drain];
+ return success;
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ switch (m_drawingContextType) {
+ case GHOST_kDrawingContextTypeOpenGL:
+ if (m_openGLContext)
+ {
+ [m_openGLView clearGLContext];
+ if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
+ m_openGLContext = nil;
+ }
+ [pool drain];
+ return GHOST_kSuccess;
+ case GHOST_kDrawingContextTypeNone:
+ [pool drain];
+ return GHOST_kSuccess;
+ break;
+ default:
+ [pool drain];
+ return GHOST_kFailure;
+ }
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::invalidate()
+{
+ GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [m_openGLView setNeedsDisplay:YES];
+ [pool drain];
+ return GHOST_kSuccess;
+}
+
+#pragma mark Cursor handling
+
+void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
+{
+ static bool systemCursorVisible = true;
+
+ NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
+
+ NSCursor *tmpCursor =nil;
+
+ if (visible != systemCursorVisible) {
+ if (visible) {
+ [NSCursor unhide];
+ systemCursorVisible = true;
+ }
+ else {
+ [NSCursor hide];
+ systemCursorVisible = false;
+ }
+ }
+
+ if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
+ tmpCursor = m_customCursor;
+ } else {
+ switch (cursor) {
+ case GHOST_kStandardCursorDestroy:
+ tmpCursor = [NSCursor disappearingItemCursor];
+ break;
+ case GHOST_kStandardCursorText:
+ tmpCursor = [NSCursor IBeamCursor];
+ break;
+ case GHOST_kStandardCursorCrosshair:
+ tmpCursor = [NSCursor crosshairCursor];
+ break;
+ case GHOST_kStandardCursorUpDown:
+ tmpCursor = [NSCursor resizeUpDownCursor];
+ break;
+ case GHOST_kStandardCursorLeftRight:
+ tmpCursor = [NSCursor resizeLeftRightCursor];
+ break;
+ case GHOST_kStandardCursorTopSide:
+ tmpCursor = [NSCursor resizeUpCursor];
+ break;
+ case GHOST_kStandardCursorBottomSide:
+ tmpCursor = [NSCursor resizeDownCursor];
+ break;
+ case GHOST_kStandardCursorLeftSide:
+ tmpCursor = [NSCursor resizeLeftCursor];
+ break;
+ case GHOST_kStandardCursorRightSide:
+ tmpCursor = [NSCursor resizeRightCursor];
+ break;
+ case GHOST_kStandardCursorRightArrow:
+ case GHOST_kStandardCursorInfo:
+ case GHOST_kStandardCursorLeftArrow:
+ case GHOST_kStandardCursorHelp:
+ case GHOST_kStandardCursorCycle:
+ case GHOST_kStandardCursorSpray:
+ case GHOST_kStandardCursorWait:
+ case GHOST_kStandardCursorTopLeftCorner:
+ case GHOST_kStandardCursorTopRightCorner:
+ case GHOST_kStandardCursorBottomRightCorner:
+ case GHOST_kStandardCursorBottomLeftCorner:
+ case GHOST_kStandardCursorDefault:
+ default:
+ tmpCursor = [NSCursor arrowCursor];
+ break;
+ };
+ }
+ [tmpCursor set];
+ [pool drain];
+}
+
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
+{
+ if ([m_window isVisible]) {
+ loadCursor(visible, getCursorShape());
+ }
+
+ return GHOST_kSuccess;
+}
+
+
+//Override this method to provide set feature even if not in warp
+inline bool GHOST_WindowCocoa::setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y)
+{
+ m_cursorWarpAccumPos[0]= x;
+ m_cursorWarpAccumPos[1]= y;
+
+ return GHOST_kSuccess;
+}
+
+
+GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(bool grab, bool warp, bool restore)
+{
+ if (grab)
+ {
+ //No need to perform grab without warp as it is always on in OS X
+ if(warp) {
+ GHOST_TInt32 x_old,y_old;
+
+ m_cursorWarp= true;
+ m_systemCocoa->getCursorPosition(x_old,y_old);
+ screenToClient(x_old, y_old, m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
+ //Warp position is stored in client (window base) coordinates
+ setWindowCursorVisibility(false);
+ return CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
+ }
+ }
+ else {
+ if(m_cursorWarp)
+ {/* are we exiting warp */
+ setWindowCursorVisibility(true);
+ /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
+ if(restore) {
+ GHOST_Rect bounds;
+ GHOST_TInt32 x_new, y_new, x_cur, y_cur;
+
+ getClientBounds(bounds);
+ x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
+ y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
+
+ if(x_new < 0) x_new = 0;
+ if(y_new < 0) y_new = 0;
+ if(x_new > bounds.getWidth()) x_new = bounds.getWidth();
+ if(y_new > bounds.getHeight()) y_new = bounds.getHeight();
+
+ //get/set cursor position works in screen coordinates
+ clientToScreen(x_new, y_new, x_cur, y_cur);
+ m_systemCocoa->setCursorPosition(x_cur, y_cur);
+
+ //As Cocoa will give as first deltaX,deltaY this change in cursor position, we need to compensate for it
+ //Issue appearing in case of two transform operations conducted w/o mouse motion in between
+ x_new=m_cursorWarpAccumPos[0];
+ y_new=m_cursorWarpAccumPos[1];
+ setCursorWarpAccum(-x_new, -y_new);
+ }
+ else {
+ GHOST_TInt32 x_new, y_new;
+ //get/set cursor position works in screen coordinates
+ clientToScreen(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1], x_new, y_new);
+ m_systemCocoa->setCursorPosition(x_new, y_new);
+ setCursorWarpAccum(0, 0);
+ }
+
+ m_cursorWarp= false;
+ return CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
+ }
+ }
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
+{
+ if (m_customCursor) {
+ [m_customCursor release];
+ m_customCursor = nil;
+ }
+
+ if ([m_window isVisible]) {
+ loadCursor(getCursorVisibility(), shape);
+ }
+
+ return GHOST_kSuccess;
+}
+
+/** Reverse the bits in a GHOST_TUns8
+static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
+{
+ ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
+ ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
+ ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
+ return ch;
+}
+*/
+
+
+/** Reverse the bits in a GHOST_TUns16 */
+static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
+{
+ shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
+ shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
+ shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
+ shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
+ return shrt;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
+ int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
+{
+ int y,nbUns16;
+ NSPoint hotSpotPoint;
+ NSBitmapImageRep *cursorImageRep;
+ NSImage *cursorImage;
+ NSSize imSize;
+ GHOST_TUns16 *cursorBitmap;
+
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (m_customCursor) {
+ [m_customCursor release];
+ m_customCursor = nil;
+ }
+
+
+ cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
+ pixelsWide:sizex
+ pixelsHigh:sizey
+ bitsPerSample:1
+ samplesPerPixel:2
+ hasAlpha:YES
+ isPlanar:YES
+ colorSpaceName:NSDeviceBlackColorSpace
+ bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
+ bitsPerPixel:1];
+
+
+ cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
+ nbUns16 = [cursorImageRep bytesPerPlane]/2;
+
+ for (y=0; y<nbUns16; y++) {
+#if !defined(__LITTLE_ENDIAN__)
+ cursorBitmap[y] = uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
+ cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
+#else
+ cursorBitmap[y] = uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
+ cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
+#endif
+
+ }
+
+
+ imSize.width = sizex;
+ imSize.height= sizey;
+ cursorImage = [[NSImage alloc] initWithSize:imSize];
+ [cursorImage addRepresentation:cursorImageRep];
+
+ hotSpotPoint.x = hotX;
+ hotSpotPoint.y = hotY;
+
+ //foreground and background color parameter is not handled for now (10.6)
+ m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
+ hotSpot:hotSpotPoint];
+
+ [cursorImageRep release];
+ [cursorImage release];
+
+ if ([m_window isVisible]) {
+ loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
+ }
+ [pool drain];
+ return GHOST_kSuccess;
+}
+
+GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
+ GHOST_TUns8 mask[16][2], int hotX, int hotY)
+{
+ return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
+}
diff --git a/source/blender/editors/mesh/bmesh_tools.c b/source/blender/editors/mesh/bmesh_tools.c index 9b31a06edb3..ad1654d2f3a 100644 --- a/source/blender/editors/mesh/bmesh_tools.c +++ b/source/blender/editors/mesh/bmesh_tools.c @@ -2990,9 +2990,10 @@ static int mesh_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) then rip the two adjacent edges in the vert fan.*/ if (em->bm->totvertsel == 1 && em->bm->totedgesel == 0 && em->bm->totfacesel == 0) { /*find selected vert*/ - BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) + BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) { if (BM_TestHFlag(v, BM_SELECT)) break; + } /*this should be impossible, but sanity checks are a good thing*/ if (!v) diff --git a/source/blender/gpu/gpu_buffers.h b/source/blender/gpu/gpu_buffers.h new file mode 100644 index 00000000000..7377046ee63 --- /dev/null +++ b/source/blender/gpu/gpu_buffers.h @@ -0,0 +1,157 @@ +/** + * $Id: gpu_buffers.h 23816 2009-10-13 19:02:30Z nicholasbishop $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __GPU_BUFFERS_H__ +#define __GPU_BUFFERS_H__ + +#define MAX_FREE_GPU_BUFFERS 8 + +#ifdef _DEBUG +/*#define DEBUG_VBO(X) printf(X)*/ +#define DEBUG_VBO(X) +#else +#define DEBUG_VBO(X) +#endif + +struct DerivedMesh; + +/* V - vertex, N - normal, T - uv, C - color + F - float, UB - unsigned byte */ +#define GPU_BUFFER_INTER_V3F 1 +#define GPU_BUFFER_INTER_N3F 2 +#define GPU_BUFFER_INTER_T2F 3 +#define GPU_BUFFER_INTER_C3UB 4 +#define GPU_BUFFER_INTER_C4UB 5 +#define GPU_BUFFER_INTER_END -1 + +typedef struct GPUBuffer +{ + int size; /* in bytes */ + void *pointer; /* used with vertex arrays */ + unsigned int id; /* used with vertex buffer objects */ +} GPUBuffer; + +typedef struct GPUBufferPool +{ + int size; /* number of allocated buffers stored */ + int start; /* for a queue like structure */ + /* when running out of space for storing buffers, + the last one used will be thrown away */ + + GPUBuffer* buffers[MAX_FREE_GPU_BUFFERS]; +} GPUBufferPool; + +typedef struct GPUBufferMaterial +{ + int start; /* at which vertex in the buffer the material starts */ + int end; /* at which vertex it ends */ + char mat_nr; +} GPUBufferMaterial; + +typedef struct IndexLink { + int element; + struct IndexLink *next; +} IndexLink; + +typedef struct GPUDrawObject +{ + GPUBuffer *vertices; + GPUBuffer *normals; + GPUBuffer *uv; + GPUBuffer *colors; + GPUBuffer *edges; + GPUBuffer *uvedges; + + int *faceRemap; /* at what index was the face originally in DerivedMesh */ + IndexLink *indices; /* given an index, find all elements using it */ + IndexLink *indexMem; /* for faster memory allocation/freeing */ + int indexMemUsage; /* how many are already allocated */ + int colType; + + GPUBufferMaterial *materials; + + int nmaterials; + int nelements; /* (number of faces) * 3 */ + int nlooseverts; + int nedges; + int nindices; + int legacy; /* if there was a failure allocating some buffer, use old rendering code */ + +} GPUDrawObject; + +typedef struct GPUAttrib +{ + int index; + int size; + int type; +} GPUAttrib; + +GPUBufferPool *GPU_buffer_pool_new(); +void GPU_buffer_pool_free( GPUBufferPool *pool ); /* TODO: Find a place where to call this function on exit */ + +GPUBuffer *GPU_buffer_alloc( int size, GPUBufferPool *pool ); +void GPU_buffer_free( GPUBuffer *buffer, GPUBufferPool *pool ); + +GPUDrawObject *GPU_drawobject_new( struct DerivedMesh *dm ); +void GPU_drawobject_free( struct DerivedMesh *dm ); + +/* called before drawing */ +void GPU_vertex_setup( struct DerivedMesh *dm ); +void GPU_normal_setup( struct DerivedMesh *dm ); +void GPU_uv_setup( struct DerivedMesh *dm ); +void GPU_color_setup( struct DerivedMesh *dm ); +void GPU_edge_setup( struct DerivedMesh *dm ); /* does not mix with other data */ +void GPU_uvedge_setup( struct DerivedMesh *dm ); +void GPU_interleaved_setup( GPUBuffer *buffer, int data[] ); +int GPU_attrib_element_size( GPUAttrib data[], int numdata ); +void GPU_interleaved_attrib_setup( GPUBuffer *buffer, GPUAttrib data[], int numdata ); + +/* can't lock more than one buffer at once */ +void *GPU_buffer_lock( GPUBuffer *buffer ); +void *GPU_buffer_lock_stream( GPUBuffer *buffer ); +void GPU_buffer_unlock( GPUBuffer *buffer ); + +/* upload three unsigned chars, representing RGB colors, for each vertex. Resets dm->drawObject->colType to -1 */ +void GPU_color3_upload( struct DerivedMesh *dm, unsigned char *data ); +/* upload four unsigned chars, representing RGBA colors, for each vertex. Resets dm->drawObject->colType to -1 */ +void GPU_color4_upload( struct DerivedMesh *dm, unsigned char *data ); +/* switch color rendering on=1/off=0 */ +void GPU_color_switch( int mode ); + +void GPU_buffer_draw_elements( GPUBuffer *elements, unsigned int mode, int start, int count ); + +/* called after drawing */ +void GPU_buffer_unbind(); + +int GPU_buffer_legacy( struct DerivedMesh *dm ); + +#endif diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c new file mode 100644 index 00000000000..6b465b64590 --- /dev/null +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -0,0 +1,1262 @@ +/** + * $Id: gpu_buffers.c 23816 2009-10-13 19:02:30Z nicholasbishop $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> + +#include "GL/glew.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" + +#include "DNA_meshdata_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_utildefines.h" + +#include "DNA_userdef_types.h" + +#include "gpu_buffers.h" + +#define GPU_BUFFER_VERTEX_STATE 1 +#define GPU_BUFFER_NORMAL_STATE 2 +#define GPU_BUFFER_TEXCOORD_STATE 4 +#define GPU_BUFFER_COLOR_STATE 8 +#define GPU_BUFFER_ELEMENT_STATE 16 + +#define MAX_GPU_ATTRIB_DATA 32 + +/* -1 - undefined, 0 - vertex arrays, 1 - VBOs */ +int useVBOs = -1; +GPUBufferPool *globalPool = 0; +int GLStates = 0; +GPUAttrib attribData[MAX_GPU_ATTRIB_DATA] = { { -1, 0, 0 } }; + +GPUBufferPool *GPU_buffer_pool_new() +{ + GPUBufferPool *pool; + + DEBUG_VBO("GPU_buffer_pool_new\n"); + + if( useVBOs < 0 ) { + if( GL_ARB_vertex_buffer_object ) { + DEBUG_VBO( "Vertex Buffer Objects supported.\n" ); + useVBOs = 1; + } + else { + DEBUG_VBO( "Vertex Buffer Objects NOT supported.\n" ); + useVBOs = 0; + } + } + + pool = MEM_callocN(sizeof(GPUBufferPool), "GPU_buffer_pool_new"); + + return pool; +} + +void GPU_buffer_pool_free(GPUBufferPool *pool) +{ + int i; + + DEBUG_VBO("GPU_buffer_pool_free\n"); + + if( pool == 0 ) + pool = globalPool; + if( pool == 0 ) + return; + + while( pool->start < 0 ) + pool->start += MAX_FREE_GPU_BUFFERS; + + for( i = 0; i < pool->size; i++ ) { + if( useVBOs ) { + glDeleteBuffersARB( 1, &pool->buffers[(pool->start+i)%MAX_FREE_GPU_BUFFERS]->id ); + } + else { + MEM_freeN( pool->buffers[(pool->start+i)%MAX_FREE_GPU_BUFFERS]->pointer ); + } + MEM_freeN(pool->buffers[(pool->start+i)%MAX_FREE_GPU_BUFFERS]); + } + MEM_freeN(pool); +} + +void GPU_buffer_pool_remove( int index, GPUBufferPool *pool ) +{ + int i; + + DEBUG_VBO("GPU_buffer_pool_remove\n"); + + while( pool->start < 0 ) + pool->start += MAX_FREE_GPU_BUFFERS; + for( i = index; i < pool->size-1; i++ ) { + pool->buffers[(pool->start+i)%MAX_FREE_GPU_BUFFERS] = pool->buffers[(pool->start+i+1)%MAX_FREE_GPU_BUFFERS]; + } + pool->size--; +} + +void GPU_buffer_pool_delete_last( GPUBufferPool *pool ) +{ + int last; + + DEBUG_VBO("GPU_buffer_pool_delete_last\n"); + + if( pool->size == 0 ) + return; + + last = pool->start+pool->size-1; + while( last < 0 ) + last += MAX_FREE_GPU_BUFFERS; + last = (last+MAX_FREE_GPU_BUFFERS)%MAX_FREE_GPU_BUFFERS; + + if( useVBOs ) { + glDeleteBuffersARB(1,&pool->buffers[last]->id); + MEM_freeN( pool->buffers[last] ); + } + else { + MEM_freeN( pool->buffers[last]->pointer ); + MEM_freeN( pool->buffers[last] ); + } + pool->size--; +} + +GPUBuffer *GPU_buffer_alloc( int size, GPUBufferPool *pool ) +{ + char buffer[60]; + int i; + int cursize; + GPUBuffer *allocated; + int bestfit = -1; + + DEBUG_VBO("GPU_buffer_alloc\n"); + + if( pool == 0 ) { + if( globalPool == 0 ) + globalPool = GPU_buffer_pool_new(); + pool = globalPool; + } + + while( pool->start < 0 ) + pool->start += MAX_FREE_GPU_BUFFERS; + + for( i = 0; i < pool->size; i++ ) { + int actuali = (pool->start+i)%MAX_FREE_GPU_BUFFERS; + cursize = pool->buffers[actuali]->size; + if( cursize == size ) { + allocated = pool->buffers[actuali]; + GPU_buffer_pool_remove(i,pool); + DEBUG_VBO("free buffer of exact size found\n"); + return allocated; + } + /* smaller buffers won't fit data and buffers at least twice as big are a waste of memory */ + else if( cursize > size && size > cursize/2 ) { + /* is it closer to the required size than the last appropriate buffer found. try to save memory */ + if( bestfit == -1 || pool->buffers[(pool->start+bestfit)%MAX_FREE_GPU_BUFFERS]->size > cursize ) { + bestfit = i; + } + } + } + if( bestfit == -1 ) { + DEBUG_VBO("allocating a new buffer\n"); + + allocated = MEM_mallocN(sizeof(GPUBuffer), "GPU_buffer_alloc"); + allocated->size = size; + if( useVBOs == 1 ) { + glGenBuffersARB( 1, &allocated->id ); + glBindBufferARB( GL_ARRAY_BUFFER_ARB, allocated->id ); + glBufferDataARB( GL_ARRAY_BUFFER_ARB, size, 0, GL_STATIC_DRAW_ARB ); + glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); + } + else { + allocated->pointer = MEM_mallocN(size, "GPU_buffer_alloc_vertexarray"); + while( allocated->pointer == 0 && pool->size > 0 ) { + GPU_buffer_pool_delete_last(pool); + allocated->pointer = MEM_mallocN(size, "GPU_buffer_alloc_vertexarray"); + } + if( allocated->pointer == 0 && pool->size == 0 ) { + return 0; + } + } + } + else { + sprintf(buffer,"free buffer found. Wasted %d bytes\n", pool->buffers[(pool->start+bestfit)%MAX_FREE_GPU_BUFFERS]->size-size); + DEBUG_VBO(buffer); + + allocated = pool->buffers[(pool->start+bestfit)%MAX_FREE_GPU_BUFFERS]; + GPU_buffer_pool_remove(bestfit,pool); + } + return allocated; +} + +void GPU_buffer_free( GPUBuffer *buffer, GPUBufferPool *pool ) +{ + int place; + + DEBUG_VBO("GPU_buffer_free\n"); + + if( buffer == 0 ) + return; + if( pool == 0 ) + pool = globalPool; + if( pool == 0 ) + globalPool = GPU_buffer_pool_new(); + + while( pool->start < 0 ) + pool->start += MAX_FREE_GPU_BUFFERS; + place = (pool->start-1 + MAX_FREE_GPU_BUFFERS)%MAX_FREE_GPU_BUFFERS; + + /* free the last used buffer in the queue if no more space */ + if( pool->size == MAX_FREE_GPU_BUFFERS ) { + GPU_buffer_pool_delete_last( pool ); + } + + pool->size++; + pool->start = place; + pool->buffers[place] = buffer; +} + +GPUDrawObject *GPU_drawobject_new( DerivedMesh *dm ) +{ + GPUDrawObject *object; + MVert *mvert; + MFace *mface; + int numverts[32768]; /* material number is an 16-bit short so there's at most 32768 materials */ + int redir[32768]; /* material number is an 16-bit short so there's at most 32768 materials */ + int *index; + int i; + int curmat, curverts, numfaces; + + DEBUG_VBO("GPU_drawobject_new\n"); + + object = MEM_callocN(sizeof(GPUDrawObject),"GPU_drawobject_new_object"); + object->nindices = dm->getNumVerts(dm); + object->indices = MEM_mallocN(sizeof(IndexLink)*object->nindices, "GPU_drawobject_new_indices"); + object->nedges = dm->getNumEdges(dm); + + for( i = 0; i < object->nindices; i++ ) { + object->indices[i].element = -1; + object->indices[i].next = 0; + } + /*object->legacy = 1;*/ + memset(numverts,0,sizeof(int)*32768); + + mvert = dm->getVertArray(dm); + mface = dm->getTessFaceArray(dm); + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + if( mface[i].v4 ) + numverts[mface[i].mat_nr+16383] += 6; /* split every quad into two triangles */ + else + numverts[mface[i].mat_nr+16383] += 3; + } + + for( i = 0; i < 32768; i++ ) { + if( numverts[i] > 0 ) { + object->nmaterials++; + object->nelements += numverts[i]; + } + } + object->materials = MEM_mallocN(sizeof(GPUBufferMaterial)*object->nmaterials,"GPU_drawobject_new_materials"); + index = MEM_mallocN(sizeof(int)*object->nmaterials,"GPU_drawobject_new_index"); + + curmat = curverts = 0; + for( i = 0; i < 32768; i++ ) { + if( numverts[i] > 0 ) { + object->materials[curmat].mat_nr = i-16383; + object->materials[curmat].start = curverts; + index[curmat] = curverts/3; + object->materials[curmat].end = curverts+numverts[i]; + curverts += numverts[i]; + curmat++; + } + } + object->faceRemap = MEM_mallocN(sizeof(int)*object->nelements/3,"GPU_drawobject_new_faceRemap"); + for( i = 0; i < object->nmaterials; i++ ) { + redir[object->materials[i].mat_nr+16383] = i; /* material number -> material index */ + } + + object->indexMem = MEM_callocN(sizeof(IndexLink)*object->nelements,"GPU_drawobject_new_indexMem"); + object->indexMemUsage = 0; + +#define ADDLINK( INDEX, ACTUAL ) \ + if( object->indices[INDEX].element == -1 ) { \ + object->indices[INDEX].element = ACTUAL; \ + } else { \ + IndexLink *lnk = &object->indices[INDEX], *lnk2; \ + lnk2 = &object->indexMem[object->indexMemUsage]; \ + lnk2->element = ACTUAL; \ + SWAP(IndexLink, *lnk, *lnk2); \ + lnk->next = lnk2; \ + object->indexMemUsage++; \ + } + + for( i=0; i < numfaces; i++ ) { + int curInd = index[redir[mface[i].mat_nr+16383]]; + object->faceRemap[curInd] = i; + ADDLINK( mface[i].v1, curInd*3 ); + ADDLINK( mface[i].v2, curInd*3+1 ); + ADDLINK( mface[i].v3, curInd*3+2 ); + if( mface[i].v4 ) { + object->faceRemap[curInd+1] = i; + ADDLINK( mface[i].v3, curInd*3+3 ); + ADDLINK( mface[i].v4, curInd*3+4 ); + ADDLINK( mface[i].v1, curInd*3+5 ); + + index[redir[mface[i].mat_nr+16383]]+=2; + } + else { + index[redir[mface[i].mat_nr+16383]]++; + } + } + + for( i = 0; i < object->nindices; i++ ) { + if( object->indices[i].element == -1 ) { + object->indices[i].element = object->nelements + object->nlooseverts; + object->nlooseverts++; + } + } +#undef ADDLINK + + MEM_freeN(index); + return object; +} + +void GPU_drawobject_free( DerivedMesh *dm ) +{ + GPUDrawObject *object; + + DEBUG_VBO("GPU_drawobject_free\n"); + + if( dm == 0 ) + return; + object = dm->drawObject; + if( object == 0 ) + return; + + MEM_freeN(object->materials); + MEM_freeN(object->faceRemap); + MEM_freeN(object->indices); + MEM_freeN(object->indexMem); + GPU_buffer_free( object->vertices, globalPool ); + GPU_buffer_free( object->normals, globalPool ); + GPU_buffer_free( object->uv, globalPool ); + GPU_buffer_free( object->colors, globalPool ); + GPU_buffer_free( object->edges, globalPool ); + GPU_buffer_free( object->uvedges, globalPool ); + + MEM_freeN(object); + dm->drawObject = 0; +} + +GPUBuffer *GPU_buffer_setup( DerivedMesh *dm, GPUDrawObject *object, int size, GLenum target, void *user, void (*copy_f)(DerivedMesh *, float *, int *, int *, void *) ) +{ + GPUBuffer *buffer; + float *varray; + int redir[32768]; + int *index; + int i; + int success; + GLboolean uploaded; + + DEBUG_VBO("GPU_buffer_setup\n"); + + if( globalPool == 0 ) { + globalPool = GPU_buffer_pool_new(); + + /* somehow GL_NORMAL_ARRAY is enabled on startup and causes edge drawing code to crash */ + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_NORMAL_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + } + buffer = GPU_buffer_alloc(size,globalPool); + if( buffer == 0 ) { + dm->drawObject->legacy = 1; + } + if( dm->drawObject->legacy ) { + return 0; + } + + index = MEM_mallocN(sizeof(int)*object->nmaterials,"GPU_buffer_setup"); + for( i = 0; i < object->nmaterials; i++ ) { + index[i] = object->materials[i].start*3; + redir[object->materials[i].mat_nr+16383] = i; + } + + if( useVBOs ) { + success = 0; + while( success == 0 ) { + glBindBufferARB( target, buffer->id ); + glBufferDataARB( target, buffer->size, 0, GL_STATIC_DRAW_ARB ); /* discard previous data, avoid stalling gpu */ + varray = glMapBufferARB( target, GL_WRITE_ONLY_ARB ); + if( varray == 0 ) { + DEBUG_VBO( "Failed to map buffer to client address space\n" ); + GPU_buffer_free( buffer, globalPool ); + GPU_buffer_pool_delete_last( globalPool ); + if( globalPool->size > 0 ) { + GPU_buffer_pool_delete_last( globalPool ); + buffer = GPU_buffer_alloc( size, globalPool ); + if( buffer == 0 ) { + dm->drawObject->legacy = 1; + success = 1; + } + } + else { + dm->drawObject->legacy = 1; + success = 1; + } + } + else { + success = 1; + } + } + + if( dm->drawObject->legacy == 0 ) { + uploaded = GL_FALSE; + while( !uploaded ) { + (*copy_f)( dm, varray, index, redir, user ); + uploaded = glUnmapBufferARB( target ); /* returns false if data got corruped during transfer */ + } + } + glBindBufferARB(target, 0); + } + else { + if( buffer->pointer != 0 ) { + varray = buffer->pointer; + (*copy_f)( dm, varray, index, redir, user ); + } + else { + dm->drawObject->legacy = 1; + } + } + + MEM_freeN(index); + + return buffer; +} + +void GPU_buffer_copy_vertex( DerivedMesh *dm, float *varray, int *index, int *redir, void *user ) +{ + int start; + int i, j, numfaces; + + MVert *mvert; + MFace *mface; + + DEBUG_VBO("GPU_buffer_copy_vertex\n"); + + mvert = dm->getVertArray(dm); + mface = dm->getTessFaceArray(dm); + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + start = index[redir[mface[i].mat_nr+16383]]; + if( mface[i].v4 ) + index[redir[mface[i].mat_nr+16383]] += 18; + else + index[redir[mface[i].mat_nr+16383]] += 9; + + /* v1 v2 v3 */ + VECCOPY(&varray[start],mvert[mface[i].v1].co); + VECCOPY(&varray[start+3],mvert[mface[i].v2].co); + VECCOPY(&varray[start+6],mvert[mface[i].v3].co); + + if( mface[i].v4 ) { + /* v3 v4 v1 */ + VECCOPY(&varray[start+9],mvert[mface[i].v3].co); + VECCOPY(&varray[start+12],mvert[mface[i].v4].co); + VECCOPY(&varray[start+15],mvert[mface[i].v1].co); + } + } + j = dm->drawObject->nelements*3; + for( i = 0; i < dm->drawObject->nindices; i++ ) { + if( dm->drawObject->indices[i].element >= dm->drawObject->nelements ) { + VECCOPY(&varray[j],mvert[i].co); + j+=3; + } + } +} + +GPUBuffer *GPU_buffer_vertex( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_buffer_vertex\n"); + + return GPU_buffer_setup( dm, dm->drawObject, sizeof(float)*3*(dm->drawObject->nelements+dm->drawObject->nlooseverts), GL_ARRAY_BUFFER_ARB, 0, GPU_buffer_copy_vertex); +} + +void GPU_buffer_copy_normal( DerivedMesh *dm, float *varray, int *index, int *redir, void *user ) +{ + int i, numfaces; + int start; + float norm[3]; + + float *nors= dm->getTessFaceDataArray(dm, CD_NORMAL); + MVert *mvert = dm->getVertArray(dm); + MFace *mface = dm->getTessFaceArray(dm); + + DEBUG_VBO("GPU_buffer_copy_normal\n"); + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + start = index[redir[mface[i].mat_nr+16383]]; + if( mface[i].v4 ) + index[redir[mface[i].mat_nr+16383]] += 18; + else + index[redir[mface[i].mat_nr+16383]] += 9; + + /* v1 v2 v3 */ + if( mface[i].flag & ME_SMOOTH ) { + VECCOPY(&varray[start],mvert[mface[i].v1].no); + VECCOPY(&varray[start+3],mvert[mface[i].v2].no); + VECCOPY(&varray[start+6],mvert[mface[i].v3].no); + } + else { + if( nors ) { + VECCOPY(&varray[start],&nors[i*3]); + VECCOPY(&varray[start+3],&nors[i*3]); + VECCOPY(&varray[start+6],&nors[i*3]); + } + if( mface[i].v4 ) + CalcNormFloat4(mvert[mface[i].v1].co, mvert[mface[i].v2].co, mvert[mface[i].v3].co, mvert[mface[i].v4].co, norm); + else + CalcNormFloat(mvert[mface[i].v1].co, mvert[mface[i].v2].co, mvert[mface[i].v3].co, norm); + VECCOPY(&varray[start],norm); + VECCOPY(&varray[start+3],norm); + VECCOPY(&varray[start+6],norm); + } + + if( mface[i].v4 ) { + /* v3 v4 v1 */ + if( mface[i].flag & ME_SMOOTH ) { + VECCOPY(&varray[start+9],mvert[mface[i].v3].no); + VECCOPY(&varray[start+12],mvert[mface[i].v4].no); + VECCOPY(&varray[start+15],mvert[mface[i].v1].no); + } + else { + VECCOPY(&varray[start+9],norm); + VECCOPY(&varray[start+12],norm); + VECCOPY(&varray[start+15],norm); + } + } + } +} + +GPUBuffer *GPU_buffer_normal( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_buffer_normal\n"); + + return GPU_buffer_setup( dm, dm->drawObject, sizeof(float)*3*dm->drawObject->nelements, GL_ARRAY_BUFFER_ARB, 0, GPU_buffer_copy_normal); +} + +void GPU_buffer_copy_uv( DerivedMesh *dm, float *varray, int *index, int *redir, void *user ) +{ + int start; + int i, numfaces; + + MTFace *mtface; + MFace *mface; + + DEBUG_VBO("GPU_buffer_copy_uv\n"); + + mface = dm->getTessFaceArray(dm); + mtface = DM_get_face_data_layer(dm, CD_MTFACE); + + if( mtface == 0 ) { + DEBUG_VBO("Texture coordinates do not exist for this mesh"); + return; + } + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + start = index[redir[mface[i].mat_nr+16383]]; + if( mface[i].v4 ) + index[redir[mface[i].mat_nr+16383]] += 12; + else + index[redir[mface[i].mat_nr+16383]] += 6; + + /* v1 v2 v3 */ + VECCOPY2D(&varray[start],mtface[i].uv[0]); + VECCOPY2D(&varray[start+2],mtface[i].uv[1]); + VECCOPY2D(&varray[start+4],mtface[i].uv[2]); + + if( mface[i].v4 ) { + /* v3 v4 v1 */ + VECCOPY2D(&varray[start+6],mtface[i].uv[2]); + VECCOPY2D(&varray[start+8],mtface[i].uv[3]); + VECCOPY2D(&varray[start+10],mtface[i].uv[0]); + } + } +} + +GPUBuffer *GPU_buffer_uv( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_buffer_uv\n"); + if( DM_get_face_data_layer(dm, CD_MTFACE) != 0 ) + return GPU_buffer_setup( dm, dm->drawObject, sizeof(float)*2*dm->drawObject->nelements, GL_ARRAY_BUFFER_ARB, 0, GPU_buffer_copy_uv); + else + return 0; +} + +void GPU_buffer_copy_color3( DerivedMesh *dm, float *varray_, int *index, int *redir, void *user ) +{ + int i, numfaces; + unsigned char *varray = (unsigned char *)varray_; + unsigned char *mcol = (unsigned char *)user; + MFace *mface = dm->getTessFaceArray(dm); + + DEBUG_VBO("GPU_buffer_copy_color3\n"); + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + int start = index[redir[mface[i].mat_nr+16383]]; + if( mface[i].v4 ) + index[redir[mface[i].mat_nr+16383]] += 18; + else + index[redir[mface[i].mat_nr+16383]] += 9; + + /* v1 v2 v3 */ + VECCOPY(&varray[start],&mcol[i*12]); + VECCOPY(&varray[start+3],&mcol[i*12+3]); + VECCOPY(&varray[start+6],&mcol[i*12+6]); + if( mface[i].v4 ) { + /* v3 v4 v1 */ + VECCOPY(&varray[start+9],&mcol[i*12+6]); + VECCOPY(&varray[start+12],&mcol[i*12+9]); + VECCOPY(&varray[start+15],&mcol[i*12]); + } + } +} + +void GPU_buffer_copy_color4( DerivedMesh *dm, float *varray_, int *index, int *redir, void *user ) +{ + int i, numfaces; + unsigned char *varray = (unsigned char *)varray_; + unsigned char *mcol = (unsigned char *)user; + MFace *mface = dm->getTessFaceArray(dm); + + DEBUG_VBO("GPU_buffer_copy_color4\n"); + + numfaces= dm->getNumTessFaces(dm); + for( i=0; i < numfaces; i++ ) { + int start = index[redir[mface[i].mat_nr+16383]]; + if( mface[i].v4 ) + index[redir[mface[i].mat_nr+16383]] += 18; + else + index[redir[mface[i].mat_nr+16383]] += 9; + + /* v1 v2 v3 */ + VECCOPY(&varray[start],&mcol[i*16]); + VECCOPY(&varray[start+3],&mcol[i*16+4]); + VECCOPY(&varray[start+6],&mcol[i*16+8]); + if( mface[i].v4 ) { + /* v3 v4 v1 */ + VECCOPY(&varray[start+9],&mcol[i*16+8]); + VECCOPY(&varray[start+12],&mcol[i*16+12]); + VECCOPY(&varray[start+15],&mcol[i*16]); + } + } +} + +GPUBuffer *GPU_buffer_color( DerivedMesh *dm ) +{ + unsigned char *colors; + int i, numfaces; + MCol *mcol; + GPUBuffer *result; + DEBUG_VBO("GPU_buffer_color\n"); + + mcol = DM_get_face_data_layer(dm, CD_ID_MCOL); + dm->drawObject->colType = CD_ID_MCOL; + if(!mcol) { + mcol = DM_get_face_data_layer(dm, CD_WEIGHT_MCOL); + dm->drawObject->colType = CD_WEIGHT_MCOL; + } + if(!mcol) { + mcol = DM_get_face_data_layer(dm, CD_MCOL); + dm->drawObject->colType = CD_MCOL; + } + + numfaces= dm->getNumTessFaces(dm); + colors = MEM_mallocN(numfaces*12*sizeof(unsigned char), "GPU_buffer_color"); + for( i=0; i < numfaces*4; i++ ) { + colors[i*3] = mcol[i].b; + colors[i*3+1] = mcol[i].g; + colors[i*3+2] = mcol[i].r; + } + + result = GPU_buffer_setup( dm, dm->drawObject, sizeof(char)*3*dm->drawObject->nelements, GL_ARRAY_BUFFER_ARB, colors, GPU_buffer_copy_color3 ); + + MEM_freeN(colors); + return result; +} + +void GPU_buffer_copy_edge( DerivedMesh *dm, float *varray, int *index, int *redir, void *user ) +{ + int i; + + MVert *mvert; + MEdge *medge; + unsigned int *varray_ = (unsigned int *)varray; + int numedges; + + DEBUG_VBO("GPU_buffer_copy_edge\n"); + + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + + numedges= dm->getNumEdges(dm); + for(i = 0; i < numedges; i++) { + varray_[i*2] = (unsigned int)dm->drawObject->indices[medge[i].v1].element; + varray_[i*2+1] = (unsigned int)dm->drawObject->indices[medge[i].v2].element; + } +} + +GPUBuffer *GPU_buffer_edge( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_buffer_edge\n"); + + return GPU_buffer_setup( dm, dm->drawObject, sizeof(int)*2*dm->drawObject->nedges, GL_ELEMENT_ARRAY_BUFFER_ARB, 0, GPU_buffer_copy_edge); +} + +void GPU_buffer_copy_uvedge( DerivedMesh *dm, float *varray, int *index, int *redir, void *user ) +{ + MTFace *tf = DM_get_face_data_layer(dm, CD_MTFACE); + int i, j=0; + + DEBUG_VBO("GPU_buffer_copy_uvedge\n"); + + if(tf) { + for(i = 0; i < dm->numFaceData; i++, tf++) { + MFace mf; + dm->getTessFace(dm,i,&mf); + + VECCOPY2D(&varray[j],tf->uv[0]); + VECCOPY2D(&varray[j+2],tf->uv[1]); + + VECCOPY2D(&varray[j+4],tf->uv[1]); + VECCOPY2D(&varray[j+6],tf->uv[2]); + + if(!mf.v4) { + VECCOPY2D(&varray[j+8],tf->uv[2]); + VECCOPY2D(&varray[j+10],tf->uv[0]); + j+=12; + } else { + VECCOPY2D(&varray[j+8],tf->uv[2]); + VECCOPY2D(&varray[j+10],tf->uv[3]); + + VECCOPY2D(&varray[j+12],tf->uv[3]); + VECCOPY2D(&varray[j+14],tf->uv[0]); + j+=16; + } + } + } + else { + DEBUG_VBO("Could not get MTFACE data layer"); + } +} + +GPUBuffer *GPU_buffer_uvedge( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_buffer_uvedge\n"); + + return GPU_buffer_setup( dm, dm->drawObject, sizeof(float)*2*(dm->drawObject->nelements/3)*2, GL_ARRAY_BUFFER_ARB, 0, GPU_buffer_copy_uvedge); +} + + +void GPU_vertex_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_vertex_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->vertices == 0 ) + dm->drawObject->vertices = GPU_buffer_vertex( dm ); + if( dm->drawObject->vertices == 0 ) { + DEBUG_VBO( "Failed to setup vertices\n" ); + return; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->vertices->id ); + glVertexPointer( 3, GL_FLOAT, 0, 0 ); + } + else { + glVertexPointer( 3, GL_FLOAT, 0, dm->drawObject->vertices->pointer ); + } + + GLStates |= GPU_BUFFER_VERTEX_STATE; +} + +void GPU_normal_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_normal_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->normals == 0 ) + dm->drawObject->normals = GPU_buffer_normal( dm ); + if( dm->drawObject->normals == 0 ) { + DEBUG_VBO( "Failed to setup normals\n" ); + return; + } + glEnableClientState( GL_NORMAL_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->normals->id ); + glNormalPointer( GL_FLOAT, 0, 0 ); + } + else { + glNormalPointer( GL_FLOAT, 0, dm->drawObject->normals->pointer ); + } + + GLStates |= GPU_BUFFER_NORMAL_STATE; +} + +void GPU_uv_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_uv_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->uv == 0 ) + dm->drawObject->uv = GPU_buffer_uv( dm ); + + if( dm->drawObject->uv != 0 ) { + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->uv->id ); + glTexCoordPointer( 2, GL_FLOAT, 0, 0 ); + } + else { + glTexCoordPointer( 2, GL_FLOAT, 0, dm->drawObject->uv->pointer ); + } + + GLStates |= GPU_BUFFER_TEXCOORD_STATE; + } +} + +void GPU_color_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_color_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->colors == 0 ) + dm->drawObject->colors = GPU_buffer_color( dm ); + if( dm->drawObject->colors == 0 ) { + DEBUG_VBO( "Failed to setup colors\n" ); + return; + } + glEnableClientState( GL_COLOR_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->colors->id ); + glColorPointer( 3, GL_UNSIGNED_BYTE, 0, 0 ); + } + else { + glColorPointer( 3, GL_UNSIGNED_BYTE, 0, dm->drawObject->colors->pointer ); + } + + GLStates |= GPU_BUFFER_COLOR_STATE; +} + +void GPU_edge_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_edge_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->edges == 0 ) + dm->drawObject->edges = GPU_buffer_edge( dm ); + if( dm->drawObject->edges == 0 ) { + DEBUG_VBO( "Failed to setup edges\n" ); + return; + } + if( dm->drawObject->vertices == 0 ) + dm->drawObject->vertices = GPU_buffer_vertex( dm ); + if( dm->drawObject->vertices == 0 ) { + DEBUG_VBO( "Failed to setup vertices\n" ); + return; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->vertices->id ); + glVertexPointer( 3, GL_FLOAT, 0, 0 ); + } + else { + glVertexPointer( 3, GL_FLOAT, 0, dm->drawObject->vertices->pointer ); + } + + GLStates |= GPU_BUFFER_VERTEX_STATE; + + if( useVBOs ) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, dm->drawObject->edges->id ); + } + + GLStates |= GPU_BUFFER_ELEMENT_STATE; +} + +void GPU_uvedge_setup( DerivedMesh *dm ) +{ + DEBUG_VBO("GPU_uvedge_setup\n"); + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new( dm ); + if( dm->drawObject->uvedges == 0 ) + dm->drawObject->uvedges = GPU_buffer_uvedge( dm ); + if( dm->drawObject->uvedges == 0 ) { + DEBUG_VBO( "Failed to setup UV edges\n" ); + return; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, dm->drawObject->uvedges->id ); + glVertexPointer( 2, GL_FLOAT, 0, 0 ); + } + else { + glVertexPointer( 2, GL_FLOAT, 0, dm->drawObject->uvedges->pointer ); + } + + GLStates |= GPU_BUFFER_VERTEX_STATE; +} + +void GPU_interleaved_setup( GPUBuffer *buffer, int data[] ) { + int i; + int elementsize = 0; + intptr_t offset = 0; + + DEBUG_VBO("GPU_interleaved_setup\n"); + + for( i = 0; data[i] != GPU_BUFFER_INTER_END; i++ ) { + switch( data[i] ) { + case GPU_BUFFER_INTER_V3F: + elementsize += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_N3F: + elementsize += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_T2F: + elementsize += 2*sizeof(float); + break; + case GPU_BUFFER_INTER_C3UB: + elementsize += 3*sizeof(unsigned char); + break; + case GPU_BUFFER_INTER_C4UB: + elementsize += 4*sizeof(unsigned char); + break; + default: + DEBUG_VBO( "Unknown element in data type array in GPU_interleaved_setup\n" ); + } + } + + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->id ); + for( i = 0; data[i] != GPU_BUFFER_INTER_END; i++ ) { + switch( data[i] ) { + case GPU_BUFFER_INTER_V3F: + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, elementsize, (void *)offset ); + GLStates |= GPU_BUFFER_VERTEX_STATE; + offset += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_N3F: + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, elementsize, (void *)offset ); + GLStates |= GPU_BUFFER_NORMAL_STATE; + offset += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_T2F: + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glTexCoordPointer( 2, GL_FLOAT, elementsize, (void *)offset ); + GLStates |= GPU_BUFFER_TEXCOORD_STATE; + offset += 2*sizeof(float); + break; + case GPU_BUFFER_INTER_C3UB: + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 3, GL_UNSIGNED_BYTE, elementsize, (void *)offset ); + GLStates |= GPU_BUFFER_COLOR_STATE; + offset += 3*sizeof(unsigned char); + break; + case GPU_BUFFER_INTER_C4UB: + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_UNSIGNED_BYTE, elementsize, (void *)offset ); + GLStates |= GPU_BUFFER_COLOR_STATE; + offset += 4*sizeof(unsigned char); + break; + } + } + } + else { + for( i = 0; data[i] != GPU_BUFFER_INTER_END; i++ ) { + switch( data[i] ) { + case GPU_BUFFER_INTER_V3F: + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_FLOAT, elementsize, offset+(char *)buffer->pointer ); + GLStates |= GPU_BUFFER_VERTEX_STATE; + offset += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_N3F: + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, elementsize, offset+(char *)buffer->pointer ); + GLStates |= GPU_BUFFER_NORMAL_STATE; + offset += 3*sizeof(float); + break; + case GPU_BUFFER_INTER_T2F: + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glTexCoordPointer( 2, GL_FLOAT, elementsize, offset+(char *)buffer->pointer ); + GLStates |= GPU_BUFFER_TEXCOORD_STATE; + offset += 2*sizeof(float); + break; + case GPU_BUFFER_INTER_C3UB: + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 3, GL_UNSIGNED_BYTE, elementsize, offset+(char *)buffer->pointer ); + GLStates |= GPU_BUFFER_COLOR_STATE; + offset += 3*sizeof(unsigned char); + break; + case GPU_BUFFER_INTER_C4UB: + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( 4, GL_UNSIGNED_BYTE, elementsize, offset+(char *)buffer->pointer ); + GLStates |= GPU_BUFFER_COLOR_STATE; + offset += 4*sizeof(unsigned char); + break; + } + } + } +} + +static int GPU_typesize( int type ) { + switch( type ) { + case GL_FLOAT: + return sizeof(float); + case GL_INT: + return sizeof(int); + case GL_UNSIGNED_INT: + return sizeof(unsigned int); + case GL_BYTE: + return sizeof(char); + case GL_UNSIGNED_BYTE: + return sizeof(unsigned char); + default: + return 0; + } +} + +int GPU_attrib_element_size( GPUAttrib data[], int numdata ) { + int i, elementsize = 0; + + for( i = 0; i < numdata; i++ ) { + int typesize = GPU_typesize(data[i].type); + if( typesize == 0 ) + DEBUG_VBO( "Unknown element in data type array in GPU_attrib_element_size\n" ); + else { + elementsize += typesize*data[i].size; + } + } + return elementsize; +} + +void GPU_interleaved_attrib_setup( GPUBuffer *buffer, GPUAttrib data[], int numdata ) { + int i; + int elementsize; + intptr_t offset = 0; + + DEBUG_VBO("GPU_interleaved_attrib_setup\n"); + + for( i = 0; i < MAX_GPU_ATTRIB_DATA; i++ ) { + if( attribData[i].index != -1 ) { + glDisableVertexAttribArrayARB( attribData[i].index ); + } + else + break; + } + elementsize = GPU_attrib_element_size( data, numdata ); + + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->id ); + for( i = 0; i < numdata; i++ ) { + glEnableVertexAttribArrayARB( data[i].index ); + glVertexAttribPointerARB( data[i].index, data[i].size, data[i].type, GL_TRUE, elementsize, (void *)offset ); + offset += data[i].size*GPU_typesize(data[i].type); + + attribData[i].index = data[i].index; + attribData[i].size = data[i].size; + attribData[i].type = data[i].type; + } + attribData[numdata].index = -1; + } + else { + for( i = 0; i < numdata; i++ ) { + glEnableVertexAttribArrayARB( data[i].index ); + glVertexAttribPointerARB( data[i].index, data[i].size, data[i].type, GL_TRUE, elementsize, (char *)buffer->pointer + offset ); + offset += data[i].size*GPU_typesize(data[i].type); + } + } +} + + +void GPU_buffer_unbind() +{ + int i; + DEBUG_VBO("GPU_buffer_unbind\n"); + + if( GLStates & GPU_BUFFER_VERTEX_STATE ) + glDisableClientState( GL_VERTEX_ARRAY ); + if( GLStates & GPU_BUFFER_NORMAL_STATE ) + glDisableClientState( GL_NORMAL_ARRAY ); + if( GLStates & GPU_BUFFER_TEXCOORD_STATE ) + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + if( GLStates & GPU_BUFFER_COLOR_STATE ) + glDisableClientState( GL_COLOR_ARRAY ); + if( GLStates & GPU_BUFFER_ELEMENT_STATE ) { + if( useVBOs ) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ); + } + } + GLStates &= !(GPU_BUFFER_VERTEX_STATE | GPU_BUFFER_NORMAL_STATE | GPU_BUFFER_TEXCOORD_STATE | GPU_BUFFER_COLOR_STATE | GPU_BUFFER_ELEMENT_STATE); + + for( i = 0; i < MAX_GPU_ATTRIB_DATA; i++ ) { + if( attribData[i].index != -1 ) { + glDisableVertexAttribArrayARB( attribData[i].index ); + } + else + break; + } + if( GLStates != 0 ) + DEBUG_VBO( "Some weird OpenGL state is still set. Why?" ); + if( useVBOs ) + glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); +} + +void GPU_color3_upload( DerivedMesh *dm, unsigned char *data ) +{ + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new(dm); + GPU_buffer_free(dm->drawObject->colors,globalPool); + dm->drawObject->colors = GPU_buffer_setup( dm, dm->drawObject, sizeof(char)*3*dm->drawObject->nelements, GL_ARRAY_BUFFER_ARB, data, GPU_buffer_copy_color3 ); +} +void GPU_color4_upload( DerivedMesh *dm, unsigned char *data ) +{ + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new(dm); + GPU_buffer_free(dm->drawObject->colors,globalPool); + dm->drawObject->colors = GPU_buffer_setup( dm, dm->drawObject, sizeof(char)*3*dm->drawObject->nelements, GL_ARRAY_BUFFER_ARB, data, GPU_buffer_copy_color4 ); +} + +void GPU_color_switch( int mode ) +{ + if( mode ) { + if( !GLStates & GPU_BUFFER_COLOR_STATE ) + glEnableClientState( GL_COLOR_ARRAY ); + GLStates |= GPU_BUFFER_COLOR_STATE; + } + else { + if( GLStates & GPU_BUFFER_COLOR_STATE ) + glDisableClientState( GL_COLOR_ARRAY ); + GLStates &= (!GPU_BUFFER_COLOR_STATE); + } +} + +int GPU_buffer_legacy( DerivedMesh *dm ) +{ + int test= (U.gameflags & USER_DISABLE_VBO); + if( test ) + return 1; + + if( dm->drawObject == 0 ) + dm->drawObject = GPU_drawobject_new(dm); + return dm->drawObject->legacy; +} + +void *GPU_buffer_lock( GPUBuffer *buffer ) +{ + float *varray; + + DEBUG_VBO("GPU_buffer_lock\n"); + if( buffer == 0 ) { + DEBUG_VBO( "Failed to lock NULL buffer\n" ); + return 0; + } + + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->id ); + varray = glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB ); + if( varray == 0 ) { + DEBUG_VBO( "Failed to map buffer to client address space\n" ); + } + return varray; + } + else { + return buffer->pointer; + } +} + +void *GPU_buffer_lock_stream( GPUBuffer *buffer ) +{ + float *varray; + + DEBUG_VBO("GPU_buffer_lock_stream\n"); + if( buffer == 0 ) { + DEBUG_VBO( "Failed to lock NULL buffer\n" ); + return 0; + } + + if( useVBOs ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, buffer->id ); + glBufferDataARB( GL_ARRAY_BUFFER_ARB, buffer->size, 0, GL_STREAM_DRAW_ARB ); /* discard previous data, avoid stalling gpu */ + varray = glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB ); + if( varray == 0 ) { + DEBUG_VBO( "Failed to map buffer to client address space\n" ); + } + return varray; + } + else { + return buffer->pointer; + } +} + +void GPU_buffer_unlock( GPUBuffer *buffer ) +{ + DEBUG_VBO( "GPU_buffer_unlock\n" ); + if( useVBOs ) { + if( buffer != 0 ) { + if( glUnmapBufferARB( GL_ARRAY_BUFFER_ARB ) == 0 ) { + DEBUG_VBO( "Failed to copy new data\n" ); + } + } + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + } +} + +void GPU_buffer_draw_elements( GPUBuffer *elements, unsigned int mode, int start, int count ) +{ + if( useVBOs ) { + glDrawElements( mode, count, GL_UNSIGNED_INT, (void *)(start*sizeof(unsigned int)) ); + } + else { + glDrawElements( mode, count, GL_UNSIGNED_INT, ((int *)elements->pointer)+start ); + } +} diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c new file mode 100644 index 00000000000..991a8251cc5 --- /dev/null +++ b/source/blender/makesrna/intern/rna_action_api.c @@ -0,0 +1,80 @@ +/** + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Arystanbek Dyussenov + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_action_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_action.h" + +#include "DNA_anim_types.h" +#include "DNA_curve_types.h" + +/* XXX disabled until RNA allows returning arrays */ +#if 0 +/* return frame range of all curves (min, max) or (0, 1) if there are no keys */ +int *rna_Action_get_frame_range(bAction *act, int *ret_length) +{ + int *ret; + float start, end; + + calc_action_range(act, &start, &end, 1); + + *ret_length= 2; + ret= MEM_callocN(*ret_length * sizeof(int), "rna_Action_get_frame_range"); + + ret[0]= (int)start; + ret[1]= (int)end; + + return ret; +} +#endif + +#else + +void RNA_api_action(StructRNA *srna) +{ +#if 0 + FunctionRNA *func; + PropertyRNA *parm; + + func= RNA_def_function(srna, "get_frame_range", "rna_Action_get_frame_range"); + RNA_def_function_ui_description(func, "Get action frame range as a (min, max) tuple."); + parm= RNA_def_int_array(func, "frame_range", 1, NULL, 0, 0, "", "Action frame range.", 0, 0); + RNA_def_property_flag(parm, PROP_DYNAMIC_ARRAY); + RNA_def_function_return(func, parm); +#endif +} + +#endif diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c new file mode 100644 index 00000000000..4ac6b98b167 --- /dev/null +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -0,0 +1,101 @@ +/** + * $Id: rna_image_api.c 23507 2009-09-27 09:19:29Z kazanbas $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Arystanbek Dyussenov + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "RNA_define.h" +#include "RNA_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_utildefines.h" + +#include "DNA_image_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +/* + User should check if returned path exists before copying a file there. + + TODO: it would be better to return a (abs, rel) tuple. +*/ +static char *rna_Image_get_export_path(Image *image, char *dest_dir, int rel) +{ + int length = FILE_MAX; + char *path= MEM_callocN(length, "image file path"); + + if (!BKE_get_image_export_path(image, dest_dir, rel ? NULL : path, length, rel ? path : NULL, length )) { + MEM_freeN(path); + return NULL; + } + + return path; +} + +char *rna_Image_get_abs_filename(Image *image, bContext *C) +{ + char *filename= MEM_callocN(FILE_MAX, "Image.get_abs_filename()"); + + BLI_strncpy(filename, image->name, FILE_MAXDIR + FILE_MAXFILE); + BLI_convertstringcode(filename, CTX_data_main(C)->name); + BLI_convertstringframe(filename, CTX_data_scene(C)->r.cfra); + + return filename; +} + +#else + +void RNA_api_image(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + func= RNA_def_function(srna, "get_export_path", "rna_Image_get_export_path"); + RNA_def_function_ui_description(func, "Produce image export path."); + parm= RNA_def_string(func, "dest_dir", "", 0, "", "Destination directory."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_boolean(func, "get_rel_path", 1, "", "Return relative path if True."); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm= RNA_def_string(func, "path", "", 0, "", "Absolute export path."); + RNA_def_function_return(func, parm); + + func= RNA_def_function(srna, "get_abs_filename", "rna_Image_get_abs_filename"); + RNA_def_function_ui_description(func, "Get absolute filename."); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm= RNA_def_string_file_path(func, "abs_filename", NULL, 0, "", "Image/movie absolute filename."); + RNA_def_function_return(func, parm); +} + +#endif + diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c new file mode 100644 index 00000000000..aa28b6b923c --- /dev/null +++ b/source/blender/makesrna/intern/rna_material_api.c @@ -0,0 +1,126 @@ +/** + * + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_material_types.h" + +#ifdef RNA_RUNTIME + +#include "BKE_material.h" +#include "BKE_texture.h" + +/* + Adds material to the first free texture slot. + If all slots are busy, replaces the first. +*/ +static void rna_Material_add_texture(Material *ma, Tex *tex, int mapto, int texco) +{ + int i; + MTex *mtex; + int slot= -1; + + for (i= 0; i < MAX_MTEX; i++) { + if (!ma->mtex[i]) { + slot= i; + break; + } + } + + if (slot == -1) + slot= 0; + + if (ma->mtex[slot]) { + ma->mtex[slot]->tex->id.us--; + } + else { + ma->mtex[slot]= add_mtex(); + } + + mtex= ma->mtex[slot]; + + mtex->tex= tex; + id_us_plus(&tex->id); + + mtex->texco= mapto; + mtex->mapto= texco; +} + +#else + +void RNA_api_material(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + /* copied from rna_def_material_mtex (rna_material.c) */ + static EnumPropertyItem prop_texture_coordinates_items[] = { + {TEXCO_GLOB, "GLOBAL", 0, "Global", "Uses global coordinates for the texture coordinates."}, + {TEXCO_OBJECT, "OBJECT", 0, "Object", "Uses linked object's coordinates for texture coordinates."}, + {TEXCO_UV, "UV", 0, "UV", "Uses UV coordinates for texture coordinates."}, + {TEXCO_ORCO, "ORCO", 0, "Generated", "Uses the original undeformed coordinates of the object."}, + {TEXCO_STRAND, "STRAND", 0, "Strand", "Uses normalized strand texture coordinate (1D)."}, + {TEXCO_STICKY, "STICKY", 0, "Sticky", "Uses mesh's sticky coordinates for the texture coordinates."}, + {TEXCO_WINDOW, "WINDOW", 0, "Window", "Uses screen coordinates as texture coordinates."}, + {TEXCO_NORM, "NORMAL", 0, "Normal", "Uses normal vector as texture coordinates."}, + {TEXCO_REFL, "REFLECTION", 0, "Reflection", "Uses reflection vector as texture coordinates."}, + {TEXCO_STRESS, "STRESS", 0, "Stress", "Uses the difference of edge lengths compared to original coordinates of the mesh."}, + {TEXCO_TANGENT, "TANGENT", 0, "Tangent", "Uses the optional tangent vector as texture coordinates."}, + + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem prop_texture_mapto_items[] = { + {MAP_COL, "COLOR", 0, "Color", "Causes the texture to affect basic color of the material"}, + {MAP_NORM, "NORMAL", 0, "Normal", "Causes the texture to affect the rendered normal"}, + {MAP_COLSPEC, "SPECULAR_COLOR", 0, "Specularity Color", "Causes the texture to affect the specularity color"}, + {MAP_COLMIR, "MIRROR", 0, "Mirror", "Causes the texture to affect the mirror color"}, + {MAP_REF, "REFLECTION", 0, "Reflection", "Causes the texture to affect the value of the materials reflectivity"}, + {MAP_SPEC, "SPECULARITY", 0, "Specularity", "Causes the texture to affect the value of specularity"}, + {MAP_EMIT, "EMIT", 0, "Emit", "Causes the texture to affect the emit value"}, + {MAP_ALPHA, "ALPHA", 0, "Alpha", "Causes the texture to affect the alpha value"}, + {MAP_HAR, "HARDNESS", 0, "Hardness", "Causes the texture to affect the hardness value"}, + {MAP_RAYMIRR, "RAY_MIRROR", 0, "Ray-Mirror", "Causes the texture to affect the ray-mirror value"}, + {MAP_TRANSLU, "TRANSLUCENCY", 0, "Translucency", "Causes the texture to affect the translucency value"}, + {MAP_AMB, "AMBIENT", 0, "Ambient", "Causes the texture to affect the value of ambient"}, + {MAP_DISPLACE, "DISPLACEMENT", 0, "Displacement", "Let the texture displace the surface"}, + {MAP_WARP, "WARP", 0, "Warp", "Let the texture warp texture coordinates of next channels"}, + {0, NULL, 0, NULL, NULL}}; + + func= RNA_def_function(srna, "add_texture", "rna_Material_add_texture"); + RNA_def_function_ui_description(func, "Add a texture to material's free texture slot."); + parm= RNA_def_pointer(func, "texture", "Texture", "", "Texture to add."); + parm= RNA_def_enum(func, "texture_coordinates", prop_texture_coordinates_items, TEXCO_UV, "", "Source of texture coordinate information."); /* optional */ + parm= RNA_def_enum(func, "map_to", prop_texture_mapto_items, MAP_COL, "", "Controls which material property the texture affects."); /* optional */ +} + +#endif + diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c new file mode 100644 index 00000000000..c6791405109 --- /dev/null +++ b/source/blender/makesrna/intern/rna_pose_api.c @@ -0,0 +1,56 @@ +/** + * $Id: rna_pose_api.c 23425 2009-09-22 19:09:04Z gsrb3d $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "DNA_object_types.h" + +/* #include "BLO_sys_types.h" */ + +#ifdef RNA_RUNTIME + +/* #include "DNA_anim_types.h" */ +#include "DNA_action_types.h" /* bPose */ + +#else + +void RNA_api_pose(StructRNA *srna) +{ + /* FunctionRNA *func; */ + /* PropertyRNA *parm; */ + +} + +#endif + diff --git a/source/gameengine/Converter/BL_ArmatureActuator.cpp b/source/gameengine/Converter/BL_ArmatureActuator.cpp new file mode 100644 index 00000000000..4105eb57d5f --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureActuator.cpp @@ -0,0 +1,267 @@ +/** + * $Id: BL_ArmatureActuator.cpp 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "DNA_actuator_types.h" +#include "BKE_constraint.h" +#include "BLI_arithb.h" +#include "BL_ArmatureActuator.h" +#include "BL_ArmatureObject.h" + +/** + * This class is the conversion of the Pose channel constraint. + * It makes a link between the pose constraint and the KX scene. + * The main purpose is to give access to the constraint target + * to link it to a game object. + * It also allows to activate/deactivate constraints during the game. + * Later it will also be possible to create constraint on the fly + */ + +BL_ArmatureActuator::BL_ArmatureActuator(SCA_IObject* obj, + int type, + const char *posechannel, + const char *constraintname, + KX_GameObject* targetobj, + KX_GameObject* subtargetobj, + float weight) : + SCA_IActuator(obj, KX_ACT_ARMATURE), + m_constraint(NULL), + m_gametarget(targetobj), + m_gamesubtarget(subtargetobj), + m_posechannel(posechannel), + m_constraintname(constraintname), + m_weight(weight), + m_type(type) +{ + if (m_gametarget) + m_gametarget->RegisterActuator(this); + if (m_gamesubtarget) + m_gamesubtarget->RegisterActuator(this); + FindConstraint(); +} + +BL_ArmatureActuator::~BL_ArmatureActuator() +{ + if (m_gametarget) + m_gametarget->UnregisterActuator(this); + if (m_gamesubtarget) + m_gamesubtarget->UnregisterActuator(this); +} + +void BL_ArmatureActuator::ProcessReplica() +{ + // the replica is tracking the same object => register it (this may be changed in Relnk()) + if (m_gametarget) + m_gametarget->RegisterActuator(this); + if (m_gamesubtarget) + m_gamesubtarget->UnregisterActuator(this); + SCA_IActuator::ProcessReplica(); +} + +void BL_ArmatureActuator::ReParent(SCA_IObject* parent) +{ + SCA_IActuator::ReParent(parent); + // must remap the constraint + FindConstraint(); +} + +bool BL_ArmatureActuator::UnlinkObject(SCA_IObject* clientobj) +{ + bool res=false; + if (clientobj == m_gametarget) + { + // this object is being deleted, we cannot continue to track it. + m_gametarget = NULL; + res = true; + } + if (clientobj == m_gamesubtarget) + { + // this object is being deleted, we cannot continue to track it. + m_gamesubtarget = NULL; + res = true; + } + return res; +} + +void BL_ArmatureActuator::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map) +{ + void **h_obj = (*obj_map)[m_gametarget]; + if (h_obj) { + if (m_gametarget) + m_gametarget->UnregisterActuator(this); + m_gametarget = (KX_GameObject*)(*h_obj); + m_gametarget->RegisterActuator(this); + } + h_obj = (*obj_map)[m_gamesubtarget]; + if (h_obj) { + if (m_gamesubtarget) + m_gamesubtarget->UnregisterActuator(this); + m_gamesubtarget = (KX_GameObject*)(*h_obj); + m_gamesubtarget->RegisterActuator(this); + } +} + +void BL_ArmatureActuator::FindConstraint() +{ + m_constraint = NULL; + + if (m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { + BL_ArmatureObject* armobj = (BL_ArmatureObject*)m_gameobj; + m_constraint = armobj->GetConstraint(m_posechannel, m_constraintname); + } +} + +bool BL_ArmatureActuator::Update(double curtime, bool frame) +{ + // the only role of this actuator is to ensure that the armature pose will be evaluated + bool result = false; + bool bNegativeEvent = IsNegativeEvent(); + RemoveAllEvents(); + + if (!bNegativeEvent) { + BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent(); + switch (m_type) { + case ACT_ARM_RUN: + result = true; + obj->SetActiveAction(NULL, 0, curtime); + break; + case ACT_ARM_ENABLE: + if (m_constraint) + m_constraint->ClrConstraintFlag(CONSTRAINT_OFF); + break; + case ACT_ARM_DISABLE: + if (m_constraint) + m_constraint->SetConstraintFlag(CONSTRAINT_OFF); + break; + case ACT_ARM_SETTARGET: + if (m_constraint) { + m_constraint->SetTarget(m_gametarget); + m_constraint->SetSubtarget(m_gamesubtarget); + } + break; + case ACT_ARM_SETWEIGHT: + if (m_constraint) + m_constraint->SetWeight(m_weight); + break; + } + } + return result; +} + +#ifndef DISABLE_PYTHON + +/* ------------------------------------------------------------------------- */ +/* Python Integration Hooks */ +/* ------------------------------------------------------------------------- */ + +PyTypeObject BL_ArmatureActuator::Type = { +#if (PY_VERSION_HEX >= 0x02060000) + PyVarObject_HEAD_INIT(NULL, 0) +#else + /* python 2.5 and below */ + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ +#endif + "BL_ArmatureActuator", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &SCA_IActuator::Type, + 0,0,0,0,0,0, + py_base_new +}; + + +PyMethodDef BL_ArmatureActuator::Methods[] = { + {NULL,NULL} //Sentinel +}; + +PyAttributeDef BL_ArmatureActuator::Attributes[] = { + KX_PYATTRIBUTE_RO_FUNCTION("constraint", BL_ArmatureActuator, pyattr_get_constraint), + KX_PYATTRIBUTE_RW_FUNCTION("target", BL_ArmatureActuator, pyattr_get_object, pyattr_set_object), + KX_PYATTRIBUTE_RW_FUNCTION("subtarget", BL_ArmatureActuator, pyattr_get_object, pyattr_set_object), + KX_PYATTRIBUTE_FLOAT_RW("weight",0.0f,1.0f,BL_ArmatureActuator,m_weight), + KX_PYATTRIBUTE_INT_RW("type",0,ACT_ARM_MAXTYPE,false,BL_ArmatureActuator,m_type), + { NULL } //Sentinel +}; + +PyObject* BL_ArmatureActuator::pyattr_get_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self); + KX_GameObject *target = (!strcmp(attrdef->m_name, "target")) ? actuator->m_gametarget : actuator->m_gamesubtarget; + if (!target) + Py_RETURN_NONE; + else + return target->GetProxy(); +} + +int BL_ArmatureActuator::pyattr_set_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self); + KX_GameObject* &target = (!strcmp(attrdef->m_name, "target")) ? actuator->m_gametarget : actuator->m_gamesubtarget; + KX_GameObject *gameobj; + + if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: BL_ArmatureActuator")) + return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error + + if (target != NULL) + target->UnregisterActuator(actuator); + + target = gameobj; + + if (target) + target->RegisterActuator(actuator); + + return PY_SET_ATTR_SUCCESS; +} + +PyObject* BL_ArmatureActuator::pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self); + BL_ArmatureConstraint* constraint = actuator->m_constraint; + if (!constraint) + Py_RETURN_NONE; + else + return constraint->GetProxy(); +} + +#endif // DISABLE_PYTHON + diff --git a/source/gameengine/Converter/BL_ArmatureActuator.h b/source/gameengine/Converter/BL_ArmatureActuator.h new file mode 100644 index 00000000000..29b07b16d52 --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureActuator.h @@ -0,0 +1,93 @@ +/** + * $Id: BL_ArmatureActuator.h 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef BL_ARMATUREACTUATOR +#define BL_ARMATUREACTUATOR + +#include "SCA_IActuator.h" +#include "BL_ArmatureConstraint.h" + +/** + * This class is the conversion of the Pose channel constraint. + * It makes a link between the pose constraint and the KX scene. + * The main purpose is to give access to the constraint target + * to link it to a game object. + * It also allows to activate/deactivate constraints during the game. + * Later it will also be possible to create constraint on the fly + */ + +class BL_ArmatureActuator : public SCA_IActuator +{ + Py_Header; +public: + BL_ArmatureActuator(SCA_IObject* gameobj, + int type, + const char *posechannel, + const char *constraintname, + KX_GameObject* targetobj, + KX_GameObject* subtargetobj, + float weight); + + virtual ~BL_ArmatureActuator(); + + virtual CValue* GetReplica() { + BL_ArmatureActuator* replica = new BL_ArmatureActuator(*this); + replica->ProcessReplica(); + return replica; + }; + virtual void ProcessReplica(); + virtual bool UnlinkObject(SCA_IObject* clientobj); + virtual void Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map); + virtual bool Update(double curtime, bool frame); + virtual void ReParent(SCA_IObject* parent); + +#ifndef DISABLE_PYTHON + + /* These are used to get and set m_target */ + static PyObject* pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + +#endif // DISABLE_PYTHON + +private: + // identify the constraint that this actuator controls + void FindConstraint(); + + BL_ArmatureConstraint* m_constraint; + KX_GameObject* m_gametarget; + KX_GameObject* m_gamesubtarget; + STR_String m_posechannel; + STR_String m_constraintname; + float m_weight; + int m_type; +}; + +#endif //BL_ARMATUREACTUATOR + + diff --git a/source/gameengine/Converter/BL_ArmatureChannel.cpp b/source/gameengine/Converter/BL_ArmatureChannel.cpp new file mode 100644 index 00000000000..817049c6977 --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureChannel.cpp @@ -0,0 +1,469 @@ +/** + * $Id: BL_ArmatureChannel.cpp 23562 2009-09-29 21:42:40Z campbellbarton $ + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "DNA_armature_types.h" +#include "BL_ArmatureChannel.h" +#include "BL_ArmatureObject.h" +#include "BL_ArmatureConstraint.h" +#include "BLI_arithb.h" +#include "BLI_string.h" + +#ifndef DISABLE_PYTHON + +PyTypeObject BL_ArmatureChannel::Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BL_ArmatureChannel", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &CValue::Type, + 0,0,0,0,0,0, + py_base_new +}; + +PyObject* BL_ArmatureChannel::py_repr(void) +{ + return PyUnicode_FromString(m_posechannel->name); +} + +PyObject *BL_ArmatureChannel::GetProxy() +{ + return GetProxyPlus_Ext(this, &Type, m_posechannel); +} + +PyObject *BL_ArmatureChannel::NewProxy(bool py_owns) +{ + return NewProxyPlus_Ext(this, &Type, m_posechannel, py_owns); +} + +#endif // DISABLE_PYTHON + +BL_ArmatureChannel::BL_ArmatureChannel( + BL_ArmatureObject *armature, + bPoseChannel *posechannel) + : PyObjectPlus(), m_posechannel(posechannel), m_armature(armature) +{ +} + +BL_ArmatureChannel::~BL_ArmatureChannel() +{ +} + +#ifndef DISABLE_PYTHON + +// PYTHON + +PyMethodDef BL_ArmatureChannel::Methods[] = { + {NULL,NULL} //Sentinel +}; + +// order of definition of attributes, must match Attributes[] array +#define BCA_BONE 0 +#define BCA_PARENT 1 + +PyAttributeDef BL_ArmatureChannel::Attributes[] = { + // Keep these attributes in order of BCA_ defines!!! used by py_attr_getattr and py_attr_setattr + KX_PYATTRIBUTE_RO_FUNCTION("bone",BL_ArmatureChannel,py_attr_getattr), + KX_PYATTRIBUTE_RO_FUNCTION("parent",BL_ArmatureChannel,py_attr_getattr), + + { NULL } //Sentinel +}; + +/* attributes directly taken from bPoseChannel */ +PyAttributeDef BL_ArmatureChannel::AttributesPtr[] = { + KX_PYATTRIBUTE_CHAR_RO("name",bPoseChannel,name), + KX_PYATTRIBUTE_FLAG_RO("has_ik",bPoseChannel,flag, POSE_CHAIN), + KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_x",bPoseChannel,ikflag, BONE_IK_NO_XDOF), + KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_y",bPoseChannel,ikflag, BONE_IK_NO_YDOF), + KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_z",bPoseChannel,ikflag, BONE_IK_NO_ZDOF), + KX_PYATTRIBUTE_FLAG_RO("ik_limit_x",bPoseChannel,ikflag, BONE_IK_XLIMIT), + KX_PYATTRIBUTE_FLAG_RO("ik_limit_y",bPoseChannel,ikflag, BONE_IK_YLIMIT), + KX_PYATTRIBUTE_FLAG_RO("ik_limit_z",bPoseChannel,ikflag, BONE_IK_ZLIMIT), + KX_PYATTRIBUTE_FLAG_RO("ik_rot_control",bPoseChannel,ikflag, BONE_IK_ROTCTL), + KX_PYATTRIBUTE_FLAG_RO("ik_lin_control",bPoseChannel,ikflag, BONE_IK_LINCTL), + KX_PYATTRIBUTE_FLOAT_VECTOR_RW("location",-FLT_MAX,FLT_MAX,bPoseChannel,loc,3), + KX_PYATTRIBUTE_FLOAT_VECTOR_RW("scale",-FLT_MAX,FLT_MAX,bPoseChannel,size,3), + KX_PYATTRIBUTE_FLOAT_VECTOR_RW("rotation_quaternion",-1.0f,1.0f,bPoseChannel,quat,4), + KX_PYATTRIBUTE_FLOAT_VECTOR_RW("rotaion_euler",-10.f,10.f,bPoseChannel,eul,3), + KX_PYATTRIBUTE_SHORT_RW("rotation_mode",0,ROT_MODE_MAX-1,false,bPoseChannel,rotmode), + KX_PYATTRIBUTE_FLOAT_MATRIX_RO("channel_matrix",bPoseChannel,chan_mat,4), + KX_PYATTRIBUTE_FLOAT_MATRIX_RO("pose_matrix",bPoseChannel,pose_mat,4), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("pose_head",bPoseChannel,pose_head,3), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("pose_tail",bPoseChannel,pose_tail,3), + KX_PYATTRIBUTE_FLOAT_RO("ik_min_x",bPoseChannel,limitmin[0]), + KX_PYATTRIBUTE_FLOAT_RO("ik_max_x",bPoseChannel,limitmax[0]), + KX_PYATTRIBUTE_FLOAT_RO("ik_min_y",bPoseChannel,limitmin[1]), + KX_PYATTRIBUTE_FLOAT_RO("ik_max_y",bPoseChannel,limitmax[1]), + KX_PYATTRIBUTE_FLOAT_RO("ik_min_z",bPoseChannel,limitmin[2]), + KX_PYATTRIBUTE_FLOAT_RO("ik_max_z",bPoseChannel,limitmax[2]), + KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_x",bPoseChannel,stiffness[0]), + KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_y",bPoseChannel,stiffness[1]), + KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_z",bPoseChannel,stiffness[2]), + KX_PYATTRIBUTE_FLOAT_RO("ik_stretch",bPoseChannel,ikstretch), + KX_PYATTRIBUTE_FLOAT_RW("ik_rot_weight",0,1.0f,bPoseChannel,ikrotweight), + KX_PYATTRIBUTE_FLOAT_RW("ik_lin_weight",0,1.0f,bPoseChannel,iklinweight), + KX_PYATTRIBUTE_RW_FUNCTION("joint_rotation",BL_ArmatureChannel,py_attr_get_joint_rotation,py_attr_set_joint_rotation), + { NULL } //Sentinel +}; + +PyObject* BL_ArmatureChannel::py_attr_getattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v); + bPoseChannel* channel = self->m_posechannel; + int attr_order = attrdef-Attributes; + + if (!channel) { + PyErr_SetString(PyExc_AttributeError, "channel is NULL"); + return NULL; + } + + switch (attr_order) { + case BCA_BONE: + // bones are standalone proxy + return NewProxyPlus_Ext(NULL,&BL_ArmatureBone::Type,channel->bone,false); + case BCA_PARENT: + { + BL_ArmatureChannel* parent = self->m_armature->GetChannel(channel->parent); + if (parent) + return parent->GetProxy(); + else + Py_RETURN_NONE; + } + } + PyErr_SetString(PyExc_AttributeError, "channel unknown attribute"); + return NULL; +} + +int BL_ArmatureChannel::py_attr_setattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v); + bPoseChannel* channel = self->m_posechannel; + int attr_order = attrdef-Attributes; + + int ival; + double dval; + char* sval; + KX_GameObject *oval; + + if (!channel) { + PyErr_SetString(PyExc_AttributeError, "channel is NULL"); + return PY_SET_ATTR_FAIL; + } + + switch (attr_order) { + default: + break; + } + + PyErr_SetString(PyExc_AttributeError, "channel unknown attribute"); + return PY_SET_ATTR_FAIL; +} + +PyObject* BL_ArmatureChannel::py_attr_get_joint_rotation(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v); + bPoseChannel* pchan = self->m_posechannel; + // decompose the pose matrix in euler rotation + float rest_mat[3][3]; + float pose_mat[3][3]; + float joint_mat[3][3]; + float joints[3]; + float norm; + double sa, ca; + // get rotation in armature space + Mat3CpyMat4(pose_mat, pchan->pose_mat); + Mat3Ortho(pose_mat); + if (pchan->parent) { + // bone has a parent, compute the rest pose of the bone taking actual pose of parent + Mat3IsMat3MulMat4(rest_mat, pchan->bone->bone_mat, pchan->parent->pose_mat); + Mat3Ortho(rest_mat); + } else { + // otherwise, the bone matrix in armature space is the rest pose + Mat3CpyMat4(rest_mat, pchan->bone->arm_mat); + } + // remove the rest pose to get the joint movement + Mat3Transp(rest_mat); + Mat3MulMat3(joint_mat, rest_mat, pose_mat); + joints[0] = joints[1] = joints[2] = 0.f; + // returns a 3 element list that gives corresponding joint + int flag = 0; + if (!(pchan->ikflag & BONE_IK_NO_XDOF)) + flag |= 1; + if (!(pchan->ikflag & BONE_IK_NO_YDOF)) + flag |= 2; + if (!(pchan->ikflag & BONE_IK_NO_ZDOF)) + flag |= 4; + switch (flag) { + case 0: // fixed joint + break; + case 1: // X only + Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ); + joints[1] = joints[2] = 0.f; + break; + case 2: // Y only + Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ); + joints[0] = joints[2] = 0.f; + break; + case 3: // X+Y + Mat3ToEulO(joint_mat, joints, EULER_ORDER_ZYX); + joints[2] = 0.f; + break; + case 4: // Z only + Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ); + joints[0] = joints[1] = 0.f; + break; + case 5: // X+Z + // decompose this as an equivalent rotation vector in X/Z plane + joints[0] = joint_mat[1][2]; + joints[2] = -joint_mat[1][0]; + norm = Normalize(joints); + if (norm < FLT_EPSILON) { + norm = (joint_mat[1][1] < 0.f) ? M_PI : 0.f; + } else { + norm = acos(joint_mat[1][1]); + } + VecMulf(joints, norm); + break; + case 6: // Y+Z + Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ); + joints[0] = 0.f; + break; + case 7: // X+Y+Z + // equivalent axis + joints[0] = (joint_mat[1][2]-joint_mat[2][1])*0.5f; + joints[1] = (joint_mat[2][0]-joint_mat[0][2])*0.5f; + joints[2] = (joint_mat[0][1]-joint_mat[1][0])*0.5f; + sa = VecLength(joints); + ca = (joint_mat[0][0]+joint_mat[1][1]+joint_mat[1][1]-1.0f)*0.5f; + if (sa > FLT_EPSILON) { + norm = atan2(sa,ca)/sa; + } else { + if (ca < 0.0) { + norm = M_PI; + VecMulf(joints,0.f); + if (joint_mat[0][0] > 0.f) { + joints[0] = 1.0f; + } else if (joint_mat[1][1] > 0.f) { + joints[1] = 1.0f; + } else { + joints[2] = 1.0f; + } + } else { + norm = 0.0; + } + } + VecMulf(joints,norm); + break; + } + return newVectorObject(joints, 3, Py_NEW, NULL); +} + +int BL_ArmatureChannel::py_attr_set_joint_rotation(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v); + bPoseChannel* pchan = self->m_posechannel; + PyObject *item; + float joints[3]; + float quat[4]; + + if (!PySequence_Check(value) || PySequence_Size(value) != 3) { + PyErr_SetString(PyExc_AttributeError, "expected a sequence of [3] floats"); + return PY_SET_ATTR_FAIL; + } + for (int i=0; i<3; i++) { + item = PySequence_GetItem(value, i); /* new ref */ + joints[i] = PyFloat_AsDouble(item); + Py_DECREF(item); + if (joints[i] == -1.0f && PyErr_Occurred()) { + PyErr_SetString(PyExc_AttributeError, "expected a sequence of [3] floats"); + return PY_SET_ATTR_FAIL; + } + } + + int flag = 0; + if (!(pchan->ikflag & BONE_IK_NO_XDOF)) + flag |= 1; + if (!(pchan->ikflag & BONE_IK_NO_YDOF)) + flag |= 2; + if (!(pchan->ikflag & BONE_IK_NO_ZDOF)) + flag |= 4; + QuatOne(quat); + switch (flag) { + case 0: // fixed joint + break; + case 1: // X only + joints[1] = joints[2] = 0.f; + EulOToQuat(joints, EULER_ORDER_XYZ, quat); + break; + case 2: // Y only + joints[0] = joints[2] = 0.f; + EulOToQuat(joints, EULER_ORDER_XYZ, quat); + break; + case 3: // X+Y + joints[2] = 0.f; + EulOToQuat(joints, EULER_ORDER_ZYX, quat); + break; + case 4: // Z only + joints[0] = joints[1] = 0.f; + EulOToQuat(joints, EULER_ORDER_XYZ, quat); + break; + case 5: // X+Z + // X and Z are components of an equivalent rotation axis + joints[1] = 0; + VecRotToQuat(joints, VecLength(joints), quat); + break; + case 6: // Y+Z + joints[0] = 0.f; + EulOToQuat(joints, EULER_ORDER_XYZ, quat); + break; + case 7: // X+Y+Z + // equivalent axis + VecRotToQuat(joints, VecLength(joints), quat); + break; + } + if (pchan->rotmode > 0) { + QuatToEulO(quat, joints, pchan->rotmode); + VecCopyf(pchan->eul, joints); + } else + QuatCopy(pchan->quat, quat); + return PY_SET_ATTR_SUCCESS; +} + +// ************************* +// BL_ArmatureBone +// +// Access to Bone structure +PyTypeObject BL_ArmatureBone::Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BL_ArmatureBone", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_bone_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &CValue::Type, + 0,0,0,0,0,0, + py_base_new +}; + +// not used since this class is never instantiated +PyObject *BL_ArmatureBone::GetProxy() +{ + return NULL; +} +PyObject *BL_ArmatureBone::NewProxy(bool py_owns) +{ + return NULL; +} + +PyObject *BL_ArmatureBone::py_bone_repr(PyObject *self) +{ + Bone* bone = static_cast<Bone*>BGE_PROXY_PTR(self); + return PyUnicode_FromString(bone->name); +} + +PyMethodDef BL_ArmatureBone::Methods[] = { + {NULL,NULL} //Sentinel +}; + +/* no attributes on C++ class since it is never instantiated */ +PyAttributeDef BL_ArmatureBone::Attributes[] = { + { NULL } //Sentinel +}; + +// attributes that work on proxy ptr (points to a Bone structure) +PyAttributeDef BL_ArmatureBone::AttributesPtr[] = { + KX_PYATTRIBUTE_CHAR_RO("name",Bone,name), + KX_PYATTRIBUTE_FLAG_RO("connected",Bone,flag, BONE_CONNECTED), + KX_PYATTRIBUTE_FLAG_RO("hinge",Bone,flag, BONE_HINGE), + KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("inherit_scale",Bone,flag, BONE_NO_SCALE), + KX_PYATTRIBUTE_SHORT_RO("bbone_segments",Bone,segments), + KX_PYATTRIBUTE_FLOAT_RO("roll",Bone,roll), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("head",Bone,head,3), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("tail",Bone,tail,3), + KX_PYATTRIBUTE_FLOAT_RO("length",Bone,length), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("arm_head",Bone,arm_head,3), + KX_PYATTRIBUTE_FLOAT_VECTOR_RO("arm_tail",Bone,arm_tail,3), + KX_PYATTRIBUTE_FLOAT_MATRIX_RO("arm_mat",Bone,arm_mat,4), + KX_PYATTRIBUTE_FLOAT_MATRIX_RO("bone_mat",Bone,bone_mat,4), + KX_PYATTRIBUTE_RO_FUNCTION("parent",BL_ArmatureBone,py_bone_get_parent), + KX_PYATTRIBUTE_RO_FUNCTION("children",BL_ArmatureBone,py_bone_get_parent), + { NULL } //Sentinel +}; + +PyObject *BL_ArmatureBone::py_bone_get_parent(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + Bone* bone = reinterpret_cast<Bone*>BGE_PROXY_PTR(self); + if (bone->parent) { + // create a proxy unconnected to any GE object + return NewProxyPlus_Ext(NULL,&Type,bone->parent,false); + } + Py_RETURN_NONE; +} + +PyObject *BL_ArmatureBone::py_bone_get_children(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + Bone* bone = reinterpret_cast<Bone*>BGE_PROXY_PTR(self); + Bone* child; + int count = 0; + for (child=(Bone*)bone->childbase.first; child; child=(Bone*)child->next) + count++; + + PyObject* childrenlist = PyList_New(count); + + for (count = 0, child=(Bone*)bone->childbase.first; child; child=(Bone*)child->next, ++count) + PyList_SET_ITEM(childrenlist,count,NewProxyPlus_Ext(NULL,&Type,child,false)); + + return childrenlist; +} + +#endif // DISABLE_PYTHON diff --git a/source/gameengine/Converter/BL_ArmatureChannel.h b/source/gameengine/Converter/BL_ArmatureChannel.h new file mode 100644 index 00000000000..249fa4c8ac7 --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureChannel.h @@ -0,0 +1,95 @@ +/** + * $Id: BL_ArmatureChannel.h 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BL_ARMATURECHANNEL +#define __BL_ARMATURECHANNEL + +#include "DNA_action_types.h" +#include "GEN_HashedPtr.h" +#include "GEN_Map.h" +#include "PyObjectPlus.h" + +class SCA_IObject; +class KX_GameObject; +class BL_ArmatureObject; +struct bConstraint; +struct bPoseChannel; +struct Object; +struct bPose; + +class BL_ArmatureChannel : public PyObjectPlus +{ + // use Py_HeaderPtr since we use generic pointer in proxy + Py_HeaderPtr; + +private: + friend class BL_ArmatureObject; + struct bPoseChannel* m_posechannel; + BL_ArmatureObject* m_armature; + +public: + BL_ArmatureChannel(class BL_ArmatureObject *armature, + struct bPoseChannel *posechannel); + virtual ~BL_ArmatureChannel(); + +#ifndef DISABLE_PYTHON + // Python access + virtual PyObject* py_repr(void); + + static PyObject* py_attr_getattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static int py_attr_setattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + static PyObject* py_attr_get_joint_rotation(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static int py_attr_set_joint_rotation(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); +#endif // DISABLE_PYTHON +}; + +/* this is a factory class to access bBone data field in the GE. + It's not supposed to be instantiated, we only need it for the Attributes and Method array. + The actual proxy object will be manually created using NewProxyPtr */ +class BL_ArmatureBone : public PyObjectPlus +{ + // use Py_HeaderPtr since we use generic pointer in proxy + Py_HeaderPtr; +private: + // make constructor private to make sure no one tries to instantiate this class + BL_ArmatureBone() {} + virtual ~BL_ArmatureBone() {} + +public: + +#ifndef DISABLE_PYTHON + static PyObject *py_bone_repr(PyObject *self); + static PyObject *py_bone_get_parent(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static PyObject *py_bone_get_children(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); +#endif + +}; + + +#endif //__BL_ARMATURECHANNEL + diff --git a/source/gameengine/Converter/BL_ArmatureConstraint.cpp b/source/gameengine/Converter/BL_ArmatureConstraint.cpp new file mode 100644 index 00000000000..4c470b67387 --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureConstraint.cpp @@ -0,0 +1,454 @@ +/** + * $Id: BL_ArmatureConstraint.cpp 23562 2009-09-29 21:42:40Z campbellbarton $ + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "DNA_constraint_types.h" +#include "DNA_action_types.h" +#include "BL_ArmatureConstraint.h" +#include "BL_ArmatureObject.h" +#include "BLI_arithb.h" +#include "BLI_string.h" + +#ifndef DISABLE_PYTHON + +PyTypeObject BL_ArmatureConstraint::Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BL_ArmatureConstraint", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &CValue::Type, + 0,0,0,0,0,0, + py_base_new +}; + +PyObject* BL_ArmatureConstraint::py_repr(void) +{ + return PyUnicode_FromString(m_name); +} + +#endif // DISABLE_PYTHON + +BL_ArmatureConstraint::BL_ArmatureConstraint( + BL_ArmatureObject *armature, + bPoseChannel *posechannel, + bConstraint *constraint, + KX_GameObject* target, + KX_GameObject* subtarget) + : PyObjectPlus(), m_armature(armature), m_constraint(constraint), m_posechannel(posechannel) +{ + m_target = target; + m_blendtarget = (target) ? target->GetBlenderObject() : NULL; + m_subtarget = subtarget; + m_blendsubtarget = (subtarget) ? subtarget->GetBlenderObject() : NULL; + m_pose = m_subpose = NULL; + if (m_blendtarget) { + Mat4CpyMat4(m_blendmat, m_blendtarget->obmat); + if (m_blendtarget->type == OB_ARMATURE) + m_pose = m_blendtarget->pose; + } + if (m_blendsubtarget) { + Mat4CpyMat4(m_blendsubmat, m_blendsubtarget->obmat); + if (m_blendsubtarget->type == OB_ARMATURE) + m_subpose = m_blendsubtarget->pose; + } + if (m_target) + m_target->RegisterObject(m_armature); + if (m_subtarget) + m_subtarget->RegisterObject(m_armature); + BLI_snprintf(m_name, sizeof(m_name), "%s:%s", m_posechannel->name, m_constraint->name); +} + +BL_ArmatureConstraint::~BL_ArmatureConstraint() +{ + if (m_target) + m_target->UnregisterObject(m_armature); + if (m_subtarget) + m_subtarget->UnregisterObject(m_armature); +} + +BL_ArmatureConstraint* BL_ArmatureConstraint::GetReplica() const +{ + BL_ArmatureConstraint* replica = new BL_ArmatureConstraint(*this); + replica->ProcessReplica(); + return replica; +} + +void BL_ArmatureConstraint::ReParent(BL_ArmatureObject* armature) +{ + m_armature = armature; + if (m_target) + m_target->RegisterObject(armature); + if (m_subtarget) + m_subtarget->RegisterObject(armature); + // find the corresponding constraint in the new armature object + if (m_constraint) { + bPose* newpose = armature->GetOrigPose(); + char* constraint = m_constraint->name; + char* posechannel = m_posechannel->name; + bPoseChannel* pchan; + bConstraint* pcon; + m_constraint = NULL; + m_posechannel = NULL; + // and locate the constraint + for (pchan = (bPoseChannel*)newpose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) { + if (!strcmp(pchan->name, posechannel)) { + // now locate the constraint + for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) { + if (!strcmp(pcon->name, constraint)) { + m_constraint = pcon; + m_posechannel = pchan; + break; + } + } + break; + } + } + } +} + +void BL_ArmatureConstraint::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map) +{ + void **h_obj = (*obj_map)[m_target]; + if (h_obj) { + m_target->UnregisterObject(m_armature); + m_target = (KX_GameObject*)(*h_obj); + m_target->RegisterObject(m_armature); + } + h_obj = (*obj_map)[m_subtarget]; + if (h_obj) { + m_subtarget->UnregisterObject(m_armature); + m_subtarget = (KX_GameObject*)(*h_obj); + m_subtarget->RegisterObject(m_armature); + } +} + +bool BL_ArmatureConstraint::UnlinkObject(SCA_IObject* clientobj) +{ + bool res=false; + if (clientobj == m_target) { + m_target = NULL; + res = true; + } + if (clientobj == m_subtarget) { + m_subtarget = NULL; + res = true; + } + return res; +} + +void BL_ArmatureConstraint::UpdateTarget() +{ + if (m_constraint && !(m_constraint->flag&CONSTRAINT_OFF) && (!m_blendtarget || m_target)) { + if (m_blendtarget) { + // external target, must be updated + m_target->UpdateBlenderObjectMatrix(m_blendtarget); + if (m_pose && m_target->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) + // update the pose in case a bone is specified in the constraint target + m_blendtarget->pose = ((BL_ArmatureObject*)m_target)->GetOrigPose(); + } + if (m_blendsubtarget && m_subtarget) { + m_subtarget->UpdateBlenderObjectMatrix(m_blendsubtarget); + if (m_subpose && m_subtarget->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) + m_blendsubtarget->pose = ((BL_ArmatureObject*)m_target)->GetOrigPose(); + } + } +} + +void BL_ArmatureConstraint::RestoreTarget() +{ + if (m_constraint && !(m_constraint->flag&CONSTRAINT_OFF) && (!m_blendtarget || m_target)) { + if (m_blendtarget) { + Mat4CpyMat4(m_blendtarget->obmat, m_blendmat); + if (m_pose) + m_blendtarget->pose = m_pose; + } + if (m_blendsubtarget && m_subtarget) { + Mat4CpyMat4(m_blendsubtarget->obmat, m_blendsubmat); + if (m_subpose) + m_blendsubtarget->pose = m_subpose; + } + } +} + +bool BL_ArmatureConstraint::Match(const char* posechannel, const char* constraint) +{ + return (!strcmp(m_posechannel->name, posechannel) && !strcmp(m_constraint->name, constraint)); +} + +void BL_ArmatureConstraint::SetTarget(KX_GameObject* target) +{ + if (m_blendtarget) { + if (target != m_target) { + m_target->UnregisterObject(m_armature); + m_target = target; + if (m_target) + m_target->RegisterObject(m_armature); + } + } + +} + +void BL_ArmatureConstraint::SetSubtarget(KX_GameObject* subtarget) +{ + if (m_blendsubtarget) { + if (subtarget != m_subtarget) { + m_subtarget->UnregisterObject(m_armature); + m_subtarget = subtarget; + if (m_subtarget) + m_subtarget->RegisterObject(m_armature); + } + } + +} + +#ifndef DISABLE_PYTHON + +// PYTHON + +PyMethodDef BL_ArmatureConstraint::Methods[] = { + {NULL,NULL} //Sentinel +}; + +// order of definition of attributes, must match Attributes[] array +#define BCA_TYPE 0 +#define BCA_NAME 1 +#define BCA_ENFORCE 2 +#define BCA_HEADTAIL 3 +#define BCA_LINERROR 4 +#define BCA_ROTERROR 5 +#define BCA_TARGET 6 +#define BCA_SUBTARGET 7 +#define BCA_ACTIVE 8 +#define BCA_IKWEIGHT 9 +#define BCA_IKTYPE 10 +#define BCA_IKFLAG 11 +#define BCA_IKDIST 12 +#define BCA_IKMODE 13 + +PyAttributeDef BL_ArmatureConstraint::Attributes[] = { + // Keep these attributes in order of BCA_ defines!!! used by py_attr_getattr and py_attr_setattr + KX_PYATTRIBUTE_RO_FUNCTION("type",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RO_FUNCTION("name",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RW_FUNCTION("enforce",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RW_FUNCTION("headtail",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RO_FUNCTION("lin_error",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RO_FUNCTION("rot_error",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RW_FUNCTION("target",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RW_FUNCTION("subtarget",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RW_FUNCTION("active",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RW_FUNCTION("ik_weight",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RO_FUNCTION("ik_type",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RO_FUNCTION("ik_flag",BL_ArmatureConstraint,py_attr_getattr), + KX_PYATTRIBUTE_RW_FUNCTION("ik_dist",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + KX_PYATTRIBUTE_RW_FUNCTION("ik_mode",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr), + + { NULL } //Sentinel +}; + + +PyObject* BL_ArmatureConstraint::py_attr_getattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ArmatureConstraint* self= static_cast<BL_ArmatureConstraint*>(self_v); + bConstraint* constraint = self->m_constraint; + bKinematicConstraint* ikconstraint = (constraint && constraint->type == CONSTRAINT_TYPE_KINEMATIC) ? (bKinematicConstraint*)constraint->data : NULL; + int attr_order = attrdef-Attributes; + + if (!constraint) { + PyErr_SetString(PyExc_AttributeError, "constraint is NULL"); + return NULL; + } + + switch (attr_order) { + case BCA_TYPE: + return PyLong_FromLong(constraint->type); + case BCA_NAME: + return PyUnicode_FromString(constraint->name); + case BCA_ENFORCE: + return PyFloat_FromDouble(constraint->enforce); + case BCA_HEADTAIL: + return PyFloat_FromDouble(constraint->headtail); + case BCA_LINERROR: + return PyFloat_FromDouble(constraint->lin_error); + case BCA_ROTERROR: + return PyFloat_FromDouble(constraint->rot_error); + case BCA_TARGET: + if (!self->m_target) + Py_RETURN_NONE; + else + return self->m_target->GetProxy(); + case BCA_SUBTARGET: + if (!self->m_subtarget) + Py_RETURN_NONE; + else + return self->m_subtarget->GetProxy(); + case BCA_ACTIVE: + return PyBool_FromLong(constraint->flag & CONSTRAINT_OFF); + case BCA_IKWEIGHT: + case BCA_IKTYPE: + case BCA_IKFLAG: + case BCA_IKDIST: + case BCA_IKMODE: + if (!ikconstraint) { + PyErr_SetString(PyExc_AttributeError, "constraint is not of IK type"); + return NULL; + } + switch (attr_order) { + case BCA_IKWEIGHT: + return PyFloat_FromDouble((ikconstraint)?ikconstraint->weight:0.0); + case BCA_IKTYPE: + return PyLong_FromLong(ikconstraint->type); + case BCA_IKFLAG: + return PyLong_FromLong(ikconstraint->flag); + case BCA_IKDIST: + return PyFloat_FromDouble(ikconstraint->dist); + case BCA_IKMODE: + return PyLong_FromLong(ikconstraint->mode); + } + // should not come here + break; + } + PyErr_SetString(PyExc_AttributeError, "constraint unknown attribute"); + return NULL; +} + +int BL_ArmatureConstraint::py_attr_setattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + BL_ArmatureConstraint* self= static_cast<BL_ArmatureConstraint*>(self_v); + bConstraint* constraint = self->m_constraint; + bKinematicConstraint* ikconstraint = (constraint && constraint->type == CONSTRAINT_TYPE_KINEMATIC) ? (bKinematicConstraint*)constraint->data : NULL; + int attr_order = attrdef-Attributes; + int ival; + double dval; + char* sval; + KX_GameObject *oval; + + if (!constraint) { + PyErr_SetString(PyExc_AttributeError, "constraint is NULL"); + return PY_SET_ATTR_FAIL; + } + + switch (attr_order) { + case BCA_ENFORCE: + dval = PyFloat_AsDouble(value); + if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */ + PyErr_SetString(PyExc_AttributeError, "constraint.enforce = float: BL_ArmatureConstraint, expected a float between 0 and 1"); + return PY_SET_ATTR_FAIL; + } + constraint->enforce = dval; + return PY_SET_ATTR_SUCCESS; + + case BCA_HEADTAIL: + dval = PyFloat_AsDouble(value); + if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */ + PyErr_SetString(PyExc_AttributeError, "constraint.headtail = float: BL_ArmatureConstraint, expected a float between 0 and 1"); + return PY_SET_ATTR_FAIL; + } + constraint->headtail = dval; + return PY_SET_ATTR_SUCCESS; + + case BCA_TARGET: + if (!ConvertPythonToGameObject(value, &oval, true, "constraint.target = value: BL_ArmatureConstraint")) + return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error + self->SetTarget(oval); + return PY_SET_ATTR_SUCCESS; + + case BCA_SUBTARGET: + if (!ConvertPythonToGameObject(value, &oval, true, "constraint.subtarget = value: BL_ArmatureConstraint")) + return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error + self->SetSubtarget(oval); + return PY_SET_ATTR_SUCCESS; + + case BCA_ACTIVE: + ival = PyObject_IsTrue( value ); + if (ival == -1) { + PyErr_SetString(PyExc_AttributeError, "constraint.active = bool: BL_ArmatureConstraint, expected True or False"); + return PY_SET_ATTR_FAIL; + } + self->m_constraint->flag = (self->m_constraint->flag & ~CONSTRAINT_OFF) | ((ival)?0:CONSTRAINT_OFF); + return PY_SET_ATTR_SUCCESS; + + case BCA_IKWEIGHT: + case BCA_IKDIST: + case BCA_IKMODE: + if (!ikconstraint) { + PyErr_SetString(PyExc_AttributeError, "constraint is not of IK type"); + return PY_SET_ATTR_FAIL; + } + switch (attr_order) { + case BCA_IKWEIGHT: + dval = PyFloat_AsDouble(value); + if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */ + PyErr_SetString(PyExc_AttributeError, "constraint.weight = float: BL_ArmatureConstraint, expected a float between 0 and 1"); + return PY_SET_ATTR_FAIL; + } + ikconstraint->weight = dval; + return PY_SET_ATTR_SUCCESS; + + case BCA_IKDIST: + dval = PyFloat_AsDouble(value); + if (dval < 0.0f) { /* also accounts for non float */ + PyErr_SetString(PyExc_AttributeError, "constraint.ik_dist = float: BL_ArmatureConstraint, expected a positive float"); + return PY_SET_ATTR_FAIL; + } + ikconstraint->dist = dval; + return PY_SET_ATTR_SUCCESS; + + case BCA_IKMODE: + ival = PyLong_AsLong(value); + if (ival < 0) { + PyErr_SetString(PyExc_AttributeError, "constraint.ik_mode = integer: BL_ArmatureConstraint, expected a positive integer"); + return PY_SET_ATTR_FAIL; + } + ikconstraint->mode = ival; + return PY_SET_ATTR_SUCCESS; + } + // should not come here + break; + + } + + PyErr_SetString(PyExc_AttributeError, "constraint unknown attribute"); + return PY_SET_ATTR_FAIL; +} + +#endif // DISABLE_PYTHON diff --git a/source/gameengine/Converter/BL_ArmatureConstraint.h b/source/gameengine/Converter/BL_ArmatureConstraint.h new file mode 100644 index 00000000000..c1510b3c56a --- /dev/null +++ b/source/gameengine/Converter/BL_ArmatureConstraint.h @@ -0,0 +1,118 @@ +/** + * $Id: BL_ArmatureConstraint.h 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BL_ARMATURECONSTRAINT +#define __BL_ARMATURECONSTRAINT + +#include "DNA_constraint_types.h" +#include "GEN_HashedPtr.h" +#include "GEN_Map.h" +#include "PyObjectPlus.h" + +class SCA_IObject; +class KX_GameObject; +class BL_ArmatureObject; +struct bConstraint; +struct bPoseChannel; +struct Object; +struct bPose; + +/** + * SG_DList : element of controlled constraint list + * head = BL_ArmatureObject::m_controlledConstraints + * SG_QList : not used + */ +class BL_ArmatureConstraint : public PyObjectPlus +{ + Py_Header; + +private: + struct bConstraint* m_constraint; + struct bPoseChannel* m_posechannel; + class BL_ArmatureObject* m_armature; + char m_name[64]; + KX_GameObject* m_target; + KX_GameObject* m_subtarget; + struct Object* m_blendtarget; + struct Object* m_blendsubtarget; + float m_blendmat[4][4]; + float m_blendsubmat[4][4]; + struct bPose* m_pose; + struct bPose* m_subpose; + +public: + BL_ArmatureConstraint(class BL_ArmatureObject *armature, + struct bPoseChannel *posechannel, + struct bConstraint *constraint, + KX_GameObject* target, + KX_GameObject* subtarget); + virtual ~BL_ArmatureConstraint(); + + BL_ArmatureConstraint* GetReplica() const; + void ReParent(BL_ArmatureObject* armature); + void Relink(GEN_Map<GEN_HashedPtr, void*> *map); + bool UnlinkObject(SCA_IObject* clientobj); + + void UpdateTarget(); + void RestoreTarget(); + + bool Match(const char* posechannel, const char* constraint); + const char* GetName() { return m_name; } + + void SetConstraintFlag(int flag) + { + if (m_constraint) + m_constraint->flag |= flag; + } + void ClrConstraintFlag(int flag) + { + if (m_constraint) + m_constraint->flag &= ~flag; + } + void SetWeight(float weight) + { + if (m_constraint && m_constraint->type == CONSTRAINT_TYPE_KINEMATIC && m_constraint->data) { + bKinematicConstraint* con = (bKinematicConstraint*)m_constraint->data; + con->weight = weight; + } + } + void SetTarget(KX_GameObject* target); + void SetSubtarget(KX_GameObject* subtarget); + +#ifndef DISABLE_PYTHON + + // Python access + virtual PyObject* py_repr(void); + + static PyObject* py_attr_getattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + static int py_attr_setattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); +#endif // DISABLE_PYTHON +}; + +#endif //__BL_ARMATURECONSTRAINT + diff --git a/source/gameengine/GameLogic/SCA_BasicEventManager.cpp b/source/gameengine/GameLogic/SCA_BasicEventManager.cpp new file mode 100644 index 00000000000..6d81784338e --- /dev/null +++ b/source/gameengine/GameLogic/SCA_BasicEventManager.cpp @@ -0,0 +1,61 @@ +/** + * Manager for 'always' events. Since always sensors can operate in pulse + * mode, they need to be activated. + * + * $Id: SCA_BasicEventManager.cpp 23490 2009-09-25 16:30:15Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "SCA_BasicEventManager.h" +#include "SCA_LogicManager.h" +#include <vector> +#include "SCA_ISensor.h" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +using namespace std; + +SCA_BasicEventManager::SCA_BasicEventManager(class SCA_LogicManager* logicmgr) + : SCA_EventManager(logicmgr, BASIC_EVENTMGR) +{ +} + +SCA_BasicEventManager::~SCA_BasicEventManager() +{ +} + +void SCA_BasicEventManager::NextFrame() +{ + SG_DList::iterator<SCA_ISensor> it(m_sensors); + for (it.begin();!it.end();++it) + { + (*it)->Activate(m_logicmgr); + } +} + diff --git a/source/gameengine/GameLogic/SCA_BasicEventManager.h b/source/gameengine/GameLogic/SCA_BasicEventManager.h new file mode 100644 index 00000000000..6b68cfcbe16 --- /dev/null +++ b/source/gameengine/GameLogic/SCA_BasicEventManager.h @@ -0,0 +1,57 @@ +/** + * Manager for sensor that only need to call Update + * + * $Id: SCA_BasicEventManager.h 23499 2009-09-26 20:03:01Z nexyon $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __SCA_BASICEVENTMGR +#define __SCA_BASICEVENTMGR + +#include "SCA_EventManager.h" +#include <vector> + +using namespace std; + +class SCA_BasicEventManager : public SCA_EventManager +{ +public: + SCA_BasicEventManager(class SCA_LogicManager* logicmgr); + ~SCA_BasicEventManager(); + + virtual void NextFrame(); + + +#ifdef WITH_CXX_GUARDEDALLOC +public: + void *operator new( unsigned int num_bytes) { return MEM_mallocN(num_bytes, "GE:SCA_BasicEventManager"); } + void operator delete( void *mem ) { MEM_freeN(mem); } +#endif +}; + +#endif //__SCA_BASICEVENTMGR + diff --git a/source/gameengine/Ketsji/KX_ArmatureSensor.cpp b/source/gameengine/Ketsji/KX_ArmatureSensor.cpp new file mode 100644 index 00000000000..007dc459793 --- /dev/null +++ b/source/gameengine/Ketsji/KX_ArmatureSensor.cpp @@ -0,0 +1,208 @@ +/** + * Armature sensor + * + * $Id: KX_ArmatureSensor.cpp 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "BKE_constraint.h" +#include "DNA_sensor_types.h" + +#include "BL_ArmatureObject.h" +#include "KX_ArmatureSensor.h" +#include "SCA_EventManager.h" +#include "SCA_LogicManager.h" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +KX_ArmatureSensor::KX_ArmatureSensor(class SCA_EventManager* eventmgr, + SCA_IObject* gameobj, + const char *posechannel, + const char *constraintname, + int type, + float value) + : SCA_ISensor(gameobj,eventmgr), + m_constraint(NULL), + m_posechannel(posechannel), + m_constraintname(constraintname), + m_type(type), + m_value(value) +{ + FindConstraint(); +} + +void KX_ArmatureSensor::Init() +{ + m_lastresult = m_invert?true:false; + m_result = false; + m_reset = true; +} + +void KX_ArmatureSensor::FindConstraint() +{ + m_constraint = NULL; + + if (m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { + BL_ArmatureObject* armobj = (BL_ArmatureObject*)m_gameobj; + // get the persistent pose structure + bPose* pose = armobj->GetOrigPose(); + bPoseChannel* pchan; + bConstraint* pcon; + // and locate the constraint + for (pchan = (bPoseChannel*)pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) { + if (!strcmp(pchan->name, m_posechannel)) { + // now locate the constraint + for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) { + if (!strcmp(pcon->name, m_constraintname)) { + if (pcon->flag & CONSTRAINT_DISABLE) + /* this constraint is not valid, can't use it */ + break; + m_constraint = pcon; + break; + } + } + break; + } + } + } +} + + +CValue* KX_ArmatureSensor::GetReplica() +{ + KX_ArmatureSensor* replica = new KX_ArmatureSensor(*this); + // m_range_expr must be recalculated on replica! + replica->ProcessReplica(); + return replica; +} + +void KX_ArmatureSensor::ReParent(SCA_IObject* parent) +{ + SCA_ISensor::ReParent(parent); + // must remap the constraint + FindConstraint(); +} + +bool KX_ArmatureSensor::IsPositiveTrigger() +{ + return (m_invert) ? !m_result : m_result; +} + + +KX_ArmatureSensor::~KX_ArmatureSensor() +{ +} + +bool KX_ArmatureSensor::Evaluate() +{ + bool reset = m_reset && m_level; + + m_reset = false; + if (!m_constraint) + return false; + switch (m_type) { + case SENS_ARM_STATE_CHANGED: + m_result = !(m_constraint->flag & CONSTRAINT_OFF); + break; + case SENS_ARM_LIN_ERROR_BELOW: + m_result = (m_constraint->lin_error < m_value); + break; + case SENS_ARM_LIN_ERROR_ABOVE: + m_result = (m_constraint->lin_error > m_value); + break; + case SENS_ARM_ROT_ERROR_BELOW: + m_result = (m_constraint->rot_error < m_value); + break; + case SENS_ARM_ROT_ERROR_ABOVE: + m_result = (m_constraint->rot_error > m_value); + break; + } + if (m_lastresult!=m_result) + { + m_lastresult = m_result; + return true; + } + return (reset) ? true : false; +} + +#ifndef DISABLE_PYTHON + +/* ------------------------------------------------------------------------- */ +/* Python functions */ +/* ------------------------------------------------------------------------- */ + +/* Integration hooks ------------------------------------------------------- */ +PyTypeObject KX_ArmatureSensor::Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "KX_ArmatureSensor", + sizeof(PyObjectPlus_Proxy), + 0, + py_base_dealloc, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0,0,0,0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + 0,0,0,0,0,0,0, + Methods, + 0, + 0, + &SCA_ISensor::Type, + 0,0,0,0,0,0, + py_base_new +}; + +PyMethodDef KX_ArmatureSensor::Methods[] = { + {NULL,NULL} //Sentinel +}; + +PyAttributeDef KX_ArmatureSensor::Attributes[] = { + KX_PYATTRIBUTE_RO_FUNCTION("constraint", KX_ArmatureSensor, pyattr_get_constraint), + KX_PYATTRIBUTE_FLOAT_RW("value",-FLT_MAX,FLT_MAX,KX_ArmatureSensor,m_value), + KX_PYATTRIBUTE_INT_RW("type",0,SENS_ARM_MAXTYPE,false,KX_ArmatureSensor,m_type), + { NULL } //Sentinel +}; + +PyObject* KX_ArmatureSensor::pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef) +{ + KX_ArmatureSensor* sensor = static_cast<KX_ArmatureSensor*>(self); + if (sensor->m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { + BL_ArmatureObject* armobj = (BL_ArmatureObject*)sensor->m_gameobj; + BL_ArmatureConstraint* constraint = armobj->GetConstraint(sensor->m_posechannel, sensor->m_constraintname); + if (constraint) + return constraint->GetProxy(); + } + Py_RETURN_NONE; +} + +#endif // DISABLE_PYTHON diff --git a/source/gameengine/Ketsji/KX_ArmatureSensor.h b/source/gameengine/Ketsji/KX_ArmatureSensor.h new file mode 100644 index 00000000000..710fbe1b7bc --- /dev/null +++ b/source/gameengine/Ketsji/KX_ArmatureSensor.h @@ -0,0 +1,89 @@ +/** + * Property sensor + * + * $Id: KX_ArmatureSensor.h 23562 2009-09-29 21:42:40Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __KX_ARMATURESENSOR +#define __KX_ARMATURESENSOR + +struct bConstraint; + +#include "SCA_ISensor.h" +#include "DNA_sensor_types.h" + +class KX_ArmatureSensor : public SCA_ISensor +{ + Py_Header; + //class CExpression* m_rightexpr; + +protected: + +public: + KX_ArmatureSensor(class SCA_EventManager* eventmgr, + SCA_IObject* gameobj, + const char *posechannel, + const char *constraintname, + int type, + float value); + + /** + * For property sensor, it is used to release the pre-calculated expression + * so that self references are removed before the sensor itself is released + */ + virtual ~KX_ArmatureSensor(); + virtual CValue* GetReplica(); + virtual void ReParent(SCA_IObject* parent); + virtual void Init(); + virtual bool Evaluate(); + virtual bool IsPositiveTrigger(); + + // identify the constraint that this actuator controls + void FindConstraint(); + +#ifndef DISABLE_PYTHON + + /* --------------------------------------------------------------------- */ + /* Python interface ---------------------------------------------------- */ + /* --------------------------------------------------------------------- */ + static PyObject* pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef); + +#endif // DISABLE_PYTHON + +private: + struct bConstraint* m_constraint; + STR_String m_posechannel; + STR_String m_constraintname; + int m_type; + float m_value; + bool m_result; + bool m_lastresult; +}; + +#endif + |