Page 1 of 1

OGRE and terrain modeling

Posted: Thu May 20, 2004 2:49 am
by fweil
Reworking Comtois former code which generates an animated waved texture, we, Cederavic and me, did something that may interest some of you :

Load the France.JPG bitmap file representing a geographical area with colored altitudes at the following address :

http://perso.wanadoo.fr/francois.weil/France.JPG

Copy this file to a data\ folder you will have under the point were you will save the following code.

The jpeg comes from an USGS to Terragen file re-exported and saved as a bitmap using Wilbur (just for those who understand what I am talking about). Whatever is the way I got the bitmap, you just have to look at it to understand it is a regular map. Just the pixels have a given color linked to altitude.

Such a color set is frequently used so that if you start to analyze pixels, you will understand quickly how to approximate the altitude of a given point by looking RGB value of the point.

The only trick I do there except other coding for OGRE arrays, is to consider the blue as the lowest level and the red as the highest. By inverting from RGB to BGR and converting color values to a 0-1 float makes easy to produce an altitude array that can be thrown to the OGRE array containing W, Y, Z.

That's it.

Now a bit about Comtois's code. He did a good work. I just changed some ways to present the code, but the basic algorithm used to load arrays works well. I even check for possible optimization that I have not found right now.

The present code shows also some good milestones I did not fixed before using OGRE engine :

- you can use any graphic file to be loaded using an available image decoder (here UseEC_OLEImageDecoder())
- you can resize the bitmap in memory to adapt to terrain / texture requirements in your app.
- by getting altitude infos and putting in directly to OGRE arrays, it becomes possible to think to something like a "path finder" to travel around the terrain (not demonstrated here, but ... it is possible).

It was a mess for me before, to undesrtand how to manage OGRE engine arrays in a "usual way". Now it is more clear for me.

Hope you will enjoy this code.

Code: Select all

;
; D'après le source de Comtois
;
; France.pb
;
; Cederavic et FWeil 20040520
;
Global PointIDMemory.l, TriangleIDMemory.l, TextureIDMemory.l

Dim Altitudes.f(1000, 1000)

Enumeration
  #PointID
  #TriangleID
  #TextureID
  #NbX = 80
  #NbZ = 80
EndEnumeration

Global xrot.f, yrot.f, zrot.f
Global CamLocateX.l, CamLocateY.l, CamLocateZ.l, CamLookAtX.l, CamLookAtY.l, CamLookAtZ.l
Global Mode.b

#ScreenWidth = 1024
#ScreenHeight = 768
#ScreenDepth = 32

Procedure Label(x.l, y.l, text.s)
  Locate(x, y)
  DrawText(text)
EndProcedure

Procedure Matrice(FX.l, FZ.l)
  address = PointIDMemory
  For b = 0 To FZ
    For a = 0 To FX
      PokeF(address, a - FX / 2)
      PokeF(address + 4, 0)
      PokeF(address + 8, b - FZ / 2)
      address + 12
    Next
  Next
  address = TriangleIDMemory
  Nb = FX + 1
  For b = 0 To FZ - 1
    For a = 0 To FX - 1
      P1 = a + (b * Nb)
      P2 = P1 + 1
      P3 = a + (b + 1) * Nb
      P4 = P3 + 1
      PokeW(address, P3)
      PokeW(address + 2, P2)
      PokeW(address + 4, P1)
      PokeW(address + 6, P2)
      PokeW(address + 8, P3)
      PokeW(address + 10, P4)
      PokeW(address + 12, P1)
      PokeW(address + 14, P2)
      PokeW(address + 16, P3)
      PokeW(address + 18, P4)
      PokeW(address + 20, P3)
      PokeW(address + 22, P2)
      address + 24
    Next
  Next
  address = TextureIDMemory
  For b = 0 To FZ
    For a = 0 To FX
      PokeF(address, a / FX)
      PokeF(address + 4, b / FZ)
      address + 8
    Next
  Next
EndProcedure

Procedure TerragenExport()
  address = PointIDMemory + 4
  For z = 0 To #NbZ
    For x = 0 To #NbX
      Sommet.f = 100 * Altitudes(x, z)
      PokeF(address, Sommet)
      address + 12
    Next
  Next
  SetMeshData(0, 0, PointIDMemory, (#NbX + 1) * (#NbZ + 1))
EndProcedure

Procedure ShowTextAndKeyTest()
  StartDrawing(ScreenOutput())
    DrawingMode(1)
    FrontColor(20, 180, 115)
    Label(0, 0, "[F1] => Toggle Mode affichage")
    Label(0, 20, "[PageUp] / [PageDown] => Wave Amplitude : " + StrF(WaveAmplitude))
    Label(0, 40, "[Up Arrow] / [Down Arrow] => Wave Period on Z axis : " + Str(WavePeriodZ))
    Label(0, 60, "[Right Arrow] / [Left Arrow] => Wave Period on X axis : " + Str(WavePeriodX))
    Label(0, 80, "[Home key] / [End key] => Wave speed : " + Str(WaveFrequency))
    Label(0, 100, "[F2] / [Shift+F2] => X rotation speed : " + StrF(xrot))
    Label(0, 120, "[F3] / [Shift+F3] => Y rotation speed : " + StrF(yrot))
    Label(0, 140, "[F4] / [Shift+F4] => Z rotation speed : " + StrF(zrot))
    Label(0, 160, "[F5] / [Shift+F5] => X Camera location : " + Str(CamLocateX))
    Label(0, 180, "[F6] / [Shift+F6] => Y Camera location : " + Str(CamLocateY))
    Label(0, 200, "[F7] / [Shift+F7] => Z Camera location : " + Str(CamLocateZ))
    Label(0, 220, "[F8] / [Shift+F8] => X Camera look at : " + Str(CamLookAtX))
    Label(0, 240, "[F9] / [Shift+F9] => Y Camera look at : " + Str(CamLookAtY))
    Label(0, 260, "[F10] / [Shift+F10] => Z Camera look at : " + Str(CamLookAtZ))
  StopDrawing()
  If KeyboardReleased(#PB_Key_F1)
      Mode = #PB_Camera_Wireframe - Mode
      CameraRenderMode(0, Mode)
  EndIf
  If KeyboardPushed(#PB_Key_LeftShift) Or KeyboardPushed(#PB_Key_RightShift)
      Coeff = -1
    Else
      Coeff = 1
  EndIf
  xrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F2) / 128
  yrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F3) / 128
  zrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F4) / 128
  CamLocateX + 1 * Coeff * KeyboardPushed(#PB_Key_F5) / 128
  CamLocateY + 1 * Coeff * KeyboardPushed(#PB_Key_F6) / 128
  CamLocateZ + 1 * Coeff * KeyboardPushed(#PB_Key_F7) / 128
  CamLookAtX + 1 * Coeff * KeyboardPushed(#PB_Key_F8) / 128
  CamLookAtY + 1 * Coeff * KeyboardPushed(#PB_Key_F9) / 128
  CamLookAtZ + 1 * Coeff * KeyboardPushed(#PB_Key_F10) / 128
EndProcedure

;
; Main starts here
;
  If InitEngine3D() And InitSprite() And InitKeyboard() And OpenScreen(#ScreenWidth, #ScreenHeight, #ScreenDepth, "FRW Matrix")
      circle.l = 360
      xrot = -0.6
      yrot = -0.8
      zrot = 0.2
      CamLocateX.l = 0
      CamLocateY.l = 0
      CamLocateZ.l = 250
      CamLookAtX.l = 0
      CamLookAtY.l = 0
      CamLookAtZ.l = 0
      PointIDMemory = AllocateMemory(12 * (#NbX + 1) * (#NbZ + 1)) ; Mémoires Mesh
      TriangleIDMemory = AllocateMemory(12 * #NbX * #NbZ * 4)
      TextureIDMemory = AllocateMemory(12 * (#NbX + 1) * (#NbZ + 1))
      Matrice(#NbX, #NbZ)
      CreateMesh(0) ; Mesh
      SetMeshData(0, 0, PointIDMemory, (#NbX + 1) * (#NbZ + 1))
      SetMeshData(0, 1, TriangleIDMemory, (#NbX) * (#NbZ) * 4)
      SetMeshData(0, 2, TextureIDMemory, (#NbX + 1) * (#NbZ + 1))
      UseEC_OLEImageDecoder()
      TextureXSize = 512
      TextureYSize = 512
      ImageID1 = LoadImage(0, "data\France 1024x1024.jpg")
      ImageID1 = ResizeImage(0, TextureXSize, TextureYSize)
      ImageID2 = CreateImage(1, TextureXSize, TextureYSize)
      StartDrawing(ImageOutput())
        DrawImage(ImageID1, 0, 0)
        For x = 0 To ImageWidth() - 1
          For y = 0 To ImageHeight() - 1
            Color = Point(x, y)
            Red = Red(Color)
            Green = Green(Color)
            Blue = Blue(Color)
            Altitudes(x, y) = (Red * $1000 + Green * $100 + Blue) / $1000000
          Next
        Next
        DrawingMode(1)
        FontSize = 32
        DrawingFont(LoadFont(#PB_Any, "Verdana", FontSize, #PB_Font_Bold | #PB_Font_HighQuality))
        FrontColor(0, 0, 0)
        Text.s = "La France"
        Label((TextureXSize - TextLength(Text) + 2) / 2, TextureYSize - 30, Text)
        FontSize = 28
        DrawingFont(LoadFont(#PB_Any, "Verdana", FontSize, #PB_Font_Bold | #PB_Font_HighQuality))
        FrontColor(255, 255, 255)
        Label((TextureYSize - TextLength(Text)) / 2, TextureYSize - 30, Text)
      StopDrawing()
      CreateTexture(0, TextureXSize, TextureYSize)
      StartDrawing(TextureOutput(0))
        DrawImage(ImageID2, 0, 0)
        DrawingMode(4)
        Box(0, 0, TextureXSize - 1, TextureYSize - 1, #White)
        Box(1, 1, TextureXSize - 3, TextureYSize - 3, #White)
      StopDrawing()
      CreateMaterial(0, TextureID(0)) ; Material
      MaterialBlendingMode(0, #PB_Material_Color) 
      MaterialFilteringMode(0, #PB_Material_Trilinear)
      CreateEntity(0, MeshID(0), MaterialID(0)) ; Entity
      CreateCamera(0, 0, 0, 100, 100) ; Camera
      AmbientColor(#White)
      TerragenExport()
      Repeat
        ClearScreen(0, 0, 0)
        ExamineKeyboard()
        ShowTextAndKeyTest()
        CameraLocate(0, CamLocateX, CamLocateY, CamLocateZ)
        CameraLookAt(0, CamLookAtX, CamLookAtY, CamLookAtZ)
        RotateEntity(0, xrot, yrot, zrot)
        RenderWorld()
        FlipBuffers()
        If GetTickCount_() - tz >= 1000
            Debug ipt
            ipt = 0
            tz = GetTickCount_()
        EndIf
        ipt + 1
      Until KeyboardPushed(#PB_Key_Escape)
    Else
      MessageRequester("Error", "Something fails to initialize 3D engine", 0)
  EndIf
End

Posted: Thu May 20, 2004 3:35 am
by fweil
I just notices that I named the jpeg file : data\France 1024x1024.jpg so that you should change it in the file name or in the source code, as you prefer.

Posted: Thu May 20, 2004 3:45 am
by fweil
Added a second resource file at :

http://perso.wanadoo.fr/francois.weil/N-America.JPG

for those who will prefer to see home ! :wink:

Was my second home years ago.

Posted: Thu May 20, 2004 4:44 am
by fweil
Added a last file at :

http://perso.wanadoo.fr/francois.weil/s ... isco-e.JPG

representing San Francisco bay area.

Posted: Thu May 20, 2004 6:21 am
by Dare2
Neat. Runs well.

This maybe my monitor (probably my monitor) but a white rectangle flickers top-ish centre-ish.

Posted: Thu May 20, 2004 7:06 am
by fweil
I do not have flickering ... probably depends on video board or monitor.

I don't know if some tricks allow to avoid or reduce flickering when using screens ?

Posted: Thu May 20, 2004 9:36 am
by benny
@fweil:
Wow ... really nice one. I like it. Good work! 8)

@Dare2:
Could this be the debugger-window ???
Try to comment out line 201 (near the end)

Code: Select all

            Debug ipt 
Hope this helps :?: :idea:

Posted: Thu May 20, 2004 2:42 pm
by fweil
The code I posted contains some remnants of the design phase. Maybe you can remove all unneded things like this ipt part at the end.

Rgrds

Posted: Fri May 21, 2004 12:22 am
by Dare2
benny wrote:@Dare2:
Could this be the debugger-window ???
Try to comment out line 201 (near the end)
Spot on! Thanks. :)

Posted: Mon May 24, 2004 7:46 am
by dige
@FWeil: Very nice! But how I can use the wave effect?

Posted: Mon May 24, 2004 8:16 am
by fweil
Here is the update containing wave stuff that Comtois first wrote ...

Code: Select all

; 
; D'après le source de Comtois 
; 
; France.pb + Wave effect
; 
; Cederavic et FWeil 20040524
; 
Global PointIDMemory.l, TriangleIDMemory.l, TextureIDMemory.l 

Dim Altitudes.f(1000, 1000) 

Enumeration 
  #PointID 
  #TriangleID 
  #TextureID 
  #NbX = 80 
  #NbZ = 80 
EndEnumeration 

Global xrot.f, yrot.f, zrot.f 
Global CamLocateX.l, CamLocateY.l, CamLocateZ.l, CamLookAtX.l, CamLookAtY.l, CamLookAtZ.l 
Global Mode.b 

#ScreenWidth = 1024 
#ScreenHeight = 768 
#ScreenDepth = 32 

#DegConv=3.14159265/180 
;-Variables Globales 
Global AngleVague.f,WaveFrequency.f,WavePeriodX.f,WavePeriodZ.f,WaveAmplitude.f 
Global xrot.f,yrot.f,zrot.f 
Global CamLocateX.l,CamLocateY.l,CamLocateZ.l,CamLookAtX.l,CamLookAtY.l,CamLookAtZ.l 
Global Mode.b 
circle.l=360 
AngleVague=Random(circle) 
WaveFrequency=10;=waves/second 
WavePeriodX=5;=1/Wave lenght 
WavePeriodZ=17;=1/Wave lenght 
WaveAmplitude=3 

Procedure Label(x.l, y.l, text.s) 
  Locate(x, y) 
  DrawText(text) 
EndProcedure 

Procedure Matrice(FX.l, FZ.l) 
  address = PointIDMemory 
  For b = 0 To FZ 
    For a = 0 To FX 
      PokeF(address, a - FX / 2) 
      PokeF(address + 4, 0) 
      PokeF(address + 8, b - FZ / 2) 
      address + 12 
    Next 
  Next 
  address = TriangleIDMemory 
  Nb = FX + 1 
  For b = 0 To FZ - 1 
    For a = 0 To FX - 1 
      P1 = a + (b * Nb) 
      P2 = P1 + 1 
      P3 = a + (b + 1) * Nb 
      P4 = P3 + 1 
      PokeW(address, P3) 
      PokeW(address + 2, P2) 
      PokeW(address + 4, P1) 
      PokeW(address + 6, P2) 
      PokeW(address + 8, P3) 
      PokeW(address + 10, P4) 
      PokeW(address + 12, P1) 
      PokeW(address + 14, P2) 
      PokeW(address + 16, P3) 
      PokeW(address + 18, P4) 
      PokeW(address + 20, P3) 
      PokeW(address + 22, P2) 
      address + 24 
    Next 
  Next 
  address = TextureIDMemory 
  For b = 0 To FZ 
    For a = 0 To FX 
      PokeF(address, a / FX) 
      PokeF(address + 4, b / FZ) 
      address + 8 
    Next 
  Next 
EndProcedure 

Procedure vagues() 
  ; Modification sur l'axe des Y 
  adresse=PointIDMemory + 4 
  For z=0 To #NbZ 
    For x=0 To #NbX 
      Sommet.f=Sin(#DegConv*(AngleVague+x*WavePeriodX+z*WavePeriodZ))*WaveAmplitude 
      PokeF(adresse,Sommet) 
      adresse+12 
    Next 
  Next 
  SetMeshData(0,0,PointIDMemory,(#NbX+1)*(#NbZ+1)) 
EndProcedure 

Procedure TerragenExport() 
  address = PointIDMemory + 4 
  For z = 0 To #NbZ 
    For x = 0 To #NbX 
      Sommet.f = 100 * Altitudes(x, z) 
      PokeF(address, Sommet) 
      address + 12 
    Next 
  Next 
  SetMeshData(0, 0, PointIDMemory, (#NbX + 1) * (#NbZ + 1)) 
EndProcedure 

Procedure ShowTextAndKeyTest() 
  StartDrawing(ScreenOutput()) 
    DrawingMode(1) 
    FrontColor(20, 180, 115) 
    Label(0, 0, "[F1] => Toggle Mode affichage") 
    Label(0, 20, "[PageUp] / [PageDown] => Wave Amplitude : " + StrF(WaveAmplitude)) 
    Label(0, 40, "[Up Arrow] / [Down Arrow] => Wave Period on Z axis : " + Str(WavePeriodZ)) 
    Label(0, 60, "[Right Arrow] / [Left Arrow] => Wave Period on X axis : " + Str(WavePeriodX)) 
    Label(0, 80, "[Home key] / [End key] => Wave speed : " + Str(WaveFrequency)) 
    Label(0, 100, "[F2] / [Shift+F2] => X rotation speed : " + StrF(xrot)) 
    Label(0, 120, "[F3] / [Shift+F3] => Y rotation speed : " + StrF(yrot)) 
    Label(0, 140, "[F4] / [Shift+F4] => Z rotation speed : " + StrF(zrot)) 
    Label(0, 160, "[F5] / [Shift+F5] => X Camera location : " + Str(CamLocateX)) 
    Label(0, 180, "[F6] / [Shift+F6] => Y Camera location : " + Str(CamLocateY)) 
    Label(0, 200, "[F7] / [Shift+F7] => Z Camera location : " + Str(CamLocateZ)) 
    Label(0, 220, "[F8] / [Shift+F8] => X Camera look at : " + Str(CamLookAtX)) 
    Label(0, 240, "[F9] / [Shift+F9] => Y Camera look at : " + Str(CamLookAtY)) 
    Label(0, 260, "[F10] / [Shift+F10] => Z Camera look at : " + Str(CamLookAtZ)) 
  StopDrawing() 
  If KeyboardReleased(#PB_Key_PageUp):WaveAmplitude+0.1:EndIf 
  If KeyboardReleased(#PB_Key_PageDown):WaveAmplitude-0.1:EndIf 
  If KeyboardReleased(#PB_Key_Up):WavePeriodZ+1:EndIf 
  If KeyboardReleased(#PB_Key_Down):WavePeriodZ-1:EndIf 
  If KeyboardReleased(#PB_Key_Left):WavePeriodX-1:EndIf 
  If KeyboardReleased(#PB_Key_Right):WavePeriodX+1:EndIf 
  If KeyboardReleased(#PB_Key_Home):WaveFrequency+1:EndIf 
  If KeyboardReleased(#PB_Key_End):WaveFrequency-1:EndIf 
  
  If KeyboardReleased(#PB_Key_F1) 
      Mode = #PB_Camera_Wireframe - Mode 
      CameraRenderMode(0, Mode) 
  EndIf 
  If KeyboardPushed(#PB_Key_LeftShift) Or KeyboardPushed(#PB_Key_RightShift) 
      Coeff = -1 
    Else 
      Coeff = 1 
  EndIf 
  xrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F2) / 128 
  yrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F3) / 128 
  zrot + 0.1 * Coeff * KeyboardPushed(#PB_Key_F4) / 128 
  CamLocateX + 1 * Coeff * KeyboardPushed(#PB_Key_F5) / 128 
  CamLocateY + 1 * Coeff * KeyboardPushed(#PB_Key_F6) / 128 
  CamLocateZ + 1 * Coeff * KeyboardPushed(#PB_Key_F7) / 128 
  CamLookAtX + 1 * Coeff * KeyboardPushed(#PB_Key_F8) / 128 
  CamLookAtY + 1 * Coeff * KeyboardPushed(#PB_Key_F9) / 128 
  CamLookAtZ + 1 * Coeff * KeyboardPushed(#PB_Key_F10) / 128 
EndProcedure 

; 
; Main starts here 
; 
  If InitEngine3D() And InitSprite() And InitKeyboard() And OpenScreen(#ScreenWidth, #ScreenHeight, #ScreenDepth, "FRW Matrix") 
      circle.l = 360 
      xrot = -0.6 
      yrot = -0.8 
      zrot = 0.2 
      CamLocateX.l = 0 
      CamLocateY.l = 0 
      CamLocateZ.l = 250 
      CamLookAtX.l = 0 
      CamLookAtY.l = 0 
      CamLookAtZ.l = 0 
      PointIDMemory = AllocateMemory(12 * (#NbX + 1) * (#NbZ + 1)) ; Mémoires Mesh 
      TriangleIDMemory = AllocateMemory(12 * #NbX * #NbZ * 4) 
      TextureIDMemory = AllocateMemory(12 * (#NbX + 1) * (#NbZ + 1)) 
      Matrice(#NbX, #NbZ) 
      CreateMesh(0) ; Mesh 
      SetMeshData(0, 0, PointIDMemory, (#NbX + 1) * (#NbZ + 1)) 
      SetMeshData(0, 1, TriangleIDMemory, (#NbX) * (#NbZ) * 4) 
      SetMeshData(0, 2, TextureIDMemory, (#NbX + 1) * (#NbZ + 1)) 
      UseEC_OLEImageDecoder() 
      TextureXSize = 512 
      TextureYSize = 512 
      ImageID1 = LoadImage(0, "data\France 1024x1024.jpg") 
      ImageID1 = ResizeImage(0, TextureXSize, TextureYSize) 
      ImageID2 = CreateImage(1, TextureXSize, TextureYSize) 
      StartDrawing(ImageOutput()) 
        DrawImage(ImageID1, 0, 0) 
        For x = 0 To ImageWidth() - 1 
          For y = 0 To ImageHeight() - 1 
            Color = Point(x, y) 
            Red = Red(Color) 
            Green = Green(Color) 
            Blue = Blue(Color) 
            Altitudes(x, y) = (Red * $1000 + Green * $100 + Blue) / $1000000 
          Next 
        Next 
        DrawingMode(1) 
        FontSize = 32 
        DrawingFont(LoadFont(#PB_Any, "Verdana", FontSize, #PB_Font_Bold | #PB_Font_HighQuality)) 
        FrontColor(0, 0, 0) 
        Text.s = "La France" 
        Label((TextureXSize - TextLength(Text) + 2) / 2, TextureYSize - 30, Text) 
        FontSize = 28 
        DrawingFont(LoadFont(#PB_Any, "Verdana", FontSize, #PB_Font_Bold | #PB_Font_HighQuality)) 
        FrontColor(255, 255, 255) 
        Label((TextureYSize - TextLength(Text)) / 2, TextureYSize - 30, Text) 
      StopDrawing() 
      CreateTexture(0, TextureXSize, TextureYSize) 
      StartDrawing(TextureOutput(0)) 
        DrawImage(ImageID2, 0, 0) 
        DrawingMode(4) 
        Box(0, 0, TextureXSize - 1, TextureYSize - 1, #White) 
        Box(1, 1, TextureXSize - 3, TextureYSize - 3, #White) 
      StopDrawing() 
      CreateMaterial(0, TextureID(0)) ; Material 
      MaterialBlendingMode(0, #PB_Material_Color) 
      MaterialFilteringMode(0, #PB_Material_Trilinear) 
      CreateEntity(0, MeshID(0), MaterialID(0)) ; Entity 
      CreateCamera(0, 0, 0, 100, 100) ; Camera 
      AmbientColor(#White) 
      TerragenExport() 
      Repeat 
        ClearScreen(0, 0, 0) 
        ExamineKeyboard() 
        CameraLocate(0, CamLocateX, CamLocateY, CamLocateZ) 
        CameraLookAt(0, CamLookAtX, CamLookAtY, CamLookAtZ) 
        ;Calculate (AngleVague+WaveFrequency)%360: (cause % operand doesn't accept floats) 
        !fild dword[v_circle] 
        !fld dword[v_AngleVague] 
        !fadd dword[v_WaveFrequency] 
        !fprem 
        !fstp dword[v_AngleVague] 
        !fstp st1 
        
        Vagues() 
        RotateEntity(0, xrot, yrot, zrot) 
        RenderWorld() 
        ShowTextAndKeyTest() 
        FlipBuffers() 
        If GetTickCount_() - tz >= 1000 
            Debug ipt 
            ipt = 0 
            tz = GetTickCount_() 
        EndIf 
        ipt + 1 
      Until KeyboardPushed(#PB_Key_Escape) 
      CloseScreen()
    Else 
      MessageRequester("Error", "Something fails to initialize 3D engine", 0) 
  EndIf 
  TerminateProcess_(GetCurrentProcess_(), 0)
End