воскресенье, 2 февраля 2025 г.

Пример приложения печатающий текст в стиле терминала из фильма Matrix "Матрица"

Спустя 12 лет довел до ума пример приложения, который печатает текст в стиле терминала из фильма Матрица с DelphiX на OpenGL. Оригинальный пример, на основе которого сделан мой пример, взят с сайта Delphi-Graphics. Мой код базируется на Delphi, OpenGL, WinAPI. Код моего примера оставляю без комментарий, т. к. написание проводилось без детального разбора работы алгоритма оригинального приложения и осуществлялся банальный подбор функций OpenGL и их параметров. На рисунке 1 представлена работа моего примера в Windows 10. 

Рисунок 1. Пример приложения вывода текста в стиле терминала из фильма "Матрица"

program matrix;

uses
  Windows,
  Messages,
  OpenGL;

var
  h_Rc: HGLRC;
  h_Dc: HDC;
  h_Wnd: HWND;
  base: GLuint;

  keys: array [0..255] of BOOL;

  text: array[0..4] of String = ('Wake up, Neo.',
                                 'The Matrix has you.',
                                 'Follow the White Rabbit.',
                                 'Disconnecting...',
                                 '');

  Active: bool = true;
  FullScreen:bool = true;
  f,z: boolean;
  a,b: byte;
  sx: integer;
  cur: byte;
  all: string;

procedure ReSizeGLScene(Width: GLsizei; Height: GLsizei);
begin
  if (Height=0) then
     Height:=1;
  glViewport(0, 0, Width, Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, Width, Height, 0, 1, -1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
end;

procedure BuildFont;
var
  font: HFONT;
  oldfont: HFONT;
begin

  base := glGenLists(96);

  font := CreateFont( -30,
                        0,
                        0,
                        0,
                        FW_BOLD,
                        0,
                        0,
                        0,
                        ANSI_CHARSET,
                        OUT_TT_PRECIS,
                        CLIP_DEFAULT_PRECIS,
                        ANTIALIASED_QUALITY,
                        FF_DONTCARE or DEFAULT_PITCH,
                        'Courier New');

  oldfont := SelectObject(h_DC, font);                       
  wglUseFontBitmaps(h_DC, 32, 96, base);
  SelectObject(h_DC, oldfont);
  DeleteObject(font);
end;

procedure KillFont;
begin
  glDeleteLists(base, 96);
end;

procedure glPrint(const Fmt: String);
begin

if Fmt = '' then
  Exit;

glPushAttrib(GL_LIST_BIT);
glListBase(base - 32);
glCallLists(Length(Fmt), GL_UNSIGNED_BYTE, Pointer(Fmt));
glPopAttrib();

end;

function IntToStr(Num: Integer) : String;
begin
  Str(Num, Result);  
end;

function InitGL:bool;
begin

  glClearColor(0.0, 0.0, 0.0, 0.0);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 640, 480, 0, 0, 1);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  BuildFont();

  a:=0;
  b:=0;
  sx:=0;
  cur:=0;

  Result:=true;
end;

procedure addchar;
begin
  if length(all)=length(text[cur]) then
  begin
    inc(cur);
    sx:=0;
    z:=false;
    all:='';
    Exit;
  end;
  if cur<=5 then
  begin
    all := all + text[cur][length(all)+1];
  end
  else
  begin
    exit;
  end;
end;

function DrawGLScene():bool;
var
  i: integer;
  kk2:integer;
  kk: integer;
begin
  glClear(GL_COLOR_BUFFER_BIT);

  glEnable(GL_ALPHA_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glLoadIdentity();

  if not z then
    inc(sx);
  if sx=200 then
    z:=true;

  if z then
  begin
    inc(B);
    if b=20 then addchar;
    if b>20 then b:=0;
  end;

  if not f then
    inc(a,5)
  else dec(a,5);

  if (a>=255)or(a=0) then f:=not f;

  glColor3f(0.196078, 0.8, 0.196078);
  for i := 0 to cur - 1 do
  begin
    glRasterPos2f(0.0, 20.0 + i*30);
    glPrint(text[i]);
  end;

  if i=5 then
  begin
    i:=0;
    cur:=0;
    a:=0;
    b:=0;
    sx:=0;
    z:=false;
    f:=False;
    all:='';
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
  end;

  kk := 18*length(all);
  kk2 := cur*30;

  glRasterPos2f(0.0, 20.0 + cur*30);
  glPrint(all);

  glColor4f(0.196078, 0.8, 0.196078, a / 255); // Colour LimeGreen
  glRectf(0.0+kk, 0.0+kk2, 23.0+kk, 23.0+kk2);

  glDisable(GL_BLEND);
  glDisable(GL_ALPHA_TEST);

  Result := true;
end;


function WndProc(hWnd: HWND;
                 message: UINT;
                 wParam: WPARAM;
                 lParam: LPARAM):
                                  LRESULT; stdcall;
begin
  if message=WM_SYSCOMMAND then
  begin
      case wParam of
        SC_SCREENSAVE,SC_MONITORPOWER:
          begin
            result:=0;
            exit;
          end;
      end;
  end;
  
  case message of
    WM_ACTIVATE:
      begin
        if (Hiword(wParam)=0) then
          active:=true
        else
          active:=false;
        Result:=0;
      end;
    WM_CLOSE:
      Begin
        PostQuitMessage(0);
        result:=0
      end;
    WM_KEYDOWN:
      begin
        keys[wParam] := TRUE;
        result:=0;
      end;
    WM_KEYUP:
      begin
    	  keys[wParam] := FALSE;
        result:=0;
      end;
    WM_SIZe:
      begin
    	  ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
        result:=0;
      end
    else

      begin
      	Result := DefWindowProc(hWnd, message, wParam, lParam);
      end;
    end;
end;


procedure KillGLWindow;
begin
  if FullScreen then
    begin
      ChangeDisplaySettings(devmode(nil^),0);
      showcursor(true);
    end;
  if h_rc<> 0 then
    begin
      if (not wglMakeCurrent(h_Dc,0)) then
        MessageBox(0,'Release of DC and RC failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
      if (not wglDeleteContext(h_Rc)) then
        begin
          MessageBox(0,'Release of Rendering Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
          h_Rc:=0;
        end;
    end;
  if (h_Dc=1) and (releaseDC(h_Wnd,h_Dc)<>0) then
    begin
      MessageBox(0,'Release of Device Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
      h_Dc:=0;
    end;
  if (h_Wnd<>0) and (not destroywindow(h_Wnd))then
    begin
      MessageBox(0,'Could not release hWnd.',' Shutdown Error',MB_OK or MB_ICONERROR);
      h_Wnd:=0;
    end;
  if (not UnregisterClass('OpenGL',hInstance)) then
    begin
      MessageBox(0,'Could Not Unregister Class.','SHUTDOWN ERROR',MB_OK or MB_ICONINFORMATION);
    end;
end;


function CreateGlWindow(title:Pchar; width,height,bits:integer;FullScreenflag:bool):boolean stdcall;
var
  Pixelformat: GLuint;
  wc:TWndclass;
  dwExStyle:dword;
  dwStyle:dword;
  pfd: pixelformatdescriptor;
  dmScreenSettings: Devmode;
  h_Instance:hinst;
  WindowRect: TRect;
begin
  WindowRect.Left := 0;
  WindowRect.Top := 0;
  WindowRect.Right := width;
  WindowRect.Bottom := height;
  h_instance:=GetModuleHandle(nil);
  FullScreen:=FullScreenflag;
  with wc do
    begin
      style:=CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
      lpfnWndProc:=@WndProc;
      cbClsExtra:=0;
      cbWndExtra:=0;
      hInstance:=h_Instance;
      hIcon:=LoadIcon(0,IDI_WINLOGO);
      hCursor:=LoadCursor(0,IDC_ARROW);
      hbrBackground:=0;
      lpszMenuName:=nil;
      lpszClassName:='OpenGl';
    end;
  if  RegisterClass(wc)=0 then
    begin
      MessageBox(0,'Failed To Register The Window Class.','Error',MB_OK or MB_ICONERROR);
      Result:=false;
      exit;
    end;
  if FullScreen then
    begin
      ZeroMemory( @dmScreenSettings, sizeof(dmScreenSettings) );
      with dmScreensettings do
        begin
          dmSize := sizeof(dmScreenSettings);
          dmPelsWidth  := width;
	        dmPelsHeight := height;
          dmBitsPerPel := bits;
          dmFields     := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
        end;

      if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN))<>DISP_CHANGE_SUCCESSFUL THEN
        Begin

          if MessageBox(0,'This FullScreen Mode Is Not Supported. Use Windowed Mode Instead?'
                                             ,'Matrix',MB_YESNO or MB_ICONEXCLAMATION)= IDYES then
                FullScreen:=false
          else
            begin

              MessageBox(0,'Program Will Now Close.','Error',MB_OK or MB_ICONERROR);
              Result:=false;
              exit;
            end;
          end;
    end;
  if FullScreen then
    begin
      dwExStyle:=WS_EX_APPWINDOW;
      dwStyle:=WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
      Showcursor(false);
    end
  else
    begin
      dwExStyle:=WS_EX_APPWINDOW or WS_EX_WINDOWEDGE;
      dwStyle:=WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
    end;
  AdjustWindowRectEx(WindowRect,dwStyle,false,dwExStyle);

  H_wnd:=CreateWindowEx(dwExStyle,
                               'OpenGl',
                               Title,
                               dwStyle,
                               0,0,
                               WindowRect.Right-WindowRect.Left,
                               WindowRect.Bottom-WindowRect.Top,
                               0,
                               0,
                               hinstance,
                               nil);
  if h_Wnd=0 then
    begin
      KillGlWindow();
      MessageBox(0,'Window creation error.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  with pfd do
    begin
      nSize:= SizeOf( PIXELFORMATDESCRIPTOR );
      nVersion:= 1;
      dwFlags:= PFD_DRAW_TO_WINDOW
        or PFD_SUPPORT_OPENGL
        or PFD_DOUBLEBUFFER;
      iPixelType:= PFD_TYPE_RGBA;
      cColorBits:= bits;
      cRedBits:= 0;
      cRedShift:= 0;
      cGreenBits:= 0;
      cBlueBits:= 0;
      cBlueShift:= 0;
      cAlphaBits:= 0;
      cAlphaShift:= 0;
      cAccumBits:= 0;
      cAccumRedBits:= 0;
      cAccumGreenBits:= 0;
      cAccumBlueBits:= 0;
      cAccumAlphaBits:= 0;
      cDepthBits:= 16;
      cStencilBits:= 0;
      cAuxBuffers:= 0;
      iLayerType:= PFD_MAIN_PLANE;
      bReserved:= 0;
      dwLayerMask:= 0;
      dwVisibleMask:= 0;
      dwDamageMask:= 0;
    end;
  h_Dc := GetDC(h_Wnd);
  if h_Dc=0 then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t create a GL device context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  PixelFormat := ChoosePixelFormat(h_Dc, @pfd);
  if (PixelFormat=0) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t Find A Suitable PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  if (not SetPixelFormat(h_Dc,PixelFormat,@pfd)) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t set PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  h_Rc := wglCreateContext(h_Dc);
  if (h_Rc=0) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t create a GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  if (not wglMakeCurrent(h_Dc, h_Rc)) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t activate the GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  ShowWindow(h_Wnd,SW_SHOW);
  SetForegroundWindow(h_Wnd);
  SetFOcus(h_Wnd);
  ReSizeGLScene(width,height);
  if (not InitGl()) then
    begin
      KillGLWindow();
      MessageBox(0,'initialization failed.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  Result:=true;
end;


function WinMain(hInstance: HINST;
		 hPrevInstance: HINST;
		 lpCmdLine: PChar;
		 nCmdShow: integer):
                        integer; stdcall;
var
  msg: TMsg;
  done: Bool;

begin
  done:=false;

  if MessageBox(0,'Would You Like To Run In FullScreen Mode?','Start FullScreen', MB_YESNO or MB_ICONQUESTION)=IDNO then
    FullScreen:=false
  else
    FullScreen:=true;

  if not CreateGLWindow('Matrix',640,480,16,FullScreen) then
  begin
    Result := 0;
    exit;
  end;

  while not done do
  begin
    if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then
    begin
      if msg.message=WM_QUIT then
        done:=true
      else
      begin
        TranslateMessage(msg);
        DispatchMessage(msg);
      end;
    end
    else
    begin

      if (Active and (not DrawGLScene()) or keys[VK_ESCAPE]) then
      begin
        done:=true;
      end;

      if (keys[VK_F1]) then
      begin
        Keys[VK_F1] := false;
        KillGLWindow();
        FullScreen := not FullScreen;

        if not CreateGLWindow('Matrix',640,480,16,fullscreen) then Result := 0;
      end;

      SwapBuffers(h_Dc);
    end;
  end;

  KillFont();
  killGLwindow();

  result:=msg.wParam;
end;

begin
  WinMain( hInstance, hPrevInst, CmdLine, CmdShow );
end.