Tuesday, July 15, 2014

Implementing shadows

Feeling relaxed after a week of vacation I'm now finishing up work on implementing shadows for EMI. This work is now in PR #956.

EMI has two types of shadows: simple shadow sprites (internally called "lame shadows" or "dumb shadows" :) ) and planar projection shadows. The projection shadows look more realistic, but are only used in a few sets. If shadow quality is set to low in the options, only shadow sprites are used.

The screenshots below are taken from the original game. In the screenshot on the left, shadow sprites can be seen under Guybrush and Elaine. The screenshot on the right shows projection shadows cast by Guybrush and the brazier on the deck of the ship. Initially with ResidualVM both shadow types were not drawn at all.


After my previous sprite fixes, enabling the shadow sprites turned out to be very simple. Previously ResidualVM's actor drawing code contained a hack that skipped drawing the costume containing the shadow sprite if shadows were not explicitly activated for that actor with a call to ActorActivateShadow from Lua. After some testing in the original game I found out that the ActorActivateShadow function doesn't affect the shadow sprites at all in the original (instead it is used to toggle projection shadows). After removing the hack, the shadow sprites showed up as expected.

I then moved on to tackle the projection shadows. This turned out to be slightly trickier. Fortunately projection shadows have been implemented previously in ResidualVM's renderers for Grim Fandango, so most of the rendering code could be reused also for EMI. To render projection shadows we need a shadow color, the light position as well as the planes on which shadows are cast on. The first problem was that I didn't know where the original game got this information from.

In Grim, the shadow color, the light position and the shadow planes are set up through Lua code, but in EMI I couldn't find anything similar in the game's Lua scripts. The information had to be stored somewhere else.

The next place I decided to look in was the .setb files, which contain scene-specific information such as camera positions, sectors and lights encoded in binary form. The shadow color seems to change between sets, so it seemed like a fair assumption that the information I was looking for would be stored in the set data.


This led me to the discovery that for sets that have projected shadows enabled, there was indeed some data at the end of the .setb file that was not read by ResidualVM (marked with red in the pic above). This previously ignored block of data contains an array of structures that I call 'shadow' structures here. Each 'shadow' structure contains a name, the light position to be used when projecting the shadow and an array of 'sector reference' structures. The 'sector reference' structure contains the name of a sector within the set as well as the shadow color to be used when a shadow is projected onto that sector.

Once I discovered this data, it was fairly easy to enable the pre-existing planar projection shadow rendering code in ResidualVM also for EMI. The main difficulty was that previously the renderers only supported one global shadow color, but in EMI the shadow color can be set per-sector. I'll discuss this issue and how I resolved it in detail in the next blog post.

With all the issues resolved, it's very hard to tell ResidualVM and the original game apart in most scenes. The screenshots below are from ResidualVM. Compare them with the screenshots of the original above :)





No comments:

Post a Comment