1102
.pdf- Создайте обработчик события 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.Написать программу, моделирующую броуновское дви жение (ЗБ-вариант).