DemoCoding - OldSkool Xor Textured Spherical Tunnel Effect

Share your advanced PureBasic knowledge/code with the community.
va!n
Addict
Addict
Posts: 1104
Joined: Wed Apr 20, 2005 12:48 pm

DemoCoding - OldSkool Xor Textured Spherical Tunnel Effect

Post by va!n »

Hi out there...
long time ago, i wrote an article explaining how to to code an oldskool tablebased tunnel effect. the article has been published in the first issue of the german coding magazine "PB.CM"! (sadly still dead?) However, all rights are reserved.

Article (c) by Mr.Vain/Secretly! aka "thorsten will". all rights reserved!



GERMAN ARTICLE VERSION
DemoCoding - Tunnel Effect:

Hallo und herzlich willkommen in der Wunderwelt der sogenannten Tabellenbasierten DemoEffekte. Was sind Tabellenbasierte DemoEffekte und warum werden hierfür Tabellen benutzt? Viele ältere DemoEffekte wie Tunnel, Sphere, BumpMap usw. sind oft lediglich 3D fakes und basieren auf vorberechnete 2D Daten, welche für die spätere Verwendung in Tabellen (Arrays) gespeichert werden. Da die benötigen Daten vorberechnet werden, brauchen wir diese später nicht im MainLoop und in Echtzeit immer wieder neu zu berechnen. Hierdurch ersparen wir uns einige Berechnungen und können die Performance des einzelnen Effekts oft um einiges steigern.

Heute werden wir uns Schritt für Schritt die Grundlagen zur Programmierung eines TunnelEffektes anschauen und Schritt für Schritt einen solchen selber Programmieren.

Was brauchen wir überhaupt um unseren ersten TunnelEffekt zu realisieren und wie funktionert dieser Effekt? Der TunnelEffekt an sich besteht aus drei wichtige Elementen. Wir hätten da zum einen die Texture, welche wir später auf unseren Tunnel mappen (kleistern). Als Texture werden wir uns eine eigene 256x256 große - sogenannte XOR - Texture berechnen. Selbstverständlich könnten wir auch einfach per Zufall Punkte, Linien oder Kreise auf unserer Texture zeichen - oder sogar zum Beispiel eine 256x256 große "tileable" Texture als BMP benutzen.

Nun werden wir unsere erste XOR Texture berechnen, in dem wir ein 256x256 großes Image erstellen und dieses nach Fertigstellung auf unserer Festplatte unter ""c:\TunnelFx_Texture.bmp" speichern. Als Ergebniss erhalten wir unsere fertige Texture als BMP Datei.

Code: Select all

Listing: Part1_Texture.pb
Desweiteren benötigen wir zwei große Texturen (Distance und Angle), die unserer darzustellenden Bildschirmgröße 640x480 entsprechen. Wenn wir uns den Blick in einen runden Tunnel vorstellen, so könnte man dieses mit einer Röhre vergleichen, die aus vielen Ringen besteht und die Ringe mit zunehmender Distance immer kleiner werden. Wir werden nun eine entsprechende "Distance Texture" erstellen und diese ebenfalls als BMP speichern. Um einen kleinen Eindruck zu erhalten, wie eine solche "Distance Texture" ausschaut, einfach die fertige BMP Datei öffnen und anschauen.

Code: Select all

Listing: Part2_Distance.pb
Nach dem wir nun die "Distance Texture" erstellt haben, benötigen wir noch die sogenannte "Angle-Texture" (Winkel). Diese Texture wird benötigt um unsere XOR Texture im Tunnel richtig anzupassen und unseren Tunnel später u.a. rotieren zu lassen. Die "Distance Texture" wird u.a. benötigt, um in den Tunnel zu fliegen.

Code: Select all

Listing: Part3_Angle.pb
Bisher haben wir die drei wichtigsten Elemente für einen TunnelEffekt und ihre jeweiligen Routinen kennengelernt, dessen Ergebnisse wir bisher immer auf eine Bitmap darstellen und als BMP speichern. Nun werden wir die berechneten Daten direkt die Tabellen (Arrays) "aTexture", "aDistance" und "aAngle" speichern und die einzelnen Routinen von Part1-Part3 zusammenlegen und somit ein wenig optimieren. Im SourceCode von Part1 erstellen wir unsere 256x256 große XOR Texture, bei der wir die x und y Schleife von 0 bis 255 durchlaufen lassen. Da wir keine weitere Routine verwenden, in der wir ebenfalls von 0 bis 255 zählen, lassen wir diese Routine wie zuvor unverändert.

Allerdings verwenden wir für die Berechnung der "Distance Table" und "Angle Table" in beiden Fällen Schleifen, dessen Werte in beiden Routinen identisch sind. Daher können wir diese beiden Routine ohne Probleme zusammenfassen und uns Zeit für die Vorberechnung sparen und kleineren Programmcode erzeugen.

Code: Select all

Listing: Part4_MergingStuff.pb
Wir werden das bisher gelernte nun anwenden, einen Screen öffnen und das erste Resultat unseres Tunnels auf dem Bildschirm darstellen.
Um den Programmcode einfacher zu lesen, werden wir einfach den Plot() Befehl verwenden und nicht auf DirectScreenAccess aufbauen.

Mit unserer "aDistance" und "aAngle" Table können wir nun ohne Probleme einen Tunnel darstellen. Damit nun aber auch unsere 256x256 große XOR Texture (aTexture) im Tunnel richtig gemapped (abgebildet) wird, werden wir diese beiden Tabellen im Innerloop einfach auslesen und wir erhalten die entsprechende X und Y Koddinaten unserer XOR Texture. Das Ergebniss speichern wir in unsererm Array aBuffer, welches wir zuvor noch zu unserem Code hinzufügen müssen! Wir könnten nun in unserem aBuffer weitere Manipulationen vornehmen oder das Ergebniss wie in der nächsten Zeile zu sehen ist, mit Plot() auf den Bildschirm darstellen lassen.

Code: Select all

        aBuffer(x,y) = aTexture (aDistance(x,y), aAngle(x,y)) 
        Plot(x, y, aBuffer(x,y) )
Der komplette Sourcecode würde nun wie folgt aussehen und wir würdeb beim ausführen des Sources ein Standbild des TunnelEffektes erhalten..

Code: Select all

Listing:  Part5_FirstTunnel.pb
Um unseren Tunnel nun zu animieren, werden wir diesen drehen lassen und dabei in den Tunnel hineinfliegen. Hierzu benötigen wir die Variablen dSpeedX.d und SpeedY.d, um die Geschwindigkeit für die X und Y Bewegung des Tunnels zu defininieren. Desweiteren benötigen wir im Innerloop die Variablen lShiftX.l und lShiftY.l, um die entsprechenden Bewegungen in den Tabellen zu berechnen und später auszulesen. In unserem Beipspiel werden wir für die Animation die Variable dAnimation.d nehmen, welche wir pro Loop um 0.005 erhöhen! (Anmerkung: Bitte für eigene Projekte mit DeltaTimes arbeiten, damit der Effekt auf jeden Rechner gleich schnell läuft!).

Code: Select all

  dAnimation.d = dAnimation.d + 0.005           
  If dAnimation.d >= 1.0 : dAnimation = 0.0 : EndIf
  
  lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
  lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d)
Nun haben wir alle wichtigen Berechnungen vorgenommen, damit der Tunnel animiert werden kann. Um nicht wieder wie zuvor nur ein Standbild zu erhalten, müssen wir bei der Darstellung des Tunnels folgende Zeilen hinzufügen bzw abändern.

Code: Select all

    lCoordinateX.l = (aDistance(x,y) + lShiftX) % lTextureSize   
    lCoordinateY.l = (aAngle(x,y)    + lShiftY) % lTextureSize  
    aBuffer(x,y) = aTexture (lCoordinateX.l , lCoordinateY.l) 
    Plot(x, y, RGB(0, 0, aBuffer(x,y) )) 
Der komplette Sourcecode für einen komplett animierten Tunnel, würde nun wie folgt aussehen:

Code: Select all

Listing:  Part6_AnimateTunnel.pb
An dieser Stelle sind wir nun bei unseren animierten Tunnel angelangt. Leider wirkt das ganze auf eine Art doch noch ein etwas langweilig. Um unseren aktuellen
animierten Tunnel noch ein wenig aufzumotzen und interessanter wirken zu lassen, werden wir ein paar kleine Änderungen vornehmen. Dabei werden wir den
eigentichen Tunnel tanzen (bzw bewegen) lassen. Es ensteht der Eindruck, als würde man die Kamera im Tunnel bewegen ;-)

Da unserer Tunnel nur auf Tables und nicht auf echtes 3D basiert, werden sich nun sicher einige Fragen, wie man den Effekt mit der Kamera umsetzen will. Wir werden hierfür einen absolut einfachen und zugleich fiesen Trick verwenden. Unser Bildschirm und unserer aDistance und aAngle Tables sind alle für 640x480 ausgelegt. Wir werden nun die Breite und Höhe der Tables aDistance und aAngle verdoppeln, um später immer nur den gewünschten Ausschnitt auf den Bildschirm darzustellen.

Code: Select all

    Dim aDistance (lScreenWidth*2, lScreenHeight*2)
    Dim aAngle    (lScreenWidth*2, lScreenHeight*2)
Bei den Berechnungen unserer aDistance und aAngle Tables, haben wir bisher z.B. immer "lScreenWidth / 2" (geteilt durch 2) gerechnet, da lScreenWidth mit der Größe von aDistance und aAngle waren. Da wir aber nun die Größe von aDistance und aAngle verdoppelt haben, brauchen wir bei der Berechnung dieser Tabellen nicht mehr "/ 2" (geteilt durch 2) rechnen!! Vorsicht!

Um unseren Tunnel nun schön tanzen zu lassen, verwenden wir einfach SIN() und fügen die Variablen lLookX und lLookY hinzu. Dafür werden folgende Zeilen geändert:

Code: Select all

    lLookX = lScreenWidth /2 + Int(lScreenWidth /2 * Sin(dAnimation * 4.0 ))
    lLookY = lScreenHeight/2 + Int(lScreenHeight/2 * Sin(dAnimation * 6.0 ))
Bei der Darstellung des Tunnels müssen wir nun selbstverständlich die Werte lLookX und lLookY berücksichtigen. Dafür ändern wir den Code wie folgt ab:

Code: Select all

    lCoordinateX.l = (aDistance(x+lLookX, y+lLookY) + lShiftX) % lTextureSize      
    lCoordinateY.l = (aAngle   (x+lLookX, y+lLookY) + lShiftY) % lTextureSize    
Wenn jetzt keine Fehler unterlaufen sind, sollten wir nun einen schönen texturierten und animierten Tunnel haben, dessen Kameraposition sich ständig zu verändern scheint. Hier nun nochmal der komplette Sourcecode:

Code: Select all

Listing:  Part7_MovingTunnel.pb
Ich hoffe, das dieses Tutorial halbwegs verständlich gestaltet ist und den einen oder anderen gefällt. Viel Spaß, Thorsten Will aka va!n.



Sources and Tutorial (c) by Thorsten Will aka va!n
All rights reserved.


English Article Version (thanks to Jim)
Hello and cordially welcomely in the miracle world of the so-called table-based demo effects. What are table-based demo effects and why are tables used for this? Many older demo effects such as tunnel, Sphere, BumpMap etc. are often only 3D fakes and are based on pre-computed 2D data, which are stored for later use in tables (arrays). Since the data are pre-computed, we do not need to compute them later in the main loop in real time again and again again. Thereby we save some computations and can often increase the performance of the individual effect.
Today we will look at the step-by-step basics for the programming of a tunnel effect.
What do we need to know about our first tunnel effect and how it functions? The tunnel effect actually consists of three important elements. On one hand we have the Texture, which we later map on to our tunnel. For the Texture we will compute our own 256x256 - so-called XOR - Texture. Of course we could use a 256x256 “tileable” Texture also simply by drawing points, lines or circles on our Texture - or we could even use a BMP.
Now we will compute our first XOR Texture, in which we provide a 256x256 image and store it on the hard disk under "c:\TunnelFx _Texture.bmp".

Code: Select all

Listing: Part1_Texture.pb
Furthermore we need two large textures (Distance and Angle), which correspond to our screen size of 640x480. If we imagine the view is a round tunnel, then we could compare this with a tube, which consists of many rings and which rings with increasing Distance become ever smaller. We will generate now an appropriate “Distance Texture” and will likewise store these as BMP. To get an idea what it looks like, you can open the “Distance Texture” BMP in an image viewer.

Code: Select all

Listing: Part2_Distance.pb
Now we've generated the “Distance Texture”, we need the so-called “Angle Texture”. To wrap the Texture around our XOR Texture in the tunnel correctly and among other things to let our tunnel rotate. The “Distance Texture” is needed among other things, in order to make the tunnerl fly.

Code: Select all

Listing: Part3_Angle.pb
So far we have become acquainted with the three most important elements for a tunnel effect and their respective routines, whose results we've represented on a bitmap and stored as BMP. Now we will store the computed data directly the arrays “aTexture”, “aDistance” and “aAngle” and will join together the individual routines of Part1-Part3 with a little optimisation. In the source code of Part1 we provide our 256x256 XOR Texture, with which we let the x and y loop from 0 to 255. We leave this unchanged, however because the same loops for computing the “Distance Table” and “fish Table” we put the code for them in the same loop as the XOR Texture. Therefore we can combine these two routines without problems and now it's time to work on the pre-calculation savings and smaller code.

Code: Select all

Listing: Part4_MergingStuff.pb
We will now apply what we've learned the so far. We will open a screen and will draw the first output of our tunnel to the screen. In order to read the program code more simply, we will use the Plot() function and not direct screen access. With our “aDistance” and “aAngle” arrays we can now easily draw a tunnel. Thus now together we use these two tables in the inner loop to select the appropriate X and Y coordinates of our 256x256 XOR Texture. Once we've done that we can put the value in the aBuffer, which we still have to add to our code! We could now make further manipulations on the output to the output before we write it to the screen using Plot().

Code: Select all

aBuffer (x, y) = aTexture (aDistance (x, y), aAngle (x, y))
Plot (x, y, aBuffer (x, y))
The complete source code would now look as follows which will show us a fixed image of the tunnel effect .

Code: Select all

Listing: Part5_FirstTunnel.pb
We're going to animate our tunnel now, and make it look like we're flying into it. For this we need the variables dSpeedX.d and dSpeedY.d, which are the speed for the X and Y movement around those axes. Furthermore we need the variables in the inside loop lShiftX.l and lShiftY.l, in order to compute and later pick the appropriate movements from the arrays. In our example we will take the variable dAnimation.d, which we increase per loop by 0.005 for the animation! (Note: For your own projects, work with delta timing, so the effect on each computer runs correctly!).

Code: Select all

dAnimation.d = dAnimation.d + 0.005
If dAnimation.d >= 1.0 : dAnimation = 0.0 : EndIf
lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d) 
Now we have made all important computations, so that the tunnel can be animated. In order not to just get one fixed image, we must add and/or change the following lines during the representation of the tunnel.

Code: Select all

lCoordinateX.l = (aDistance(x,y) + lShiftX) % lTextureSize
lCoordinateY.l = (aAngle(x,y) + lShiftY) % lTextureSize
aBuffer(x,y) = aTexture (lCoordinateX.l , lCoordinateY.l)
Plot(x, y, RGB(0, 0, aBuffer(x,y) ))
The complete source code for a completely animated tunnel, would look as follows:

Code: Select all

Listing: Part6_AnimateTunnel.pb
Here we have animated tunnel. Unfortunately the whole effect is somewhat nevertheless still boring. In order to make our current animated tunnel still more funky and more interesting, we can make a few small changes. We will make tunnel to move up and down to give the impresson of the camera moving in the tunnel ;-) Since our tunnel's only made from arrays and not from genuine 3D, some questions become now need to be asked as one wants to convert the effect with the camera. We will use for this an absolutely simple and at the same time safe trick. As our aDistance and aAngle arrays are suitable only for a screen of 640x480, we will double now the width and height of the them, in order to represent in each case the desired section later on the screen.

Code: Select all

Dim aDistance (lScreenWidth*2, lScreenHeight*2)
Dim aAngle (lScreenWidth*2, lScreenHeight*2) 
With the computations our, we so far e.g. always counted aDistance and aAngle Tables “lScreenWidth/2” (divided by 2), since lScreenWidth was the size of aDistance and aAngle. However since we've now doubled the size of aDistance and aAngle, we do not need to "/ 2" (divided by 2) with the computation of these tables any longer!! Caution! In order to let our tunnel dance beautifully now, we use simply SIN () and add the variables lLookX and lLookY. The following lines are changed:

Code: Select all

lLookX = lScreenWidth /2 + Int(lScreenWidth /2 * Sin(dAnimation * 4.0 ))
lLookY = lScreenHeight/2 + Int(lScreenHeight/2 * Sin(dAnimation * 6.0 ))
During the drawing of the tunnel we must now change the values lLookX and lLookY to make it look natural. We change the code as follows:

Code: Select all

lCoordinateX.l = (aDistance(x+lLookX, y+lLookY) + lShiftX) % lTextureSize
lCoordinateY.l = (aAngle (x+lLookX, y+lLookY) + lShiftY) % lTextureSize
If we haven't made any mistakes now, we should have now a beautiful texturemapped and animated tunnel, whose camera position seems to constantly change. Here now again is the complete source code:

Code: Select all

Listing: Part7_MovingTunnel.pb
I hope you and others enjoy this tutorial,
Much fun, Thorsten Will aka va! n.


Part1_Texture.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part1: Creating Mapping Texture:
; *   --------------------------------
; *   This small example shows you, how to create an easy XOR 256x256 sized texture. 
; *   We will use this texture later for texturemapping of our tunnel. Ofcourse you
; *   can use any other created or loaded tileable texture too :)
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

lTextureSize.l  = 256                     
CreateImage (0, lTextureSize, lTextureSize) 
 
; -------- Generating Mapping Texture --------

StartDrawing(ImageOutput(0))               

  For y.l = 0 To lTextureSize -1     
    For x.l = 0 To lTextureSize -1
      lColor.l = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize)                     
      Plot (x, y, RGB( 0, 0, lColor.l) )               
    Next 
  Next

StopDrawing()

; -------- Saving Texture --------

SaveImage (0, "c:\TunnelFx_Texture.bmp")               
End

; *************************************************************************************

Part2_Distance.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x    
; *************************************************************************************
; *
; *   Part2: Creating Distance Table / Texture:
; *   -----------------------------------------
; *   This small example shows you, how to to create the needed distance table and how
; *   does this looks like for doing the flying trip into our tunnel.
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

CreateImage (0, lScreenWidth, lScreenHeight)
 
; -------- Generating Distance Table / Texture --------

StartDrawing(ImageOutput(0))

  dDistance.d = 32.0
            
  For x = 0 To lScreenWidth -1      
    For y = 0 To lScreenHeight -1
      lColor.l = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth/2) * (x-lScreenWidth/2) + (y-lScreenHeight/2) * (y-lScreenHeight/2) )) % lTextureSize
      Plot (x, y, RGB(lColor, lColor, lColor) )
    Next
  Next

StopDrawing()
    
; -------- Saving Table as Texture --------

SaveImage (0, "c:\TunnelFx_Distance.bmp")
End

; *************************************************************************************


Part3_Angle.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x    
; *************************************************************************************
; *
; *   Part3: Creating Angle Table / Texture:
; *   --------------------------------------
; *   This small example shows you, how to to create the needed angle table and how
; *   does this looks like for doing the rotations of our tunnel. Dont forget to enable
; *   inline assembler. Otherwise as you see, you will see nothing. :D
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rigths reserved.
; *
; *************************************************************************************

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn 
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

CreateImage (0, lScreenWidth, lScreenHeight)
 
; -------- Generating Angle Table / Texture --------

StartDrawing(ImageOutput(0))

   dParts.d = 0.5 

   For x = 0 To lScreenWidth -1              
     For y = 0 To lScreenHeight -1
       dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight/2, x-lScreenWidth/2) / #PI)
       lColor = Int (256 - dAngle) & 255
       Plot (x, y, RGB(lColor, lColor, lColor) )
     Next
   Next

StopDrawing()

; -------- Saving Table as Texture --------

SaveImage (0, "c:\TunnelFx_Angle.bmp")
End

; *************************************************************************************


Part4_MergingStuff.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part4: Merging routines from Part1-Part3 into two single loops:
; *   ---------------------------------------------------------------
; *   Now we are going to optimize our stuff from Part1-Part3 while merging Part2 and
; *   Part3 into just a single loop and storing the data from Part1-Part3 directly into
; *   our defined arrays. Merging Part2 with Part3 will reduce the generated ASM output
; *   and increase the speed of our table pre-generation.
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn 
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize, lTextureSize)
Dim aDistance (lScreenWidth, lScreenHeight)
Dim aAngle    (lScreenWidth, lScreenHeight)
  
; -------- Generating Mapping Texture --------

For x.l = 0 To lTextureSize -1
  For y.l = 0 To lTextureSize -1
    aTexture(x,y) = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize)               
  Next 
Next

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5 
          
For x = 0 To lScreenWidth -1      
  For y = 0 To lScreenHeight -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth/2) * (x-lScreenWidth/2) + (y-lScreenHeight/2) * (y-lScreenHeight/2) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight/2, x-lScreenWidth/2) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next
  
; *************************************************************************************


Part5_FirstTunnel.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part5: Showing our first tunnel:
; *   --------------------------------
; *   As we learned in the first three basic tutorials, we need one texture and two
; *   table based datas (aDistance and aAngle), also two images to produce our tunnel
; *   like effect. Now we will try to merge this knowledge to produce our first tunnel
; *   like effect in real on our screen :) Dont forget to enable inline ASM.
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn 
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize, lTextureSize)
Dim aDistance (lScreenWidth, lScreenHeight)
Dim aAngle    (lScreenWidth, lScreenHeight)
Dim aBuffer   (lScreenWidth, lScreenHeight)

; -------- Generating Mapping Texture --------

For x.l = 0 To lTextureSize -1
  For y.l = 0 To lTextureSize -1
    aTexture(x,y) = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize) 
  Next 
Next

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5 
          
For x = 0 To lScreenWidth -1      
  For y = 0 To lScreenHeight -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth/2) * (x-lScreenWidth/2) + (y-lScreenHeight/2) * (y-lScreenHeight/2) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight/2, x-lScreenWidth/2) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next
 
; *************************************************************************************

InitSprite() 
OpenScreen(lScreenWidth, lScreenHeight, 32, "TunnelFx Tutorial")

Repeat

  ; -------- Calculate Texture coordinates and draw Tunnel -------

  StartDrawing(ScreenOutput())
    For x = 0 To lScreenWidth -1
      For y = 0 To lScreenHeight-1
        aBuffer(x,y) = aTexture (aDistance(x,y), aAngle(x,y)) 
        Plot(x, y, RGB(0, 0, aBuffer(x,y) ))
      Next 
    Next
  StopDrawing()

  FlipBuffers(2)
Until GetAsyncKeyState_(#VK_ESCAPE) 

; *************************************************************************************


Part6_AnimateTunnel.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part6: Lets animate the tunnel:
; *   -------------------------------
; *   After turorial part5, we will try to alive the tunnel and animate it a litte bit.
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn 
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize, lTextureSize)
Dim aDistance (lScreenWidth, lScreenHeight)
Dim aAngle    (lScreenWidth, lScreenHeight)
Dim aBuffer   (lScreenWidth, lScreenHeight)

; -------- Generating Mapping Texture --------

For x.l = 0 To lTextureSize -1
  For y.l = 0 To lTextureSize -1
    aTexture(x,y) = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize) 
  Next 
Next

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5 
          
For x = 0 To lScreenWidth -1      
  For y = 0 To lScreenHeight -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth/2) * (x-lScreenWidth/2) + (y-lScreenHeight/2) * (y-lScreenHeight/2) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight/2, x-lScreenWidth/2) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next
 
; *************************************************************************************

InitSprite() 
OpenScreen(lScreenWidth, lScreenHeight, 32, "TunnelFx Tutorial")

dSpeedX.d = 2.0
dSpeedY.d = 2.0

Repeat
  ; ------- Stuff for doing the animation -------
  
  dAnimation.d = dAnimation.d + 0.005                 ; timeGetTime_() / 1000
  If dAnimation.d >= 1.0 : dAnimation = 0.0 : EndIf
  
  lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
  lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d)

  ; -------- Calculate Texture coordinates and draw Tunnel -------

  StartDrawing(ScreenOutput())
    For x = 0 To lScreenWidth -1
      For y = 0 To lScreenHeight-1
        lCoordinateX.l = (aDistance(x,y) + lShiftX) % lTextureSize     
        lCoordinateY.l = (aAngle(x,y)    + lShiftY) % lTextureSize     
        aBuffer(x,y) = aTexture (lCoordinateX.l , lCoordinateY.l) 
        Plot(x, y, RGB(0, 0, aBuffer(x,y) ))
      Next 
    Next
  StopDrawing()

  FlipBuffers(1)
Until GetAsyncKeyState_(#VK_ESCAPE)

; *************************************************************************************


Part7_MovingTunnel.pb

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part7: Moving Tunnel:
; *   ---------------------
; *   This is the last turorial part, where we will try to get the tunnel fx more 
; *   interesting while moving the tunnel, by using SIN().
; * 
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *************************************************************************************

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn 
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256 
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize  , lTextureSize   )
Dim aDistance (lScreenWidth*2, lScreenHeight*2)
Dim aAngle    (lScreenWidth*2, lScreenHeight*2)
Dim aBuffer   (lScreenWidth  , lScreenHeight  )

; -------- Generating Mapping Texture --------

For x.l = 0 To lTextureSize -1
  For y.l = 0 To lTextureSize -1
    aTexture(x,y) = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize) 
  Next 
Next

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5 
          
For x = 0 To lScreenWidth*2 -1      
  For y = 0 To lScreenHeight*2 -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth) * (x-lScreenWidth) + (y-lScreenHeight) * (y-lScreenHeight) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight, x-lScreenWidth) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next

; *************************************************************************************

InitSprite() 
OpenScreen(lScreenWidth, lScreenHeight, 32, "TunnelFx Tutorial")

dSpeedX.d = 2.0
dSpeedY.d = 2.0

Repeat
  ; ------- Stuff for doing the animation -------
  
  dAnimation.d = dAnimation.d + 0.005
  
  lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
  lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d)

  lLookX = lScreenWidth /2 + Int(lScreenWidth /2 * Sin(dAnimation * 4.0 ))
  lLookY = lScreenHeight/2 + Int(lScreenHeight/2 * Sin(dAnimation * 6.0 ))

  ; -------- Calculate Texture coordinates and draw Tunnel -------

  StartDrawing(ScreenOutput())
    For y = 0 To lScreenHeight-1
      For x = 0 To lScreenWidth -1
        lCoordinateX.l = (aDistance(x+lLookX, y+lLookY) + lShiftX) % lTextureSize     
        lCoordinateY.l = (aAngle   (x+lLookX, y+lLookY) + lShiftY) % lTextureSize     
        aBuffer(x,y) = aTexture (lCoordinateX.l , lCoordinateY.l) 
        Plot(x, y, RGB(0, 0, aBuffer(x,y) ))
      Next 
    Next
  StopDrawing()

  FlipBuffers(1)
Until GetAsyncKeyState_(#VK_ESCAPE)

; *************************************************************************************


Have phun... if you have any optimisation ideas or better ways to do this, let me know...
va!n aka Thorsten

Intel i7-980X Extreme Edition, 12 GB DDR3, Radeon 5870 2GB, Windows7 x64,
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

Thank you for sharing these OldSkool FX!
Dare2 cut down to size
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

This is just wonderful.

Quite a tutorial.

Many thanks for sharing :)
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Post by flaith »

nice piece of code, thanks va!n :D
“Fear is a reaction. Courage is a decision.” - WC
User avatar
Blue
Addict
Addict
Posts: 967
Joined: Fri Oct 06, 2006 4:41 am
Location: Canada

Post by Blue »

Thank you for this very interesting project.

I thoroughly enjoyed it.
PB Forums : Proof positive that 2 heads (or more...) are better than one :idea:
dige
Addict
Addict
Posts: 1411
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post by dige »

Thank you va!n ... very good stuff!
this was a good reason to try the drawingbuffer() stuff. seems that
Plot() is already optimized. I've got only 1FPS more ...
Please check it out:

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part7: Moving Tunnel:
; *   ---------------------
; *   This is the last turorial part, where we will try to get the tunnel fx more
; *   interesting while moving the tunnel, by using SIN().
; *
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *   Added using DrawingBuffer() by DiGe
; *
; *************************************************************************************
Structure Pixel
  Pixel.l
EndStructure

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize  , lTextureSize   )
Dim aDistance (lScreenWidth*2, lScreenHeight*2)
Dim aAngle    (lScreenWidth*2, lScreenHeight*2)
Dim aBuffer   (lScreenWidth  , lScreenHeight  )

; -------- Generating Mapping Texture --------

For x.l = 0 To lTextureSize -1
  For y.l = 0 To lTextureSize -1
    aTexture(x,y) = (x * 256 / lTextureSize) ! (y * 256 / lTextureSize)
  Next
Next

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5
         
For x = 0 To lScreenWidth*2 -1     
  For y = 0 To lScreenHeight*2 -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth) * (x-lScreenWidth) + (y-lScreenHeight) * (y-lScreenHeight) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight, x-lScreenWidth) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next

; *************************************************************************************

InitSprite()
OpenWindow(0, 0, 0, lScreenWidth, lScreenHeight, "Tunnel", #WS_OVERLAPPEDWINDOW )
OpenWindowedScreen(WindowID(0), 0, 0, lScreenWidth, lScreenHeight, 0, 0, 0)

dSpeedX.d = 2.0
dSpeedY.d = 2.0

time.l  = ElapsedMilliseconds() + 3000
Count.l = 0

Repeat
  ; Counting FPS
  If time < ElapsedMilliseconds()
    SetWindowTitle(0, Str(Count/3) + " FPS")
    Count = 1
    time = ElapsedMilliseconds() + 3000
  Else
    Count + 1
  EndIf
  
  
  ; ------- Stuff for doing the animation -------
 
  dAnimation.d = dAnimation.d + 0.005
 
  lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
  lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d)

  lLookX = lScreenWidth /2 + Int(lScreenWidth /2 * Sin(dAnimation * 4.0 ))
  lLookY = lScreenHeight/2 + Int(lScreenHeight/2 * Sin(dAnimation * 6.0 ))

  ; -------- Calculate Texture coordinates and draw Tunnel -------

  If StartDrawing(ScreenOutput())
    Buffer      = DrawingBuffer()             ; Get the start address of the screen buffer
    Pitch       = DrawingBufferPitch()        ; Get the length (in byte) took by one horizontal line
    PixelFormat = DrawingBufferPixelFormat()  ; Get the pixel format. 
    
    For y = 0 To lScreenHeight-1
      *Line.Pixel = Buffer+Pitch*y
      For x = 0 To lScreenWidth -1
        lCoordinateX.l = (aDistance(x+lLookX, y+lLookY) + lShiftX) % lTextureSize     
        lCoordinateY.l = (aAngle   (x+lLookX, y+lLookY) + lShiftY) % lTextureSize     
        aBuffer(x,y) = aTexture (lCoordinateX.l , lCoordinateY.l)
        ;Plot(x, y, RGB(0, 0, aBuffer(x,y) ))
        
        If PixelFormat = #PB_PixelFormat_32Bits_RGB
          *Line\Pixel = RGB(aBuffer(x,y), 0, 0 ) ; Write the pixel directly to the memory !
        Else                        ; Else it's 32bits_BGR
          *Line\Pixel = RGB(0, 0, aBuffer(x,y)) ; Write the pixel directly to the memory !
        EndIf
        *Line + 4
      Next
    Next
    StopDrawing()
  EndIf

  FlipBuffers(0)
  If WindowEvent() = #PB_Event_CloseWindow : Break : EndIf
Until GetAsyncKeyState_(#VK_ESCAPE)
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

Hey MrVain! XMax is over! What are you doing? ;)
Nice FX, did you do faster versions, in compos?
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

Here's a faster version, not fully optimised ;)

Code: Select all

; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x
; *************************************************************************************
; *
; *   Part7: Moving Tunnel:
; *   ---------------------
; *   This is the last turorial part, where we will try to get the tunnel fx more
; *   interesting while moving the tunnel, by using SIN().
; *
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rights reserved.
; *
; *   Added using DrawingBuffer() by DiGe
; *
; *************************************************************************************
Structure Pixel
  Pixel.l
EndStructure

Procedure.d ATan2(y.d, x.d)
  !FLD qword[p.v_y]
  !FLD qword[p.v_x]
  !FPATAN
  ProcedureReturn
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256
lScreenWidth.l  = 640
lScreenHeight.l = 480

Dim aTexture  (lTextureSize  , lTextureSize   )
Dim aDistance (lScreenWidth*2, lScreenHeight*2)
Dim aAngle    (lScreenWidth*2, lScreenHeight*2)
Dim aBuffer   (lScreenWidth  , lScreenHeight  )

; *************************************************************************************

InitSprite()
OpenWindow(0, 0, 0, lScreenWidth, lScreenHeight, "Tunnel", #WS_OVERLAPPEDWINDOW )
OpenWindowedScreen(WindowID(0), 0, 0, lScreenWidth, lScreenHeight, 0, 0, 0)

If StartDrawing(ScreenOutput())
  Buffer      = DrawingBuffer()             ; Get the start address of the screen buffer
  Pitch       = DrawingBufferPitch()        ; Get the length (in byte) took by one horizontal line
  PixelFormat = DrawingBufferPixelFormat()  ; Get the pixel format.
 
  If PixelFormat = #PB_PixelFormat_32Bits_RGB

    ; -------- Generating Mapping Texture --------
    
    For x.l = 0 To lTextureSize -1
      For y.l = 0 To lTextureSize -1
        aTexture(x,y) = RGB((x * 256 / lTextureSize) ! (y * 256 / lTextureSize), 0, 0)
      Next
    Next

  Else

    ; -------- Generating Mapping Texture --------
    
    For x.l = 0 To lTextureSize -1
      For y.l = 0 To lTextureSize -1
        aTexture(x,y) = RGB( 0, 0, (x * 256 / lTextureSize) ! (y * 256 / lTextureSize))
      Next
    Next

  EndIf
  StopDrawing()
EndIf

; -------- Generating Distance and Angle Table --------

dDistance.d = 32.0
dParts.d    =  0.5
         
For x = 0 To lScreenWidth*2 -1     
  For y = 0 To lScreenHeight*2 -1
    aDistance(x,y) = Int(dDistance * lTextureSize / Sqr( (x-lScreenWidth) * (x-lScreenWidth) + (y-lScreenHeight) * (y-lScreenHeight) )) % lTextureSize
    dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight, x-lScreenWidth) / #PI)
    aAngle(x,y) = Int (256 - dAngle) & 255
  Next
Next


dSpeedX.d = 2.0
dSpeedY.d = 2.0

time.l  = ElapsedMilliseconds() + 3000
Count.l = 0

Repeat

  ; Counting FPS
  If time < ElapsedMilliseconds()
    SetWindowTitle(0, Str(Count/3) + " FPS")
    Count = 1
    time = ElapsedMilliseconds() + 3000
  Else
    Count + 1
  EndIf
 
 
  ; ------- Stuff for doing the animation -------
 
  dAnimation.d = dAnimation.d + 0.005
 
  lShiftX.l = Int(lTextureSize * dSpeedX.d * dAnimation.d)
  lShiftY.l = Int(lTextureSize * dSpeedY.d * dAnimation.d)

  lLookX = lScreenWidth /2 + Int(lScreenWidth /2 * Sin(dAnimation * 4.0 ))
  lLookY = lScreenHeight/2 + Int(lScreenHeight/2 * Sin(dAnimation * 6.0 ))

  ; -------- Calculate Texture coordinates and draw Tunnel -------

  If StartDrawing(ScreenOutput())
    Buffer      = DrawingBuffer()             ; Get the start address of the screen buffer
    Pitch       = DrawingBufferPitch()        ; Get the length (in byte) took by one horizontal line
    PixelFormat = DrawingBufferPixelFormat()  ; Get the pixel format.

    mx = lLookX + lScreenWidth -1
    my = lLookY + lScreenHeight-1

    y  = lLookY

    Repeat

      *Line.Pixel = Buffer
      x  = lLookX

      Repeat
      
        lCoordinateX.l = (aDistance(x, y) + lShiftX) % lTextureSize     
        lCoordinateY.l = (aAngle   (x, y) + lShiftY) % lTextureSize     

        *Line\Pixel = aTexture (lCoordinateX.l , lCoordinateY.l)
        *Line + 4

        x+1
      Until x>=mx

      Buffer+Pitch
      y+1
    Until y>=my

    StopDrawing()
  EndIf

  FlipBuffers(0)
  If WindowEvent() = #PB_Event_CloseWindow : Break : EndIf
Until GetAsyncKeyState_(#VK_ESCAPE) 
va!n
Addict
Addict
Posts: 1104
Joined: Wed Apr 20, 2005 12:48 pm

Post by va!n »

@djes:
nice you like this effect! and yes i try to code faster versions for own use and possible final productions.

for example i managed to run this tunnel effect in realtime with some extreme and heavy optimizing tricks with more over 300++ fps/s :P (for this extreme fast and heavy optimized version i spend about 6 month of work, so the optimized version(s) will not available for public. I only wanted to show a most readable version using Plot() to show people how they can code such an effect (just the basics ^^)

Btw, very nice fast optimized version you did! *thumbs up*
va!n aka Thorsten

Intel i7-980X Extreme Edition, 12 GB DDR3, Radeon 5870 2GB, Windows7 x64,
dige
Addict
Addict
Posts: 1411
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Post by dige »

@va!n: could you provide a compiled version of your 300% speed imporved version? just to see how PB rocks! :D
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

va!n wrote:@djes:
nice you like this effect! and yes i try to code faster versions for own use and possible final productions.

for example i managed to run this tunnel effect in realtime with some extreme and heavy optimizing tricks with more over 300++ fps/s :P (for this extreme fast and heavy optimized version i spend about 6 month of work, so the optimized version(s) will not available for public.
Amazing! Could be a subject for a paper, some time after the demo has been publicly released, from the first optim to the "non reversible". I'd like to do that, but I haven't been so far on an effect for a long time.
va!n wrote: Btw, very nice fast optimized version you did! *thumbs up*
Thank you, just trying to not push things too far, to be readable. :)
User avatar
dobro
Enthusiast
Enthusiast
Posts: 766
Joined: Sun Oct 31, 2004 10:54 am
Location: France
Contact:

Post by dobro »

why this code dont run with Japbe ?? (error Label ASM)

with purebasic editor , same , but error no same (structure error...)

:shock: :shock: :shock:

Code: Select all


; *************************************************************************************
; *   P r o j e c t :    T u n n e l - F x   
; *************************************************************************************
; *
; *   Part3: Creating Angle Table / Texture:
; *   --------------------------------------
; *   This small example shows you, how to to create the needed angle table and how
; *   does this looks like for doing the rotations of our tunnel. Dont forget to enable
; *   inline assembler. Otherwise as you see, you will see nothing. :D
; *
; *   Source and Tutorial (c) by Thorsten Will aka va!n
; *   All rigths reserved.
; *
; *************************************************************************************

#image=0

Procedure.d ATan2(y.d, x.d)
  !FLD qword[P.v_y]
  !FLD qword[P.v_x]
  !FPATAN
  ProcedureReturn
EndProcedure

; -------- Init Code --------

lTextureSize.l  = 256
lScreenWidth.l  = 640
lScreenHeight.l = 480

CreateImage (0, lScreenWidth, lScreenHeight)
 
; -------- Generating Angle Table / Texture --------

StartDrawing(ImageOutput(#image))
  
  dParts.d = 0.5
  
  For x = 0 To lScreenWidth -1             
    For y = 0 To lScreenHeight -1
      dAngle.d = (dParts * lTextureSize * ATan2(y-lScreenHeight/2, x-lScreenWidth/2) / #PI)
      lColor = Int (256 - dAngle) & 255
      Plot (x, y, RGB(lColor, lColor, lColor) )
    Next
  Next
  
StopDrawing()

; -------- Saving Table as Texture --------

SaveImage (0, "c:\TunnelFx_Angle.bmp")


; *************** ecran minimum **********************************
If InitSprite() = 0 Or InitKeyboard() = 0
  MessageRequester("Error", "Can't open DirectX 7 or later", 0)
  End
EndIf

OpenScreen(1024,768,32,"texture")



Repeat
  
  If StartDrawing(ScreenOutput()) 
      DrawText(10,10,"ok")
      DrawImage(ImageID(#image),100,100,256,256)
    StopDrawing()
  EndIf
  
  ; Inverse the buffers (the back become the front (visible)... And we can do the rendering on the back)
  
  
  FlipBuffers()
  
  ; ClearScreen(RGB(0,0,0)) 
  
  ExamineKeyboard()
Until  KeyboardPushed(#PB_Key_Escape)

; *************************************************************************************

; *************************************************************************************
Image
Windows 98/7/10 - PB 5.42
■ sites : http://michel.dobro.free.fr/
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Runs OK with JaPBe here.

JaPBe 3.8.6.710

There was a problem some time back with JaPBe changing the asm case or something like that, so be sure the ASM terms are formatted properly.

cheers
User avatar
dobro
Enthusiast
Enthusiast
Posts: 766
Joined: Sun Oct 31, 2004 10:54 am
Location: France
Contact:

Post by dobro »

thanks !

the solution is "automatic change of Casse" .. pref of japbe ... :)
Image
Windows 98/7/10 - PB 5.42
■ sites : http://michel.dobro.free.fr/
User avatar
zxtunes.com
Enthusiast
Enthusiast
Posts: 375
Joined: Wed Apr 23, 2008 7:51 am
Location: Saint-Petersburg, Russia
Contact:

Post by zxtunes.com »

13 FPS!!

Slowly, almost as on mine ZX Spectrum.:)
Post Reply