Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / C++Builder. ╨г╤З╨╡╨▒╨╜╤Л╨╣ ╨║╤Г╤А╤Б

.pdf
Скачиваний:
0
Добавлен:
12.11.2023
Размер:
9.63 Mб
Скачать

- Создайте обработчик события OnMouseMove формы.

v o i d __ f a s t c a l l T F o rm l: : FormMouseMove (TObject ♦ S e n d e r , T s h i f t S t a t e S h i f t , i n t X, i n t Y)

{

i f (Down)

{

 

g l R o t a t e f (X

x l , 0 . 0 , 1 . 0 , 0 . 0 ) ;

 

 

g l R o t a t e f (Y

y l , 1 . 0 , 0 . 0 , 0 . 0 ) ;

 

 

I n v a l i d a t e R e c t ( H a n d l e , NULL,

f a l s e ) ;

 

 

x l

=

X;

 

 

 

 

y l

=

Y;

 

 

 

}

 

 

 

 

 

 

}

 

 

 

 

 

 

 

- Создайте обработчик события OnMouseUp формы.

v o i d

__ f a s t c a l l

T F orm l:: FormMouseUp (TObject

*Sender,

TM ouseButton B u t to n , T S h i f t S t a t e

S h i f t , i n t

X,

i n t

Y)

 

 

 

 

 

{

 

 

 

 

 

 

Down

=

f a l s e ;

 

 

 

}

- Запустите приложение и проверьте правильность его работы. Теперь при нажатой кнопке мыши при движении курсора куб

вращается по двум осям, что позволяет хорошо рассмотреть его с разных позиций. Для осуществления этого режима введен флаг, булевская переменная Down, которая принимает истинное значе­ ние при удерживаемой кнопке мыши, и две вспомогательные пе­ ременные x l и y l, связанные с экранными координатами указате­ ля. В момент нажатия кнопки запоминаются координаты курсора, и при движении курсора куб поворачивается по двум осям на угол, величина которого зависит от разницы текущей и предыдущей ко-

Пример 11.17

- В секции p r i v a t e определения класса формы объявите переменные:

HDC DC;

HGLRC h r с ;

- Объявите глобальные переменные:

G L f l o a t

A n g le ;

G L f l o a t

х [ 6 ] , у [6];

- Разместите на форме компонент TTimer.

-Создайте обработчик события O n C r e a t e формы. Само­ стоятельно разберитесь с назначением каждого оператора обра­ ботчика.

v o i d __ f a s t c a l l T F o rm l: : FormCreate (TObject ^Sender)

{

i n t

i ;

 

 

 

 

 

DC = G e tD C ( H a n d le );

 

 

 

 

S e tD C P ix e lF o rm a t(D C );

 

 

 

 

h r c

=

w glC rea te C o n te x t(D C ) ;

 

 

wglMakeCurrent(DC, h rc ) ;

 

 

 

f o r

(i = 0 ; i < 6 ; i++)

 

 

 

 

{

 

 

 

 

 

 

x [ i ] = s i n ( M _ P l/ 3 * i ) ;

 

 

 

y [ i ]

= c o s ( M _ P I/3 * i)

;

 

 

 

}

 

 

 

 

 

 

g l C l e a r C o l o r (1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ;

 

g l E n a b l e (GL_DEPTH_TEST) ;

/ /

разрешаем тест

глубины

/ /

Добавляем источник

света

0

 

glEnable(GL_LIGHTING);

/ /

разрешаем работу

 

 

 

 

/ /

с

освещенностью

 

g l E n a b l e (GL_LIGHT0) ; / / включаем источник

света 0

glEnable(GL_COLOR_MATERIAL) ;

 

 

g l C o l o r 3 f ( 0 . 0 , 0 . 0 , 1 . 0 ) ;

 

 

 

A n g le

= 0;

 

 

 

 

}

Положения центров кубиков хранятся во вспомогательных массивах х и у, заполняемых при начале работы приложения.

- Создайте обработчик события O nD estroy .

void __fastcall T F o r m l : : F o r m D e s t r o y ( T O b j e c t *S ender)

{

w g l M a k e C u r r e n t (0, 0 ) ; w g l D e l e t e C o n t e x t ( h r c ) ; R e le a s e D C ( H a n d le , DC); D e le te D C (D C );

}

- Создайте обработчик события O n P a in t.

void __fastcall T F o r m l : : F o r m P a i n t ( T O b j e c t *S ender)

{

int i;

/ / о ч и с т к а буфера ц в е т а и буфера глубины

g l C l e a r (GL__COLOR_BUFFER_BIТ | GL_DEPTH_BUFFER_BIT) ;

g l P u s h M a t r i x ( ) ;

 

g l R o t a t e f ( A n g l e ,

0 . 0 , 0 . 0 , 1 . 0 ) ; / / поворот на угол

/ /

Цикл рисован ия

шести кубиков

for

(i =0; i < 6 ; i ++)

{

g l P u s h M a t r i x ( ) ;

/ / запомнили точ ку

g l T r a n s l a t e f ( х [ i ] , y [ i ] , 0 . 0 ) ;

g l R o t a t e f ( - 6 0 * i ,

0 . 0 , 0 . 0 ,

1 . 0 ) ; / / поворот куба

g l u t S o l i d C u b e ( 0 . 5 ) ;

 

g l P o p M a t r i x ( ) ; / /

вернулись

в точку

}

g l P o p M a t r i x ( ) ;

S w a p B u f f e r s ( D C ) ;

}

- Создайте обработчик события O nR esize.

void __fastcall T F o r m l : : F o r m R e s i z e ( T O b j e c t *S ender)

{

g l V i e w p o r t (0, 0, C l i e n t W i d t h , C l i e n t H e i g h t ) ;

g lM atrix M o d e (GL_PROJECTION) ; g l L o a d l d e n t i t y () ;

g l u P e r s p e c t i v e (1 8 . 0 ,

double ( C lie n tW id th ) / C l i e n t H e i g h t , 7. 0, 10 . 0) ; g lM atrix M o d e (GL_MODELVIEW) ;

g l L o a d l d e n t i t y () ;

g l T r a n s l a t e f ( 0 . 0 , 0 . 0, - 9 . 0 ) ;

g l R o t a t e f ( 60 . 0, 1 . 0 , 0 . 0, 1 . 0 ) ;

I n v a l i d a t e R e c t ( H a n d l e ,

NULL, false) ;

}

 

-Создайте обработчик

события OnTimer компонента

T T i m e r l .

 

void __fastcall T F o rm l: : Tim erITim er(TObject *Sender)

{

/ /

Каждый

"тик" изменяется

значение угла

A ngle++;

 

 

if

(Angle

>= 60 . 0) Angle =

0 . 0;

I n v a l i d a t e R e c t ( H a n d l e , NULL, false) ;

}

Поворот всей системы с течением времени обеспечивается тем, что в обработчике таймера значение переменной Angle, свя­ занной с углом поворота, увеличивается, после чего экран перери­ совывается.

- Запустите приложение и проверьте правильность его работы. Использование системного таймера является самым простым

решением задачи, но имеет очевидные недостатки. На маломощ­ ных компьютерах уже этот пример выводит кадры рывками, а если количество объектов перевалит за два десятка, то удовлетвори­ тельную скорость воспроизведения можно будет получить только на очень хороших машинах.

Следующий пример является продолжением предыдущего.

- Внесите изменения в разработанную программу таким обра­ зом, чтобы она позволяла нарисовать 50 параллелепипедов (рис. 11.14).

- Задайте вращение модели по двум осям.

Функция API G e tT ic k C o u n t возвращает количество милли­ секунд, прошедших с начала сеанса работы операционной системы.

-П ри воспроизведении кадра (обработчик события Оп-

Pa i n t) .

// о пределяем и выводим количество кадров в секунду

NewCount

= G e t T i c k C o u n t () ;

Fram eC ount++;

 

 

 

if ( (N e w C ount - L astC ount)

> 1000) // прошла секунда

{

 

 

 

 

 

f p s R a t e

=

FrameCount*1000 . / (NewCount-LastCount) ;

C a p t i o n

=

"FPS

+ F lo a tT o S tr F (fp sR a te ,

 

 

f f F i x e d ,

10,

3 ) ;

L a s t C o u n t

=

NewCount;

 

FrameCount

=

0;

 

 

}

В этом фрагменте мы определяем, прошла ли очередная се­ кунда, и вычисляем количество кадров, выведенных за эту секун­ ду. Получающееся количество воспроизведенных кадров в секунду зависит от многих факторов, в первую очередь, конечно, от харак­ теристик компьютера. Будет совсем неплохо, если эта цифра будет в районе ста. Но если задаться целью увеличить скорость работы этого примера, то выяснится, что сделать это будет невозможно, независимо от характеристик компьютера. Сколь бы малым ни за­ давать интервал таймера, выдаваемая частота воспроизведения не изменится, системный таймер в принципе не способен обрабаты­ вать тики с интервалом менее десяти миллисекунд. Еще один не­ достаток такого подхода состоит в том, что если обработчик тика таймера не успевает отработать все действия за положенный ин­ тервал времени, то последующие вызовы этого обработчика ста­ новятся в очередь. Это приводит к тому, что приложение работает с разной скоростью на различных компьютерах.

11.6.2. Использование мультимедийного таймера

Еще один способов создания анимации - это использование мультимедийного таймера. Мультимедийный таймер позволяет обрабатывать события с любой частотой, настолько часто, на­ сколько это позволяют сделать ресурсы компьютера. Для демонст­ рации использования мультимедийного таймера рассмотрим при­ мер из предыдущего раздела, в котором рисуются все те же 50 па­ раллелепипедов.

Пример 11.18

- Д л я использования предыдущего примера удалите компо­

нент T i m e r l и обработчик события O nTim er.

Для использования мультимедийного таймера необходимо выполнить следующие действия:

-Поместить в заголовочном файле модуля команду препро­ цессора:

#i n c l u d e <mmsystem.h>

-В файле реализации модуля объявите глобальную перемен­ ную - идентификатор таймера.

UINT T im erID ; / / идентификатор таймера

Мультимедийный таймер также нуждается в идентификаторе, как и обычный системный.

- Определите функцию, обрабатывающую тик таймера (ана­ лог обработчика событий для системного таймера).

v o id

__s t d c a l l

TimeFunc(UINT

u T im erlD ,

UINT

uM essage,

DWORD dwUser,

DWORD dwl, DWORD dw2)

{

/ /

Каждый

"тик" и зм е н я е т

зн а ч е н и е у г л а

A n g le +=

0 . 1 ;

 

i f

(Angle

>= 3 6 0 . 0 ) A n g le

= 0 . 0 ;

I n v a l i d a t e R e c t ( F o r m l - > H a n d l e , NULL, f a l s e ) ;

}

Эта функция не может являться методом класса.

- При создании окна формы (обработчик события OnCreate)

запустить таймер специальной функцией API.

TimerID

= t i m e S e t E v e n t (2,

0, TimeFunc, О,

TIME_PERIODIC);

 

- П о

окончании работы

приложения (обработчик события

O n D e stro y ) таймер необходимо остановить.

t i m e K i l l E v e n t ( T i m e r l D ) ;

Если это не сделать, то работа операционной системы будет заметно замедляться. Самым важным в этой цепочке действий яв­ ляется команда установки таймера tim eS etE vent. Назначение аргументов этой функции следующее:

-первый аргумент команды - интервал таймера в миллисе­ кундах;

-второй аргумент - разрешение таймера, т.е. количество миллисекунд, ограничивающее время на отработку каждого тика таймера. Если это число задано нулем, как в нашем примере, то обработка таймера должна происходить с максимальной точно­ стью (в документации рекомендуется задавать ненулевое значение для уменьшения системных потерь);

-следующий параметр - это имя функции, ответственной за обработку каждого тика;

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

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

Итак, в примере каждые две миллисекунды наращивается угол поворота системы и перерисовывается экран. Конечно, за две миллисекунды экран не будет перерисован, однако те кадры, кото­ рые компьютер не успевает воспроизвести, не будут накапливать­ ся. При использовании мультимедийного таймера сравнительно

легко планировать поведение приложения во времени: на любом компьютере и в любой ситуации система будет вращаться с при­ близительно одинаковой скоростью, просто на маломощных ком­ пьютерах повороты будут рывками.

Упражнения

1. Написать программу для моделирования падения 3Dшарика в соответствии с законами физики.

2.Написать программу вращения тетраэдра вокруг одной из вершин.

3.Написать программу для моделирования движения 3Dшарика, брошенного под углом к горизонту.

4.Написать программу вращения шестиугольника вокруг своего центра.

5.Написать программу вращения тетраэдра вокруг своего центра тяжести.

6.Написать программу для моделирования броска баскетбо­ листа по кольцу. Программа должна определить, попал мяч в кольцо или нет. Предусмотреть в программе, что кольцо имеет баскетбольный щит и мяч может попасть в кольцо от щита.

7.Написать программу моделирования вращения Земли во­ круг Солнца и Луны вокруг Земли.

8.Написать программу вращения шестиугольника вокруг од­ ной из вершин.

9.Написать программу моделирования вращения Земли во­ круг Солнца.

10.Написать программу вращения параллелепипеда вокруг своего центра.

11.Написать программу колебания груза на пружине с рисо­ ванием графиков перемещений и скоростей.

12.Написать программу, моделирующую броуновское дви­ жение (ЗБ-вариант).

Соседние файлы в папке книги