Comments 32
если мы смотрим на плоскость и она от нас как бы легла в глубину по оси z и при этом угол между x и y 90 градусов.
Так положить можно просто умножив вектор координат точек плоскости на матрицу вращения вокруг X на угол 90 градусов.
UFO landed and left these words here
Нет, через Direct Draw. В cvideo задаётся адрес видеопамяти и размер строки, которые получены от Direct Draw при захвате вторичной плоскости.
UFO landed and left these words here
Аккуратно-то аккуратно, но в устаревшей манере — я в современном Си++ плаваю, как топор (всякие лямбды и прочее для меня тёмный лес).
Там в Direct Draw просто получается адрес видеопамяти, который передаётся в класс видео CVideo. Дальше интерфейсный класс CIEngine определяет все функции вывода графики и загрузки данных. От этого класса унаследован класс CEngine_Base, в котором определены многие функции и добавлены защищённые функции, общие для все трёх вариантов движков (текстурирование линий, например). А уже от этого класса унаследованы сами движки. Все эти классы первым делом у CVideo запрашивают параметры видеоэкрана, а рисуют уже как в MS-DOS — записью в видеопамять.
Кстати, чтобы совсем отвязаться от DirectDraw в пользу GDI можно попробовать SetDIBitsToDevice, также выделяешь кусок памяти, растеризуешь и в нужный момент блитишь на поверхность окна.
Э… А зачем нужно от Direct Draw отвязываться? К нему наоборот привязываться надо исходя из соображений быстродействия.
Э… портировать на Linux например, да и нет у него нынче никакого быстродействия.
Ну, портировать-то как раз просто. Нужно просто в cVideo задать адрес буфера, выделенного с помощью malloc или new. А после рисования вывести этот буфер чем угодно. А клавиатура в движке привязана классом CKeyboard. То есть, клавиатура там виртуальная.
Кстати, я под QNX предыдущую версию портировал.

#include "cmain.h"

CMain cMain;
extern CVideo cVideo;

//-Конструктор класса--------------------------------------------------------
CMain::CMain()
{
 VideoBufferPtr=new unsigned long[WINDOW_WIDTH*WINDOW_HEIGHT];
 cVideo.SetVideoPointer(VideoBufferPtr,WINDOW_WIDTH); 
}
//-Деструктор класса---------------------------------------------------------
CMain::~CMain()
{ 
 delete[](VideoBufferPtr);
}
//-Замещённые функции предка-----------------------------------------------
//-Новые функции класса------------------------------------------------------

//----------------------------------------------------------------------------------------------------
//открытие окна
//----------------------------------------------------------------------------------------------------
bool CMain::OpenWindow(void)
{
 PgSetDrawBufferSize(65535);
 cControl.Init(); 
 return(true);
}
//----------------------------------------------------------------------------------------------------
//закрытие окна
//----------------------------------------------------------------------------------------------------
bool CMain::CloseWindow(void)
{
 cControl.Close();	
 return(true);	
}
//-Функции обработки сообщений класса----------------------------------------
//----------------------------------------------------------------------------------------------------
//нажали или отпустили кнопку мыши
//----------------------------------------------------------------------------------------------------
void CMain::OnActivate_MouseButton(long x,long y,bool left,bool right,bool center)
{
 OnPaint();
}
//----------------------------------------------------------------------------------------------------
//перерисовать картинку
//----------------------------------------------------------------------------------------------------
void CMain::OnPaint(void)
{
 PhDim_t ImageSize;
 int ImageBPL=WINDOW_WIDTH*sizeof(unsigned long);
 ImageSize.w=WINDOW_WIDTH;
 ImageSize.h=WINDOW_HEIGHT;
 PhPoint_t pos;
 pos.x=0;
 pos.y=0;
 PgDrawImagev(VideoBufferPtr,Pg_IMAGE_DIRECT_8888,&pos,&ImageSize,ImageBPL,0);  
}

//----------------------------------------------------------------------------------------------------
//обработка таймера
//----------------------------------------------------------------------------------------------------
void CMain::OnActivate_Timer(void)
{ 
 cVideo.SetVideoPointer(VideoBufferPtr,WINDOW_WIDTH);
 cControl.Processing();	
 OnPaint();
}

//-Новые функции класса------------------------------------------------------

//-Прочее--------------------------------------------------------------------



/* Y o u r   D e s c r i p t i o n                       */
/*                            AppBuilder Photon Code Lib */
/*                                         Version 2.03  */

/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <photon/PkKeyDef.h>
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>

/* Local headers */
#include "ablibs.h"
#include "abimport.h"
#include "proto.h"
#include "ckbrd.h"

extern CKeyboard cKeyboard;

int OnActivate_Raw(PtWidget_t *widget, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo )
{
 if (cbinfo->event->type==Ph_EV_KEY)
 {
  PhKeyEvent_t *kev=(PhKeyEvent_t *)PhGetData(cbinfo->event);
  long scan=kev->key_scan;
  if (kev->key_flags&Pk_KF_Scan_Valid) 
  {
   if (kev->key_flags&Pk_KF_Key_Down) cKeyboard.SetKeyState(scan,true);
                                else  cKeyboard.SetKeyState(scan&0x7f,false);
   //printf("Scan:%i\r\n",scan);                                
  }
 }
 return(Pt_CONTINUE);
}
Прикольно. :) Правда, а это точно софтверный рендер — вот то, что с освещением? Вроде как карта освещенённости используется, но её в софверном варианте довольно дорого выводить.

Пожалуйста, укажите лицензию на код и, если совсем не затруднит, создайте репозиторий, например на GitHub или Bitbucket. Спасибо!

Лицензию на код? Я в них не разбираюсь. Как называется лицензия, если вы можете делать с кодом абсолютно всё, что захотите? Free?
А на Github я не могу выложить — я никогда не работал с такими системами разработки и понятия не имею, как там вообще осуществляется работа. Я попытался понять, нифига не понял, что там вообще нужно делать. Дело в том, что я в команде никогда не работал (хоть я и пишу ПО по работе в НИИ, но я самоучка); у нас таких систем не применяется и опыта работы с ними у меня полный ноль.
Спасибо. :) Ну, значит, MIT. :)

Правда, не знаю, кому эти исходники пригодиться вообще могут. Мне тут на одном форуме сказали, фигурально, что я фигнёй страдаю с софтверными движками и обычным OpenGL. И что давно вместо «forward rendering» используют Deferred Rendering и надо брать готовый движок типа Ogre3d и не изобретать велосипед. :) И я в общем, почти согласен с первым, но вот со вторым не соглашусь — зачем мне Ogre3D, если я игру делать всё равно не буду. :)
Правда, не знаю, кому эти исходники пригодиться вообще могут

По этой теме точно не следует волноваться! Сообществу, имхо, чем больше кодов выложено, тем лучше ;)

Кстати, я совсем забыл — в этом движке используется пирамида фильтрации текстур. Ну тут совсем просто — определяется, во сколько раз текстура сжата и выбирается нужная текстура и всё. Функция FindTexture как раз этим и занимается. Также в движке есть вывод спрайта — он просто закомментирован (функция PutSprite). В качестве спрайта используется текстура стены (он тестовый). Но для его вывода нужно раскомментировать в функциях текстурирования в cEngine_Base.cpp заполнение Z-буфера. Раньше я как раз я Z-буфером спрайты и выводил. Но это медленно, поэтому я отключил спрайты и как будет желание и время, подумаю, как обойтись без него.

da-nie нельзя ли, пожалуйста, продублировать скачивание файлов, чтобы качать их НЕ с яндекса?
(до сих пор пытаюсь скачать пример и постоянно обламываюсь)

Странно, я думал, с яндекс-диска никаких проблем со скачиванием не бывает. Вот ссылка на другом ресурсе.
Кое-что расскажу о том, как это работает. В файлах исходников геометрический портал в cengine_gportal.cpp. Там вот что делается.
1) Задаём исходный портал, совпадающий с областью поля зрения игрока.
2) Задаём прямую отсечения — это не более, чем прямая, перпендикулярная вектору зрения игрока. Она нужна, чтобы выбросить из рассмотрения всё, что за спиной игрока. Дело в том, что использованная математика требует, чтобы всё происходило перед игроком.
3) Назначаем текущим сектором, сектор, где находится игрок.
4) Выводим стены текущего сектора
5) Рекурсивно бегаем через все порталы сектора, модифицируя портал на ходу и меняя текущий сектор (как зашли в сектор -выводим все его стены). При этом блокируем портал при заходе в него и разблокирует после выхода (через один портал можно смотреть по-разному, в зависимости от того, как мы в него пришли).

Вот и всё.А вот картинка, как обрезаются порталы и стены.



В OpenGL обрезать нужно только порталы — стены сами обрежутся OpenGL с помощью Z-буфера.

Вроде всё. :)
Совсем забыл случай, когда стена пересекает прямую области видимости. В этом случае я просто обрезаю стену областью видимости так, чтобы она была со стороны игрока.
Попробовал конвертировать карту типа Wolf-3D. Получилось плохо и вот почему: если каждую клетку назначить сектором с порталами в 4 стороны (или меньше — со стороны блоков-стен нужно поставить стену, а не портал), то при рекурсивном обходе порталов в некоторых зонах начинаются тормоза (вплоть до FPS=0). Фишка в том, что раз сектора мелкие, обход через все порталы всех остальных порталов и секторов вырождается в здоровенную рекурсию. То есть, вот так втупую лабиринт из Wolf-3D с порталами не подружить. Нужно как-то объединять сектора в один большой сектор, не создавая порталов.
Всё, понял в чём дело. В функции конвертации карты из Wold-3D создавался двойной набор порталов. Вот оно и дико тормозило. Исправил — и всё заработало на ура. :) Не такая уж там и большая рекурсия, даже без объединения квадратов.

Конвертировал вот такой вот функцией в редакторе 3DEngineEditor. Карта задаётся в текстовом файле:'#'-стена.

//---------------------------------------------------------------------------
//импорт карты из текстового файла
//---------------------------------------------------------------------------
void CDocument_Map::ImportMapFromTXT(FILE *file,CDialog_SettingsWall *cDialog_SettingsWall_Ptr,CDialog_SettingsSector *cDialog_SettingsSector_Ptr)
{
 long n;
 DeleteAll();
 //считываем текстовый файл в плоский массив (0-следующая строка)
 vector<unsigned char> vector_data;
 bool element=false;
 long x=0;
 long y=0;
 long max_x=0;
 while(1)
 {
  unsigned char b;
  if (fread(&b,sizeof(char),1,file)<=0) break;//файл закончился
  if (b<static_cast<unsigned char>(' '))
  {
   if (element==true)//если в строке что-то было считано, делаем перевод строки 
   {
    y++;
    vector_data.push_back(0);
   }
   element=false;
   if (x>max_x) max_x=x;
   x=0;//строка начинается заново
   continue;
  }
  element=true;
  x++;
  vector_data.push_back(b);
 }
 long max_y=y;
 //разворачиваем считанную карту в двумерный массив [max_y][max_x] и заполняем его точками
 vector< vector<unsigned char> > vector_map(max_y,vector<unsigned char>(max_x,'.'));
 x=0;
 y=0;
 long size=vector_data.size();
 for(n=0;n<size;n++)
 {
  if (vector_data[n]==0 || n==size-1)
  {
   y++;
   x=0;
   continue;
  }
  if (vector_data[n]==static_cast<unsigned char>('#')) vector_map[max_y-y-1][x]=static_cast<unsigned char>('#');//непустое поле
  x++;
 }
 vector_data.clear();
 //создаём сектора и сегменты по считанной карте
 CSector cSector_Create;//создаваемый сектор
 cSector_Create.vector_SSectorPoint.clear();
 cSector_Create.Select=false;
 cSector_Create.sSector_State=cDialog_SettingsSector_Ptr->GetState();
 CWall cWall_Create;//создаваемая стена
 cWall_Create.Frontier=false;
 cWall_Create.Select=false;
 cWall_Create.sWall_State=cDialog_SettingsWall_Ptr->GetState();
 for(y=0;y<max_y;y++)
 {
  for(x=0;x<max_x;x++)
  {
   unsigned char b=vector_map[y][x];
   if (b==static_cast<unsigned char>('.'))//сектор пустой
   {
    //создаём пустой сектор
    cSector_Create.vector_SSectorPoint.clear();
    //добавляем точки четырёхугольника, описывающего сектор
    SSectorPoint sSectorPoint[4];
    sSectorPoint[0].X=(x+1)*100;
    sSectorPoint[0].Y=(y+1)*100;
    sSectorPoint[1].X=(x+1)*100;
    sSectorPoint[1].Y=(y+0)*100;
    sSectorPoint[2].X=(x+0)*100;
    sSectorPoint[2].Y=(y+0)*100;
    sSectorPoint[3].X=(x+0)*100;
    sSectorPoint[3].Y=(y+1)*100;

    cSector_Create.vector_SSectorPoint.push_back(sSectorPoint[0]);
    cSector_Create.vector_SSectorPoint.push_back(sSectorPoint[1]);
    cSector_Create.vector_SSectorPoint.push_back(sSectorPoint[2]);
    cSector_Create.vector_SSectorPoint.push_back(sSectorPoint[3]);
    //добавляем сектор в список секторов
    vector_CSector.push_back(cSector_Create);
	//если вокруг сектора есть стектора-стены, то добавляем стену, иначе добавляем линию раздела
	for(n=0;n<4;n++)
	{
     long next_n=(n+1)%4;
     cWall_Create.X1=sSectorPoint[n].X;
     cWall_Create.Y1=sSectorPoint[n].Y;
     cWall_Create.X2=sSectorPoint[next_n].X;
     cWall_Create.Y2=sSectorPoint[next_n].Y;
     cWall_Create.Frontier=true;//стена - линия раздела
	 if (n==0)//правая стена
	 {
      if (x<max_x-1)
	  {
       if (vector_map[y][x+1]!=static_cast<unsigned char>('.')) cWall_Create.Frontier=false;//сектор справа - стена
	  }
	  else cWall_Create.Frontier=false;
	 }

	 if (n==1)//нижняя стена
	 {
      if (y>0)
	  {
       if (vector_map[y-1][x]!=static_cast<unsigned char>('.')) cWall_Create.Frontier=false;//сектор снизу - стена
                                                           else continue;//не ставим портал, так как он уже есть с этой стороны
	  }
	  else cWall_Create.Frontier=false;
	 }

	 if (n==2)//левая стена
	 {
      if (x>0)
	  {
       if (vector_map[y][x-1]!=static_cast<unsigned char>('.')) cWall_Create.Frontier=false;//сектор слева - стена
	                                                       else continue;//не ставим портал, так как он уже есть с этой стороны
	  }
	  else cWall_Create.Frontier=false;
	 }

	 if (n==3)//верхняя стена
	 {
      if (y<max_y-1)
	  {
       if (vector_map[y+1][x]!=static_cast<unsigned char>('.')) cWall_Create.Frontier=false;//сектор сверху - стена
	  }
	  else cWall_Create.Frontier=false;
	 }
	 //добавляем сегмент
     vector_CWall.push_back(cWall_Create);
	}
   }
  }
 }
}
Так это ж самая очевидная схема работы с порталами. :) Я сперва именно геометрический портал и сделал. А потом, много позже, сообразил, что можно по экрану отсекать в случае, когда стены не могут наклоняться влево-вправо. В общем, для Wolf-3D под OpenGL геометрический портал просто идеален. Только надо при выводе стен не выводить одну и ту же стенку дважды (делать отметку о том, что стена уже выведена) и выводить не фрагмент, видимый через портал, а всю стену сразу. Так же и с персонажами и прочими объектами — выводить только из тех секторов-квадратов, которые были выведены через портал и заодно можно проверить, что объекты вообще видны в портал (описать прямоугольник вида сверху вокруг объектов и сравнивать его попадание в портал).
Движок легендарный, спору нет. Я даже не во все игры успел на нем погонять.
Это который от Сильвермана — Build. Это легендарный 100%. Shadow Warrion, Blood, Duke Nukem 3D.

Но к этой статье приложен не он, а самодельный, гораздо менее мощный.
И вот что меня удивляет. 20 лет, как вышел Duke 3D. Армия программистов. А движков самодельных (хоть с OpenGL, Direct 3D, хоть сотфверных) сделано очень мало. Как так-то? Вообще странно, столько программистов, а весь инет не завален их интересными программами (например, графическими). То ли они пишут только на работе, а дома нет, то ли ещё есть какая причина, но при таком количестве программистов их программ должно быть сильно дофига.
Можно также спросить, где Бахи, Бетховены, Пушкины, Толстые и всякие другие Моцарты…
Only those users with full accounts are able to leave comments. Log in, please.