BBC BASIC for Windows
Programming >> Graphics and Games >> Quest for fire library (group effort )
http://bb4w.conforums.com/index.cgi?board=graphics&action=display&num=1513920790

Quest for fire library (group effort )
Post by michael on Dec 22nd, 2017, 04:33am

I invite anyone to come to Richards forum and contribute to the fire library:

I am working on a sample and I would be happy to see your example of a fire animation. (it can be any size, within reason)

Here is a link:

http://bbcbasic.conforums.com/index.cgi?board=graphics&action=display&num=1513881353
Re: Quest for fire library (group effort )
Post by David Williams on Dec 26th, 2017, 2:03pm

A fire effect:

https://pastebin.com/AkjKJGWD

Scroll the page all the way down until you reach the "RAW Paste Data" section, then left-click somewhere inside that box, press CTRL+A to select all code, then CTRL+C to copy the code onto the Windows clipboard, then paste the code inside the BB4W IDE with CTRL+V.

Or do it your own way smiley


David.
--
Re: Quest for fire library (group effort )
Post by David Williams on Dec 26th, 2017, 5:37pm

https://pastebin.com/P2EwQPg2

Re: Quest for fire library (group effort )
Post by michael on Dec 27th, 2017, 12:47am

Very nice fire samples.
Re: Quest for fire library (group effort )
Post by David Williams on Dec 27th, 2017, 2:12pm

on Dec 27th, 2017, 12:47am, michael wrote:
Very nice fire samples.


Thanks. I've now modified the program to be frame-rate independent, so that the animation runs at pretty much the same speed whether on slow or fast computers. It also has lower CPU usage:

https://pastebin.com/kFjw5vab

A further (possible) improvement would be to reduce the size of the fire particles as their height increases.

I'll take the opportunity here to re-post this (relevant!) bit of code by Jan Vibe which I converted to BB4W from the original ARM BBC BASIC:

Code:
      REM > INFERNO
      REM by Jan Vibe
      REM Adapted for BB4W by DW

      REMMODE27:OFF:PROCT
      MODE 19 : OFF : PROCpaletted : PROCT

      FORN%=0TO1280STEP2:GCOLRND(15):PLOT N%,0:NEXT

      FORY%=2TO960STEP2
        FORX%=0TO1280STEP2
          R%=2*(RND(3)-2):C%=ABS(POINT(X%+R%,Y%-2)):IF RND(1)>.8 C%=C%MOD15+1
          GCOLC%:LINE X%,Y%,X%,Y%:PROCT
        NEXT X%
      NEXT Y%

      REPEAT:PROCT:UNTIL0

      DEFPROCT
      IF TIME>T% THEN
        LOCAL F%:S%=S%MOD15+1
        FORN%=1TO15:F%=(S%+N%)MOD15+1:COLOURF%,255,16*N%,0:NEXT
        PROCanimate
        T%=TIME+5
      ENDIF
      ENDPROC

      REM Code from the BB4W Programmer's Reference:

      DEF PROCanimate
      LOCAL C%, pal%()
      DIM pal%(15)
      SYS "GetPaletteEntries", @hpal%, 0, 16, ^pal%(0)
      pal%() AND= &E0F0F0
      FOR C% = 0 TO 15 : SWAP ?^pal%(C%), ?(2+^pal%(C%)) : NEXT
      SYS "SetDIBColorTable", @memhdc%, 0, 16, ^pal%(0)
      SYS "InvalidateRect", @hwnd%, 0, 0
      ENDPROC

      DEF PROCpaletted
      LOCAL bits%, hbm%, oldbm%, bmih{}
      DIM bmih{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, \
      \        Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, \
      \        ClrUsed%, ClrImportant%}
      bmih.Size% = DIM(bmih{})
      bmih.Width% = @vdu%!208
      bmih.Height% = @vdu%!212
      bmih.Planes.l& = 1
      bmih.BitCount.l& = 4
      SYS "CreateDIBSection", @memhdc%, bmih{}, 0, ^bits%, 0, 0 TO hbm%
      IF hbm% = 0 ERROR 100, "Couldn't create DIBSection"
      SYS "SelectObject", @memhdc%, hbm% TO oldbm%
      SYS "DeleteObject", oldbm%
      PROCanimate
      ENDPROC
 



David.
--




Re: Quest for fire library (group effort )
Post by David Williams on Dec 27th, 2017, 8:02pm

This version works with BB4W and BBCSDL (tested under BBCSDL Win32 and Android (ARM)).

https://pastebin.com/dp5EhtfJ

The YouTube video linked-to below shows the program running on a Motorola Moto G4 Plus (Snapdragon 617 ARM-basec SoC):

https://youtu.be/7i8532in0H0


David.
--
Re: Quest for fire library (group effort )
Post by David Williams on Dec 28th, 2017, 06:06am

I've modified my program so that the fire has a broader base (the X co-ordinate of each fire 'particle' is normally distributed via the Box–Muller transform):

https://pastebin.com/BdrggHwz

Two further improvements that could be made:

1) Reduce fire particle size with increasing height
2) Use a better colour table :)
3) Apply a smoothing filter

Anyway, I think I'm done with this fire thing for now. It's been fun.


David.
--


PS. I mentioned Box-Muller transform, here's a quick demo:

Code:
      REM Box-Muller transform demo
      REM See Wikipedia https://tinyurl.com/argkvx3
      MODE 8 : OFF
      ORIGIN 640, 512
      scale = 100
      REPEAT
        u1 = RND(1)
        u2 = RND(1)
        r = SQR(-2 * LNu1)
        a = 2*PI * u2
        x = r * COSa
        y = r * SINa
        PLOT 2*scale*x, 2*scale*y
      UNTIL FALSE
 

Re: Quest for fire library (group effort )
Post by DDRM on Jan 2nd, 2018, 2:05pm

Hi everyone, and a Happy New Year!

I had a play with this. I make no claims that it is any better than previous offerings... but it was fun doing it.

Seems to run OK in BB4W and in BBC_SDL, though rather slower in the latter - you might want to change the WAIT to 5, or even 1.

If you want your flames to go more red at the top, change the "-2*x%" in the green component of the colour definitions in PROCDrawFlame to "-4*x%".

I have thought about converting it to use D3D: then you could blend colours for the triangles, and make them semi-transparent, so you could see the firewood through them. I'm not sure what will happen if I try to rewrite the vertex buffers dynamically - it may cause a spectacular crash... Does anyone know?

Best wishes,

D
Code:
      MODE 21
        *REFRESH OFF
      GCOL 3
      FOR duration%=0 TO 100
        CLS
        PROCDrawLog(180,420,-30,100,320)
        PROCDrawLog(200,370,0,80,340)
        PROCDrawLog(240,330,30,90,320)
        PROCDrawFlame(430,420,210,500)
        PROCDrawFlame(300,400,210,400)
        PROCDrawFlame(500,400,210,400)
        PROCDrawFlame(400,300,210,300)
  
        PROCDrawLog(780,420,-30,30,120)
        PROCDrawLog(800,370,0,25,140)
        PROCDrawLog(840,330,30,30,120)
        PROCDrawFlame(830,420,50,70)
        PROCDrawFlame(800,400,50,70)
        PROCDrawFlame(900,400,50,70)
        PROCDrawFlame(850,350,50,85)
        *REFRESH
        WAIT 10
      NEXT duration%
      *REFRESH ON
      END
      :
      DEFPROCDrawLog(px%,py%,a%,w%,l%)
      COLOUR 3,50,30,0
      MOVE px%,py%
      MOVE px%+w%*COSRAD(a%+90),py%+w%*SINRAD(a%+90)
      PLOT 117,px%+l%*COSRAD(a%)+w%*COSRAD(a%+90),py%+l%*SINRAD(a%)+w%*SINRAD(a%+90)
      ENDPROC
      :
      DEFPROCDrawFlame(px%,py%,w%,h%)
      LOCAL x%,hw%,dw%,dh%,f%()
      DIM f%(26,2)
      hw%=w%/2
      dw%=hw%*0.8
      dh%=h%/25
      FOR x%=0 TO 25
        f%(x%,1)=px%+RND(x%)
        f%(x%,0)=f%(x%,1)-hw%+ABS(dw%-dw%*SQR(x%)/2.5)+RND(x%)*hw%/100
        f%(x%,2)=f%(x%,1)+hw%-ABS(dw%-dw%*SQR(x%)/2.5)-RND(x%)*hw%/100
      NEXT x%
      f%(26,1)=px%+RND(x%)
      f%(26,0)=f%(26,1)
      f%(26,2)=f%(26,1)
      GCOL 3
      FOR x%=0 TO 25
        COLOUR 3,200+x%,150-2*x%,26-x%
        MOVE f%(x%,1),x%*dh%+py%
        MOVE f%(x%+1,1),(x%+1)*dh%+py%
        PLOT 85,f%(x%,0),x%*dh%+py%
        PLOT 85,f%(x%+1,0),(x%+1)*dh% +py%
        MOVE f%(x%,1),x%*dh%+py%
        MOVE f%(x%+1,1),(x%+1)*dh%+py%
        PLOT 85,f%(x%,2),x%*dh%+py%
        PLOT 85,f%(x%+1,2),(x%+1)*dh%+py%
  
        COLOUR 3,220+x%,220-2*x%,70-x%
        MOVE f%(x%,1),x%*dh%+py%
        MOVE f%(x%+1,1),(x%+1)*dh%+py%
        PLOT 85,(f%(x%,0)+f%(x%,1))/2,x%*dh%+py%
        PLOT 85,(f%(x%+1,0)+f%(x%+1,1))/2,(x%+1)*dh% +py%
        MOVE f%(x%,1),x%*dh%+py%
        MOVE f%(x%+1,1),(x%+1)*dh%+py%
        PLOT 85,(f%(x%,2)+f%(x%,1))/2,x%*dh%+py%
        PLOT 85,(f%(x%+1,2)+f%(x%+1,1))/2,(x%+1)*dh% +py%
      NEXT x%
      ENDPROC
 

Re: Quest for fire library (group effort )
Post by David Williams on Jan 3rd, 2018, 02:31am

on Jan 2nd, 2018, 2:05pm, DDRM wrote:
Hi everyone, and a Happy New Year!

I had a play with this. I make no claims that it is any better than previous offerings... but it was fun doing it.


Happy New Year to you, too smiley

I like your approach to the quest for fire - seems like clever use of the PLOT command. Amazingly I wasn't aware that PLOT can also be used to draw parallelograms (which you used for drawing the logs). I really am slow to learn. By the way, those logs were too dark on my screen! I didn't notice them until I actually looked for them after I had glanced at the code. Anyway, that was easy enough to fix.


David.
--
Re: Quest for fire library (group effort )
Post by michael on Jan 3rd, 2018, 05:34am

That was a unique fire demo DDRM. Looks cool. Happy new year to all.
Re: Quest for fire library (group effort )
Post by DDRM on Jan 11th, 2018, 08:48am

Hi Folks,

Well, I did have a go at doing it in D3D. It's proved harder than I hoped to get nice transparency effects - I can't seem to get it to show the logs through the flames, for example, and I don't think I understand emissivity properly! Anyway, I think the Gouraud shading makes the flames look quite nice, and they DO overlap each other...

Here's the code for the "flames" bit. It requires an FVF file for the logs: I'll post code for that separately in a moment.

At the moment this is written specifically for BB4W, and Windows, since it uses a number of D3D SYS calls. I don't know how to do the equivalent in SDL, but I'd be interested to see a conversion! I presume a lot of the stuff with FVF buffers should be fairly straightforward to convert.

Best wishes,

D
Code:
      HIMEM=LOMEM+1E8
      MODE 21

      nbufs%=4
      REM Here I'm actually reserving arrays 1 BIGGER than nbufs% (i.e. 0 - nbufs%), which gives me one for background scene - here, the logs
      DIM l%(nbufs%), b%(nbufs%), n%(nbufs%), f%(nbufs%), s%(nbufs%), m%(nbufs%), t%(nbufs%), y(nbufs%), p(nbufs%), r(nbufs%), X(nbufs%), Y(nbufs%), Z(nbufs%), e(2), a(2)

      INSTALL @lib$+"D3DLIBA"

      ON CLOSE PROCcleanup(nbufs%):QUIT
      ON ERROR PROCcleanup(nbufs%):PRINT REPORT$:END

      d% = FN_initd3d(@hwnd%, 1, 1)
      IF d% = 0 ERROR 100, "Can't initialise Direct3D"

      REM David's attempt to set alpha blending
      SYS !(!d%+200),d%,27,1:REM SetRenderState(Alphablendenable)
      SYS !(!d%+200),d%,19,5:REM SetRenderState( set source blend to D3DBLEND_SRCALPHA)
      SYS !(!d%+200),d%,20,6:REM SetRenderState(set destination blend to D3DBLEND_INVSRCALPHA)

      SYS !(!d%+200),d%,148,1:REM SetRenderState(set emissive materialcoloursource to "diffuse vertex colour")


      DIM l%(0) 103
      l%(0)!0 = 3 : REM directional light, taken from manual (parameters adjusted)
      l%(0)!4 = FN_f4(1)  : REM red component
      l%(0)!8 = FN_f4(1)  : REM green component
      l%(0)!12 = FN_f4(1) : REM blue component
      l%(0)!64 = FN_f4(0.5) : REM. X component of direction
      l%(0)!68 = FN_f4(-0.50) : REM. Y component of direction
      l%(0)!72 = FN_f4(0.2) : REM. Z component of direction

      nv%=26*12 :REM number of layers * 4 triangles (2 on each side between this layer and the next)
      REM Set up vertex buffers for each flame
      FOR x%=0 TO nbufs%-1
        n%(x%)=nv%
        f%(x%)=&52
        s%(x%)=28
        X(x%)=(x%-0.8)/2
        Y(x%)=(x% MOD 2)*0.5
        b%(x%)=FN_setupVbuf(d%,n%(x%),f%(x%),s%(x%))
        IF b%(x%)=0 THEN PROCcleanup(nbufs%):PRINT x%,"Disaster!":END
      NEXT x%
      REM Set up vertex buffer by loading the background "scene" - here it's just 3 logs!
      b%(nbufs%)=FN_load3d(d%,"logs.fvf",n%(nbufs%),f%(nbufs%),s%(nbufs%))
      IF b%(nbufs%)=0 THEN PROCcleanup(nbufs%):PRINT "Logs failed to load!":END
      X(nbufs%)=-0.5
      Z(nbufs%)=0.8

      p() = 0    :REM No pan/roll/yaw - could be omitted!
      r() = 0
      y()=0
      e() = 0,0,-10  :REM Eye/camera position
      a() = 0, 1, 0  :REM Point you are looking at (centre of screen)


      FOR duration%=0 TO 100
        FOR x%=0 TO nbufs%-1
          PROCWriteVBufData(b%(x%),n%(x%),s%(x%),1,2)   :REM Re-write the actual vertex data to make flames flicker
        NEXT x%
        PROC_render(d%, &FF101010, 1, l%(), nbufs%+1, m%(), t%(), b%(), n%(), f%(), s%(), y(), p(), r(), X(), Y(), Z(), e(), a(), PI/4, 5/4, 1, 1000,0)
        WAIT 10
      NEXT duration%
      PROCcleanup(nbufs%)
      END
      :
      DEF FN_setupVbuf(D%,N%,V%,L%)  :REM Adapted from D3DLib (as some of the other D3D stuff!)
      LOCAL B%,R%
      SYS!(!D%+92),D%,N%*L%,0,V%,0,^B% TO R%:REM CreateVertexBuffer
      IF R% THEN=0
      =B%
      :
      DEFPROCWriteVBufData(B%,N%,L%,w,h)
      LOCAL P%,vb%
      LOCAL x%,n%,n2%,c1%,c2%,dc%,hn,hw,dw,f()
      n%=5  :REM you can change this to change the shape/detail of the flame - there will be n%^2 layers.
      REM If you make n% more than 5, you may need to increase the memory reserved at the beginning, or it will crash!
      n2%=n%^2
      hn=n%/2
      DIM f(n2%+1,2)
      hw=w/2
      dw=hw*0.8
      FOR x%=0 TO n2%
        f(x%,1)=RND(x%)/(4*n2%)
        f(x%,0)=f(x%,1)-hw+ABS(dw-dw*SQR(x%)/hn)+RND(x%)*hw/(8*n2%)
        f(x%,2)=f(x%,1)+hw-ABS(dw-dw*SQR(x%)/hn)-RND(x%)*hw/(8*n2%)
      NEXT x%
      f(n2%+1,1)=RND(n2%)/(2*n2%)
      f(n2%+1,0)=f(n2%+1,1)
      f(n2%+1,2)=f(n2%+1,1)

      SYS!(!B%+44),B%,0,N%*L%,^P%,0:REM pVB::Lock
      vb%=P%  :REM vb% is a pointer to where we have got to in the buffer
      dc%=(1<<16)-(2<<8)-1 :REM calculate difference in colour for one level higher
      FOR x%=0 TO n2%
        REM Calculate colours for edge and centre of the flame at this level
        c1%=((160+x%)<<16)+((130-2*x%)<8)+26-x% +(&80<<24)
        c2%=((190+x%)<<16)+((158-x%)<<8)+55-x%  +(&80<<24)
        REM write data for the trapezium between this level and the next
        REM 4 triangles, two from the left edge to the centre, and two from the right edge to the centre
        PROCDoPoint(f(x%,0),x%*h/(n2%+1),0,c1%,vb%)
        PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
        PROCDoPoint(f(x%,1),x%*h/(n2%+1),0,c2%,vb%)
  
        PROCDoPoint(f(x%,0),x%*h/(n2%+1),0,c1%,vb%)
        PROCDoPoint(f(x%+1,0),(x%+1)*h/(n2%+1),0,c1%-dc%,vb%)
        PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%,vb%)
  
        PROCDoPoint(f(x%,2),x%*h/(n2%+1),0,c1%,vb%)
        PROCDoPoint(f(x%,1),x%*h/(n2%+1),0,c2%,vb%)
        PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
  
        PROCDoPoint(f(x%,2),x%*h/(n2%+1),0,c1%,vb%)
        PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
        PROCDoPoint(f(x%+1,2),(x%+1)*h/(n2%+1),0,c1%-dc%,vb%)
      NEXT x%
      SYS!(!B%+48),B%:REM pVB::Unlock
      ENDPROC
      :
      DEFPROCDoPoint(x,y,z,c%, RETURN vb%)
      REM Here are the coordinates of the point
      !vb%=FN_f4(x)
      vb%!4=FN_f4(y)
      vb%!8=FN_f4(z)
      REM Now need normals!  Assume all points are flat in the plane,so normal faces towards Z
      vb%!12=FN_f4(0.0)
      vb%!16=FN_f4(0.0)
      vb%!20=FN_f4(1.0)
      REM Now the colour
      vb%!24=c%
      vb%+=28  :REM Update pointer. I do it here so it's easy to change if you change the vertex size
      ENDPROC
      :
      DEF PROCcleanup(nbufs%) : REM From example, slightly amended to allow for multiple buffers
      LOCAL x%
      FOR x%=0 TO nbufs%
        t%(nbufs%) += 0:IF t%(nbufs%) PROC_release(t%(nbufs%))
        b%(nbufs%) += 0:IF b%(nbufs%) PROC_release(b%(nbufs%))
        b%(nbufs%) += 0:IF b%(nbufs%) PROC_release(b%(nbufs%))
      NEXT x%
      d% += 0   :IF d%    PROC_release(d%)
      ENDPROC
 

Re: Quest for fire library (group effort )
Post by DDRM on Jan 11th, 2018, 09:05am

Here's the code for the logs: you'll need my Make3DLib, which I actually keep in my D3D folder: amend the INSTALL address accordingly.

Make3DLib is available in the files area on the groups.io site: if you give me a couple of minutes I'll update the version there to make sure it is the current one.

Best wishes,

D
Code:
      INSTALL "Make3DLib"
      nf%=6

      maxverts%=FNGetNumVerts("Cylinder",nf%,1,nf%,TRUE)
      DIM v(maxverts%-1,2),n(maxverts%-1,2),t(maxverts%-1,1)
      nf%=6
      totalverts%=3*FNGetNumVerts("Cylinder",nf%,1,nf%,TRUE)
      vf%=&52
      f%=FNOpenFVF("logs",totalverts%,vf%)

      nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
      PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
      PROCStretch(nv%,v(),2,0.3,0.3)
      PROCFindNormals(nv%,v(),n())
      PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

      nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
      PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
      PROCStretch(nv%,v(),2,0.3,0.3)
      PROCFindNormals(nv%,v(),n())
      PROCRotate(v(),0,0,PI/8)
      PROCRotate(n(),0,0,PI/8)
      PROCShift(v(),0.1,0.5,-0.5,nv%)
      PROCShift(n(),0.1,0.5,-0.5,nv%)
      PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

      nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
      PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
      PROCStretch(nv%,v(),2,0.3,0.3)
      PROCFindNormals(nv%,v(),n())
      PROCRotate(v(),0,-PI/8,-PI/8)
      PROCRotate(n(),0,-PI/8,-PI/8)
      PROCShift(v(),0.1,-0.5,0.5,nv%)
      PROCShift(n(),0.1,-0.5,0.5,nv%)
      PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

      PROCCloseFVF(f%)
      END
 

Re: Quest for fire library (group effort )
Post by DDRM on Jan 12th, 2018, 08:40am

OK, I've sorted out a couple of things. It turns out the order you render the buffers is critical: if you want things to show through, they need to have been rendered first - at least, when I put the logs in buffer 0 they show through, rather than being hidden. If you change Z(0)=0.8 to Z(0)=0.5, you'll be able to see the flames interleaved with the logs, since the front log is now closer than the flames.

I've also amended the code so that the flames become more transparent (less opaque) as they get higher. I don't know if you like it, but it demonstrates a technique...

I spotted that a problem I'd had earlier was not due to memory within BB4W, but the way I reserved buffer space, so I've removed the HIMEM change at the beginning.

Best wishes,

D
Code:
MODE 21

nbufs%=4
REM Here I'm actually reserving arrays 1 BIGGER than nbufs% (i.e. 0 - nbufs%), which gives me one for background scene - here, the logs
DIM l%(0), b%(nbufs%), n%(nbufs%), f%(nbufs%), s%(nbufs%), m%(nbufs%), t%(nbufs%), y(nbufs%), p(nbufs%), r(nbufs%), X(nbufs%), Y(nbufs%), Z(nbufs%), e(2), a(2)

INSTALL @lib$+"D3DLIBA"

ON CLOSE PROCcleanup(nbufs%):QUIT
ON ERROR PROCcleanup(nbufs%):PRINT REPORT$:END

d% = FN_initd3d(@hwnd%, 1, 1)
IF d% = 0 ERROR 100, "Can't initialise Direct3D"

REM Set alpha blending
SYS !(!d%+200),d%,27,1:REM SetRenderState(Alphablendenable)
SYS !(!d%+200),d%,19,5:REM SetRenderState( set source blend to D3DBLEND_SRCALPHA)
SYS !(!d%+200),d%,20,6:REM SetRenderState(set destination blend to D3DBLEND_INVSRCALPHA)
SYS !(!d%+200),d%,148,1:REM SetRenderState(set emissive materialcoloursource to "diffuse vertex colour")

DIM l%(0) 103
l%(0)!0 = 3 : REM directional light, taken from manual (parameters adjusted)
l%(0)!4 = FN_f4(1)  : REM red component
l%(0)!8 = FN_f4(1)  : REM green component
l%(0)!12 = FN_f4(1) : REM blue component
l%(0)!64 = FN_f4(0.5) : REM. X component of direction
l%(0)!68 = FN_f4(-0.50) : REM. Y component of direction
l%(0)!72 = FN_f4(0.2) : REM. Z component of direction

nv%=26*12 :REM number of layers * 4 triangles (2 on each side between this layer and the next). Amend if you change the n% in PROCWriteVBufData!

REM Set up vertex buffer by loading the background "scene" - here it's just 3 logs!
b%(0)=FN_load3d(d%,"logs.fvf",n%(0),f%(0),s%(0))
IF b%(0)=0 THEN PROCcleanup(nbufs%):PRINT "Logs failed to load!":END
X(0)=-0.2
Y(0)=0.3
Z(0)=0.8

REM Set up vertex buffers for each flame
FOR x%=1 TO nbufs%
  n%(x%)=nv%
  f%(x%)=&52
  s%(x%)=28
  X(x%)=(x%-0.8)/2
  Y(x%)=(x% MOD 2)*0.5
  b%(x%)=FN_setupVbuf(d%,n%(x%),f%(x%),s%(x%))
  IF b%(x%)=0 THEN PROCcleanup(nbufs%):PRINT x%,"Disaster!":END
NEXT x%

e() = 0,0,-10  :REM Eye/camera position
a() = 0, 1, 0  :REM Point you are looking at (centre of screen)


FOR duration%=0 TO 100
  FOR x%=1 TO nbufs%
    PROCWriteVBufData(b%(x%),n%(x%),s%(x%),1,2)   :REM Re-write the actual vertex data to make flames flicker
  NEXT x%
  PROC_render(d%, &FF101010, 1, l%(), nbufs%+1, m%(), t%(), b%(), n%(), f%(), s%(), y(), p(), r(), X(), Y(), Z(), e(), a(), PI/4, 5/4, 1, 1000,0)
  WAIT 10
NEXT duration%
PROCcleanup(nbufs%)
END
:
DEF FN_setupVbuf(D%,N%,V%,L%)  :REM Adapted from D3DLib (as some of the other D3D stuff!)
LOCAL B%,R%
SYS!(!D%+92),D%,N%*L%,0,V%,0,^B% TO R%:REM CreateVertexBuffer
IF R% THEN=0
=B%
:
DEFPROCWriteVBufData(B%,N%,L%,w,h)
LOCAL P%,vb%
LOCAL x%,n%,n2%,c1%,c2%,dc%,hn,hw,dw,f()
n%=5  :REM you can change this to change the shape/detail of the flame - there will be n%^2 layers.
REM If you make n% more than 5, you will need to increase the space allocated for the buffers, or it will crash!
REM Now  calculate coordinates for flame
n2%=n%^2
hn=n%/2
DIM f(n2%+1,2)
hw=w/2
dw=hw*0.8
FOR x%=0 TO n2%
  f(x%,1)=RND(x%)/(4*n2%)
  f(x%,0)=f(x%,1)-hw+ABS(dw-dw*SQR(x%)/hn)+RND(x%)*hw/(8*n2%)
  f(x%,2)=f(x%,1)+hw-ABS(dw-dw*SQR(x%)/hn)-RND(x%)*hw/(8*n2%)
NEXT x%
f(n2%+1,1)=RND(n2%)/(2*n2%)
f(n2%+1,0)=f(n2%+1,1)
f(n2%+1,2)=f(n2%+1,1)
REM Now write it into the buffer
SYS!(!B%+44),B%,0,N%*L%,^P%,0:REM pVB::Lock
vb%=P%  :REM vb% is a pointer to where we have got to in the buffer
dc%=(1<<16)-(2<<8)-1 :REM calculate difference in colour for one level higher
FOR x%=0 TO n2%
  REM Calculate colours for edge and centre of the flame at this level
  c1%=((160+x%)<<16)+((130-2*x%)<8)+26-x% +((&80-x%*5)<<24)  :REM Last section controls opacity
  c2%=((190+x%)<<16)+((158-x%)<<8)+55-x%  +((&80-x%*5)<<24)  :REM Change the "x%*5" to reduce the fade-out of flames
  REM write data for the trapezium between this level and the next
  REM 4 triangles, two from the left edge to the centre, and two from the right edge to the centre
  PROCDoPoint(f(x%,0),x%*h/(n2%+1),0,c1%,vb%)
  PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
  PROCDoPoint(f(x%,1),x%*h/(n2%+1),0,c2%,vb%)
  
  PROCDoPoint(f(x%,0),x%*h/(n2%+1),0,c1%,vb%)
  PROCDoPoint(f(x%+1,0),(x%+1)*h/(n2%+1),0,c1%-dc%,vb%)
  PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%,vb%)
  
  PROCDoPoint(f(x%,2),x%*h/(n2%+1),0,c1%,vb%)
  PROCDoPoint(f(x%,1),x%*h/(n2%+1),0,c2%,vb%)
  PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
  
  PROCDoPoint(f(x%,2),x%*h/(n2%+1),0,c1%,vb%)
  PROCDoPoint(f(x%+1,1),(x%+1)*h/(n2%+1),0,c2%-dc%,vb%)
  PROCDoPoint(f(x%+1,2),(x%+1)*h/(n2%+1),0,c1%-dc%,vb%)
NEXT x%
SYS!(!B%+48),B%:REM pVB::Unlock
ENDPROC
:
DEFPROCDoPoint(x,y,z,c%, RETURN vb%)
REM Here are the coordinates of the point
!vb%=FN_f4(x)
vb%!4=FN_f4(y)
vb%!8=FN_f4(z)
REM Add normals. Assume all points are flat in the plane,so normal faces towards Z
vb%!12=FN_f4(0.0)
vb%!16=FN_f4(0.0)
vb%!20=FN_f4(1.0)
REM Now the colour
vb%!24=c%
vb%+=28  :REM Update pointer. I do it here so it's easy to change if you change the vertex size
ENDPROC
:
DEF PROCcleanup(nbufs%) : REM From example, slightly amended to allow for multiple buffers
LOCAL x%
FOR x%=0 TO nbufs%
  t%(nbufs%) += 0:IF t%(nbufs%) PROC_release(t%(nbufs%))
  b%(nbufs%) += 0:IF b%(nbufs%) PROC_release(b%(nbufs%))
  b%(nbufs%) += 0:IF b%(nbufs%) PROC_release(b%(nbufs%))
NEXT x%
d% += 0   :IF d%    PROC_release(d%)
ENDPROC
 

Re: Quest for fire library (group effort )
Post by DDRM on Jan 12th, 2018, 08:59am

...and here's (an improved) version of the Logs program that doesn't need Make3dLib (the relevant sections are included).

Best wishes,

D
Code:
REM Version of Logs with routines from Make3dLib included
nf%=8
maxverts%=FNGetNumVerts("Cylinder",nf%,1,nf%,TRUE)
DIM v(maxverts%-1,2),n(maxverts%-1,2),t(maxverts%-1,1)
totalverts%=3*FNGetNumVerts("Cylinder",nf%,1,nf%,TRUE)
vf%=&52
f%=FNOpenFVF("logs",totalverts%,vf%)

nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
PROCStretch(nv%,v(),2,0.3,0.3)
PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
PROCStretch(nv%,v(),2,0.3,0.3)
PROCRotate(v(),0,0,PI/8)
PROCRotate(n(),0,0,PI/8)
PROCShift(v(),0.1,0.5,-0.5,nv%)
PROCShift(n(),0.1,0.5,-0.5,nv%)
PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

nv%=FNGetNumVerts("cylinder",nf%,1,nf%,TRUE)
PROCMake3D_Cylinder(nf%,v(),n(),t(),1,nf%,TRUE,0.0,1.0,0.0,1.0)
PROCStretch(nv%,v(),2,0.3,0.3)
PROCRotate(v(),0,-PI/8,-PI/8)
PROCRotate(n(),0,-PI/8,-PI/8)
PROCShift(v(),0.1,-0.5,0.5,nv%)
PROCShift(n(),0.1,-0.5,0.5,nv%)
PROCExtendFVF(f%,vf%,nv%,v(),n(),t(),&FF403510,&FFF0F0F0)

PROCCloseFVF(f%)
END
:
DEFFNGetNumVerts(n$,nf%,startface%,endface%,capped%)
LOCAL nv%,vpf%
PROCUpcase(n$)
CASE n$ OF
  WHEN "CYLINDER","PRISM","TRUNCCONE":
    IF capped% THEN vpf%=12 ELSE vpf%=6
    nv%=vpf%*(endface%-startface%+1)
  WHEN "SPHERE":
    nv%=(endface%-startface%+1)*2*nf% *6
    IF startface%=1 THEN nv%-=nf%*6
    IF endface%=nf% THEN nv%-=nf%*6
  WHEN "CONE":
    IF capped% THEN vpf%=6 ELSE vpf%=3
    nv%=vpf%*(endface%-startface%+1)
ENDCASE
=nv%
:
DEFPROCMake3D_Cylinder(nf%,v(),vn(),t(),startface%,endface%,capped%,texminu,texmaxu,texminv,texmaxv):Make3dLibNormals%=TRUE
LOCAL r,r2
r=1
r2=1
LOCAL vpf%,nv%,l,tdu,tdv,a,pa,py,pz,py2,pz2,ey,ez,ey2,ez2,ny,nz,ny2,nz2,eny,enz,eny2,enz2
IF capped% THEN vpf%=12 ELSE vpf%=6
nv%=vpf%*(endface%-startface%+1)
l=1
tdu=texmaxu-texminu
tdv=texmaxv-texminv
FOR a=0 TO endface%-startface%
  pa=a+startface%-1
  py=r*SIN(pa*2*PI/nf%)
  pz=r*COS(pa*2*PI/nf%)
  py2=r*SIN((pa+1)*2*PI/nf%)
  pz2=r*COS((pa+1)*2*PI/nf%)
  ey=r2*SIN(pa*2*PI/nf%)
  ez=r2*COS(pa*2*PI/nf%)
  ey2=r2*SIN((pa+1)*2*PI/nf%)
  ez2=r2*COS((pa+1)*2*PI/nf%)
  v(a*vpf%,0)=0
  v(a*vpf%,1)=py
  v(a*vpf%,2)=pz
  v(a*vpf%+1,0)=l
  v(a*vpf%+1,1)=ey2
  v(a*vpf%+1,2)=ez2
  v(a*vpf%+2,0)=l
  v(a*vpf%+2,1)=ey
  v(a*vpf%+2,2)=ez
  v(a*vpf%+3,0)=0
  v(a*vpf%+3,1)=py
  v(a*vpf%+3,2)=pz
  v(a*vpf%+4,0)=0
  v(a*vpf%+4,1)=py2
  v(a*vpf%+4,2)=pz2
  v(a*vpf%+5,0)=l
  v(a*vpf%+5,1)=ey2
  v(a*vpf%+5,2)=ez2
  IF Make3dLibNormals% THEN
    ny=(r+1)*SIN(pa*2*PI/nf%)
    nz=(r+1)*COS(pa*2*PI/nf%)
    ny2=(r+1)*SIN((pa+1)*2*PI/nf%)
    nz2=(r+1)*COS((pa+1)*2*PI/nf%)
    eny=(r2+1)*SIN(pa*2*PI/nf%)
    enz=(r2+1)*COS(pa*2*PI/nf%)
    eny2=(r2+1)*SIN((pa+1)*2*PI/nf%)
    enz2=(r2+1)*COS((pa+1)*2*PI/nf%)
    vn(a*vpf%,0)=0
    vn(a*vpf%,1)=ny
    vn(a*vpf%,2)=nz
    vn(a*vpf%+1,0)=l
    vn(a*vpf%+1,1)=eny2
    vn(a*vpf%+1,2)=enz2
    vn(a*vpf%+2,0)=l
    vn(a*vpf%+2,1)=eny
    vn(a*vpf%+2,2)=enz
    vn(a*vpf%+3,0)=0
    vn(a*vpf%+3,1)=ny
    vn(a*vpf%+3,2)=nz
    vn(a*vpf%+4,0)=0
    vn(a*vpf%+4,1)=ny2
    vn(a*vpf%+4,2)=nz2
    vn(a*vpf%+5,0)=l
    vn(a*vpf%+5,1)=eny2
    vn(a*vpf%+5,2)=enz2
  ENDIF
  t(a*vpf%,0)=texminu
  t(a*vpf%,1)=texminv+tdv*pa/nf%
  
  t(a*vpf%+1,0)=texmaxu
  t(a*vpf%+1,1)=texminv+tdv*(pa+1)/nf%
  
  t(a*vpf%+2,0)=texmaxu
  t(a*vpf%+2,1)=texminv+tdv*pa/nf%
  
  t(a*vpf%+3,0)=texminu
  t(a*vpf%+3,1)=texminv+tdv*pa/nf%
  
  t(a*vpf%+4,0)=texminu
  t(a*vpf%+4,1)=texminv+tdv*(pa+1)/nf%
  
  t(a*vpf%+5,0)=texmaxu
  t(a*vpf%+5,1)=texminv+tdv*(pa+1)/nf%
  IF capped% THEN
    v(a*vpf%+6,0)=0
    v(a*vpf%+6,1)=py
    v(a*vpf%+6,2)=pz
    v(a*vpf%+7,0)=0
    v(a*vpf%+7,1)=0
    v(a*vpf%+7,2)=0
    v(a*vpf%+8,0)=0
    v(a*vpf%+8,1)=py2
    v(a*vpf%+8,2)=pz2
    v(a*vpf%+9,0)=l
    v(a*vpf%+9,1)=ey
    v(a*vpf%+9,2)=ez
    v(a*vpf%+10,0)=l
    v(a*vpf%+10,1)=0
    v(a*vpf%+10,2)=0
    v(a*vpf%+11,0)=l
    v(a*vpf%+11,1)=ey2
    v(a*vpf%+11,2)=ez2
    IF Make3dLibNormals% THEN
vn(a*vpf%+6,0)=-10
vn(a*vpf%+6,1)=py
vn(a*vpf%+6,2)=pz
vn(a*vpf%+7,0)=-10
vn(a*vpf%+7,1)=0
vn(a*vpf%+7,2)=0
vn(a*vpf%+8,0)=-10
vn(a*vpf%+8,1)=py2
vn(a*vpf%+8,2)=pz2
vn(a*vpf%+9,0)=l+10
vn(a*vpf%+9,1)=ey2
vn(a*vpf%+9,2)=ez2
vn(a*vpf%+10,0)=l+10
vn(a*vpf%+10,1)=0
vn(a*vpf%+10,2)=0
vn(a*vpf%+11,0)=l+10
vn(a*vpf%+11,1)=ey2
vn(a*vpf%+11,2)=ez2
    ENDIF
    t(a*vpf%+6,0)=texminu
    t(a*vpf%+6,1)=texminv+tdv*a/nf%
    t(a*vpf%+7,0)=texminu+tdu/2
    t(a*vpf%+7,1)=texminv+tdv/2
    t(a*vpf%+8,0)=texminu
    t(a*vpf%+8,1)=texminv+tdv*(a+1)/nf%
    t(a*vpf%+9,0)=texmaxu
    t(a*vpf%+9,1)=texminv+tdv*a/nf%
    t(a*vpf%+10,0)=texminu+tdu/2
    t(a*vpf%+10,1)=texminv+tdv/2
    t(a*vpf%+11,0)=texmaxu
    t(a*vpf%+11,1)=texminv+tdv*(a+1)/nf%
  ENDIF
NEXT a
ENDPROC
:
DEFPROCRotate(a(),xa,ya,za)
LOCAL xrm(),yrm(),zrm()
DIM xrm(2,2),yrm(2,2),zrm(2,2)
xrm()=1,0,0,0,COS(xa),-SIN(xa),0,SIN(xa),COS(xa)
yrm()=COS(ya),0,SIN(ya),0,1,0,-SIN(ya),0,COS(ya)
zrm()=COS(za),-SIN(za),0,SIN(za),COS(za),0,0,0,1
xrm()=xrm().yrm()
xrm()=xrm().zrm()
a()=a().xrm()
ENDPROC
:
DEFPROCShift(a(),dx,dy,dz,nv%)
LOCAL x%
FOR x%=0 TO nv%-1
  a(x%,0)+=dx
  a(x%,1)+=dy
  a(x%,2)+=dz
NEXT x%
ENDPROC
:
DEFPROCStretch(nv%,v(),xf,yf,zf)
LOCAL x%
FOR x%=0 TO nv%-1
  v(x%,0)*=xf
  v(x%,1)*=yf
  v(x%,2)*=zf
NEXT x%
ENDPROC
:
DEFFNOpenFVF(name$,nv%,vf%)
LOCAL f%,vs%
vs%=0
IF vf% AND 2 THEN vs%+=12
IF vf% AND &10 THEN vs%+=12
IF vf% AND &40 THEN vs%+=4
IF vf% AND &80 THEN vs%+=4
IF vf% AND &100 THEN vs%+=8
REM Create file and fill it with data
f%=OPENOUT(@dir$+name$+".FVF")
PROC4(f%,nv%)
PROC4(f%,(vs%<<16)+vf%)
=f%
:
DEFPROCCloseFVF(f%)
CLOSE #f%
ENDPROC
:
DEFPROCExtendFVF(f%,vf%,nv%,vdat(),ndat(),tdat(),acol%,scol%)
LOCAL x%
FOR x%=0 TO nv%-1
  IF vf% AND 2 THEN PROC4(f%,FN_f4(vdat(x%,0))):PROC4(f%,FN_f4(vdat(x%,1))):PROC4(f%,FN_f4(vdat(x%,2)))
  IF vf% AND &10 THEN PROC4(f%,FN_f4(ndat(x%,0))):PROC4(f%,FN_f4(ndat(x%,1))):PROC4(f%,FN_f4(ndat(x%,2)))
  IF vf% AND &40 THEN PROC4(f%,acol%)
  IF vf% AND &80 THEN PROC4(f%,scol%)
  IF vf% AND &100 THEN PROC4(f%,FN_f4(tdat(x%,0))):PROC4(f%,FN_f4(tdat(x%,1)))
NEXT x%
ENDPROC
:
DEFPROCUpcase(RETURN n$)
LOCAL x%
FOR x%=1 TO LEN(n$)
  IF ASC(MID$(n$,x%,1))>96 AND ASC(MID$(n$,x%,1))<123 THEN n$=LEFT$(n$,x%-1)+CHR$(ASC(MID$(n$,x%,1))-32)+MID$(n$,x%+1)
NEXT x%
ENDPROC
:
REM Stolen from Richard's D3D demo
DEF PROC4(F%, A%)
BPUT#F%,A% : BPUT#F%,A%>>8 : BPUT#F%,A%>>16 : BPUT#F%,A%>>24
ENDPROC
:
REM Stolen from Richard's D3D library
DEF FN_f4(A#)
LOCAL A%,P%,U#
PRIVATE F%
IF F%=0 THEN
  DIM P% 10
  [OPT 2
  .F%
  mov esi,[ebp+2]:mov edi,[ebp+7]
  fld qword [esi]:fstp dword [edi]
  ret
  ]
ENDIF
!(^U#+4)=&3FF00000
A#*=U#
CALL F%,A#,A%
=A%