Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Листинг 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.






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.