Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

У цій статті буде розглянуто написання програм на Objective-C / Objectiver-C ++. працюючих з бібліотекою OpenGL. При цьому на відміну від попередньої статті ми буде робити повноцінне Cocoa, використовувати Interface Builder для дизайну вікна, використовувати стандартні класи для завантаження текстур і обробляти повідомлення від миші.

Отже нашим першим кроком, як і раніше, буде запуск XCode і вибір типу програми - 'Cocoa Application'.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 1. Вибір типу програми.

Дамо ім'я проекту - OpenGL Cocoa Example і поставимо шлях для нього.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 2. Завдання імені та каталогу для додатка.

Після створення проекту відразу ж приступимо до роботи з файлом MainMenu.nib (викликавши Interface Builder подвійним клацанням миші по цьому файлу).

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 3. Початок роботи з MainMenu.nib.

Для роботи з OpenGL в Cocoa найзручніше використовувати об'єкти, успадковані від класу NSOpenGLView. Даний клас містить практично все, що потрібно для роботи з OpenGL, при цьому у багатьох випадках виявляється достатнім просто перевизначити метод drawRect :.

Відкриємо в вікні проекту Interface Builder 'а' ятати Classes і почнемо набирати в поле пошуку назву класу, від якого ми хочемо успадкувати - NSOpenGLView.

Після того, як потрібний нам клас буде знайдений, виділимо його і натиснемо Enter. Це призведе до створення нового класу, успадкованого від NSOpenGLView, як його імені буде запропоновано MyOpenGLView.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 4. Створення нового класу на базі NSOpenGLView.

Далі за допомогою команди меню Classes / Create Files for MyOpenGLView. створимо необхідні файли для цього класу (рис 5) і додамо їх до поточного проекту (рис 6).

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 5. Створення .m і .h файлів для нового класу.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 6. Збереження створених файлів.

Відкриємо тепер палітру об'єктів на останній закладці, де ми побачимо готовий об'єкт для роботи з OpenGL - NSOpenGLView.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 7. Стандартні компоненти для роботи з OpenGL і Web.

Перетягнемо його у вікно нашого застосування і вирівняємо як показано на рис 8.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 8. Розташування OpenGL-компонента у вікні.

Після цього задамо параметри Autosizing як на рис. 9.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 9. Завдання параметрів Autosizing.

Далі, оскільки ми хочемо використовувати об'єкт не класу NSOpenGLView, а об'єкт похідного від нього класу MyOpenGLView, то нам потрібно скористатися можливістю Interface Builder 'а явно задавати використовуваний клас. Для цього відкриємо в інспектора ятати Custom Class і виберемо в якості реально використовуваного класу MyOpenGLView.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 10. Завдання реально використовуваного класу.

Крім явної настройки використовуваного класу потрібно поставити ряд атрибутів, безпосередньо пов'язаних з роботою з OpenGL. Для цього відкриємо закладку Attributes і задамо параметри OpenGL як показано на рис. 11.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 11. Завдання параметрів OpenGL.

Перейдемо тепер в XCode і в файлі MyOpenGLView.m задамо найпростішу реалізацію методу drawRect :.

Зверніть увагу, що всередині методу drawRect: ми відразу ж використовуємо команди OpenGL. Також зверніть увагу, що виведення зображення засобами OpenGL обов'язково завершується командою glFlush ().

Якщо зараз зібрати і запустити наш додаток, то ми побачимо чорний прямокутник всередині нашого вікна. Зверніть увагу, що при зміні розмірів вікна розміри чорного прямокутника змінюються відповідно.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 12. Чинне додаток.

Змінимо наш додаток для виведення чогось більш змістовного, ніж просто чорний екран. Тут нам дуже знадобиться можливість суміщення коду на С ++ і коду на Objective-C за рахунок використання Objective-C ++.

Перейменуємо в XCode файл MyOpenGLView.m в MyOpenGLView.mm (щоб повідомити компілятору, що даний файл містить програму на Objective-C ++).

Далі додамо в наш проект файли Vector2D.h. Vector2D.cpp. Vector3D.h. Vector3D.cpp. Vector4D.h і Vector4D.cpp.

Також змінимо вміст файлу MyOpenGLView.mm як в наступному лістингу.

Глобальні змінні і функція drawBox не несуть в собі ніякої Mac OS X - специфіки і служать для завдання параметрів проектування і повороту об'єкта.

Подивимося на метод reshape. Він практично повністю еквівалентний оброблювачу події reshape в GLUT за винятком того, що нові розміри не передаються явно, а їх потрібно витягти за допомогою посилки собі повідомлення bounds.

У відповідь на це повідомлення повертається структура NSRect, що містить в поле size розмір візуального компонента. Зверніть увагу, що розмір задається речовими (float) числами.

Після того, як розміри отримані, слід стандартний код, який нічим не відрізняється від відповідного коду для GLUT.

Наступними кроками буде додавання можливості повороту об'єкта мишею і підтримки текстур.

Для підтримки обертання об'єкта мишею нам знадобиться перевизначити в класі MyOpenGLView методи mouseDown: і mouseDragged :.

Перший з цих методів викликається при натисканні будь-якої з клавіш миші (якщо ви до сих вважаєте що на Маках у миші всього одна кнопка, то Ви сильно помиляєтесь). Даний метод отримує в якості параметра покажчик на об'єкт класу NSEvent. передавальний всю інформацію про відповідному подію. З цієї інформації нам потрібні лише координати точки, в якій відбулося натискання кнопки миші. Після чого дані координати запам'ятовуються в змінної mouseOld.

Для отримання координат курсора миші в момент об'єкту event надсилається повідомлення locationInWindow. яке повертає координати з системі координат вікна. Після цього можна скористатися методом convertPoint: fromView: класу NSView.

Цей метод служить для перекладу координат з системи координат одного view в систему координат іншого view. Якщо замість покажчика на об'єкт класу NSView, з системи координата якого слід перевести координати, передається nil. то в цьому випадку відбувається переклад з системи координат вікна, що містить даний view.

Mac OS X розрізняє (як і GLUT) прості переміщення миші (коли ні натиснута жодна кнопка миші) - їм відповідає повідомлення mouseMoved: - і переміщення миші з утриманням будь-якої з кнопок миші - їм відповідає повідомлення mouseDragged :.

Подібний розподіл має певний сенс - найчастіше потрібні саме повідомлення останнього типу, в той час як повідомлення першого типу найчастіше можна успішно ігнорувати. Тому в Mac OS X є можливість вмикати / вимикати посилку повідомлення mouseMoved: (хоча можливо це пов'язано зі спадщиною NextStep'а і боротьбою за його швидкодія).

Все, що нам потрібно зробити в методі mouseDragged :. це знайти різницю координат миші між минулим викликом і поточним, по цій різниці відкоригувати кути повороту і запам'ятати поточне значення координат в mouseOld. Після цього необхідно надіслати собі повідомлення setNeedsDisplay: з параметром YES, щоб викликати перемальовування вмісту вікна.

Відповідний код методу наводиться нижче.

Для завантаження текстури з файлу найпростіше скористатися готовими класами, наданими Mac OS X - за рахунок цього ми фактично отримуємо готову підтримку великої кількості форматів текстур.

Винесемо код завантаження текстури з файлу з заданим ім'ям в окремий метод:

Як видно з наведеного коду, вміст файлу з текстурою завантажується в об'єкт класу NSData (за допомогою повідомлення класу dataWithContentsOfFile:), по ньому створюється зображення у вигляді примірника класу NSBitmapImageRep (за допомогою повідомлення класу imageRepWithData:), після чого будується перевернуте в вертикалі зображення і завантажується в текстуру OpenGL стандартним чином.

На жаль завантаження текстури з файлу не слід поміщати в метод init * для даного класу. Це пов'язано зі специфікою завантаження з nib-файлів об'єктів, що вводяться користувачем класів.

Якщо для стандартних класів Interface Builder може просто покласти в nib-файл заархівований образ компоненти, то з одними класами він так вчинити не може, оскільки не має в своєму розпорядженні кодом для цих класів.

В результаті в nib-файл кладуться деякі дані, за якими на етапі завантаження спеціальним чином будується потрібний об'єкт. Ця процедура досить своєрідно і як наслідок для ініціалізації подібних об'єктів краще використовувати повідомлення awakeFromNib. посилається кожному об'єкту після того, як всі об'єкти їх nib-файлу будуть повністю завантажені.

Ми скористаємося альтернативним підходом (фактично це варіант load on demand або lazy evaluation) - в самому методі drawRect: ми перевіримо ідентифікатор текстури (всі instance -змінного в Objective-C завжди инициализируются нулем), і якщо він дорівнює нулю, то створимо текстуру завантаживши зображення з файлу.

На наступному малюнку наводиться скріншот вийшла програми.

Steps3d - tutorials - програмування для mac os x - пишемо opengl-додаток з використанням cocoa

Рис 13. Остаточне додаток.

Дуже зручним місцем для зберігання текстур є ресурси програми, точніше nib-файл. Для того, щоб додати текстуру в nib-файл достатньо відкрити закладку Images і перетягнути файл з текстурою туди.

Оскільки nib-файл - це насправді каталог, який є частиною каталогу додатка, то наш файл з текстурою буде звичайним файлом, лежачим десь всередині каталогу програми. Для отримання шляху на зображення з заданим ім'ям слід скористатися класом NSBundle і його методами.

Вище наведений метод loadTextureFromResource служить для завантаження в OpenGL текстури з ресурсів програми. Для цього спочатку необхідно отримати покажчик на об'єкт класу NSBundle. що відповідає тому nib -файлу, в якому знаходиться необхідна текстура.

Якщо ми хочемо помістити цю текстуру в головний nib-файл (MainMenu.nib), то для доступу до нього зручно скористатися методом класу mainBundle. повертає покажчик на об'єкт класу NSBundle, відповідним головному nib -файлу.

Після цього для отримання шляху до файлу із зображенням на його імені використовується повідомлення pathForImageResource: (для доступу до звукових ресурсів можна використовувати повідомлення pathForSoundResource. Для доступу до ресурсів будь-якого типу служить повідомлення pathForResource: ofType:).

Після того як отриманий шлях до файлу з текстурою, вона завантажується раніше введеним методом loadTextureFromFile :.

Конструктор uCoz