Показаны сообщения с ярлыком objective-c. Показать все сообщения
Показаны сообщения с ярлыком objective-c. Показать все сообщения

суббота, 11 июля 2015 г.

Сцены OpenGL в Mac OS X

Ну раз говорится про Mac OS, значит программировать будем в cocoa на языке Objective-C. Cоздадим простейшее приложение OpenGL, рисующее на "сцене" куб. Откроем среду разработки XCode и в меню выберем создать новый проект. В списке предложений находим Cocoa Application. Жмем Next.
Выбираем именем рашего проекта CocoaOpenGL и языком разработки объектный си. Далее произвольно заполняем остальные пустые поля. Жмем Next.

Видим "пустой" каркас нашего будущего приложения: 

Во вкладке Build Settings находим поле Linked Frameworks and Libraries и в появившемся списке выбираем OpenGL.framework . Фреймворк, как вы догадались, это заголовочные файлы и бинарные библиотеки для OpenGL .
 Далее создаем пустые заголовочный и реализующий класс Сube файлы из меню "создать новый файл". Класс Cube и является классом, реализующим OpenGL сцену.
Задаем имя CocoaOpenGL.h . Жмем Next:
Это мы создали заголовочный файл. Теперь аналогично создаем файл реализации(эти оба файла пока пусты):
Жмем Next:
 Пишем в поле имени CocoaOpenGL . Жмем Next:
 И, наконец, create, мы увидим пустой файл реализации:
 Новый заголовочный файл заполняем таким содержимым:
@interface Cube : NSOpenGLView
{
    bool        initialized;
    float        rotation[3];
}
+ (NSOpenGLPixelFormat*) basicPixelFormat;
- (void) drawRect: (NSRect) bounds;
- (void) resizeGL:(NSRect)rect;
- (id)initWithFrame:(NSRect)frameRect;
@end
Файл реализации делаем пока таким:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "CocoaOpenGL.h"
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
@implementation Cube
+ (NSOpenGLPixelFormat*) basicPixelFormat{
}
- (void) resizeGL:(NSRect)rect{
}
- (id)initWithFrame:(NSRect)frameRect{
}
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
// Здесь будет рисующий код
}
@end
Далее заполняем пустые тела функций следующим содержанием. basicPixelFormat :
    static NSOpenGLPixelFormatAttribute    attributes[] =   
    {
        //NSOpenGLPFAWindow,
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32,
        (NSOpenGLPixelFormatAttribute)nil
    };
    return ([[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]);
initWithFrame :
     NSOpenGLPixelFormat    *pf;
     pf   = [Cube basicPixelFormat];
     self = [super initWithFrame: frameRect pixelFormat: pf];  
     return (self);
drawRect :
    int            width, height;
    int            i, j;       
    float         aspectRatio;

    if (!initialized)
    {
        rotation[0] = 45.0f;
        rotation[1] = 45.0f;
        rotation[2] = 45.0f;
        initialized = true;
    }

    width  = rect.size.width;
    height = rect.size.height;
 
    glViewport(0, 0, width, height);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)width/(GLfloat)height;
    //  gluPerspective(100.0f, aspectRatio, 1, 2);
    if(width <= height)
    {
        glOrtho(-2.0f, 2.0f, -2/aspectRatio, 2/aspectRatio, 2.0f, -2.0f);
    }
    else
    {
        glOrtho(-2*aspectRatio, 2*aspectRatio, -2.0f, 2.0f, 2.0f, -2.0f);
    }
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(rotation[0], 1.0f, 0.0f, 0.0f);
    glRotatef(rotation[1], 0.0f, 1.0f, 0.0f);
    glRotatef(rotation[2], 0.0f, 0.0f, 1.0f);
    // рисуем куб
    glEnable(GL_DEPTH_TEST); 
    glBegin(GL_QUADS);
    for (i = 0; i < 6; i ++)
    {
        glColor3fv(colors[i]);
        for (j = 0; j < 4; j ++)
            glVertex3fv(corners[sides[i][j]]);
    }
    glEnd();  
  
    glFlush();
    //[[self openGLContext]flushBuffer];

Обратите внимание, что функция resizeGL - пуста. Код, который реагирует на изменение собственно сцены (не так в Линуксе и я Windows), находится теле основной рисующей функции drawRect. 
Также после директив импорта в файле реализации вставляем такие строки:
static const GLfloat    corners[8][3] =   
{
    {  1.0f,  1.0f,  1.0f },    // Front top right
    {  1.0f, -1.0f,  1.0f },    // Front bottom right
    { -1.0f, -1.0f,  1.0f },    // Front bottom left
    { -1.0f,  1.0f,  1.0f },    // Front top left
    {  1.0f,  1.0f, -1.0f },    // Back top right
    {  1.0f, -1.0f, -1.0f },    // Back bottom right
    { -1.0f, -1.0f, -1.0f },    // Back bottom left
    { -1.0f,  1.0f, -1.0f }     // Back top left
};
static const int    sides[6][4] =   
{
    { 0, 1, 2, 3 },                // Front
    { 4, 5, 6, 7 },                // Back
    { 0, 1, 5, 4 },                // Right
    { 2, 3, 7, 6 },                // Left
    { 0, 3, 7, 4 },                // Top
    { 1, 2, 6, 5 }                 // Bottom
};
static const GLfloat    colors[6][3] = 
{
    { 1.0f, 0.0f, 0.0f },        // Red
    { 0.0f, 1.0f, 0.0f },        // Green
    { 1.0f, 1.0f, 0.0f },        // Yellow
    { 0.0f, 0.0f, 1.0f },        // Blue
    { 1.0f, 0.0f, 1.0f },        // Magenta
    { 0.0f, 1.0f, 1.0f }         // Cyan
};
Далее идем в Main.storyboard:
 Добавляем OpenGL View c хранилища элементов управления главный вид приложения. В правом верхнем углу среды разработки в поле Custom Class указываем на класс Cube (при выделенном элементе управления OpenGL View)
 Далее на вкладке Build Settings поле Objective-C Automatic Reference Counting в значение No
 Для нашего OpenGL View элемента надо установить поле Depth в любое не-none значение:
 Компилируем, запускаем и видим:

вторник, 23 июня 2015 г.

Objective-C шпоргалка, часть 1

  • Члены класса называются инвариантами;
  • Создаваемые в Objective-С объекты классов размещаются в динамической памяти;
  • Тип id является указателем на объект любого класса (как void *);
  • Если тип, возвращаемы методом, не указан, то считается, что возвращается значение типа id;
  • Нулевой указатель именуется константой nil;
  • Инвариант isa присутствует в любом объекте класса, унаследовавшего класс NSObject;
  • Все классы должны наследовать класс NSObject;
  • Каждый класс сам по себе тоже является объектом;
  • Каждый объект Objective-C содержит в себе атрибут (инвариант) isa - указатель на объект класса данного объекта (экземпляра). Объект класса для данного объекта (экземпляра) автоматически создается компилятором и существует как один экземпляр, на который через isa ссылаются все экземпляры данного класса;
  • Все зарезервированные слова Objective-C, отличающиеся от зарезервированных слов языка С, начинаются с символа @;
  • Строковая константа класса NSString записывается как @”my string”;
  • Директива @protected стоит по умолчанию. Ограничивает область видимости инвариантов класса методами класса и методами производных классов;
  • Тип BOOL (на самом деле это unsigned char) принимает только константные значения YES и NO;
  • Объект выполняет метод если ему приходит сообщение, именуемое так же, как и требуемый метод. Такое сообщение называется селектор метода;
  • Директива @selector(name_of_method) возвращает скомпилированный селектор для имени метода name_of_method;
  • При посылке сообщения в nil оно исчезает;
  • Если в начале прототипа метода поставить знак '+', то такой метод будет считаться методом класса;
  • Такой метод не принимает неявный параметр self, соответственно указатель super тоже работать не будет;
  • Если в начале прототипа метода поставить знак '-', то такой метод называется методом объекта (или экземпляра);
  • Указатель на данные экземпляра текущего(данного) объекта доступен посредством зарезервированного слова self, а указатель на данные экземпляра базового класса – через super;
  • Все методы объектов в Objective-C являются виртуальными и всегда следуют динамическому полиморфизму;
  • В метод объекта также передается неявный параметр _cmd – селектор этого метода из глобальной таблицы селекторов;
  • Примеры описания (объявления) методов - метода объекта класса, метода класса без аргументов, с одним, с двумя и с переменным числом аргументов, соответственно:
    + (id)method1;
    - (void) method2;
    - (void) method3: (float) the_value1;
    - (void) method4: (float) the_value2 second_part_of_name_method4: (float) the_value3;
    - method: (id) object, ...;
  • Синтаксис вызова метода без параметров, с фиксированным числом параметров, с переменным числом параметров, соответственно, таков:
    [receiver method];
    [receiver method_arg1: 10 arg2: 10] - в данном случае имя метода является таким - method_arg1:arg2:  Этот момент наиболее трудно воспринимаем;
    [receiver variable_number_parameters: var1, var2, var3]; 
  • При посылке сообщения объекту, который принадлежит классу, не реализовавшему заказанный метод, возникает исключение, которое приводит к незапланированному завершению;
  • Поэтому, когда такая ситуация возможна, лучше провести соответствующую проверку:
    if ([object respondsToSelector: @selector(method::)])
    {
    [object method: 10 : 10];
    }
    else
    {

    }
    respondsToSelector - это метод класса NSObject;
  • Сообщение [receiver method] преобразуется в Си функцию с прототипом:
    id objc_msgSend(id receiver, SEL method, ...);
  • Тип SEL определен как char const*;
  • Во время выполнения все селекторы индексируются целыми значениями согласно глобальной таблице селекторов;
  • Глобальная таблица селекторов - это такая таблица, в которой некоему уникальному целому числу (селектору) соответствует своя строка (имя метода);
  • Функция objc_msgSend пользуясь инварианом isa oбъекта просматривает локальный список селекторов класса, чтобы определить, отвечает ли объект данного класса на сообщение method. Если у объекта-приемника данный селектор отсутствует, функция objc_msgSend() просматривает список селекторов его базового класса и т.д.;

четверг, 28 июня 2012 г.

Создание GTK+ приложения на Оbjective-C

Создаем файлы такого содержания:
MainWindow.h:


#import <gtk/gtk.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

id myMainWindow;

@interface MainWindow : NSObject{
GtkWidget *winMain; //главное GtkWindow
GtkWidget *button;
}


-(id)initWithArgCount:(int *)argc andArgVals:(char *[])argv;
-(void)destroyWidget; /* Освобождает ранее занятые ресурсы Gtk виджетами */
-(void)startGtkMainLoop; /* Стартует главный цикл обработки сообщений GTK */
-(void)printSomething; /* Пример некой Objective-C функции */

/** Cи фунции обратного вызова (обработчики сообщений)*/

void on_MainWindow_destroy(GtkObject *object, gpointer user_data);/* Вызывается, когда закрывается главное окно приложения */
void on_btnPushMe_clicked(GtkObject *object, gpointer user_data); /* Вызывается, когда мы жмем кнопку */

@end



MainWindow.m:


#import "MainWindow.h"

@implementation MainWindow

-(id)initWithArgCount:(int *)argc andArgVals:(char *[])argv{
if (self = [super init]) { //вызываем init родительского класса
winMain = gtk_window_new (GTK_WINDOW_TOPLEVEL); //создаем главное окно приложения
gtk_window_set_title (GTK_WINDOW (winMain), "Ну привет");
gtk_window_set_default_size(GTK_WINDOW(winMain), 230, 150);
button = gtk_button_new_with_label ("Нажми её!");//создаем кнопку
gtk_container_add (GTK_CONTAINER (winMain), button);
g_signal_connect (winMain, "destroy", G_CALLBACK (on_MainWindow_destroy), NULL);
//привязываем обработчики сообщений
g_signal_connect (button, "clicked", G_CALLBACK (on_btnPushMe_clicked), NULL);
gtk_widget_show_all(winMain); //показываем
}

myMainWindow = self; //назначаем C-compatible указатель
return self; //возвращаем указатель на этот объект
}

-(void)startGtkMainLoop{
gtk_main(); //стартуем основной цикл обработки сообщений gtk
}

-(void)printSomething{
NSLog(@"Printed from Objective-C`s NSLog function.");
printf("Also printed from standard printf function.\n");
}

-(void)destroyWidget{
myMainWindow = NULL;
if(GTK_IS_WIDGET (button)){
gtk_widget_destroy(button); //разрушаем кнопку
}
if(GTK_IS_WIDGET (winMain)){
gtk_widget_destroy(winMain); //разрушаем главное окно приложения
}
}

-(void)dealloc{
[self destroyWidget];
[super dealloc];
}

void
on_MainWindow_destroy(GtkObject *object, gpointer user_data){
gtk_main_quit(); //выходим из основного цикла обработки сообщений
}

void
on_btnPushMe_clicked(GtkObject *object, gpointer user_data){
printf("Button was clicked\n");
[myMainWindow printSomething]; //вызываем Objective-C функцию из Cи функции с помощью глобального указателяusing
}

@end


main.m:


#import "MainWindow.h"
#import <Foundation/NSAutoreleasePool.h>

int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//создаем пул
gtk_init(&argc, &argv); //инициализируем gtk
MainWindow *mainWindow = [[MainWindow alloc] initWithArgCount:&argc andArgVals:argv];
//создаем GUI
[mainWindow startGtkMainLoop]; //стартуем главный цикл обработки сообщений GTK loop
[mainWindow release]; //освобождаем ресурсы занятые GUI
[pool release]; //освобождаем пул
return 0; //выходим из приложения
}


Компилируем это все из текущей директории следующей командой:
$ gcc `pkg-config --cflags --libs gtk+-2.0` -lgnustep-base -fconstant-string-class=NSConstantString MainWindow.m main.m -I /usr/include/GNUstep/ -L /usr/lib/GNUstep/ -std=c99 -O3

Запускаем на выполнение: ./a.out и видим:

Также выводиться сообщение в консоль.
Стоит ли говорить, что необходимо наличие установленных GTK и Objective-C? Необходимы пакеты gnustep-core-devel и libgtk2.0-dev. Все действия проводились на машине с установленной Debian Wheeze.
Да, еще надо сделать следующее. Если выскакивает ошибка "error: #error The current setting for native-objc-exceptions does not match that of gnustep-base ... please correct this." необходимо в файле /usr/include/GNUstep/GNUstepBase/GSConfig.h строку (221-я):
#define BASE_NATIVE_OBJC_EXCEPTIONS 1
заменить на:
#define BASE_NATIVE_OBJC_EXCEPTIONS 0