> > I've also got a fast way to do lighting in 16bpp if you need
> > that later on.
> I'd be interested in knowing the method you use. Personally,
> I use plain and stupid runtime 8 bit->16 bit conversion, and
> I'd be glad to get something more clever and fast :)
> TIA

Fast, certainly.  Clever, perhaps not.  I worked all this out with
no prior experience of lighting, but it works OK for me.
Also it was written before I used Allegro.  Converting it to use
Allegro's bitmap system was a challange, but it had clipping and my
sprite code didn't.

Firstly, this is intended for a 2D game with monochromatic lighting.
You might be able to do it in 3D, but you're on your own.

I use 32 light levels.  There are 65536 possible colour combinations
and I assume the same for 15bpp too because it makes life simpler.

32*65536 = 2MB, a fairly large but not impossible lookup table.
The lookup table is made by using a loop of 0..31 (light level)
and an inner loop of 0..65535 (colour codes)
For each light level, split the colour code into it's RGB components
with getr getg getb etc.  Scale the light level to 255 (<<3).
Then subtract this from each of the RGB components, clipping them
to 0 as necessary.

I have an 8bpp bitmap the size of the game window that contains
the light level.  This is in actual fact the amount of darkness
we're going to project on the scene, from 0-255. 0 is day and 255
is utterly black.


To shade the scene, I have a piece of code (with an ASM version too)
like this:

unsigned short *imgptr;
unsigned char *lptr;

lptr = (unsigned char *)darkmap->line[0];
iptr = (unsigned short *)gamewin->line[0];

num = gamewin->w*gamewin->h;
for(ctr=0;ctr<num;ctr++)
    *imgptr++ = colourtable[(((*lptr)&0xf8)<<13)+*imgptr];


This takes each byte of the lightmap, removes the lower 3 bits and
shifts it left by 13, effectively converting it from 0-255 to 0-31
AND finding the correct lookup table for that light level at the same
time.  Then, it's just a matter of adding the colour code to get the
correct entry in the table, and you write it back to the game window.
It is pretty damn fast: much more than doing all that shifting stuff.

Now, to build the actual darkmap as fast as possible.
Originally I used a 256x256 lookup table and my own optimised sprite
routines, but the lack of clipping would sometimes cause crashes when
dealing with partly-offscreen light sources.

I do everything with light masks, a sprite that is a circular light
gradient made with Autodesk Animator.
Each turn of the game, I clear the darkmap to the current ambient
light value (depending on the time of day) and use draw_trans_sprite
in 8bpp mode with an RGBMAP that subtracts the values in the
light mask from the darkness map.  A light source will literally eat
it's way through the darkness.  It also allows scope for dark sources
as well as light sources if you use an inverted RGBMAP.

The RGBMAP is made from a 256x256 subtractive array, e.g.

for(ctr1=0;ctr1<256;ctr1++)
	for(ctr2=0;ctr2<256;ctr2++)
		{
		val=ctr1-ctr2;
		if(val<0)
			val=0;
		lightmap.data[ctr1][ctr2]=val;
		}

The final piece of the puzzle is making the light mask occlude.
My game is a tilemap, so I have to make the walls block light.
I divide the light mask into a set of tiles which are used to
illuminate out from the source.  Because the source is at
the centre of the light mask, the first few squares won't get cut
off.  The inner 3x3 square is always drawn.



> 
> --
> Lyrian

-- 
JP Morris - aka DOUG the Eagle (Dragon) -=UDIC=-  doug@it-he.org
Fun things to do with the Ultima games            (http://www.it-he.org)
Developing a U6/U7 clone                          (http://fly.to/ire)
d+++ e+ N+ T++ Om U1234!56!7'!S'!8!9!KA u++ uC+++ uF+++ uG---- uLB----
uA--- nC+ nR---- nH+++ nP++ nI nPT nS nT wM- wC- y a(YEAR - 1976)

From - Mon May 15 21:48:12 2000
X-Mozilla-Status: 8019
X-Mozilla-Status2: 00000000
FCC: /home/jpmorris/nsmail/Sent
Message-ID: <3920628C.775A81F1@it-he.org>
Date: Mon, 15 May 2000 21:48:12 +0100
From: Joseph Morris <jpm@it-he.org>
X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; uuencode=0; html=0; linewidth=0
X-Mailer: Mozilla 4.7 [en] (X11; I; Linux 2.3.99-pre3 i586)
X-Accept-Language: en
MIME-Version: 1.0
To: Vincent Penquerc'h <vincent@calcaphon.com>
Subject: Re: [AGP] about 16 bit color mode effects
References: <DOEPJBFJKJJJOCHIMOPIIEAOCEAA.vincent@calcaphon.com>
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Vincent Penquerc'h wrote:
> 
> > I've also got a fast way to do lighting in 16bpp if you need
> > that later on.
> I'd be interested in knowing the method you use. Personally,
> I use plain and stupid runtime 8 bit->16 bit conversion, and
> I'd be glad to get something more clever and fast :)
> TIA

Fast, certainly.  Clever, perhaps not.  I worked all this out with
no prior experience of lighting, but it works OK for me.
Also it was written before I used Allegro.  Converting it to use
Allegro's bitmap system was a challange, but it had clipping and my
sprite code didn't.

Firstly, this is intended for a 2D game with monochromatic lighting.
You might be able to do it in 3D, but you're on your own.

I use 32 light levels.  There are 65536 possible colour combinations
and I assume the same for 15bpp too because it makes life simpler.

32*65536 = 2MB, a fairly large but not impossible lookup table.
The lookup table is made by using a loop of 0..31 (light level)
and an inner loop of 0..65535 (colour codes)
For each light level, split the colour code into it's RGB components
with getr getg getb etc.  Scale the light level to 255 (<<3).
Then subtract this from each of the RGB components, clipping them
to 0 as necessary.

I have an 8bpp bitmap the size of the game window that contains
the light level.  This is in actual fact the amount of darkness
we're going to project on the scene, from 0-255. 0 is day and 255
is utterly black.


To shade the scene, I have a piece of code (with an ASM version too)
like this:

unsigned short *imgptr;
unsigned char *lptr;

lptr = (unsigned char *)darkmap->line[0];
iptr = (unsigned short *)gamewin->line[0];

num = gamewin->w*gamewin->h;
for(ctr=0;ctr<num;ctr++)
    *imgptr++ = colourtable[(((*lptr)&0xf8)<<13)+*imgptr];


This takes each byte of the lightmap, removes the lower 3 bits and
shifts it left by 13, effectively converting it from 0-255 to 0-31
AND finding the correct lookup table for that light level at the same
time.  Then, it's just a matter of adding the colour code to get the
correct entry in the table, and you write it back to the game window.
It is pretty damn fast: much more than doing all that shifting stuff.

Now, to build the actual darkmap as fast as possible.
Originally I used a 256x256 lookup table and my own optimised sprite
routines, but the lack of clipping would sometimes cause crashes when
dealing with partly-offscreen light sources.

I do everything with light masks, a sprite that is a circular light
gradient made with Autodesk Animator.
Each turn of the game, I clear the darkmap to the current ambient
light value (depending on the time of day) and use draw_trans_sprite
in 8bpp mode with an RGBMAP that subtracts the values in the
light mask from the darkness map.  A light source will literally eat
it's way through the darkness.  It also allows scope for dark sources
as well as light sources if you use an inverted RGBMAP.

The RGBMAP is made from a 256x256 subtractive array, e.g.

for(ctr1=0;ctr1<256;ctr1++)
	for(ctr2=0;ctr2<256;ctr2++)
		{
		val=ctr1-ctr2;
		if(val<0)
			val=0;
		lightmap.data[ctr1][ctr2]=val;
		}

The final piece of the puzzle is making the light mask occlude.
My game is a tilemap, so I have to make the walls block light.
I divide the light mask into a set of tiles which are used to
illuminate out from the source.  I use 32x32 tiles, and the
lightmask I'm using currently divides into 7x7 tiles.

Because the source is at the centre of the light mask, the first
few squares won't get cut off.  The inner 3x3 square is always drawn.

However, if the light is blocked at the the 5x5 zone, we need to make sure
that the appropriate tiles in the 7x7 zone are also blocked.
This had me foxed for some time, but the mthod I used in the end was this:

1. plot a light path from the centre outwards a bit like a bresenham line
but with the coords to use precalculated.

2. Each tile along the path is lit with the appropriate tile from the lightmask

3. If a tile blocks light, illuminate that tile, but stop drawing that light
path
so no other tiles are lit

4. Because of the light paths used, there is redundancy (two beams can
illuminate
the same square to ensure no 'shadows' that shouldn't be there).
To prevent overbrightening, I maintain an array of flags the same size as the
light mask.
As each tile is lit, the flag corresponding to that coordinate in the mask is
set.
If the flag is already set the lighting does not take place.
For each lightsource this array is set back to 0 for re-use.


Wow!  I think that covers the whole lighting engine.  I hope you can understand.

> 
> --
> Lyrian

-- 
JP Morris - aka DOUG the Eagle (Dragon) -=UDIC=-  doug@it-he.org
Fun things to do with the Ultima games            (http://www.it-he.org)
Developing a U6/U7 clone                          (http://fly.to/ire)
d+++ e+ N+ T++ Om U1234!56!7'!S'!8!9!KA u++ uC+++ uF+++ uG---- uLB----
uA--- nC+ nR---- nH+++ nP++ nI nPT nS nT wM- wC- y a(YEAR - 1976)
