Archived

This topic is now archived and is closed to further replies.

Ultima Online and 16-bit lighting effects

This topic is 6270 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

First i would like to say long time listener first time poster..hehe. Ok, ok..here i go: I went to ultima online site today www.uo.com to take a look at the graphics they use in game..i liked the style and is exactly like what i want to use for my design i am planning. So: 1) If you have seen the graphics or played game i would like to know if they use "iso" or "tile-based looking like iso" tilesets. They are exactly the idea of style i would like to have..no need to comment on how hard it would be or the need of a team of graphic artists..its only the style i look for cuz i know im no great artist 8) 2) They use 16-bit graphics with lighting effects such as day/night, torches/lights, etc. So, my question is how do they create the change in day..if thats what they really do. I know in a 8-bit palette you can manipulate palettes to create darker colors, animation, etc...but how do you do that with a 16-bit engine?? Do you have to creat a whole new set of tiles with a different shade(say darker for night-time) or do they do something else. I especially like the torch in caves, day/night realism but havent seen any subjects on how to do it for a 2D tile-based game. sheww. well i broke the ice and will eagerly be watching for some insightful comments from all you crazy iso-landers. 8) cyas...

Share this post


Link to post
Share on other sites
Ultima Online uses a diamond shape tile 44x44 pixels exactly. Their file formats are fairly complicated but you can find out all about them

here

As for their day night sequences, I think they do this in asm, as I''ve played UO before on a P133 with an old 2mb video card and it still ran a slow but decent framerate.

There''s lots of source out there (MMX enhanced as well) to do fades on your screen. Basically you render your scene, then run the fade code on your backbuffer. Then flip

As for their localized lighting, I think they define an area around a light source, and during the fade (if it''s not daylight level) skip the areas that have light sources. I''m not exactly sure about this though.

Their lighting method is not that advanced and I''ve noticed light goes through walls.






Share this post


Link to post
Share on other sites
Thanks for the comments i will have to digest this info about the tiles and your ideas on lighting.

I would like to ask to here more comments about the lighting as i have not seen any other explanations for it (examples, code, etc). Im not saying i dont find your ideas wrong, in fact the fading ideas are good. I just want to here a variety of possible ideas of what some of you out there have actually attempted in this area.

GQ.

Share this post


Link to post
Share on other sites
Had some time to think about the "fading" you elude me too, yet i still have not figured out how to do this. So, first ill give my idea of fading which is either randomly setting pixels to a dark or black color, OR as some kind of routine that goes through every the pixels in memory and shifts the rgb to a darker shade.
(Let me state that i am using directX and all that directdraw stuff so you can let me reference stuff in sdk if you know it.)

I assume that somehow the second is prefered(or another way) as the first will only destroy the image and not make it darker. So how in directX would i perform these lighting tricks and shading of colors if 16-bit colors are used.

*sniff, sniff..i hate when i dont grasp something*
GQ

Share this post


Link to post
Share on other sites
Let me see if I can explain it. First of all, it''s require that you write your own little blt function just for this aspect

Let''s first say that you have a pointer to your 16bit DirectX surface. This variable is declared as:

short *ptrSurface = NULL;

You also need an illumination map that is the exact same size (in bytes) as the surface. This is the dwHeight * lPitch in the DDSURFACEDESC struct. This variable is declared as:

short *ptrIllum = NULL;

With each object that radiates light, have a pre-initialized array of shorts that contains the brightness level of the pixels around the object. The higher the short value, the brighter the pixel is. The center of the array (if viewed as a 2D array) should be the brightest. Let''s say this array has the 2D dimensions mTorchLW, mTorchLH (width, height respectively).

Before you draw the map, first set the default illumination level... the ambience if you will. This is the brightness of everything that''s not in the range of an object that radiates light.

You should first loop through the objects that would be on the screen and check to see if it radiates light. For all the objects that illuminate light, you will have to modify the short values in the illumination map accordingly. This is hard to explain, but easy to implement.

Basically what you do is first figure out where the object would be on the screen. With that coordinate, you would find the equivilant ''pixel'' on the illumination map.

Now that you have the pixel spot where you draw your torch, calculate where it''s base is. That is usually just the height of the torch. Now that you have that value (BaseX, BaseY), you can calculate where to start setting your illumination. That is:

IllumStartX = BaseX - (mTorchLW >> 1);
IllumStartY = BaseY - (mTorchLH >> 1);

Scan across the illum. map and torch illum. map. Check the illum. map''s light value. If it''s less than the torch''s illum. map value, then set illum. map''s light value to whatever is in the torch''s illum map. Pretty confusing, huh. Don''t worry... it''s not hard to implement.

Now that the comparison is all done and the illumination map is set, draw your map like normal. Once that is done, run the surface through your blt function that will apply the illumination level. Start with the pointer at the beginning of the surface and the illum. map. Scan through each and every pixel on the surface and set the illum level.


void SetIllumLvl(short *SurfPixels, short *IllumMap, unsigned long SurfSize){
short *cur_pixel, *cur_illum

cur_pixel = SurfPixels;
cur_illum = IllumMap

while (SurfSize){
//This next line takes the pixel value and times it
// by the light level.
*cur_pixel = ((*cur_pixel * cur_illum) / 0x0000FFFF);

//Move to the next pixel
++cur_pixel; ++cur_illum;
--SurfSize;
}
}


I haven''t check to see if this compiles, but you should get the general idea. SurfSize is the number of pixels to be processes, not the number of bytes.


I hope that helps... and if it doesn''t I hope that it doesn''t make things worse.

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
Now i gettt itttt. Sweat!

Your explaination is very good Dino, its almost exactly what i needed to hear.(PS thanks also Darrell.)

Im sure many others will be glad to read it too since ive looked through all the posts(365 days worth) and not one explained it, at least not to my knowledge....good job!

I will take a few days to really think about it ...in the mean time can you explain 1 little thing: your need to divide the new pixel illumination value by hex number 0x0000ffff? what does this do and its purpose?

Anyways, just wanna say how stoked i am to have found a wealth of knowledgable folks willing to share!! 8)

Share this post


Link to post
Share on other sites
When drawing the map normally, you go under the assumption that the light level is at 100%. That means that the value of the pixel is 0 to 65536 (or 0x0000FFFF). This is the actual color of the pixel.

The basic idea behind the illumination map is that it represents the degree of brightness. The actual formula is suppose to be:

new color = original color * (light level / max light level).

In a 16 bit illumination map, max light level is 65536. The previous post had the () in the wrong spots that made it confusing.

Now that I know you understand that concept... here''s some optimization you can do:

1) You technically don''t have to use 16 bit values for the illumination map. You could go 8 bit. It works the same way except that you divide by 0x000000FF and not 0x0000FFFF.

2) You can take out the division and replace with a bit shift. If you do this, then here''s the formula and you MUST () the * portions (due to operator precedence):

new_clr = ((orig_clr * light_lvl) >> 16) & 0x0000FFFF;
-or for 8 bit-
new_clr = ((orig_clr * light_lvl) >> 8) & 0x0000FFFF;

They both do the same as dividing by 65536 and 256, respectively. The 0x0000FFFF is to make sure you get a valid 16 bit valu.

Note: this is a very basic method of doing illumination. This is what I gathered by looking at the UO screenshots and as you may have noticed, the light rays weren''t being clipped. Clipping the light is alot harder to do, but there are ways.

If each type of object that illuminates light has it''s own ''light array'' like the example I gave with the torch, then you can to cool tricks like changing the array so that the lit areas ''flicker'' a bit.


Good Luck and I hope this helps you out.


Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
Dino,

One question:
By implementing this technique would you have to create a new blitting function that draws the bitmaps that accomodates this?

I guess not 'cause the function would adjust the bitmaps on a pixel level, right?

also for this:
        
void SetIllumLvl(short *SurfPixels, short *IllumMap, unsigned long SurfSize)


what would *SurfPixels be?
Would *IllumMap be the array that holds the illum map?
would SurfSize be the size of the whole screen...like 640x480?


Sorry your explanation is good, but I'm still a little fuzzy when it gets into this sort of thing

""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 9, 2000 8:52:05 PM

Share this post


Link to post
Share on other sites
short *SurfPixels is the pointer to the DirectX surface.
short *IllumMap is the pointer to your illumination array (the big one).
unsigned long SurfSize is the number of pixels in the surface. If your surface is 640 x 480 then the SurfSize would be (640 * 480, which is 307200).

The only blitting function that you would have to implement is one that overlays the entire illumination map object to already drawn screen. The order would be:


1. Reset the illumination map to the ambience light level
2. Loop through all the objects, drawing them to your surface
and modifying the illumination map at the same time.
3. Get the pointer to the DirectX surface you have been drawing
to.
4. Pass that pointer, the pointer to the illumination map, and
the size of the surface (# of pixels) to the SetIllumLvl()
function.
5. Flip() or do whatever you normally do to get the back
buffer to the screen.


Does that clear things up?

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
oh...so would the "big" illumination map be the same amount of elements as the number of pixels you're using for the whole screen?...

like illumination_array[640][480] if you are using 640x480 for the resolution?

also is it sort of like you're drawing the whole screen to the back buffer then altering the pixels that need to be changed with the SetIllumLvl() function before the Flip()?

So, the SetIllumLvl() function is actually altering the color values? I think I'm starting to get it now At least I hope.



"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.



Edited by - Nazrix on October 9, 2000 9:09:09 PM

Share this post


Link to post
Share on other sites
Exactly! Now you are getting it. It''s not the best system in the world... but it works (I think )

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
That''s totally brilliant!

Thanks a lot, Dino




"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.

Share this post


Link to post
Share on other sites
Sorry to bother you again, but I've one more question.

short *ptrIllum = NULL;

I'm not sure if I understand where the *ptrIllum variable comes in...



"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 9, 2000 9:30:53 PM

Share this post


Link to post
Share on other sites
That was the variable declaration for the illumination map. I tossed it in there just to show that it''s an array of shorts. You can have your illumination map declared in any which way.

Ignore it if you want.

Good Luck.

P.S. I hope you are writing all this down Tanstaffl!

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
Okay I understand the concept but I'm having a terrible time with syntax and implemetation.

can you give me an example of how you'd call the SetIllumLvl function?
My back buffer is called lpddsback and the array for the big illumination map is called illum_map[640][480].

If I try and pass the lpddsback to the function it says I can't convert it from LPDIRECTDRAWSURFACE4 to short. I'm having trouble passing the array too...

Also, if I try to set the ptrSurface (from
short *ptrSurface = NULL equal to lpddsback it gives me errors about not being able to convert.





"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 10, 2000 4:33:08 AM

Share this post


Link to post
Share on other sites
Nazrix, it looks as though it wants you you''ll have to lock the surface and then pass a pointer to the surface to the function.

try this:

lpddsback->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);

then, you can use

short *surface = (short *)ddsd.lpSurface;

also, when your done dont forget to lpddsback->Unlock(NULL);

i didn''t read all of the posts, but just skimmed over the last few.. so i hope this is what you want

Share this post


Link to post
Share on other sites
Thanks, Quantum, but it didn''t seem to help. It works as far as syntax is concerned but it just exits back to windows when I run the program.

I''ll probably end up making this one of my usual 4 page posts.


"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.

Share this post


Link to post
Share on other sites
What do you mean it exits back to windows? It crashes?

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
Hey I think I got it to work

The only thing I don't understand is how would I pass the array of the big Illum Map?

Like if I had it declared as
illum_map[640][480]

Also, should I set the surface to the DirectX surface every frame or just once because it is really slow...



"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 10, 2000 3:03:37 PM

Share this post


Link to post
Share on other sites
Also do you know what would be a good general range of values for the elements in the illum map would be?


"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 10, 2000 3:12:41 PM

Share this post


Link to post
Share on other sites
Well, an array is a pointer. You can just passed the array variable to the function. For example:
//--------------------------------------------------
short *ptrSurface;
short illum_map[640][480];
unsigned long surf_size = 307200;
DDSURFACEDESC ddsd;

//--Some of your code that initializes the surface--

//Get the pointer to the surface
lpddsback->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
ptrSurface = (short *)ddsd.lpSurface;

//Call the illumination levels
SetIllumLvl(ptrSurface, illum_map, surf_size);

//Unlock the surface
lpddsback->Unlock(NULL);
//--------------------------------------------------


Now the performance issues.

From my understanding... locking a surface is slow. So each an every lock you do slows it down considerably. There are ways around this like creating your backbuffer using pre-allocated system memory. This way you have the pointer from the get go and don''t have to do a lock.

Now that I think about it, if your backbuffer that you are drawing to is in video memory, then writing to that memory directly will be slow. Try making the backbuffer in system memory, the primary surface in video memory, and instead of a Flip(), use a BltFast.

Give that a try

Dino M. Gambone
Good judgement is gained through experience. Experience, however, is gained through bad judgement.

Share this post


Link to post
Share on other sites
The tiles are square (like all tiles) but made to look diamond shape.

For lighting, if you want to lighten/darken the whole screen, use a GammaRamp.

If you want to make, for instance, a lighted area around a torch, just alpha-blit in a white disk.

Share this post


Link to post
Share on other sites
Buster, what''s a GammaRamp?


"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

""You see... I'm not crazy... you see?!? Nazrix believes me!" --Wavinator

Need help? Well, go FAQ yourself.

Share this post


Link to post
Share on other sites
GammaRamp can be used to control your monitors gamma setting (brightness) However not all video cards support this, alot do but not all.

Related to this thread are the following gamedev tutorials:
MMX enhanced Alpha Blending
Using GammaRamp


I''ve been thinking about how I''m going to add lighting to my engine but I can''t think of an efficent way to do this.

My Engine is like Ultima Online. My drawing routines draw top to bottom, left to right. I first draw all base tiles. I then draw all objects. All objects are stackable, so I stack walls/stairs etc ontop of each other. How can I draw a light source so that new objects/tiles drawn further down the "Y" position don''t overwrite my illumination area (which I would do with alpha blending a white disc)

Any suggestions?

Thanks,
Darrell.

Share this post


Link to post
Share on other sites
Thanks, Darrel.
Your engine looks pretty nice BTW

Do most video cards support alpha-blending?




"""" "'Nazrix is cool' -- Nazrix" --Darkmage --Godfree"-Nazrix" -- runemaster --and now dwarfsoft" -- dwarfsoft --pouya" -- Nazrix

""You see... I'm not crazy... you see?!? Nazrix believes me!" --Wavinator

Need help? Well, go FAQ yourself.


Edited by - Nazrix on October 11, 2000 10:15:03 PM

Share this post


Link to post
Share on other sites