Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
You are not logged in 
CSDb User Forums

Forums > CSDb Entries > Release id #185647 : Gumbo Revised
2020-02-07 22:54

Registered: Aug 2003
Posts: 10
Release id #185647 : Gumbo Revised

Dear all

I have previously written about how the heat-bobs works:
Release id #179166 : Gumbo

And (indirectly) about how the raster scroller works:
No More Grey Dots
(Look for the note I wrote - I'll condense it and post it here instead, when I find the time).

And I thought it would now be in place to write a bit about how the Fire Effect in Gumbo works (especially since it is featured a bit more prominently in Gumbo Revised).

Note: This piece is intentionally written to be more accessible to all levels of coders - so it contains quite a bit of introductory explanation.

In many ways, the fire effect is not rocket science (and it's certainly not the most optimized effect in the demo), but it does have a couple of tricks up its sleeve that I think makes it look more interesting:

1) The side-to-side wave pattern
2) Its non-linear transformation of heat-values to colors (ECM based)

The core mechanics of the fire effect is based on the following simple principle for propagating the fire:
cell[x,y] = ((cell[x,y]+cell[x-1,y+1]+cell[x,y+1]+cell[x+1,y+1])-cooling[x,y]) / 4

(Coordinate system as a typical C64 char screen)

In essence, the new value of a given cell is calculated as 1) the mean of itself + the three cells below and 2) a local cooling factor (think blobs of hotter/colder areas) is subtracted to give a more organic feel to the flames. This blends the heat of the cells, cools it a bit and moves the heat up one line.

The loop runs from top to bottom, and new flames are seeded by heating the line below the visible matrix with semi-random values (cooler towards the sides, and one effect have a small cool zone in the middle). If no heating is applied the fire will die out by itself, as the happens when the demo cycles between effects.

Looking at the simple equation above it's quite natural to work with 6-bit heat values, as adding four of those will never overflow.

Ignoring the cooling-zones, an implementation could look like this:
	clc // Just once - we'll never overflow

	lda matrix+(xpos)+(ypos*40)
	adc matrix+(xpos-1)+((ypos+1)*40)
	adc matrix+(xpos+0)+((ypos+1)*40)
	adc matrix+(xpos+1)+((ypos+1)*40)
	sta matrix+(xpos)+(ypos*40)

	(roll out code for all matrix cells)

Next we'd want to address the cooling zones, but we do not want to deal with the hassle of handling underflows, and setting/clearing carry. This is simply handled by generating a number of page-long tables, that both takes care of dividing by four and subtracting a value (and handling underflows). I use 4 such tables in the demo:

subtab0: sub0 followed by div4
subtab1: sub1 followed by div4
subtab2: sub2 followed by div4
subtab3: sub3 followed by div4

The first 16 bytes of each table look like this:
subtab0: $00,$00,$00,$00,$01,$01,$01,$01,$02,$02,$02,$02,$03,$03,$03,$03 ...
subtab1: $00,$00,$00,$00,$00,$01,$01,$01,$01,$02,$02,$02,$02,$03,$03,$03 ...
subtab2: $00,$00,$00,$00,$00,$00,$01,$01,$01,$01,$02,$02,$02,$02,$03,$03 ...
subtab3: $00,$00,$00,$00,$00,$00,$00,$01,$01,$01,$01,$02,$02,$02,$02,$03 ...

In the code that rolls out the speedcode, it's easy to look up the values in the cooling map (between 0 and 3) and select the right table to use. That allow us to have code that looks something like this:
	lda matrix+(xpos)+(ypos*40)
	adc matrix+(xpos-1)+((ypos+1)*40)
	adc matrix+(xpos+0)+((ypos+1)*40)
	adc matrix+(xpos+1)+((ypos+1)*40)
	lda subtabXX,y         // Will point to subtab0,subtab1,subtab2 or subtab3
	sta matrix+(xpos)+(ypos*40)

This could more or less have been it - wrap it up, and make a charset that goes from 00-63 and call it a day, but I decided to do a slightly more complicated (and somewhat slower) version, I though looked more interesting:

Wave pattern:
Frankly, this is a total hack of the core algorithm, but I think it looks pretty neat. The idea is to dynamically vary which of the cells below gets evaluated in the calculating the mean on a line-by-line basis.

This is what the normal case looks like:

Shifting to the right (uppercase X = double sampled):

Shifting to the left (uppercase X = double sampled):

The code now looks something like this:
	ldx addpatt+linenum    // Once per line (x <- 0 or 1)


	lda matrix+(xpos)+(ypos*40)
	adc matrix+(xpos-1)+((ypos+1)*40),x
	adc matrix+(xpos+0)+((ypos+1)*40)
	adc matrix+(xpos+0)+((ypos+1)*40),x
	lda subtabXX,y         // Will point to subtab0,subtab1,subtab2 or subtab3
	sta matrix+(xpos)+(ypos*40)

Note: Yes, I could have saved an ADC (was kept in since the code can also be generated to address the general case - I have a flag that will swap between modes, when the code is rolled out).

The actual wave-pattern looks like this:
	.byte 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1

And the pattern is then gradually cycled to the right, giving an upward motion of the waves.

I use a high-res (single color) dithered charset between 00-63, which allows the final 2 bits to be used to select ECM colors. My original plan was to have a simple linear transformation of heat-values into a color ramp covering a lot of colors:
// Color ramps

// bk :  07
// ec1:  08
// ec2:  02
// ec3:  0b

// CHAR COL ; BK COL ; bit patt ; direction
// -----------------------------------------
// 01       ; 07     ; 00       ; 00-3f
// 0a       ; 07     ; 00       ; 3f-00
// 0a       ; 08     ; 01       ; 00-3f
// 04       ; 08     ; 01       ; 3f-00
// 04       ; 02     ; 10       ; 00-3f
// 09       ; 02     ; 10       ; 3f-00
// 09       ; 0b     ; 11       ; 00-3f
// 00       ; 0b     ; 11       ; 3f-00

However, that simply turned out not to look that great. The result looked waaaaay too blocky, and had a "flat" feeling to it. It was also clear from my experimentation, that while gradual transition between colors looked good in the hot part of the flames, it needed to transition much faster in the "almost cold" parts in order to give the flames a well-defined outline. Also, the first few steps for the "hot" colors were rarely used, as the heat-seed needs to fluctuate quite a bit for the effect to be lively.

In the end, I used a ramp with fewer colors (char-col and background, ec1, ec2), and the following system for mapping colors:

Color-mapping (after 4*ADC, before division, hence 256 steps):
$00-$1f: [fixed color] black
$20-$3f: [step size=2] black -> red
$40-$5f: [step size=2] red -> orange
$60-$7f: [step size=2] orange -> yellow
$80-$bf: [step size=1] yellow -> white
$c0-$ff: [fixed color] white

(Example - the red/yellow color ramp)

The final code looks something like this:
	lda matrix+(xpos)+(ypos*40)
	adc matrix+(xpos-1)+((ypos+1)*40),x
	adc matrix+(xpos+0)+((ypos+1)*40)
	adc matrix+(xpos+0)+((ypos+1)*40),x
	lda subtabXX,y         // Will point to subtab0,subtab1,subtab2 or subtab3
	sta matrix+(xpos)+(ypos*40)

	lda charcodes,y
	sta screen+(xpos)+(ypos*40)
	lda colcodes,y
	sta $d800+(xpos)+(ypos*40)

Final remarks
The final bits of the effects was essentially down to starting the fire with a slow ramp-up of the heat (really improved the effect a lot when it builds up), ending each loop with cutting the heat-generation for the "burn out" effect + add some funky colors (in the revised version of Gumbo) to rotate through to make it more interesting to watch on repeat =)

-Raz, Feb 7th 2020
2020-03-10 21:03

Registered: Mar 2005
Posts: 365
Amazing writeup, thanks Raz!
2020-03-14 20:54

Registered: Aug 2003
Posts: 10
Quote: Amazing writeup, thanks Raz!

Thanks, mate =)

I hope it would be of interest, even if it's not rocket science.

RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Users Online
White Flame
hqn/Silicon Ltd
Street Tuff/TRSi
Guests online: 104
Top Demos
1 Uncensored  (9.7)
2 Edge of Disgrace  (9.6)
3 Coma Light 13  (9.6)
4 Memento Mori  (9.6)
5 Comaland 100%  (9.6)
6 The Shores of Reflec..  (9.6)
7 Unboxed  (9.6)
8 Lunatico  (9.6)
9 Remains  (9.5)
10 C=Bit 18  (9.5)
Top onefile Demos
1 Dawnfall V1.1  (9.5)
2 Gumbo Revised  (9.5)
3 Smile to the Sky  (9.5)
4 Daah, Those Acid Pil..  (9.5)
5 Bad Boy  (9.5)
6 Crystal Gazer  (9.5)
7 Cuarentenauta  (9.5)
8 Instinct  (9.5)
9 The Tuneful Eight [u..  (9.5)
10 The Night It Snowed  (9.5)
Top Groups
1 PriorArt  (9.4)
2 Booze Design  (9.4)
3 Censor Design  (9.4)
4 Fossil  (9.4)
5 Performers  (9.3)
Top Hardware-Gurus
1 Jope  (10)
2 Soci  (9.9)
3 Grue  (9.8)
4 Zer0-X  (9.8)
5 Lemming  (9.8)

Home - Disclaimer
Copyright © No Name 2001-2020
Page generated in: 0.054 sec.