Полезное:
Как сделать разговор полезным и приятным
Как сделать объемную звезду своими руками
Как сделать то, что делать не хочется?
Как сделать погремушку
Как сделать так чтобы женщины сами знакомились с вами
Как сделать идею коммерческой
Как сделать хорошую растяжку ног?
Как сделать наш разум здоровым?
Как сделать, чтобы люди обманывали меньше
Вопрос 4. Как сделать так, чтобы вас уважали и ценили?
Как сделать лучше себе и другим людям
Как сделать свидание интересным?
Категории:
АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника
|
Листинг 8.2. Демонстрационная программа новой функции масштабирования (VYREN.C)// ВКЛЮЧАЕМЫЕ ФАЙЛЫ //////////////////////////////////////// #include <io.h> #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <dos.h> #include <bios.h> #include <fcntl.h> #include <memory.h> #include <malloc.h> #include <math.h> #include <string.h> #include <graph.h> #include "graphics.h" // включаем нашу графическую библиотеку // ПРОТОТИПЫ //////////////////////////////////////////////////////// void Create_Scale_Data_X(int scale, int far *row); void Create_Scale_Data_Y(int scale, int * row); void Build_Scale_Table(void); void Scale_Sprite(sprite_ptr sprite,int scale); void Clear_Double_Buffer(void); // ОПРЕДЕЛЕНИЯ ///////////////////////////////////////////// #define MAX_SCALE 200 // число звезд на звездном небе #define SPRITE_X_SIZE 80 // максимальные размеры #define SPRITE_Y_SIZE 48 // растрового изображения // ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ /////////////////////////////////// sprite object; // обобщенный спрайт, который // содержит кадры с космическим // кораблем pcx_picture text_cells;// PCX-файл с изображениями int *scale_table_y[MAX_SCALE+l]; // таблица с предварительно // рассчитанными коэффициентами // масштабирования int far *scale_table_x[MAX_SCALE+l]; // таблица с предварительно // рассчитанными коэффициентами // масштабирования // ФУНКЦИИ ///////////////////////////////////////////////// void Create_Scale_Data_X(int scale, int far *row) { // эта функция масштабирует полосу текстуры для всех возможных // размеров и создает огромную таблицу соответствий int х; float x_scale_index=0, x_scale_step; // рассчитываем шаг масштабирования или число исходных пикселей // для отображения на результирующее изображение за цикл x_scale_step = (float)(sprite_width)/(float)scale; x_scale_index+=x_scale_step; for (x=0; x<scale; x++) { // помещаем данные в массив для последующего использования row[x] = (int)(x_scale index+,5); if (row[x] > (SPRITE_X_SIZE-1)) row[x] = (SPRITE_X_SIZE-1); // рассчитываем следующий индекс x_scale index+=x_scale_step; } // конец цикла } // конец Create_Scale_Data_X /////////////////////////////////////////////////////////// void Create_Scale_Data Y(int scale, int *row) { // эта функция масштабирует полосу текстуры для всех возможных // размеров и создает огромную таблицу соответствий int у; float y_scale_index=0, у scale_step; // рассчитываем шаг масштабирования или число исходных пикселей // для отображения на результирующее изображение за цикл у_scale_step = (float)(sprite_height)/(float)scale; y_scale index+=y_scale_step; for (y=0; y<scale; y++) { // помещаем данные в-массив для последующего использования row[y] = ((int)(y_scale_index+.5)) * SPRITE_X_SIZE; if (row[y] > (SPRITE_Y_SIZE-1)*SPRITE_X_SIZE) row[y] = (SPRITE_Y_SIZE-1)*SPRITE_X_SIZE; // рассчитываем следующий индекс y_scale_index+==y_scale_step; } // конец цикла } // конец Create_Scale_Data_Y ////////////////////////////////////////// void Build_Scale_Table (void) { // эта функция строит таблицу масштабирования путем расчета // коэффициентов масштабирования для всех возможных размеров // от 1 до 200 пикселей int scale; // резервируем память for (scale=l; scale<=MAX_SCALE; scale++) { scale_table_y[scale] = (int *)malloc(scale*sizeof(int)+1); scale_table_x[scale] = (int far *)_fmalloc(scale*sizeof(int)+l); } // конец цикла // создаем таблицу масштабирования для осей X и Y for (scale=l; scale<=MAX_SCALE; scale++) { // рассчитываем коэффициент для данного масштаба Create_Scale_Data_Y(scale, (int *)scale_table_y[scale]); Create_Scale_Data_X(scale, (int far *)scale_table_x[scale]); }// конец цикла }// конец Build_Scale_Table //////////////////////////////////////////////////////////// void Scale_Sprite(sprite_ptr sprite,int scale) { // эта функция масштабирует спрайт (без отсечения). Масштабирование производится с //использованием заранее рассчитанной таблицы, которая определяет, как будет изменяться //каждый вертикальный столбец. Затем другая таблица используется для учета //масштабирования этих столбцов по оси Х char far *work_sprite; // текстура спрайта int *row_y; // указатель на масштабированные // по оси Y данные (заметьте, что // это ближний указатель) int far *row_x; // указатель на масштабированные // по оси Х данные (заметьте, что // это дальний указатель) unsigned char pixel; // текущий текстель lnt x, // рабочие переменные У, column, work_offset, video_offset, video_start; // если объект слишком мал, то и рисовать его не стоит if (scale<1) return; // рассчитываем необходимые для масштабирования данные row_y = scale_table_y[scale]; row_x = scale table_x[scale]; // выбираем соответствующий кадр спрайта work_sprite = sprite->frames[sprite->curr_frame]; // рассчитываем начальное смещение video_start = (sprite->y << 8) + (sprite->y << 6) + sprite->x; // изображение рисуется слева направо и сверху вниз for (x=0; x<scale; х++) { // пересчитываем адрес следующего столбца video_offset = video_start + х; // определяем, какой столбец должен быть отображен, // исходя из индекса масштабирования по оси Х column = row_x[x]; // Наконец рисуем столбец обычным образом for (y=0; y<scale; y++) { // проверка на "прозрачность" pixel = work_sprite[work_offset+column]; if (pixel) double buffer[video_offset] = pixel; // индекс следующей строки экрана и смещение //в области хранения текстуры video_offset += screen_width; work_offset = row_y[y]; } // конец цикла по Y } // конец цикла ро Х } // конец Scale_Sprite ////////////////////////////////////////// void Clear Double_Buffer(void) { // угадали что это? _fmemset(double_buffer, 0, SCREEN__WIDTH * SCREEN_HEIGHT + 1); } // конец Clear_Double_Buffer // ОСНОВНАЯ ПРОГРАММА ////////////////////////////////////// void main(void) { // Загружаем 12 предварительно отсканированных кадров спрайта // и последовательно меняем их до тех пор пока игрок не изменит // координату Z объекта, нажав клавишу "," или "." int done=0, // флаг завершения count=0, // счетчик времени изменения кадра scale=64; // текущий масштаб спрайта float scale_distance = 24000, // произвольная константа // для согласования // плоской текстуры и трассированного // пространства view_distance = 256, // дистанция до объекта х=0, // позиция корабля в трехмерном // пространстве у=0, z=1024; // установка видеорежима 320х200х256 _setvideomode(_MRES256COLOR); sprite_width = 80; sprite_height =48;
// создание таблицы для подсистемы масштабирования Build_Scale_Table (); // инициализация файла PCX, содержащего кадры PCX_Init((pcx_picture_ptr)&text cells); // загрузка файла PCX, содержащего кадры PCX_Load("vyrentxt.pcx", (pcx_picture_ptr)&text_cells,1); // резервируем память под дублирующий буфер Init_Double_Buffer (); Sprite_Init((sprite_ptr)&object,0,0,0,0,0,0); // загружаем 12 кадров с космическим кораблем PCX_Grap_Bitmap ((pcx_picture_ptr) &text_cells, (sprite_ptr)&object,0,0,0); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,1,1,0); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,2,2,0); PCX_Grap_Bitmap ((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,3,0,1); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells,(sprite_ptr)&object,4,1,1); PCX_Grap_Bitmap ((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,5,2,1); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,6,0,2); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,7,1,2); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,8,2,2); PCX_Grap_Bitinap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,9,0,3); PCX_Grap_Bitmap((pcx_picture_ptr)&text_cells, (sprite_ptr)&object,10,1,3); PCX__Grap_Bitmap((pcx_picture_ptr) &text_cells, (sprite_ptr)&object,11,2,3); // начальная позиция корабля object.curr_frame =0; object.x = 0; object, у =0; Clear_Double_Buffer(); // ожидаем нажатия клавиш и рисуем корабль while(!done) { // нажал ли игрок клавишу? if (kbhit()) { switch(getch()) { case '.': // отдаляем корабль { z+=16; } break; case ',':// приближаем корабль { z-=l6; //не позволяем кораблю подойти слишком близко if(Z<256) z=256; } break; case 'q': // выход из программы { done=1; } break; default:break; } // конец оператора switch } // конец оператора if //рассчитываем размер растрового изображения scale = (int)(scale_distance/z); // исходя из размера растрового изображения, // рассчитываем проекции координат Х и Y object.x= (int)((float)x*view_distance / (float)z) + 160 - (scale>>1); object.y = 100 - (((int)((float y*view_distanc=e / (float)z) + (scale>>1))); // увеличиваем счетчик кадров if (++count==2) { count=0; if (++object.curr_frame==12) object.curr_frame=0; } // конец оператора if // очищаем дублирующий буфер Clear_Double_Buffer(); // масштабируем спрайт Scale_Sprite((sprite_ptr)&object,scale); Show_Double_Buffer(double_buffer); // выводим информацию на экран _settextposition(24,0); printf("z Coordinate is %f",z); } // конец оператора while // Удаляем файл PCX PCX_Delete((pcx_picture_ptr) &text_cells); // восстанавливаем текстовый режим _setvideomode(_DEFAULTMODE); } // конец функции main После выполнения программы из Листинга 8.2, вы, возможно, будете удивлены возможностями оцифровки изображений макетов и приведенным вариантом трехмерной мультипликации. Кто знает, может быть вы создадите что-нибудь именно по типу игры Wing Commander, а вовсе не очередную вариацию DOOM? Однако пора переходить к алгоритму отсечения. Отсечение спрайтов в трехмерном пространстве После построения аксонометрической проекции спрайта отсечение выполняется довольно легко. Алгоритм просто тестирует, не выходит ли проекция отмасштабированного спрайта за границы экрана, и, кроме того, проверяет, находится ли Z-координата спрайта внутри наблюдаемой области пространства. Следовательно, проблема отсечения сводится к проверке расположения прямоугольника относительно границ экрана. Решение этой проблемы мы уже рассмотрели раньше (в четвертой главе, «Механизмы двухмерной графики»). Как вы помните, существует два подхода к этой проблеме: можно использовать алгоритм пространства образов и алгоритм пространства объектов. Первый путь намного проще. Перед обновлением каждого пикселя спрайта, мы проверяем, находится ли он внутри границ экрана (или окна просмотра), и если это так, то замещаем пиксель. Недостаток этого метода — низкая производи тельность (хотя иногда все же приходится прибегать к алгоритму пространства образов из-за чересчур сложной геометрической формы визуализируемого объекта). В общем же случае алгоритм пространства образов работает медленнее алгоритма пространства объектов. При использовании объектно-пространственного алгоритма мы должны каким-то образом до прорисовки определить, какая часть спрайта будет нарисована и на основании этого вновь вычислить его проекцию. По существу, мы должны отсечь границами экрана прямоугольник, который получается в результате масштабирования спрайта. Это кажется несложным. Мы разбирали текст такой программы в предыдущей главе (Листинг 7.3), но я повторю этот алгоритм еще один раз! Такой уж я. Алгоритм 8.1 предполагает, что: § Экран ограничивается точками (0,0) и (scrfeen_x, screen_y); § Верхняя левая точка спрайта (sprifce_x, sprite_y); § Спрайт имеет размеры width и height. Предположим, отсечение пространства по оси Z уже было сделано и внешне образ выглядит вполне правдиво. Пусть также были сосчитаны масштаб объекта и координаты проекций по осям Х и Y.
|