{*******************************************************}
{                                                       }
{       Small Game Engine (IDX_Engine.pas)              }
{                                                       }
{       Copyright (C) 2011 IdeyaX studio                }
{                                                       }
{*******************************************************}

unit IDX_Engine;

interface
uses
    Windows,
    Messages,
    OpenGl,
    Fmod,
    Fmodtypes,
    Fmoderrors;

const
 cIDXProcInit=0;
 cIDXProcDraw=1;
 cIDXProcUpdate=2;
 cIDXProcClose=3;

 cIDXEngineVersion='s1.0';
 AppName = 'OGL_IDXEngine';

  FX_BLEND_NORMAL = 0;
  FX_BLEND_ADD    = 1;
  FX_BLEND_MULT   = 2;
  FX_BLEND_BLACK  = 3;
  FX_BLEND_WHITE  = 4;
  FX_BLEND_MASK   = 5;

type
 TAtlasItem=record
   x1,y1:Single;
   x2,y2:Single;
   w,h:Integer;
end;
  
type
 TWndRec=record

  Width:integer;
  Height:integer;
  DWidth:integer;
  DHeight:integer;
  Fps:integer;
  ColorBit:integer;
  isFullScreen:boolean;
  VSync:boolean;

  Handle: hWnd;
  DC: HDC;
  HRC: HGLRC;
  Instance: HINST;
  Title  : PAnsiChar;

  OptionsFileName:string;

  Pause:Boolean;
  Focus:Boolean;
  isRun:Boolean;
  isFinished:Boolean;
  isMinimized:Boolean;
  isVisible:Boolean;
  isActive:Boolean;

  time_delta:DWORD;
  time_old:DWORD;
  time_new:DWORD;

  deltaTime:Single;

  Keys: array [0..255] of Boolean;

  Mouse:record
          X,Y:Integer;
          Wheel:Integer;
          bLeft:Boolean;
          bRight:Boolean;
          bMiddle:Boolean;
        end;

  OnInit:procedure;
  OnDraw:procedure;
  OnUpdate:procedure;
  OnClose:procedure;
end;

type
  TSoundIni=record
   PlayMusic:Boolean;
   PlaySound:Boolean;
   Enable:Boolean;

   MusicVolume:Integer;
   SoundVolume:Integer;

   SoundList:array of PFSoundSample;
   MusicList:array of record
                       isStream:Boolean;
                       Item: PFMusicModule;
                       sItem: PFSoundStream;
                      end;

   EffectStream,MusicStream : PFSoundStream;

end;

Var
 gameWND:TWndRec;
 gameSound:TSoundIni;

 fIDXLogFile:TextFile;
 IDXENGTexureList:array of GLuint;

procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;

function IDXENG_Init(Width,Height:Integer;
                  Title: PAnsiChar;
                  Fullscreen:Boolean=False;
                  ScreenFPS:Integer=30;
                  VSync:Boolean=False):Boolean;

procedure IDXENG_Register(procId:Integer; proc:Pointer);
procedure IDXENG_Draw();
procedure IDXENG_Procces();
procedure IDXENG_Close;
procedure IDXENG_SetPerspective(Width, Height: Integer;fovy:Single=45.0;zNear:Single=0.1; zFar:Single=300 );
procedure IDXENG_SetCaption(text:PAnsiChar);


Procedure IDXENG_Log_Open;
Procedure IDXENG_Log_Close;
Procedure IDXENG_Log_Add(const value:AnsiString;AddTime:Boolean=False);
Procedure IDXENG_Log_AddError(const value:AnsiString);
Procedure IDXENG_Log_AddWarning(const value:AnsiString);

function IDXENG_SysFont_Create(const Name: String; Width, Height, Weight: Integer; DC: HDC) : Cardinal;
procedure IDXENG_SySFont_Free(Font: Cardinal);
procedure IDXENG_Draw2d_Text(Font: Cardinal; const Text: String; X, Y: Single; Color: Integer=$FFFFFF; Alpha : Byte=255);

procedure IDXENG_Draw2d_Circle( cx,cy,r:single;
                                Color:Integer=$FFFFFF;
                                Alpha:Byte=255;
                                num_segments:integer=26;
                                fill:boolean=False );
procedure IDXENG_Draw2d_Line( ax, ay, bx, by:Single; Width:Single=1; Color: Integer=$FFFFFF;Alpha : Byte=255 );
procedure IDXENG_Draw2d_Rectangle(X, Y:Single; Width, Height:Single; Color:integer=$FFFFFF; Alpha : byte=255;Angle:integer=0; Fill : boolean = false;Center:boolean=false);
procedure IDXENG_Draw2d_Pixel( X, Y : Single; Color : LongWord; Alpha : Byte = 255 );
procedure IDXENG_Draw2d_Texture(x,y:Single;
                                Width,Height:Single;
                                textId:cardinal;
                                Angle:Integer=0;
                                Center:boolean=false;
                                Color:Integer=$FFFFFF;
                                Alpha:Byte=255;
                                flipx:Boolean=False;
                                flipy:Boolean=False);

procedure IDXENG_Draw2d_TextureA(x,y:Single;
                                  Width,Height:Single;
                                  textId:cardinal;
                                  apos:TAtlasItem;
                                  Angle:Integer=0;
                                  Center:boolean=false;
                                  Color:Integer=$FFFFFF;
                                  Alpha:Byte=255;
                                  flipx:Boolean=False;
                                  flipy:Boolean=False
                                  );
                                                                  
procedure IDXENG_SetPixelSize(value:Single);
function IDXENG_PointInRect(X,Y:Single;Rx,RY,Rw,Rh:Single):Boolean;
function IDXENG_PointInCircle(X,Y:Single;Cx,Cy,Cr:Single):boolean;

procedure IDXENG_Enable2d(A:boolean);
procedure IDXENG_SetClearColor(color:Integer );

procedure IDXENG_SetBlendMode( Mode : Byte; SeparateAlpha : Boolean = False );

procedure IDXENG_TexturesListClear();
procedure IDXENG_TexturesListAddItem(Texture: GLuint);
procedure IDXENG_TextureSet(ID:GLuInt);
function IDXENG_TextureCreate(Width, Height, Format : Word; pData : Pointer) : GLuInt;
function IDXENG_TextureLoadPNG(Filename: String;var Texture: GLuint) : Boolean ;
function IDXENG_TextureLoadBMP(Filename : String; var Texture : GLuInt) : Boolean;
function IDXENG_TextureLoadJPG(Filename: String; var Texture: GLuint): Boolean;
function IDXENG_TextureLoadTGA(Filename: String; var Texture: GLuint): Boolean;
function IDXENG_TextureLoad(Filename: String; var Texture : GLuint) : Boolean;
procedure IDXENG_Options_Save;
procedure IDXENG_Options_Load(Filename:string);


function IDXUtils_IntToStr(x: integer): string;
function IDXUtils_StrToInt(const S: string): Integer;
function IDXUtils_ExtractFileDir(const FileName: string): string;
function IDXUtils_randomInt(startval:Integer; endval:Integer):Integer;
function IDXUtils_randomFloat(startval, endval:Single):Single;
function IDXUtils_ClampByte(x:Integer):Integer;
function IDXUtils_Bezier(p0:Integer; p1:Integer; p2:Integer; p3:Integer; t:Single):Integer;
function IDXUtils_RectsOverlap(x0,y0,w0,h0,x2,y2,w2,h2:Single):Boolean;
function IDXUtils_RectCircleOverlap(x0,y0,w0,h0,cx,cy,r:Single):Boolean;
function IDXUtils_RectRectCol(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2:Single):Boolean;
function IDXUtils_PointRectCol(px, py, rx1, ry1, rx2, ry2:Single):Boolean;

function IDXSound_Init:Boolean;
function IDXSound_Load(Filename: String; var sound : Integer; len:Integer=0 ) : Boolean;
procedure IDXSound_Play(Id:integer;Volume:Integer=100;pan:Integer=127);
procedure IDXSound_PlayMusic(id:Integer);
function IDXSound_LoadMusic(Filename: String; var music : Integer) : Boolean;
procedure IDXSound_PauseMusic(id:Integer; value:Boolean);
procedure IDXSound_Close;

implementation
 uses
     SysUtils,
     Graphics,
     JPEG,
     pngimage,
     Classes;


function IDXUtils_RectCircleOverlap(x0,y0,w0,h0,cx,cy,r:Single):Boolean;
var
 testX,
 testY:Single;
begin
	testX := cX;
	testY := cY;

	If TestX < x0 Then TestX:= x0;
	If TestX > (x0 + w0) Then TestX:= (x0 + w0);
	If TestY < y0 Then Testy:= y0;
	If TestY > (y0 + h0) Then Testy:= (y0 + h0);

	If ((cX - TestX) * (cX - TestX) + (cY - TestY) * (cY - TestY)) < (r * r) Then
         Result:=True
        Else
         Result:=False;
end;

function IDXUtils_RectRectCol(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2:Single):Boolean;
begin
   Result:=not(((r1x1 < r2x1) and (r1x2 < r2x1)) or
               ((r1x1 > r2x2) and (r1x2 > r2x2)) or
               ((r1y1 < r2y1) and (r1y2 < r2y1)) or
               ((r1y1 > r2y2) and (r1y2 > r2y2)));
end;

function IDXUtils_PointRectCol(px, py, rx1, ry1, rx2, ry2:Single):Boolean;
begin
   Result:=((px >= rx1) and (px <= rx2) and (py >= ry1) and (py <= ry2));
end;

function IDXUtils_RectsOverlap(x0,y0,w0,h0,x2,y2,w2,h2:Single):Boolean;
begin
   Result:=True;
    If (x0 > (x2 + w2)) Or ((x0 + w0) < x2) Then
    begin
      Result:=False;
      Exit;
    end;
    If (y0 > (y2 + h2)) Or ((y0 + h0) < y2) Then
    begin
      Result:=False;
      Exit;
    end;
End;


function IDXUtils_Bezier(p0:Integer; p1:Integer; p2:Integer; p3:Integer; t:Single):Integer;
var
  b:Single;
begin
    b:= 1 - t;
    Result:=Trunc(p1 * (b * b * b) + 3 * p0 * (b * b) * t + 3 * p3 * (b) * (t * t) + p2 * (t * t * t));
End;

function IDXUtils_randomInt(startval:Integer; endval:Integer):Integer;
begin
    Result:=(Random(endval - startval) + startval);
end;

function IDXUtils_randomFloat(startval, endval:Single):Single;
var
  v:Single;
begin
try
  Result:= ( random ) * ( endval - startval ) + startval;
except
  Result:= startval;
end;
end;

function IDXUtils_ClampByte(x:Integer):Integer;
begin
    If x < 0 Then x:= 0;
    If x > 255 Then x:= 255;
    Result:=x;
end;

function IDXUtils_IntToStr(x: integer): string;
begin
 Str(x, Result);
end;

function IDXUtils_StrToInt(const S: string): Integer;
var
  E: Integer;
begin
  Val(S, Result, E);
end;

function IDXUtils_ExtractFileDir(const FileName: string): string;
var
  I: Integer;
begin
  I := LastDelimiter(PathDelim + DriveDelim,Filename);
  
  if (I > 1) and (FileName[I] = PathDelim) and
    (((FileName[I - 1] <> PathDelim) and (FileName[I-1] <> DriveDelim)) or
    (ByteType(FileName, I-1) = mbTrailByte)) then Dec(I);

  while (ByteType(FileName, I-1) = mbTrailByte) and (I > 0) do Dec(I);

  Result := Copy(FileName, 1, I);
end;

procedure IDXENG_Options_Save;
//var
//  ini : TIniFile;
begin
 if Length(gameWND.OptionsFileName)=0 then Exit;
//  if fileexists(Filename) then
//  begin
//    ini:=TIniFile.Create(Filename);
//
//    if playingmusic then
//     ini.SetIniSectionKeyValue('Media','PlayMusic','1')
//    else
//     ini.SetIniSectionKeyValue('Media','PlayMusic','0');
//
//    if playingsound then
//     ini.SetIniSectionKeyValue('Media','PlaySound','1')
//    else
//     ini.SetIniSectionKeyValue('Media','PlaySound','0');
//
//     ini.SaveToFile;
//
//    ini.Free;
//  end


end;


procedure IDXENG_Options_Load(Filename:string);
//var
//  ini : TIniFile;
begin
 gameWND.OptionsFileName:=Filename;
//  if fileexists(Filename) then
//  begin
//    ini:=TIniFile.Create(Filename);
//
//    if playingmusic then
//     ini.SetIniSectionKeyValue('Media','PlayMusic','1')
//    else
//     ini.SetIniSectionKeyValue('Media','PlayMusic','0');
//
//    if playingsound then
//     ini.SetIniSectionKeyValue('Media','PlaySound','1')
//    else
//     ini.SetIniSectionKeyValue('Media','PlaySound','0');
//
//     ini.SaveToFile;
//
//    ini.Free;
//  end


end;

function IDXENG_TextureCreate(Width, Height, Format : Word; pData : Pointer) : GLuInt;
var
  Texture : GLuint;
begin
  glGenTextures(1, Texture);
  glBindTexture(GL_TEXTURE_2D, Texture);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  if Format = GL_RGBA then
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, pData)
  else
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, pData);

  result :=Texture;
end;


procedure IDXENG_TexturesListClear();
var
  i:Integer;
begin
   for i:=0 to High(IDXENGTexureList) do
   if IDXENGTexureList[i]>0 then glDeleteTextures( 1, @IDXENGTexureList[i] );
   SetLength(IDXENGTexureList,0);
end;

procedure IDXENG_TexturesListAddItem(Texture: GLuint);
var
  i,k:Integer;
begin

  k:=-1;
  for i:=0 to High(IDXENGTexureList) do
  if IDXENGTexureList[i]=0 then
  begin
    k:=i;
    Break;
  end;

  if k=-1 then
  begin
   i:=High(IDXENGTexureList)+1;
   SetLength(IDXENGTexureList,i+1);
  end
  else
  i:=k;

  IDXENGTexureList[i]:=Texture;
end;


procedure IDXENG_TextureSet(ID:GLuInt);
begin
 glBindTexture(Gl_TEXTURE_2D,ID);
end;


function IDXENG_TextureLoadPNG(Filename: String;var Texture: GLuint) : Boolean ;
const
  LoadFromResource = false;
var
  Data : Array of DWORD;
  W, Width : Integer;
  H, Height : Integer;
  AlphaPtr: PByte;
  PNG : TPngObject;
  //Texture : gluint;
begin
  Result:=False;

  if not FileExists(filename) then
  begin
    exit;
  end;

  PNG := TPngObject.Create;
  PNG.LoadFromFile(Filename);

  if PNG.Header.ColorType = COLOR_PALETTE then
    begin
      PNG.free;
      exit;
    end;

  Width := PNG.Width;
  Height := PNG.Height;
  SetLength(Data, Width * Height);

  if (PNG.Header.ColorType = COLOR_RGB) or
     (PNG.Header.ColorType = COLOR_GRAYSCALE) then
  begin
    For H := 0 to Height - 1 do
      For W := 0 to Width - 1 do
        Data[W + (H * Width)] := $FF000000 or PNG.Pixels[W, (Height-1)-H];
  end
  else
  if (PNG.Header.ColorType = COLOR_RGBALPHA) or
     (PNG.Header.ColorType = COLOR_GRAYSCALEALPHA) then
  begin
    for H := 0 to Height - 1 do
    begin
      AlphaPtr := PByte(PNG.AlphaScanline[(Height-1)-H]);
      for W := 0 to Width - 1 do
      begin
        Data[W + (H * Width)] := (($FF000000 and (AlphaPtr^ shl 24)) or PNG.Pixels[W, (Height-1)-H]);
        Inc(AlphaPtr);
      end;
    end;
  end;

  PNG.free;

  Texture := IDXENG_TextureCreate(Width, Height,GL_RGBA, Data);
  IDXENG_TexturesListAddItem(Texture);
  Result := True;
end;


function IDXENG_TextureLoadBMP(Filename : String; var Texture : GLuInt) : Boolean;
type
TRGB = array [0..1] of record
Blue, Green, Red : Byte;
end;
var
    p : ^TRGB;
    idx : integer;
    B,B2 : TBitmap;
    i,j : Integer;
    Width,
    Height, ow,oh : Integer;
    pBits : pByteArray;
    Detail : real;
begin
result:=false;
Detail:=1.0;

  B := TBitmap.Create;

 try
  if fileexists(Filename) then
   B.LoadFromFile(Filename)
  else
  begin
   IDXENG_Log_AddError('File not found "'+Filename+'"');
   Exit;
  end;

  except
   IDXENG_Log_AddError('Couldn''t load BMP from "'+Filename+'"');
   Exit;
 end;

 B.PixelFormat := pf24bit;

  Detail:=1;

  oW := B.Width;
  oH := B.Height;

  B2:=TBitmap.Create;

  B2.Width:=round(B.Width*Detail);
  B2.Height:=round(B.Height*Detail);
  B2.PixelFormat:=pf24bit;

  SetStretchBltMode(B2.Canvas.Handle, COLORONCOLOR);
  StretchBlt(B2.Canvas.Handle, 0,0,B2.Width,B2.Height,B.Canvas.Handle, 0,0,B.Width,B.Height,SRCCOPY );

  B.Height:=B2.Height;
  B.Width:=B2.Width;

  BitBlt(B.Canvas.Handle,0,0,B.Width,B.Height,B2.Canvas.Handle,0,0,SRCCOPY);

  B2.Free;

  Width := B.Width;
  Height := B.Height;

  GetMem(pBits,Width*Height*4);

 idx := 0;
 for j := 0 to Height - 1 do
 begin
   p := B.ScanLine[j];
   for i := 0 to Width - 1 do
   with p[ i ] do
   begin
     pBits[idx] := Red;
     pBits[idx+1] := Green;
     pBits[idx+2] := Blue;
     if RGB(Red, Green, Blue) = $ff00ff then
       pBits[idx + 3] := 0
     else
       pBits[idx + 3] := 255;
       idx := idx + 4;
   end;
 end;

    Texture := IDXENG_TextureCreate(Width,Height,GL_RGBA,pbits);
    IDXENG_TexturesListAddItem(Texture);

  freemem(pbits);

  B.Free;
  result  := TRUE;
end;

{------------------------------------------------------------------}
{  Load JPEG textures                                              }
{------------------------------------------------------------------}
function IDXENG_TextureLoadJPG(Filename: String; var Texture: GLuint): Boolean;
var
  Data : Array of LongWord;
  W, Width : Integer;
  H, Height : Integer;
  BMP : TBitmap;
  JPG : TJPEGImage;
  C : LongWord;
  Line : ^LongWord;
  ResStream : TResourceStream;      // used for loading from resource
begin
  result :=FALSE;
  JPG:=TJPEGImage.Create;


    try
      JPG.LoadFromFile(Filename);
    except
      IDXENG_Log_AddError('Couldn''t load JPG - "'+ Filename +'"');
      Exit;
    end;

  // Create Bitmap
  BMP:=TBitmap.Create;
  BMP.pixelformat:=pf32bit;
  BMP.width:=JPG.width;
  BMP.height:=JPG.height;
  BMP.canvas.draw(0,0,JPG);        // Copy the JPEG onto the Bitmap

  //  BMP.SaveToFile('D:\test.bmp');
  Width :=BMP.Width;
  Height :=BMP.Height;
  SetLength(Data, Width*Height);

   For H:=0 to Height-1 do
  Begin
    Line :=BMP.scanline[Height-H-1];   // flip JPEG
    For W:=0 to Width-1 do
    Begin
      c:=Line^ and $FFFFFF; // Need to do a color swap
      Data[W+(H*Width)] :=(((c and $FF) shl 16)+(c shr 16)+(c and $FF00)) or $FF000000;  // 4 channel.
      inc(Line);
    End;
  End;

  BMP.free;
  JPG.free;

  Texture :=IDXENG_TextureCreate(Width, Height, GL_RGBA, addr(Data[0]));
  IDXENG_TexturesListAddItem(Texture);
  result :=TRUE;
end;



{------------------------------------------------------------------}
{  Loads 24 and 32bpp (alpha channel) TGA textures                 }
{------------------------------------------------------------------}
function IDXENG_TextureLoadTGA(Filename: String; var Texture: GLuint): Boolean;
var
  TGAHeader : packed record   // Header type for TGA images
    FileType     : Byte;
    ColorMapType : Byte;
    ImageType    : Byte;
    ColorMapSpec : Array[0..4] of Byte;
    OrigX  : Array [0..1] of Byte;
    OrigY  : Array [0..1] of Byte;
    Width  : Array [0..1] of Byte;
    Height : Array [0..1] of Byte;
    BPP    : Byte;
    ImageInfo : Byte;
  end;
  TGAFile   : File;
  bytesRead : Integer;
  image     : Pointer;    {or PRGBTRIPLE}
  Width, Height : Integer;
  ColorDepth    : Integer;
  ImageSize     : Integer;
  I : Integer;
  Front: ^Byte;
  Back: ^Byte;
  Temp: Byte;

  ResStream : TResourceStream;      // used for loading from resource
begin
  result :=FALSE;
  GetMem(Image, 0);


    if FileExists(Filename) then
    begin
      AssignFile(TGAFile, Filename);
      Reset(TGAFile, 1);

      // Read in the bitmap file header
      BlockRead(TGAFile, TGAHeader, SizeOf(TGAHeader));
      result :=TRUE;
    end
    else
    begin
      IDXENG_Log_AddError('File not found  - ' + Filename);
      Exit;
    end;

  if Result = TRUE then
  begin
    Result :=FALSE;

    // Only support uncompressed images
    if (TGAHeader.ImageType <> 2) then  { TGA_RGB }
    begin
      Result := False;
      CloseFile(tgaFile);
      IDXENG_Log_AddError('Couldn''t load "'+ Filename +'". Compressed TGA files not supported.');
     IDXENG_Log_Add('False');
      Exit;
    end;

    // Don't support colormapped files
    if TGAHeader.ColorMapType <> 0 then
    begin
      Result := False;
      CloseFile(TGAFile);
      IDXENG_Log_AddError('Couldn''t load "'+ Filename +'". Colormapped TGA files not supported.');
      IDXENG_Log_Add('False');
      Exit;
    end;

    // Get the width, height, and color depth
    Width  := TGAHeader.Width[0]  + TGAHeader.Width[1]  * 256;
    Height := TGAHeader.Height[0] + TGAHeader.Height[1] * 256;
    ColorDepth := TGAHeader.BPP;
    ImageSize  := Width*Height*(ColorDepth div 8);

    if ColorDepth < 24 then
    begin
      Result := False;
      CloseFile(TGAFile);
      IDXENG_Log_AddError('Couldn''t load "'+ Filename +'". Only 24 bit TGA files supported.');
      IDXENG_Log_Add('False');
      Exit;
    end;

    GetMem(Image, ImageSize);

      BlockRead(TGAFile, image^, ImageSize, bytesRead);
      if bytesRead <> ImageSize then
      begin
        Result := False;
        CloseFile(TGAFile);
        IDXENG_Log_AddError('Couldn''t read file "'+ Filename +'".');
        Exit;
      end;

  end;

  if TGAHeader.BPP = 24 then
  begin
    for I :=0 to Width * Height - 1 do
    begin
      Front := Pointer(Integer(Image) + I*3);
      Back := Pointer(Integer(Image) + I*3 + 2);
      Temp := Front^;
      Front^ := Back^;
      Back^ := Temp;
    end;
    Texture :=IDXENG_TextureCreate(Width, Height, GL_RGB, Image);
  end
  else
  begin
    for I :=0 to Width * Height - 1 do
    begin
      Front := Pointer(Integer(Image) + I*4);
      Back := Pointer(Integer(Image) + I*4 + 2);
      Temp := Front^;
      Front^ := Back^;
      Back^ := Temp;
    end;
    Texture :=IDXENG_TextureCreate(Width, Height, GL_RGBA, Image);
  end;

  IDXENG_TexturesListAddItem(Texture);

  Result :=TRUE;
  FreeMem(Image);
end;


function IDXENG_TextureLoad(Filename: String; var Texture : GLuint) : Boolean;
begin
  Result:=False;
 IDXENG_Log_Add('Texture ['+Filename+']: ',TRUE);
  if copy(filename, length(filename)-3, 4) = '.bmp' then
    Result:=IDXENG_TextureLoadBMP(Filename, Texture);
  if copy(filename, length(filename)-3, 4) = '.jpg' then
    Result:=IDXENG_TextureLoadJPG(Filename, Texture);
  if copy(filename, length(filename)-3, 4) = '.png' then
    Result:=IDXENG_TextureLoadPNG(Filename,Texture);
  if copy(filename, length(filename)-3, 4) = '.tga' then
    Result:=IDXENG_TextureLoadTGA(Filename,Texture);

 if (Result) then IDXENG_Log_Add('OK') else IDXENG_Log_Add('ERROR');
end;







procedure IDXENG_Enable2d(A:boolean);
begin
 if A then
 begin
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix;
  glLoadIdentity;
  gluOrtho2D(0,gameWND.Width,gameWND.Height,0);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix;
  glLoadIdentity;
 end
 else
 begin
  glPopMatrix;
  glMatrixMode(GL_PROJECTION);
  glPopMatrix;
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_DEPTH_TEST);
 end;

 end;


function IDXENG_PointInRect(X,Y:Single;Rx,RY,Rw,Rh:Single):Boolean;
begin
  Result := ( X > RX ) and ( X < RX + RW ) and
            ( Y > RY ) and ( Y < RY + RH );
end;

function IDXENG_PointInCircle(X,Y:Single;Cx,Cy,Cr:Single):boolean;
begin
  Result := sqr( Cx - X ) + sqr( Cy - Y ) < sqr( Cr );
end;


procedure IDXENG_SetBlendMode( Mode : Byte; SeparateAlpha : Boolean = False );
  var
    srcBlend : LongWord;
    dstBlend : LongWord;
begin
  case Mode of
    FX_BLEND_NORMAL:
      begin
        srcBlend := GL_SRC_ALPHA;
        dstBlend := GL_ONE_MINUS_SRC_ALPHA;
      end;
    FX_BLEND_ADD:
      begin
        srcBlend := GL_SRC_ALPHA;
        dstBlend := GL_ONE;
      end;
    FX_BLEND_MULT:
      begin
        srcBlend := GL_ZERO;
        dstBlend := GL_SRC_COLOR;
      end;
    FX_BLEND_BLACK:
      begin
        srcBlend := GL_SRC_COLOR;
        dstBlend := GL_ONE_MINUS_SRC_COLOR;
      end;
    FX_BLEND_WHITE:
      begin
        srcBlend := GL_ONE_MINUS_SRC_COLOR;
        dstBlend := GL_SRC_COLOR;
      end;
    FX_BLEND_MASK:
      begin
        srcBlend := GL_ZERO;
        dstBlend := GL_SRC_COLOR;
      end;
  else
    begin
      srcBlend := GL_SRC_ALPHA;
      dstBlend := GL_ONE_MINUS_SRC_ALPHA;
    end;
  end;
  if SeparateAlpha  Then
    glBlendFuncSeparate( srcBlend, dstBlend, GL_ONE, GL_ONE_MINUS_SRC_ALPHA )
  else
    glBlendFunc( srcBlend, dstBlend );
end;


procedure IDXENG_SetPixelSize(value:Single);
begin
  glLineWidth(value);
end;

//------------------------------------------------------------------------------
// Инициализация растрового шрифта
//------------------------------------------------------------------------------
function IDXENG_SysFont_Create(const Name: String; Width, Height, Weight: Integer; DC: HDC) : Cardinal;
var
  font : HFONT;
begin
  Result := glGenLists(256);
  font := CreateFont(Height, Width, 0, 0, Weight, 0, 0, 0, RUSSIAN_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
            ANTIALIASED_QUALITY, FF_DONTCARE or DEFAULT_PITCH, PChar(Name));
  SelectObject(DC, font);
  wglUseFontBitmaps(DC, 0, 256, Result);
  DeleteObject(font);
end;

//------------------------------------------------------------------------------
// Удаление растрового шрифта
//------------------------------------------------------------------------------
procedure IDXENG_SySFont_Free(Font: Cardinal);
begin
  glDeleteLists(Font, 256);
end;

//------------------------------------------------------------------------------
// Вывод текста растровым шрифтом
//------------------------------------------------------------------------------
procedure IDXENG_Draw2d_Text(Font: Cardinal; const Text: String; X, Y: Single; Color: Integer=$FFFFFF; Alpha : Byte=255);
begin
 if (text = '') then Exit;

//  glDisable(GL_TEXTURE_2D);
glPushMatrix();
if Alpha<>255 then glEnable(GL_BLEND);
  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);
  glRasterPos2f(X, Y);
  glListBase(Font);
  glCallLists(Length(Text), GL_UNSIGNED_BYTE, PChar(Text));
if Alpha<>255 then glDisable(GL_BLEND);
glPopMatrix();

end;

procedure IDXENG_Draw2d_Pixel( X, Y : Single; Color : LongWord; Alpha : Byte = 255 );
begin
glPushMatrix;
 if Alpha<>255 then glEnable(GL_BLEND); glEnable( GL_BLEND );

  glBegin( GL_POINTS );

  glColor4ub( ( Color and $FF0000 ) shr 16, ( Color and $FF00 ) shr 8, Color and $FF, Alpha );
  glVertex2f( X + 0.5, Y + 0.5 );

  glEnd();

if Alpha<>255 then glDisable( GL_BLEND );
glPopMatrix;
end;


procedure IDXENG_Draw2d_Line( ax, ay, bx, by:Single; Width:Single=1; Color: Integer=$FFFFFF;Alpha : Byte=255 );
begin
glPushMatrix;
//    glDisable(GL_TEXTURE_2D);
if Alpha<>255 then glEnable(GL_BLEND);
  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);

//    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glLineWidth(width);
    glBegin(GL_LINES);
    glVertex2f( ax, ay);
    glVertex2f( bx, by);
    glEnd();

if Alpha<>255 then glDisable(GL_BLEND);
//    glEnable(GL_TEXTURE_2D);
glPopMatrix;
end;

procedure IDXENG_Draw2d_Rectangle(X, Y:Single; Width, Height:Single; Color:integer=$FFFFFF; Alpha : byte=255;Angle:integer=0; Fill : boolean = false;Center:boolean=false);
var
Rx,Ry:real;
begin

glPushMatrix;

glTranslatef(X,Y,0);

if Angle<>0 then glrotatef(Angle,0,0,1);
if Alpha<>255 then glEnable(GL_BLEND);

 if fill then
 begin

  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);
   glBegin(GL_QUADS);
  if center then
  begin
      Rx:=Width/2;
      Ry:=Height/2;
    glVertex2f(-rx,-ry);
    glVertex2f(rx,-ry);
    glVertex2f(rx,ry);
    glVertex2f(-rx,ry);
 end
 else
 begin
    glVertex2f(0,0);
    glVertex2f(0+Width,0);
    glVertex2f(0+Width,0+Height);
    glVertex2f(0,0+Height);
 end;

   glEnd;

 end else
 begin

  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);
   glBegin(GL_LINE_STRIP);
  if center then
  begin
      Rx:=Width/2;
      Ry:=Height/2;

    glVertex2f(-rx,-ry);
    glVertex2f(rx,-ry);
    glVertex2f(rx,ry);
    glVertex2f(-rx,ry);
    glVertex2f(-rx,-ry);
   end
   else
   begin

    glVertex2f(0,0);
    glVertex2f(0+Width,0);
    glVertex2f(0+Width,0+Height);
    glVertex2f(0,0+Height);
    glVertex2f(0,0-1);

   end;

   glEnd;

 end;
 glPopMatrix;

if Alpha<>255 then glDisable(GL_BLEND);
end;

procedure IDXENG_Draw2d_Circle( cx,cy,r:single;
                                Color:Integer=$FFFFFF;
                                Alpha:Byte=255;
                                num_segments:integer=26;
                                fill:boolean=False );
var
i:integer;
theta,x,y:single;
begin

glPushMatrix;
if Alpha<>255 then glEnable(GL_BLEND);

if fill=false then glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

glColor4ub(
            (   Color and $FF              ),
            ( ( Color and $FF00   ) shr 8  ),
            ( ( Color and $FF0000 ) shr 16 ),
                Alpha
         );

glBegin( GL_POLYGON );

	for i:= 0 to num_segments do
	begin
		theta:= (2.0 * 3.1415926 * i) / (num_segments);

		x := r * cos(theta);
		y := r * sin(theta);

		glVertex2f(x + cx, y + cy);

	end;
	glEnd();

if fill=false then  glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

if Alpha<>255 then glDisable(GL_BLEND);
glPopMatrix;
end;


procedure IDXENG_Draw2d_TextureA(x,y:Single;
                                  Width,Height:Single;
                                  textId:cardinal;
                                  apos:TAtlasItem;
                                  Angle:Integer=0;
                                  Center:boolean=false;
                                  Color:Integer=$FFFFFF;
                                  Alpha:Byte=255;
                                  flipx:Boolean=False;
                                  flipy:Boolean=False
                                  );
var
  Rx,Ry:real;
  flp:array[0..3] of record
                      x,y:Single;
                     end;
begin
  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);

//  if Alpha<>255 then
  glEnable(GL_BLEND);

  glEnable( GL_TEXTURE_2D );

  glPushMatrix();
  glTranslatef(X,Y,0);
  if Angle<>0 then glrotatef(Angle,0,0,1);
  glBindTexture(GL_TEXTURE_2D, textId);
  glBegin(GL_QUADS);

     flp[0].x:=apos.x1; flp[0].y:=apos.y1;
     flp[1].x:=apos.x2; flp[1].y:=apos.y1;
     flp[2].x:=apos.x2; flp[2].y:=apos.y2;
     flp[3].x:=apos.x1; flp[3].y:=apos.y2;

//     flp[0].x:=apos.sx; flp[0].y:=apos.sy+apos.sh;
//     flp[1].x:=apos.sx+apos.sw; flp[1].y:=apos.sy+apos.sh;
//     flp[2].x:=apos.sx+apos.sw; flp[2].y:=apos.sy;
//     flp[3].x:=apos.sx; flp[3].y:=apos.sy;

//     if flipy then
//     begin
//      flp[0].x:=apos.sx; flp[0].y:=apos.sy;
//      flp[1].x:=apos.sx+apos.sw; flp[1].y:=apos.sy;
//      flp[2].x:=apos.sx+apos.sw; flp[2].y:=apos.sy+apos.sh;
//      flp[3].x:=apos.sx; flp[3].y:=apos.sy+apos.sh;
//     end;

                if Center then
                 begin
                   Rx:=Width/2;
                   Ry:=Height/2;
                   glTexCoord2f(flp[0].x,flp[0].y); glVertex2f(-rx,-ry);
                   glTexCoord2f(flp[1].x,flp[1].y); glVertex2f(rx,-ry);
                   glTexCoord2f(flp[2].x,flp[2].y); glVertex2f(rx,ry);
                   glTexCoord2f(flp[3].x,flp[3].y); glVertex2f(-rx,ry);
                 end
                 else
                 begin
                   glTexCoord2f(flp[0].x,flp[0].y); glVertex2f(0,0);
                   glTexCoord2f(flp[1].x,flp[1].y); glVertex2f(0+Width,0);
                   glTexCoord2f(flp[2].x,flp[2].y); glVertex2f(0+Width,0+Height);
                   glTexCoord2f(flp[3].x,flp[3].y); glVertex2f(0,0+Height);
                 end;

        glEnd();
  glPopMatrix();
  glDisable( GL_TEXTURE_2D );

if Alpha<>255 then glDisable(GL_BLEND);
end;

procedure IDXENG_Draw2d_Texture(x,y:Single;
                                Width,Height:Single;
                                textId:cardinal;
                                Angle:Integer=0;
                                Center:boolean=false;
                                Color:Integer=$FFFFFF;
                                Alpha:Byte=255;
                                flipx:Boolean=False;
                                flipy:Boolean=False);
var
  Rx,Ry:real;
  flp:array[0..3] of TPoint;
begin
  glcolor4ub(GetRValue(Color),GetGValue(Color),GetBValue(Color),Alpha);

  if Alpha<>255 then glEnable(GL_BLEND);

  glEnable( GL_TEXTURE_2D );

  glPushMatrix();
  glTranslatef(X,Y,0);
  if Angle<>0 then glrotatef(Angle,0,0,1);
                glBindTexture(GL_TEXTURE_2D, textId); // use floor
                glBegin(GL_QUADS);

     flp[0].x:=0; flp[0].y:=1;
     flp[1].x:=1; flp[1].y:=1;
     flp[2].x:=1; flp[2].y:=0;
     flp[3].x:=0; flp[3].y:=0;

     if flipy then
     begin
      flp[0].x:=0; flp[0].y:=0;
      flp[1].x:=1; flp[1].y:=0;
      flp[2].x:=1; flp[2].y:=1;
      flp[3].x:=0; flp[3].y:=1;
     end;

                if Center then
                  begin
                      Rx:=Width/2;
                      Ry:=Height/2;
                   glTexCoord2f(flp[0].x,flp[0].y); glVertex2f(-rx,-ry);
                   glTexCoord2f(flp[1].x,flp[1].y); glVertex2f(rx,-ry);
                   glTexCoord2f(flp[2].x,flp[2].y); glVertex2f(rx,ry);
                   glTexCoord2f(flp[3].x,flp[3].y); glVertex2f(-rx,ry);
                 end
                 else
                 begin
                   glTexCoord2f(flp[0].x,flp[0].y); glVertex2f(0,0);
                   glTexCoord2f(flp[1].x,flp[1].y); glVertex2f(0+Width,0);
                   glTexCoord2f(flp[2].x,flp[2].y); glVertex2f(0+Width,0+Height);
                   glTexCoord2f(flp[3].x,flp[3].y); glVertex2f(0,0+Height);
                 end;

  glEnd();
  glPopMatrix();
  glDisable( GL_TEXTURE_2D );

if Alpha<>255 then glDisable(GL_BLEND);
end;




Procedure IDXENG_Log_Open;
begin
  AssignFile(fIDXLogFile,'Log.txt');
  Rewrite(fIDXLogFile);
  WriteLn(fIDXLogFile,'IDEYAX OGLEngine '+cIDXEngineVersion);
  WriteLn(fIDXLogFile, DateToStr(Date)+' '+TimeToStr(Time));
  CloseFile(fIDXLogFile);
end;

Procedure IDXENG_Log_Close;
begin
  IDXENG_Log_Add('Завершение...',True);
end;

Procedure IDXENG_Log_Add(const value:AnsiString;AddTime:Boolean=False);
var
  str : AnsiString;
begin
  If AddTime then str:=TimeToStr(Time)+'| '+value
  else
  str:=value;
  AssignFile(fIDXLogFile, 'Log.txt');
  Append(fIDXLogFile);
  WriteLn(fIDXLogFile,str);
  Flush(fIDXLogFile);
  CloseFile(fIDXLogFile);
end;

Procedure IDXENG_Log_AddError(const value:AnsiString);
begin
 MessageBox( 0, PChar( value ), 'ERROR!', MB_OK or MB_ICONERROR );
 IDXENG_Log_Add( 'ERROR: '+value,True);
end;

Procedure IDXENG_Log_AddWarning(const value:AnsiString);
begin
 MessageBox( 0, PChar( value ), 'WARNING!', MB_OK or MB_ICONWARNING );
 IDXENG_Log_Add( 'WARNING: '+value,True);
end;

procedure IDXENG_SetCaption(text:PAnsiChar);
begin
  gameWND.Title := text;
 if gameWND.Handle <> 0 Then
    SetWindowText( gameWND.Handle, gameWND.Title);
end;

procedure IDXENG_SetClearColor(color:Integer );
begin
 glClearColor(GetRValue(Color),GetGValue(Color),GetBValue(Color), 255);
end;


procedure IDXENG_EnableVsync(value : Boolean);
var
   i : Integer;
begin
   if WGL_EXT_swap_control then
   begin
      i := wglGetSwapIntervalEXT;
      case value of
         True    : if i<>1 then wglSwapIntervalEXT(1);
         False   : if i<>0 then wglSwapIntervalEXT(0);
      else
         Assert(False);
      end;
   end;

end;

function IDXENG_GetTimer: integer;
var
  T : LARGE_INTEGER;
  F : LARGE_INTEGER;
begin
  QueryPerformanceFrequency(Int64(F));
  QueryPerformanceCounter(Int64(T));
  result := round(1000 * T.QuadPart / F.QuadPart);
end;


procedure IDXENG_Register(procId:Integer; proc:Pointer);
begin

 case procId of
   cIDXProcInit: gameWND.OnInit:= proc;
   cIDXProcDraw: gameWND.OnDraw:= proc;
   cIDXProcUpdate:gameWND.OnUpdate:= proc;
   cIDXProcClose:gameWND.OnClose:= proc;
 else ;
 end;

end;

procedure IDXENG_Procces();
var
 Msg : TMsg;
 wTime,
 wTime_Delta,
 wTime_Last:Integer;
begin

 gameWND.isFinished := False;

 while not gameWND.isFinished do
 begin

      if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then
        begin
          if msg.message=WM_QUIT then
            gameWND.isFinished:=true
          else
            begin
	          TranslateMessage(msg);
	          DispatchMessage(msg);
            end;
        end
      else
        begin

          if not gameWND.isActive then
              WaitMessage
          else
          begin

            wTime       := IDXENG_GetTimer;
            wTime_Delta := wTime - gameWND.time_old;



            while wTime_Delta >= gameWND.time_delta do
            begin
                if Assigned( gameWND.OnUpdate ) Then gameWND.OnUpdate();
                dec(wTime_Delta, gameWND.time_delta);
            end;
              gameWND.time_old := wTime - wTime_Delta;

             gameWND.deltaTime:=(GetTickCount-wTime_Last)/1000;
             wTime_Last:=GetTickCount;

             IDXENG_Draw();
          end;
        end;
 end;

 if Assigned( gameWND.OnClose ) Then gameWND.OnClose();

 IDXENG_Close;

end;

procedure IDXENG_Draw();
begin
 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 glPushMatrix;
  if Assigned(gameWND.OnDraw) then gameWND.OnDraw;
 glPopMatrix;
 glFlush;
 SwapBuffers(gameWND.DC);
end;



procedure IDXENG_SetPerspective(Width, Height: Integer;fovy:Single=45.0;zNear:Single=0.1; zFar:Single=300 );
begin
 if Height = 0 then Height := 1;
 glViewport(0, 0, Width, Height);
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity;
 gluPerspective(fovy, Width / Height, zNear, zFar);
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity;
end;

procedure IDXENG_Close;
begin
 if gameWND.isFullScreen then
    begin
      ChangeDisplaySettings(devmode(nil^),0);
      showcursor(true);
    end;

  if gameWND.HRC<> 0 then
    begin
      if (not wglMakeCurrent(gameWND.DC,0)) then IDXENG_Log_AddError('Ошибка при удалении контекста устройства');
      if (not wglDeleteContext(gameWND.HRC)) then
      begin
        IDXENG_Log_AddError('Ошибка при удалении контекста воспроизведения OpenGL');
        gameWND.HRC:=0;
      end;
    end;

  if (gameWND.Dc=1) and (releaseDC(gameWND.Handle,gameWND.Dc)<>0) then
    begin
      IDXENG_Log_AddError('Ошибка при удалении контекста устройства');
      gameWND.Dc:=0;
    end;

  if (gameWND.Handle<>0) and (not destroywindow(gameWND.Handle))then
    begin
      IDXENG_Log_AddError('Ошибка при удалении хендела окна');
      gameWND.Handle:=0;
    end;
  if (not Windows.UnregisterClass(AppName,hInstance)) then
    begin
      IDXENG_Log_AddError('Ошибка при завершении класса окна');
    end;

    IDXENG_Log_Close;
end;


function IDXENG_WndProc(hWnd: HWND; mess: UINT;wParam: WPARAM;lParam: LPARAM): LRESULT; stdcall;
begin

 if mess=WM_SYSCOMMAND then
    begin
      case wParam of
        SC_SCREENSAVE,SC_MONITORPOWER:
          begin
            result:=0;
            exit;
          end;
      end;
    end;
  case mess of
    WM_ACTIVATE:
      begin
        if (Hiword(wParam)=0) then
          gameWND.isActive:=true
        else
          gameWND.isActive:=false;
        Result:=0;
      end;
    WM_CLOSE:
      Begin
        PostQuitMessage(0);
        result:=0
      end;
     WM_MOUSEMOVE:
     begin
      gameWND.Mouse.X    := WORD( lParam );
      gameWND.Mouse.Y    := lParam shr 16;
     end;
    WM_LBUTTONUP   : gameWND.Mouse.bLeft := False;
    WM_RBUTTONUP   : gameWND.Mouse.bRight:= False;
    WM_MBUTTONUP   : gameWND.Mouse.bMiddle:= False;
    WM_LBUTTONDOWN : gameWND.Mouse.bLeft := True;
    WM_RBUTTONDOWN : gameWND.Mouse.bRight:= True;
    WM_MBUTTONDOWN : gameWND.Mouse.bMiddle:= True;
    WM_KEYDOWN:
      begin
        gameWND.keys[wParam] := TRUE;
        result:=0;
      end;
    WM_KEYUP:
      begin
    	gameWND.keys[wParam] := FALSE;
        result:=0;
      end;
    WM_SIZe:
      begin
        if gameWND.isRun then
    	IDXENG_SetPerspective(LOWORD(lParam),HIWORD(lParam));
        result:=0;
      end
    else
       begin
      	Result := DefWindowProc(hWnd, mess, wParam, lParam);
      end;
  end;

end;

function IDXENG_InitGL:Boolean;
begin
  IDXENG_EnableVsync(gameWND.VSync);
  glShadeModel(GL_SMOOTH);
  glClearColor(0.0, 0.0, 0.0, 0.5);
  glClearDepth(1.0);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
  Result:=true;
end;

function IDXENG_Init(Width,Height:Integer;
                  Title: PAnsiChar;
                  Fullscreen:Boolean=False;
                  ScreenFPS:Integer=30;
                  VSync:Boolean=False):Boolean;
var
  Pixelformat: GLuint;
  wc:TWndclass;
  dwExStyle:dword;
  dwStyle:dword;
  pfd: pixelformatdescriptor;
  dmScreenSettings: Devmode;
  WindowRect,wndRect: TRect;
  cx,cy,tx,ty:Integer;
begin

 Result:=False;
   
 if FindWindow(AppName,nil) <> 0 then
 begin
  MessageBox(0,'Повторный запуск приложения','Ошибка',MB_OK or MB_ICONERROR);
  Exit;
 end;  


  IDXENG_Log_Open;

  InitOpenGL;
  ReadExtensions;
  ReadImplementationProperties;

  gameWND.Width:=Width;
  gameWND.Height:=Height;
  gameWND.DWidth:=Width div 2;
  gameWND.DHeight:=Height div 2;;
  gameWND.Fps:=ScreenFPS;
  gameWND.isFullScreen:=FullScreen;
  gameWND.VSync:=Vsync;
  gameWND.Title:=Title;
  gameWND.isRun:=False;
  gameWND.ColorBit:=32;

  WindowRect.Left := 0;
  WindowRect.Top := 0;
  WindowRect.Right := width;
  WindowRect.Bottom := height;


  gameWND.Instance:=GetModuleHandle(nil);

  with wc do
  begin
      style             :=CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
      lpfnWndProc       :=@IDXENG_WndProc;
      cbClsExtra        :=0;
      cbWndExtra        :=0;
      hInstance         :=gameWND.Instance;
      hIcon             :=LoadIcon(gameWND.Instance,'ICO');
      hCursor           :=LoadCursor(0,IDC_ARROW);
      hbrBackground     :=GetStockObject(BLACK_BRUSH);
      lpszMenuName      :=nil;
      lpszClassName     :=AppName;
  end;

  if Windows.RegisterClass(wc)=0 then
  begin
      IDXENG_Log_AddError('Ошибка регистрации окна приложения');
      exit;
  end;

  if gameWND.isFullScreen then
    begin
      ZeroMemory( @dmScreenSettings, sizeof(dmScreenSettings) );
      with dmScreensettings do
        begin
          dmSize := sizeof(dmScreenSettings);
          dmPelsWidth  := gameWND.width;
	  dmPelsHeight := gameWND.height;
          dmBitsPerPel := gameWND.ColorBit;
          dmFields     := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
        end;

      if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN))<>DISP_CHANGE_SUCCESSFUL THEN
        Begin
          if MessageBox(0,'Это разрешение экрана не поддерживается. Использовать оконный режим?'
                                             ,AppName,MB_YESNO or MB_ICONEXCLAMATION)= IDYES then
                gameWND.isFullScreen:=false
          else
          begin
            MessageBox(0,'Выходим из игры','Error',MB_OK or MB_ICONERROR);
            exit;
          end;
        end;
    end;

 tx:=0;
 ty:=0;

  if gameWND.isFullScreen 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 or WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU or WS_VISIBLE; // Styl okna

   SystemParametersInfo(SPI_GETWORKAREA, 0, @wndRect, 0);

   tx:= (wndRect.Right div 2)-((width ) div 2);
   ty:= (wndRect.Bottom div 2)-((height ) div 2);
  end;

  AdjustWindowRectEx(WindowRect,dwStyle,false,dwExStyle);

  gameWND.Handle:=CreateWindowEx(dwExStyle,
                                 PChar(AppName),
                                 Title,
                                 dwStyle,
                                 tx,ty,
                                 width,
                                 height,
                                 0,
                                 0,
                                 gameWND.Instance,
                                 nil);
    if gameWND.Handle=0 then
    begin
      IDXENG_Log_AddError('Ошибка при создании окна');
      IDXENG_Close();
      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:= gameWND.ColorBit;
      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;

  gameWND.Dc := GetDC(gameWND.Handle);

  if gameWND.Dc=0 then
    begin
      IDXENG_Log_AddError('Не удалось получить контекст устройства');
      IDXENG_Close();
      exit;
    end;

  PixelFormat := ChoosePixelFormat(gameWND.Dc, @pfd);

  if (PixelFormat=0) then
    begin
      IDXENG_Log_AddError('Не удалось найти подходящий формат пикселя');
      IDXENG_Close();
      exit;
    end;

  if (not SetPixelFormat(gameWND.Dc,PixelFormat,@pfd)) then
    begin
      IDXENG_Log_AddError('Не удалось установить формат пикселя');
      IDXENG_Close();
      exit;
    end;

   gameWND.HRc := wglCreateContext(gameWND.Dc);

   if (gameWND.HRC=0) then
   begin
      IDXENG_Log_AddError('Не удалось создать контекст воспроизведения OpenGL');
      IDXENG_Close();
      exit;
   end;

    if (not wglMakeCurrent(gameWND.Dc, gameWND.HRC)) then
    begin
      IDXENG_Log_AddError('Не удалось установить контекст воспроизведения OpenGL');
      IDXENG_Close();
      exit;
    end;

   if gameWND.isFullScreen =false then
   begin
     GetClientRect(gameWND.Handle, windowRect);

     cx := (gameWND.Width - (windowRect.right - windowRect.left));
     cy := (gameWND.Height - (windowRect.bottom - windowRect.top));

     SystemParametersInfo(SPI_GETWORKAREA, 0, @windowRect, 0);

       tx:= (windowRect.Right div 2)-((gameWND.Width + cx) div 2);
       ty:= (windowRect.Bottom div 2)-((gameWND.Height + cy) div 2);

      SetWindowPos(gameWND.Handle, 0, tx, ty,gameWND.Width + cx,gameWND.Height + cy,0);

   end;



  ShowWindow(gameWND.Handle,SW_SHOW);

  SetForegroundWindow(gameWND.Handle);

  SetFOcus(gameWND.Handle);

  IDXENG_SetPerspective(width,height);

  if (not IDXENG_InitGL()) then
    begin
      IDXENG_Log_AddError('Ошибка инициализации OpenGL');
      IDXENG_Close();
      exit;
    end;

  IDXENG_Log_Add('#################################');
  IDXENG_Log_Add('# Разрешение экрана : '+inttostr(gameWND.Width) + 'x' + inttostr(gameWND.Height)+'x'+inttostr(gameWND.ColorBit));
  if gameWND.isFullScreen then
  IDXENG_Log_Add('# Запуск приложения : На полный экран')
  else
  IDXENG_Log_Add('# Запуск приложения : В окне');
  IDXENG_Log_Add('# Производитель     : '+glGetString(GL_VENDOR));
  IDXENG_Log_Add('# Рендер            : '+glGetString(GL_RENDERER));
  IDXENG_Log_Add('# OpenGL вер.       : '+glGetString(GL_VERSION));
  IDXENG_Log_Add('# Расширения OpenGL :');
  IDXENG_Log_Add(glGetString(GL_EXTENSIONS));
  IDXENG_Log_Add('#################################');

  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  SwapBuffers(gameWND.DC);

  gameWND.time_delta := gameWND.Fps;
  gameWND.time_old := IDXENG_GetTimer - gameWND.time_delta;
  gameWND.time_new := IDXENG_GetTimer;

  gameWND.isRun:=True;

  if Assigned( gameWND.OnInit ) Then gameWND.OnInit();

  Result:=true;

end;


function IDXSound_Init:Boolean;
begin
  result:=false;

  gameSound.PlayMusic:=false;
  gameSound.PlaySound:=false;
  gameSound.Enable:=False;
  gameSound.MusicVolume:=100;
  gameSound.SoundVolume:=100;

  SetLength(gameSound.soundList,0);
  SetLength(gameSound.musicList,0);

  try
    FMOD_Load(nil);
    if not FSOUND_Init(44100, 32, 0) then
    begin
      IDXENG_Log_AddError('Ошибка запуска звукового движка!');
      IDXENG_Log_AddError(FMOD_ErrorString(FSOUND_GetError()));
      FSOUND_Close();
      exit;
    end;
  except
   exit;
  end;

  IDXENG_Log_Add('[ Звуковой движок запущен ]',True);
  FSOUND_SetSFXMasterVolume(100);
  gameSound.Enable:=True;
  result:=true;
end;

function IDXSound_VolUp(c:integer):integer;
begin
  if gameSound.Enable=False then Exit;
  FSOUND_SetVolumeAbsolute(c,FSOUND_GetVolume(c)+1);
  result:=FSOUND_GetVolume(c);
end;

function IDXSound_VolDown(c:integer):integer;
begin
  if gameSound.Enable=False then Exit;
  FSOUND_SetVolumeAbsolute(c,FSOUND_GetVolume(c)-1);
  result:=FSOUND_GetVolume(c);
end;

function IDXSound_GoForward(s:PFSoundStream):integer;
begin
  if gameSound.Enable=False then Exit;
  result:=FSOUND_Stream_GetPosition(s)+1;
  FSOUND_Stream_SetPosition(s,result);
end;

function IDXSound_GoBackward(s:PFSoundStream):integer;
begin
  if gameSound.Enable=False then Exit;
  result:=FSOUND_Stream_GetPosition(s)-1;
  FSOUND_Stream_SetPosition(s,result);
end;

procedure IDXSound_Play(Id:integer;Volume:Integer=100;pan:Integer=127);
begin
  if gameSound.Enable=False then Exit;
  if Id<0 then Exit;
  if Id>High(gameSound.SoundList) then Exit;
  if gameSound.PlaySound=False then Exit;

  FSOUND_PlaySound(FSOUND_free,gameSound.SoundList[id]);
  FSOUND_SetVolume(FSOUND_free, Volume);
  FSOUND_SetPan(FSOUND_free, pan);
end;


procedure IDXSound_Stop(Id:integer);
begin
  if gameSound.Enable=False then Exit;
  if Id<0 then Exit;
  if Id>High(gameSound.SoundList) then Exit;
  if gameSound.PlaySound=False then Exit;


    FMUSIC_StopSong(gameSound.SoundList[id]);
    FMUSIC_FreeSong(gameSound.SoundList[id]);
    gameSound.SoundList[id]:=nil;
end;


function IDXSound_Load(Filename: String; var sound : Integer; len:Integer=0 ) : Boolean;
var
  i:Integer;
begin
  Result:=False;
  sound:=-1;
  if gameSound.Enable=False then Exit;

  if fileexists(Filename)  then
  begin
   Result:=True;
   i:=High(gameSound.SoundList)+1;
   SetLength(gameSound.SoundList,i+1);

   gameSound.SoundList[i]:= FSound_Sample_Load(FSOUND_Free,PChar(Filename),FSOUND_HW2D,0,len);

   sound:=i;

   IDXENG_Log_Add('Загрузка "'+Filename+'" : OK');
  end
  else
   IDXENG_Log_AddError('Файл "'+Filename+'" не найден');

end;

function IDXSound_LoadMusic(Filename: String; var music : Integer) : Boolean;
var
 i:integer;
begin
 Result:=False;
 music:=-1;
 if gameSound.Enable=False then Exit;

 try
  if fileexists(Filename)  then
  begin
    i:=High(gameSound.MusicList)+1;
    SetLength(gameSound.MusicList,i+1);

    gameSound.MusicList[i].isStream:=False;
    gameSound.MusicList[i].Item:=nil;
    gameSound.MusicList[i].sItem:=nil;

    gameSound.MusicList[i].Item:= FMUSIC_LoadSong(pchar(Filename));

    if gameSound.MusicList[i].Item=nil then
    begin
       IDXENG_Log_AddWarning('Загружаем "'+Filename+'" в потоковом режиме');

       gameSound.MusicList[i].sItem:= FSOUND_Stream_Open(PChar(Filename), FSOUND_LOOP_NORMAL or FSOUND_NORMAL, 0, 0);

      if gameSound.MusicList[i].sItem = nil then
      begin
        IDXENG_Log_AddError(FMOD_ErrorString(FSOUND_GetError()));
        exit;
      end;

      gameSound.MusicList[i].isStream:=True;
    end;

    music:=i;
    IDXENG_Log_Add('Загрузка "'+Filename+'" : OK');
  end
  else
  begin
   IDXENG_Log_AddError('Файл "'+Filename+'" не найден');
  end;
 except
  IDXENG_Log_AddError('Ошибка в процедуре загрузки');
 end;

end;


procedure IDXSound_Close;
var
i:integer;
begin
  if gameSound.Enable=False then Exit;

  for i:=0 to High(gameSound.MusicList) do
  begin
    if gameSound.MusicList[i].isStream then
    begin
      FSOUND_Stream_Stop(gameSound.MusicList[i].sItem);
      FSOUND_Stream_Close(gameSound.MusicList[i].sItem);
    end
    else
    begin
      FMUSIC_StopSong(gameSound.MusicList[i].Item);
      FMUSIC_FreeSong(gameSound.MusicList[i].Item);
    end;

    gameSound.MusicList[i].Item:=nil;
    gameSound.MusicList[i].sItem:=nil;
  end;

  SetLength(gameSound.MusicList,0);

   for i:=0 to high(gameSound.SoundList) do
   begin
     //FMUSIC_StopSong(gameSound.SoundList[i]);
     FSOUND_Sample_Free(gameSound.SoundList[i]);
     gameSound.SoundList[i]:= nil;
   end;

  SetLength(gameSound.SoundList,0);


  FSOUND_Close;
  
 IDXENG_Log_Add('[ Звуковой движок выгружен ]',True);
end;


procedure IDXSound_PauseMusic(id:Integer; value:Boolean);
begin
 if gameSound.Enable=False then Exit;
  if gameSound.Enable=False then Exit;
  if Id<0 then Exit;
  if Id>High(gameSound.MusicList) then Exit;


 FMUSIC_SetPaused(gameSound.MusicList[id].Item, value);
end;

procedure IDXSound_PlayMusic(id:Integer);
begin
  if gameSound.Enable=False then Exit;
  if Id<0 then Exit;
  if Id>High(gameSound.MusicList) then Exit;
  if gameSound.PlayMusic=False then Exit;

  if (gameSound.MusicList[id].Item<>nil) then
  begin
     FMUSIC_PlaySong(gameSound.MusicList[id].Item);
     FMUSIC_SetMasterVolume(gameSound.MusicList[id].Item,gameSound.MusicVolume);
  end;

  if (gameSound.MusicList[id].sItem<>nil) then
  begin
     FSOUND_Stream_Play(FSOUND_FREE,gameSound.MusicList[id].sItem);
//     FMUSIC_SetMasterVolume(gameSound.MusicList[id],gameSound.MusicVolume);
  end;

end;

procedure IDXSound_StopMusic(id:Integer);
begin
  if gameSound.Enable=False then Exit;
  if Id<0 then Exit;
  if Id>High(gameSound.MusicList) then Exit;
try

 if gameSound.MusicList[id].sItem<>nil then
 begin
   FSOUND_Stream_Stop(gameSound.MusicList[id].sItem);
 end;

 if gameSound.MusicList[id].Item<>nil then
 begin
    FMUSIC_StopSong(gameSound.MusicList[id].Item);
    FMUSIC_FreeSong(gameSound.MusicList[id].Item);
 end;
    gameSound.MusicList[id].sItem:=nil;
    gameSound.MusicList[id].Item:=nil;
except
 IDXENG_Log_AddError('Ошибка в StopMusic');
end;


end;



end.
