Внимание!

Эта публикация перенесена в раздел уроков по адресу Полноэкранный режим C# + TAO Framework (простое решение)..
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

Полноэкранный режим C# + TAO Framework (простое решение).



Что мы узнаем в этом уроке
В данном небольшом уроке речь пойдет о работе Ваших приложений, которые используют ТАО, в полноэкранном или как его еще называют игровом режиме.

[spoiler]
Что нужно знать

Для изучения данного материала нужно знать следующее:
1. Инициализация библиотеки Tao OpenGL на языке C# в среде .NET.
2. Подробное описание инициализации и визуализации в OpenGL на языке C#.

В принципе, переход в полноэкранный режим (далее ПР; ) задача довольно простая, но требует значительных затрат по времени при написании кода. Однако, один раз написав код - его без труда можно использовать во всех OpenGL приложениях. Нужно сказать сразу, что полная инициализация ПР через WinApi здесь рассматриваться не будет, инач зачем тогда использовать С# и .Net обвертки типа ТАО? Также нужно отметить, что сам по себе С# дает падение производительности при обработке графики примерно на 15%. [1] И наконец, по небольшому, но собственному опыту скажу, что работать с графикой в родном С++ все-таки удобней :)Ну что, если Вы все еще с нами, то пожалуй начнем! Весь процесс создания полноэкранного режима разобьем на две части: 1. Создание оконного масштабируемого приложения; 2. Создание приложения работающего в ПР используя стандартные функции System.Windows.Forms (я называю это «простая инициализация» );1. Подготовка оконного масштабируемого приложенияДля начала создаем проект Windows Forms и дадим ему название TAO_Fullscreen_Mode. Точно так же, как и в уроке «Инициализация библиотеки Tao OpenGL на языке C# в среде .NET», добавляем ссылки (Links; ) на библиотеки Tao.OpenGL.dll, Tao.FreeGlut.dll, Tao.Platform.Windows.dll. Главное окно программы переименуем из Form1 в FormFS, параметр Name в свойствах окна (Рис.1). И дадим ему название TAO Fullscreen Form (параметр Text; ). Наше приложение будет использовать реакции нажатия клавиш. Для того, чтобы включить обработчик событий нажатия клавиш, необходимо в свойствах формы для параметра KeyPreview выставить значение True (Рис.1). Таким образом, когда наше окно имеет фокус (выделено в настоящий момент) будут обрабатываться события нажатия клавиш клавиатуры.


Рис. 1.

Необходимо так же поместить на форму элемент управления SimpleOpenGlControl. В свойствах элемента нужно изменить параметр Dock на значение Fill (окно примет вид, как показано на Рис.2) и переименовать его в TaoWin.


Рис. 2.

Кажется ни чего не забыли :) Теперь можно отложить мышку в сторону и перейти непосредственно к написанию кода.Для работы с импортированными библиотеками, необходимо включить соответствующие пространства имен:Листинг 1:

/*http://esate.ru, KinsT*/

using System;
using System.Drawing;
using System.Windows.Forms;
// Библиотеки для работы с графикой;
using Tao.OpenGl;
using Tao.Platform.Windows;
using Tao.FreeGlut;

Используя директивы #region и #endregion, выделите блок кода внутри класса FormFS и дайте этому блоку название «+++ GLOBAL +++». Сейчас Ваш код должен выглядеть, как показано в Листинге 2:Листинг 2:

/*http://esate.ru, KinsT*/

namespace TAO_Fullscreen_Mode
{
   public partial class FormFS : Form
   {
      #region +++ GLOBAL +++
      
      #endregion

      public FormFS()
      {
        InitializeComponent();
      }
   }
} 

Создадим переменную FS типа bool внутри блока GLOBAL, чтобы она была видна во всех функциях-методах данного класса. Переменная FS является флагом указывающим, какой из режимов полноэкранный или оконный выбран для работы приложения в данный момент.Необходимо также добавить еще один конструктор класса с параметром (вообще говоря, можно просто изменить стандартный, но этика программирования мне подсказывает, что от стандартных конструкторов без параметров при написании кода лучше не отказываться). В качестве параметра конструктора будем использовать переменную типа bool с названием fullscreen. В каждом конструкторе инициализируем работу элемента TaoWin.Листинг 3:

/*http://esate.ru, KinsT*/

namespace TAO_Fullscreen_Mode
{
   public partial class FormFS : Form
   {
      #region +++ GLOBAL +++
      // Флаг полноэкранного режима;
      private bool FS;
      #endregion

      public FormFS()
      {
        InitializeComponent();
        // Инициализируем работу TaoWin;
        TaoWin.InitializeContexts();
      }
      public FormFS(bool fullscreen)
      {
        InitializeComponent();
     // Инициализируем работу TaoWin;
        TaoWin.InitializeContexts();
      }
   }
} 

Таким образом, конструктор с параметром будет определять в каком режиме запустить приложение, а стандартный конструктор без параметра по-умолчанию запустит приложение в оконном режиме.

Создайте функцию private void InitGL() в которой будем проводить инициализацию OpenGL. Все настройки, которые касаются OpenGL сцены расположены именно в этой функции:Листинг 4:
/*http://esate.ru, KinsT*/

      // Инициализация OpenGL
      private void InitGL()
      {
        Glut.glutInit();
        Glut.glutInitDisplayMode(Glut.GLUT_RGBA |
           Glut.GLUT_DEPTH |
           Glut.GLUT_DOUBLE);
        // Разрешить плавное цветовое сглаживание;
        Gl.glShadeModel(Gl.GL_SMOOTH);
        // Разрешить тест глубины;
        Gl.glEnable(Gl.GL_DEPTH_TEST);
        // Разрешить очистку буфера глубины;
        Gl.glClearDepth(1.0f);
        // Определение типа теста глубины;
        Gl.glDepthFunc(Gl.GL_LEQUAL);
        // Слегка улучшим вывод перспективы;
        Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
        // Разрешаем смешивание;
        Gl.glEnable(Gl.GL_BLEND);
        // Устанавливаем тип смешивания;
        Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
      }

В функции glutInitDisplayMode(… ) мы инициализировали двойной буфер кадра, буфер глубины и режим отображения цветов GLUT_RGBA – для вывода цвета будет использовано 3 компоненты цвета плюс альфа канал прозрачности. Использование такой модели цветов дает возможность в дальнейшем использовать смешивание и использовать по-выбору различные режимы сглаживания (точек, линий, полигонов). Последние три функции кода рассмотрим подробнее:Gl.glHint(target, mode) – управляет способом выполнения растеризации примитивов и может принимать различные параметры. Так в качестве target могут выступать следующие: GL_FOG_HINT - точность вычислений при наложении тумана. GL_LINE_SMOOTH_HINT - управление качеством прямых. GL_PERSPECTIVE_CORRECTION_HINT - точность интерполяции координат при вычислении цветов и наложении текстуры. GL_POINT_SMOOTH_HINT - управление качеством точек. При значении параметра mode равным GL_NICEST точки рисуются как окружности. GL_POLYGON_SMOOTH_HINT - управление качеством вывода сторон многоугольника.В качестве параметра mode выступают такие параметры:
GL_FASTEST - используется наиболее быстрый алгоритм рисования.
GL_NICEST - используется алгоритм, обеспечивающий лучшее качество.
GL_DONT_CARE - выбор алгоритма зависит от реализации.
Gl.glEnable(Gl.GL_BLEND) – разрешаем смешивание. Эту функцию необходимо вызывать, если в дальнейшем вы планируете, к примеру, наслаивать друг на друга слои с различной степенью прозрачности;Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA) – выбираем наиболее подходящие параметры смешивания.

Далее необходимо создать функцию изменения размеров окна OpenGL (сцены) private void ResizeGlScene(). Эта функция должна быть вызвана всякий раз, когда мы хотим изменить размеры сцены. Например, при растягивании окна мышью, когда мы находимся в оконном режиме. Также эта функция должна быть вызвана хотя бы один раз при инициализации ПР, чтобы установить размеры области вывода и настроить нашу сцену перед отрисовкой. Размер сцены устанавливается в зависимости от текущих размеров элемента TaoWin, это как раз одно из свойств, благодаря которому можно упростить себе работу при инициализации ПР.
Листинг 5:
/*http://esate.ru, KinsT*/

      // Изменение размеров окна OpenGL
      void ResizeGlScene()
      {
        // Предупредим деление на нуль;
        if (TaoWin.Height == 0)
        {
           TaoWin.Height = 1;
        }
        // Сбрасываем текущую область просмотра;
        Gl.glViewport(0, 0, TaoWin.Width, TaoWin.Height);
        // Выбираем матрицу проекций;
        Gl.glMatrixMode(Gl.GL_PROJECTION);
        // Сбрасываем выбранную матрицу;
        Gl.glLoadIdentity();
        // Вычисляем новые геометрические размеры сцены;
        Glu.gluPerspective(45.0f, 
           (float)TaoWin.Width / (float)TaoWin.Height, 
           0.1f, 100.0f);
        // Выбираем матрицу вида модели;
        Gl.glMatrixMode(Gl.GL_MODELVIEW);
        // Сбрасываем ее;
        Gl.glLoadIdentity();
      } 

Хотелось бы отметить следующее, что при вызове функции gluPerspective и задании ее параметров нужно быть внимательным, хотя в определении функции и написано, что параметры имеют тип double, но лучше приводить все параметры к типу float. Ниже приведу описание каждого параметра функции:

Функция gluPerspective - устанавливает матрицу перспективной проекции.void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);fovy - область угла просмотра по вертикали в градусах;aspect - (ширина области просмотра) / (высоту области просмотра). Очень важный параметр, нужен чтобы шар не казался яйцом. Если не привести этот параметр к типу float, то Вы обязательно получите яичко вместо шарика :)zNear - расстояние до ближней плоскости отсечения (всё что ближе - не рисуется);zFar - расстояние до дальней плоскости отсечения (всё что дальше - не рисуется).Теперь, когда Вы имеете все функции необходимые для инициализации сцены, нужно создать функцию, которая будет подготавливать все, что Вы захотите нарисовать. Назовем ее private void DrawGlScene(), код данной функции приведен в Листинге 6:Листинг 6:
/*http://esate.ru, KinsT*/

//РИСОВАНИЕ СЦЕНЫ
      private void DrawGlScene()
      {
        // Выбираем цвет очистки экрана;
        Gl.glClearColor(0.0f, 0.8f, 0.0f, 1.0f);
        // Очищаем экран выбранным цветом;
        Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
        // Сбрасываем текущую матрицу проекций;
        Gl.glLoadIdentity();
        // Обязательно нужно сместиться по оси Z,
        // иначе ни чего не будет видно;
        Gl.glTranslatef(0, 0, -3.0f);

        Gl.glPushMatrix();
        // Здесь можно рисовать что угодно :)
        Gl.glPopMatrix();

        Gl.glFlush();
        TaoWin.Invalidate();
      }

Мы еще ни разу не запустили наше приложение, обидно :( Правильно ли мы программируем?! Сейчас мы это выясним! Нужно, во-первых, провести инициализацию OpenGL сцены с помощью подготовленных ранее функций, а во-вторых, добавить обработчик события происходящего при изменении размеров окна. Не будем останавливаться и проведем инициализацию OpenGL, для этого добавим вызов функций InitGL(), ResizeGlScene() и DrawGlScene() в конструкторы класса FormFS. <span style="text-decoration: underline;">Причем последовательность вызова функций в этом случае имеет большое значение!</span>Листинг 7:
/*http://esate.ru, KinsT*/

#region CONSTRUCTORS
      public FormFS()
      {
        InitializeComponent();
        // Инициализируем работу TaoWin;
        TaoWin.InitializeContexts();
        // Инициализация OpenGL
        // !!!Последовательность строк имеет значение!!!
        InitGL();
        ResizeGlScene();
        DrawGlScene();
      }

      public FormFS(bool fullscreen)
      {
        InitializeComponent();
        // Инициализируем работу TaoWin;
        TaoWin.InitializeContexts();
        // Инициализация OpenGL
        // !!!Последовательность строк имеет значение!!!
        InitGL();
        ResizeGlScene();
        DrawGlScene();
      }
#endregion

Теперь настало время вспомнить о мышке! Перейдите к конструктору формы и выделите мышью форму FormFS. Далее зайдите в «Свойства», далее в «События» (Events; ), кнопка с желтенькой молнией (Рис.3). Выберите свойство Resize и нажмите двойным кликом на белом поле. После этого студия сгенерирует обработчик этого события, внутри него поместите вызов функций ResizeGlScene() и DrawGlScene(), порядок следования тоже имеет значение. Потому что вначале должно произойти изменений размеров сцены, а потом уже отрисовка.


Рис. 3.

Листинг 8:
/*http://esate.ru, KinsT*/

// Обработчик события изменения размеров формы;
      private void FormFS_Resize(object sender, EventArgs e)
      {
        // Вначале изменим размеры сцены;
        ResizeGlScene();
        // А потом перерисуем;
        DrawGlScene();
      }

Теперь оконное масштабируемое приложение готово к работе, смело запускайте! Ну что, ни каких ошибок не обнаружено? Тогда будем двигаться дальше и перейдем непосредственно к инициализации полноэкранного режима.

2. «Простая инициализация» полноэкранного режима

Добавьте следующий код рисования диска в функцию DrawGlScene(), между Gl.glPushMatrix() и Gl.glPopMatrix(), этот код нужен только для нас, чтобы мы могли видеть правильность отображения на экране (масштабируемость изображения, отсутствие эффекта «яйца» и т.д.):
Листинг 9:
/*http://esate.ru, KinsT*/

//
        Gl.glPushMatrix();
        // Здесь можно рисовать что угодно :)
        Glu.GLUquadric disk = Glu.gluNewQuadric();
        Glu.gluQuadricDrawStyle(disk, Glu.GLU_FILL);
        Gl.glColor3ub(255, 0, 150);
        Glu.gluDisk(disk, 0.5, 1.0, 30, 1);

        Gl.glPopMatrix();

Наконец, создадим функцию private void ScreenMode(bool fullscreen), которая отвечает за изменение режима отображения окна – либо в ПР, либо в оконном режиме:
Листинг 10:
/*http://esate.ru, KinsT*/

// Смена режима (Fullscreen/Window)
      private void ScreenMode(bool fullscreen)
      {
        // Присваиваем значение "глобальной" переменной;
        FS = fullscreen;

        if (FS)
        {   // *** ПОЛНОЭКРАННЫЙ РЕЖИМ ***
           // Скрываем рамку окна;
           this.FormBorderStyle = FormBorderStyle.None;
           // Разворачиваем окно;
           this.WindowState = FormWindowState.Maximized;
           // --- Не обязательный пункт --- 
           // Делаем курсор в форме руки;
           // TaoWin.Cursor = Cursors.Hand;
        }
        else
        {   // *** ОКОННЫЙ РЕЖИМ ***
           // Возвращаем состояние окна;
           this.WindowState = FormWindowState.Normal;
           // Показываем масштабируемую рамку окна;
           this.FormBorderStyle = FormBorderStyle.Sizable;
           // --- Не обязательный пункт ---
           // Возвращаем курсор по-умолчанию;
           // TaoWin.Cursor = Cursors.Default;
           // Задаем размеры окна;
           this.Width = 400;   // Ширина;
           this.Height = 300;  // Высота;
        }
        ResizeGlScene();
      }

Если Вы уже успели заметить, то в приведенном выше коде, нет ни каких «шаманских заклинаний» :) Используются только стандартные оконные преобразования путем вызова нужных функций. Сейчас кто-то может сказать: «То, что здесь написано, не имеет ни какого отношения к игровому режиму!», вначале я тоже так подумал, но если посудить логически и посмотреть, скажем, на исходный код ПР написанный на С с применением WinApi функций [2] , то Вы увидите, что там ни чего отличного от нашего кода не происходит (кроме задания вручную формата пикселей для всего экрана). Тем более, что Джель не умеет создавать «свой» собственный ПР [3] , как скажем Managet DirectX. Так что я думаю – здесь все средства хороши :)

Итак, что же мы делаем: 1. Проверяем флаг ПР; 2.1. Если мы находимся в ПР, то нужно убрать рамку (Border; ) окна. Далее, присваивая свойству WindowState (отвечает за состояние окна: свернуто, развернуто или восстановлено) параметр FormWindowState.Maximized, разворачиваем наше окно на весь экран; 2.2. Если мы оказались в оконном режиме, то присваиваем свойству WindowState параметр FormWindowState.Normal – восстанавливаем наше окно. Через свойство FormBorderStyle зададим окну границу с изменяемыми размерами (FormBorderStyle.Sizable; ). В самом конце через свойства Width и Height зададим начальный размер окна. 3. Когда размеры окна изменены, то нужно вызвать функцию изменения размеров OpenGL сцены ResizeGlScene().

Теперь осталось заполнить обработчик событий нажатия клавиатуры. Перейдите к конструктору формы и выделите мышью форму FormFS. Далее зайдите в «Свойства» -> «События» (Events; ), кнопка с желтенькой молнией (Рис.3). Выберите свойство KeyUp и нажмите двойным кликом на белом поле. Студия генерирует обработчик события. Нам нужно создать три различных реакции: 1. Переход в полноэкранный режим;2. Переход в оконный режим;3. Выход из приложения.ВЫХОДДля выхода из приложения будем использовать клавишу ЕSCAPE. Пишем для нее обработчик, дословно: «Если код НАЖАТОЙ кнопки совпадает с кодом клавиши ЕSCAPE, то завершим наше приложение». Точно такое же правило будет использовано для других кнопок, только вместо «завершим наше приложение» будет прописан вызов соответствующих функций.ПОЛНОЭКРАННЫЙ РЕЖИМПереход в ПР будем производить по нажатию на кнопку F2. Когда кнопка нажата вызываем функцию ScreenMode(… ) с параметром true. И прячем наш курсор мыши Cursor.Hide().ОКОННЫЙ РЕЖИМВозвращаться в оконный режим будем по нажатию клавиши F1. Когда кнопка нажата вызываем функцию ScreenMode(… ) с параметром false. Показываем курсор Cursor.Show().
Листинг 11:
/*http://esate.ru, KinsT*/

// События нажатий клавиш клавиатуры; 
      private void FormFS_KeyUp(object sender, KeyEventArgs e)
      {
        #region EXIT
        // Нажата клавиша ЕSCAPE;
        if (e.KeyCode == Keys.Escape)
        {
           // Завершим приложение;
           this.Close();
        }
        #endregion

        #region Полноэкранный или оконный режим
        // Нажата клавиша F2;
        if (e.KeyCode == Keys.F2)
        {
           // Изменяем режим на ПОЛНОЭКРАННЫЙ;
           ScreenMode(true);
           // Прячем курсор;
           Cursor.Hide();
        }
        // Нажата клавиша F1;
        if (e.KeyCode == Keys.F1)
        {
           // Изменяем режим на оконный;
           ScreenMode(false);
           // Показываем курсор;
           Cursor.Show();
        }
        #endregion
      }

Вот, пожалуй, и все! Компилируем, запускаем и жмем по очереди на кнопки F2, F1, пробуем растягивать наше окно, радуемся (это обязательно :) и жмем в конце ЕSCAPE. На этом первая часть урока закончилась! В следующей части урока «Полноэкранный режим C# + TAO Framework (простое решение). Часть 2.», мы обогатим наше приложение. Добавим возможность выбора разрешения экрана, глубины цвета и частоты обновления экрана.Жду Ваших комментариев!ИСТОЧНИКИ1. Михаил Фленов, http://www.flenov.info/favorite.php?artid=292. http://pmg.org.ru/nehe/nehe01.htm3. http://www.opengl.org.ru/books/open_gl/chapter4.15.html

ФАЙЛЫ ПРОЕКТА
Скачать исходный код проекта (VS 2008)
0       3619        19.08.2010        10

Внимание!

Эта публикация перенесена в раздел уроков по адресу Полноэкранный режим C# + TAO Framework (простое решение)..
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

0  
21.08.2010 00:00:00
О_О
ппц
это так делать на полный экран??
читать не стал так как пишу на плюсах
так вот в полюсах это одна строчка!!!
ппц
0  
21.08.2010 00:00:00
Смысле на плюсах? С++ что-ли?
А можно посмотреть что за код в одну строчку? Я еще год назад искал код ПР и адекватно работающий нашел только от NeНе (Источники — вторая ссылка).

Isaer, пожалуйста покажи о чем ты говоришь. В одну строчку только в Managet DirectX видел.
0  
23.08.2010 00:00:00
Отлично. Очень порадовала статья и подход — качесвтенно, подробно. Чувствуется, что работа провелась значительная.

читать не стал так как пишу на плюсах
так вот в полюсах это одна строчка!!!Наверно, isaer имеет ввиду GAME_MODE в GLUT — там действительно достаточно только определить режим и перевести приложение в режим GAME_MODE.
Примерно так (С++, glut):

...
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutGameModeString("1280x1024:85");
glutEnterGameMode();
...
0  
23.08.2010 00:00:00
Я тоже пробовал так переходить в полноэкранку, но что-то криво всегда получалось. Бывало, что после перехода в ПР экран был либо прозрачным, либо черным и для появления картинки нужно было какую-нибудь реакцию Джеля вызвать (например перерисовать по нажатию на клавишу).

Конечно это удобно через ГЛАТ переходить, тем более я так понимаю, что эти функции и в Линуксе тоже адекватны?
0  
23.08.2010 00:00:00
Ну это функции библиотеки glut, а одна из фишек glut — это кросплатформенность.

Единственное, glut переходя в game_mode создает новое полноэкранное окно, поэтому я думал, что при работе с .net возможны проблемы (сам не пробовал под .net), так что наверно ты с ними и встретился.
0  
25.08.2010 00:00:00
Я вообще всё по другому делал: Ставил расширение экрана 800х600, делал форму на вечь экран, убирал рамку.
0  
25.08.2010 00:00:00
Привет!
«делал форму на вечь экран, убирал рамку»Так ты получается тоже самое делал :) Только я пока еще не написал, как разрешение экрана менять, это во второй части будет.
0  
25.08.2010 00:00:00
все я написал статью про освещение =)) фухх, стоколько сней мароки было…
0  
20.11.2011 00:00:00
перезагрузил сервер, сейчас не должно наблюдаться
0  
20.11.2011 00:00:00
bolwoe spasibo, vse rabotaet:)
^