Читайте также: |
|
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ////////////////////////////////////////
#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.
Дата добавления: 2015-07-12; просмотров: 125 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Листинг 8.1. Новая функция масштабирования спрайтов (без отсечения). | | | Алгоритм 8.1. Масштабирование спрайта. |