The shadow and lighting parts were done in direct3d8 and while the techniques were custom, directdraw could simply not have performed the effects at a speed one would consider to be acceptable.
ShadowsThe shadows are simply the sprites themselves skewed, darkened and made to blend with the background. A tool was created that loaded the bitmaps and recognized the templates within. One could then set rectangular regions of any size (up to a pixel) that could not be occupied by another sprite. Within this tool was also the ability to set a shadow anchor point as some sprites shadows often seemed to be floating by them due to a discrepancy between the sprite and rectangle containing it. Some arbitrary point could not be defined to place shadows at that would look fine for all sprites.
Shadow OrientationTo orient the shadows based on the angle they made the light I simply took the angle between a vector representing the light and another representing the object. This worked because if a vector was extended from the light to the object then the continuation of that vector would conicide exactly with a shadow vector. And to get a shadow projection vector? Simply rotate the object by the angle between this extened vector and the object.
The angle is then:
θ = cos-1 [ V.W ]
[ |V| * |W|] When I stopped, the objects simply looked for the lights in their vicinity and created and oriented a shadow for each light. Note that Recognition of quadrant was required to make this method work properly. And certain objects were not allowed to rotate their shadows (buildings).
The shadows were skewewed by adjusting the top left and right vertices making them look flat. The length of the shadow was found with this equation that was based on simple geometric principles.
LShadow = (hOject)[ DLight ]
[ hLight - hObject
] Where D is the distance from the light. If the angle of the bottom left vertex of our skewed quad is θ then Δx = LShadow * cos(θ) and Δy = LShadow * sin(θ). Δx and Δy further skew our shadow. Playing with these and other values of the vertices can improve the look of the shadow. For example the tool which created template data (shadow anchor points, collision sectors data, maximum rotation angle) for each bitmap one could have it also have extrusion values for each vertex be either an absolute value or based on the length of the shadow.
LightsLights were done by having a tool that allowed you to choose the light color and power (in watts) from which the illuminance was calculated for the simple attenuation equation of:
E = I d2
If a minimum value for E is chose then it is possible to find the radius of the light.
_____________ dmax = / [ I ] √ [ Emin ]
Dividing this value by tile width (tiles_radius) gave how many tiles to the right to go and looping from (LightTileX - tile_radius, LightY - Light_radius_) through to LightTileX + tile_radius, LightY + Light_radius_) each vertex was giving a value based on the distance from the light source. This value was a percentage defined as P = 1 - (d/dmax). These values were calculated once when a light was added, or again if deleted of moved and stored into the map. Multiple such lighting values could be stored per tile. Dynamic lights however, had to constantly delete , move then re-add themselves, making them quite power hungry. I was able to allieviate this a fair bit by giving them a higher E_min and creating a look up table for the square roots up to a relatively high value that was predicted as the maximum required for dynamic light distance calculations. When drawing the map each tile looped through all its lights and adjusted its diffuse color based on the equation:
FinalR ( |LightR | |ColorR| ) |ColorR|FinalG = ( |LightG | - |ColorG| ) * P + |ColorG|FinalB ( |LightB | |ColorB| ) |ColorB|
Essentially moving the current diffuse value towards our light. For sprites which had any x,y location it made sense to define a 3x3 array which stored the final color for each tile on the screen and having the sprite find what vertex of the tile each of its corner is closest too and adjusting its color thus. If part of its self was outside the screen there was a choice to calculate this value or simply use 0xFFFFFFFF.
This is how I did it then ( near 2 years ago), I don't know if I would do it the same way again but I do not think the methods to be too bad.
[Edited by - Fruny on March 11, 2005 1:55:36 PM]