Versión española


Isomot v1.0 by Ignacio Pérez Gil (perezg_ret[]terra.es)

- What's Isomot?
- Distribution notes
- How to set up Isomot on your Allegro project
- The grid
- Walls and floor tiles
- Objects
- Collision detection
- Grid coordinates
- Converting between 2d and 3d coordinates
- Shadows
- Transparent objects
- Drawing the isometric view
- Errors
- The ism_lng.h file
- Functions summary
- Programming tips
- History
- Acknowledgements


- What's Isomot?


     Isomot is an isometric engine for Allegro, in the purest Filmation tradition (Do you remember Knight Lore, Alien 8, Sweevo's World, Head Over Hells, etc?) with an 1,1/2,1/2 prespective, where the x axis is the one going down-right, the y one going down-left and z going up. This doc is not intended to be an isometric tutorial, so I will assume that you know a little about this kind of prespective. Anyway, if you have any questions, you can e-mail me at perezg_ret[]terra.es. Isomot has a multilingual interface; Spanish and English are the languages actually supported (this doc is about the English interface). Adding new languages is very easy, if you want to make the interface for another one, just e-mail me and I'll tell you how.


- Distribution notes

     This source code is distributed "as is", without any warranty, and I will not accept any responsability for any kind of damage it can cause.

     You are free to use it on your own programs, as is or with your own changes, but only for non-commercial purpouses. Anyway, a mention on the credits, and a short e-mail telling me about what you think about it, the changes you've made, etc. would be very nice of you :)


- How to set up Isomot on your Allegro project

     To use Isomot, you just have to add the file isomot.c to your project (and don't forget to #include "isomot.h" on the file or files where you are going to use it). Easy, isn't it?


- The grid

     Every room's floor of a typical isometric game has a grid over wich nearly all objects are placed. To define this grid, you must use the following function:

int ism_set_grid(int x, int y, int width);
Description: Defines (or redefines) the grid.
Parameters: x, y: the grid's dimensions.
width: the width of the grid's cells.
Notes: The width parameter must be grater than 1.
Returns: 0 if it worked, -1 if it failed.

     For example, to set a grid like this one:


Image 1: the grid.

   you must use ism_set_grid(8,8,16); If you can't understand why the indicated grid's width is 16, just look at this enlarged image of a floor tile:


Image 2: floor tile.

     Important note: defining the grid is the first thing you must do when using Isomot; any other function of it's interface called before ism_set_grid will return an error.

     Keep in mind that every time you set the grid, everything on it is erased, so, use it with care if you are doing it more than one time (for example, to resize it), not just once at the start of the program.

     When calling ism_set_grid, a certain amount of memory is reserved for the engine's internal use. At the moment of releasing this memory (on your program ending code, usually), you should call the following function:

int ism_destroy_all(void);
Description: Frees all memory used by Isomot.
Parameters: None.
Comments: If you want to use Isomot's capabilities again after calling this function, you must call ism_set_grid again.
Returns: 0 if it worked, -1 if it failed.


- Walls and floor tiles


Image 3: Head Over Heels.

     On the previous image from "Head Over Heels", you can see a few of the most common elements of any room's scenery: the different kinds of walls and floor tiles. Isomot draws walls as a set of images, each one aligned with the floor tiles. To set all of them, you must use the following functions:

int ism_set_wall(BITMAP *map, unsigned short axis, int wall);
Description: Sets a bitmap to one or many of the parts of a wall.
Parameters: map: the wall's bitmap.
axis: the axis of the wall or walls you are going to set. Possible values are HIGH_X, LOW_X, HIGH_Y and LOW_Y.
wall: the wall's number. You can use ALL to set all the walls of the axis.
Returns: 0 if it worked, -1 if it failed.

int ism_set_floor_tile(BITMAP *map, int x, int y);
Description: Sets one or many floor tiles.
Parameters: map: the floor tile's bitmap.
x, y: the coordinates of the grid's cell wich floor tile you are setting. You can use ALL on one of them to set an entire row of floor tiles. You can use it too on both of them to set all floor tiles to the given bitmap.
Returns: 0 if it worked, -1 if it failed.

int ism_set_floor(BITMAP *map);
Description: Sets a bitmap to be used when drawing the room's floor, appart from the floor tiles you can set with ism_set_floor_tiles.
Parameters: map: the map to be used as room's floor.
Returns: 0 if it worked, -1 if it failed.

     And you can move the floor, it's tiles and the walls with the following functions:

int ism_move_wall(unsigned char axis, int wall, int offsetaxis, int offsetheight);
Description: Moves one or many walls.
Parameters:
axis: the wall's axis. Possible values are HIGH_X, LOW_X, HIGH_Y and LOW_Y.
wall: the wall's number. You can use ALL to move all the walls of the given axis.
offsetaxis, offsetheight: the offset ot the wall on the given axis and on height.
Returns: 0 if it worked, -1 if it failed.

int ism_move_floor_tile(int x, int y, int offsetx, int offsety);
Description: Moves one or many floor tiles.
Parameters:
x, y: the coordinates of the floor tile. You can use ALL on one of them to move a full row of tiles. You can use it on both of them too, to move all floor tiles.
offsetx, offsety: the floor tile offset on the x and y axis.
Returns: 0 if it worked, -1 if it failed.

int ism_move_floor(int offsetx, int offsety);
Description: Moves the floor.
Parameters: offsetx, offsety: the floor offset on the x and y axis.
Returns: 0 if it worked, -1 if it failed.


- Objects

     Objects are everything that are drawn on the room, like stone blocks, enemies, etc. Every object you place on the room is identified by a value of the ism_id data type. There are two kinds of objects: grid and free objects. Grid objects are those that are placed on a certain square of the grid, and have the same widths as the floor tiles. Grid objects are basically the standard blocks of the game over wich the player will jump, etc. You can't move a grid object on their x or y coordinates; you can only change their z. On the other way, free objects are those objects that can be at any place, moving arround, for example, the player's character, enemies, or everything else able to be moved on the x or y axis and have different widths than the floor tiles ones. To get a clearer view of this, just take a look to the following picture:


Image 4: Knight Lore.

     On this image, taken from Ultimate's Knight Lore (the first Filmation game), all obstacles are grig objects, fixed on an 8x8 grid, but Sabreman (the hat guy) and the guardian (the helmet one) can walk arround the room, so they are free objects.

     To put a grid object on the room you must use the function:

ism_id ism_put_grid_object(int x, int y, int z, int height, BITMAP *map, BITMAP *shadow);
Description: Puts a grid object on the isometric view.
Parameters:
x, y: the grid cell's coordinates where you want to put the object.
z: the object's coordinate on the z axis, refering to it's base. You can use TOP to put it just over the highest object there, like if it hat fallen from the sky. This is useful to build stacks of objects, each one just over the previous, withouth calculating each object's z coordinate.
height: the object's height.
map: the object's map. If you use NULL, the object will not be drawn, but it will be there for everithing else (collisions, etc.).
shadow: the object's shadow. If you use NULL, the object will have no shadow.
Returns: The object's identifier if it worked, ID_ERROR if it failed.

     To put a free object on the room you must use the function:

ism_id ism_put_free_object(int x, int y, int z, int widthx, int widthy, int height, BITMAP *map, BITMAP *shadow);
Description: Puts a free object on the isometric view.
Parameters:
x, y, z: the coordinates where the object must be placed, refering to the leftmost point of it's base.
widthx, widthy: the widths of the object on it's X and Y dimensions.
height: the object's height.
map: the object's map. If you use NULL, the object will not be drawn, but it will be there for everithing else (collisions, etc.).
shadow: the object's shadow. If you use NULL, the object will have no shadow.
Returns: The identifier of the object if it worked, ID_ERROR if it failed.

     To get a certain data about an object:

int ism_get_object_data(ism_id id, unsigned char data);
Description: Returns the requested object's data.
Parameters: id: the object's identifier.
data: the data you want to know about the object. Possible values are: D_X (the x coordinate), D_Y (the y coordinate), D_Z (the z coordinate), D_WIDTH_X (width on the x dimension), D_WIDTH_Y (width on the y dimension), D_HEIGHT (height) and D_TRANSP (the object's transparency percentage).
Notes: Asking about a grid object's x or y coordinate will return the x or the y of it's grid cell.
Returns: The requested data if it worked, -1 if it failed.

     There's an easier way to get an object's coordinates than three calls to the previous function:

int ism_get_object_coords(ism_id id, int *dstx, int *dsty, int *dstz);
Description: Stores the object's coordinates on the given variables.
Parameters: id: the object's identifier.
dstx, dsty, dstz: the variables where the function will store the object's coordinates.
Notes: If the object is a grid one, the value stored on dstx and dsty will be the x or the y of it's grid cell.
Returns: 0 if it worked, -1 if it failed.

     To modify a given data about an object, use the following function:

int ism_change_object_data(ism_id id, unsigned char data, int value, unsigned char mode);
Description: Modifies the requested object's data.
Parameters: id: the object's identifier.
data: the object's data that you want to modify. Possible values are: D_X (the x coordinate), D_Y (the y coordinate), D_Z (the z coordinate), D_WIDTH_X (width on the x dimension), D_WIDTH_Y (width on the y dimension), D_HEIGHT (height) and D_TRANSP (the object's transparency percentage).
value: the new value to be assigned to the requested data.
mode: the way you want to change the data. You can use CHANGE to change the old data by value, or ADD to add value to it.
Notes: An error will occur if you try to change a grid object's x or y coordinate, it's width, or it's transparency percentage.
Returns: 0 if it worked, -1 if it failed.

     And to change an object's coordinates on an easy way, just use this:

int ism_move_object(ism_id id, int x, int y, int z, unsigned char mode);
Description: Modifies an object's coordinates.
Parameters: id: the object's identifier.
x, y, z: the new coordinate's values.
mode: the way you want to move the object. You can use CHANGE to move the object to x, y, z, or ADD to add x, y, z to the previous coordinates.
Notes: An error will occur if you try to move a grid object with this function.
Returns: 0 if it worked, -1 if it failed.

     To change an object's map:

int ism_change_object_map(ism_id id, BITMAP *map);
Description: Changes an object's image.
Parameters: id: the object's identifier.
map: the map with the new object's image.
Returns: 0 if it worked, -1 if it failed.

     To change an object's shadow:

int ism_change_object_shadow(ism_id id, BITMAP *shadow);
Description: Changes an object's shadow.
Parameters: id: the object's identifier.
shadow: the map with the new object's shadow.
Returns: 0 if it worked, -1 if it failed.

     The function to remove an object is:

int ism_remove_object(ism_id id);
Description: Removes an object from the isometric view.
Parameters: id: the object's identifier.
Returns: 0 if it worked, -1 if it failed.

     To know if a certain object does exist:

int ism_object_exists(ism_id id);
Description: Says if there is an object with the given identifier.
Parameters: id: the object's identifier.
Returns: 1 if the object exists, 0 if not.

     Finally, to remove all objects, walls and floor tiles:

int ism_empty_isom_world(void);
Description: Completely empties the isometric world.
Parameters: None.
Returns: 0 if it worked, -1 if it failed.


- Collision detection

     If, when trying to move an object, it chrashes against another one, against a wall, or against the floor, the called function will return an error, and the movement will not be done. Appart from that, the identifiers of all object, walls, etc. against it had crashed will be stored on an engine's inner stack. To take a look at this stack and see what the object collided with, you can use succesive calls to the following function:

int ism_get_coll_id(void);
Description: Extracts an identifier from the collisions stack.
Parameters: None.
Returns: NO_ID if the last object you tried to move crashed against nothing, or if you've already extracted all the identifiers from the collisions stack on previous calls to this function, the identificator of an object against it collided with, or ID_HIGH_X_WALL, ID_HIGH_Y_WALL, ID_LOW_X_WALL, ID_LOW_Y_WALL or ID_FLOOR if it crashed against a wall or against the floor.

     If you want to know if the last move object collided against a given object, wall, or against the floor:

int ism_collided_against(ism_id id);
Description: Checks if the last moved object has collded against the floor, a given wall or a given object.
Parameters: id: the id of the object you want to check, or ID_HIGH_X_WALL, ID_HIGH_Y_WALL, ID_LOW_X_WALL, ID_LOW_Y_WALL or ID_FLOOR if you want to check the walls or the floor.
Returns: 1 if the last moved object collided against the given object, wall or floor, 0 if not.

     If you want to know how many objects, including walls and the floor, the last moved object collided with:

int ism_collisions_number(void);
Description: Returns the number of objects, including the walls and floor, against wich the last moved object collided with.
Parameters: None.
Returns: The nomber of objects, including the walls and floor, against wich the last moved object collided with.


- Grid coordinates

     When using the engine functions to ger a grid object's coordinates, what you get for x and y is not their axis based real coordinates, what we can call "free coordinates", but the ones refered to their position on the grid, what we can call "grid coordinates". Most of the time this must be more than enough for our purpouses, but sometimes it can be useful to translate coordinates from one format to the other, when comparing the positions of a grid object and a free one, or to calculate the distance from one to the other, for example. To do this, we have the two following functions:

int ism_get_free_coords(int x, int y, int *fx, int *fy);
Description: Gets the x and y coords, refered to the coordinates axis, of the leftmost point of a grid's cell.
Parameters: x, y: the cell's grid coords.
fx, fy: pointers to the variables where you want the free x and y to be stored.
Returns: 0 if it worked, -1 if it failed.

int ism_get_cell_coords(int x, int y, int *cx, int *cy);
Description: Gets the grid coordinates of the cell over wich a certain point is placed.
Parameters: x, y: x and y free coords of the given point.
xc, yc: pointers to the variables where you want the grid x and y to be stored.
Returns: 0 if it worked, -1 if it failed.


- Converting between 2d and 3d coordinates

     When using Isomot, you will tipycally use two kind of coordinates, the 2d ones refering to your bitmaps, and the 3d ones from your isometric world. Sometimes, you may want to translate coordinates between thos two kinds (to use the mouse, for example). You can do this with the two following functions:

int ism_coords_iso_to_2d(int xiso, int yiso, int ziso, int *x2d, int *y2d);
Description: Turns 3d isometric coordinates into their 2d bitmap equivalents. That is, tells you where a certain isometric point will be drawn on your destination bitmap.
Parameters: xiso, yiso, ziso: the isometric coordinates you want to translate into 2d.
x2d, y2d: pointers to the variables where you want the 2d x and y to be stored.
Comments: The returned 2d coordinates refer to the bitmap where you are drawing your isometric world, and where on it are you are drawing the isometric origin of coordinates, that is, the three parameters of your last call to ism_draw_isom_world(BITMAP *map_dest, int x0, int y0);.
Returns: 0 if it worked, -1 if it failed.

int ism_coords_2d_to_iso(int *xiso, int *yiso, int *ziso, int x2d, int y2d, int fixedv, unsigned char fixedc);
Description: Turns 2d bitmap coordinates into their 3d isometric equivalents. That is, tells what's the isometric point that's aligned with a certain 2d point of your destination bitmap and is into a certain plane.
Parameters: xiso, yiso, ziso: pointers to the variables where you want the isometric coordinates to be stored.
x2d, y2d: x and y of the 2d bitmap point.
fixedv, fixedc: the plane that contains the isometric point. It must be parallel to the x, y or z plane, so fixedc can be D_X, D_Y or D_Z, while fixedv is the coordinate of that plane.
Comments: Please note that a 2d bitmap point doesn't give you a single isometric point, but a whole line that's perpendicular to the bitmap, so to give you a single point, that line must be intersected by a plane, and that's the one you must tell the function with the last two parameters. For example, if you want to know about an iso point on the floor that's at your 2d bitmap coordinates, fixedv must be 0 and fixedc D_Z.
Returns: 0 if it worked, -1 if it failed.


- Shadows

     When placing new objects, we can define the shape of the shadow they cast over the floor and the other objects just below it, using the shadow parameter. This parameter is just a map with a mask showing the shape of the shadow. The drawing intensity of the shadows is a 50% by default, but you can change this percentage with the following function:

int ism_set_shadowing_percentage(char shadowper);
Description: Sets the shadowing percentage between 0% (no shadows) and 100% (full black shadows).
Parameters: shadowper: shadowing percentage to set.
Returns: 0 if it worked, -1 if it failed.


- Transparent objects

     Another one of free object's posibilities is a certain transparency level. All free objects are created with a 0% transparency level by default, but you can change it between 0 (opaque) and 100 (totally invisible) using the ism_change_objet_data function, telling it to modify the D_TRANSP data. You can check an object's transparency level too, just by using the ism_get_objet_data function, telling it to give you the D_TRANSP data. As an extra detail, and to give it a touch of realism, the more transparent an object is, the lighter it's shadow will be casted.

     To draw transparent objects, Isomot uses the standard Allegro functions. As we all know, they are not very fast, so if you want Isomot to use your own functions, or any other library ones, like FBlend for example (I recommend you this one, make a search for it on www.allegro.cc for more info about it), you can tell Isomot using the following function:

int ism_set_transp_func(void(*f_transp)(BITMAP *, BITMAP *, int, int, int));
Description: Sets the function Isomot must use to draw transparent objects.
Parameters: f_transp: the transparency function to be used.
Comments: f_transp must be a function returning void, and taking the following parameters: (BITMAP *src, BITMAP *dst, int x, int y, int fact), where src and dst are the source and destiny maps (that is, src will be drawn on dst), x and y are the coordinates of dst where scr will be drawn, and fact is the transparency factor, ranging from 0 (what's a 100% transparency level) to 255 (0%). This is just the same declaration as the one for the fblend_trans function from the FBlend library, and that's not a coincidence ;). So, if you use FBlend on your project, you just have to call ism_set_transp_func(fblend_trans); fot Isomot to use it, instead of the Allegro standard blenders.
Returns: 0 if it worked, -1 if it failed.


- Drawing the isometric view

     All right. We have defined the grid, we have asigned bitmaps to the walls and floor tiles, and we have inserted a few objects. How can we see now the results of all this work? Easy. Just call the following function:

int ism_draw_isom_world(BITMAP *map_dest, int x0, int y0);
Description: Draws the isometric view to a given map.
Parameters: map_dest: the bitmap where you want the isometric view to be drawn.
x0, y0: the point of map_dest where you want the (0,0,0) point to be drawn.
Returns: 0 if it worked, -1 if it failed.

     Appart from this, you can tell Isomot to draw the isometric world darkened using a given percentage, so you can show a room at night, or whatever you want. You can tell Isomot what percentage to use with the following:

int ism_set_gloom_percentage(char gloomper);
Description: Sets the darkening percentage to be used each time you call ism_draw_isom_world.
Parameters: gloomper: darkening percentage, from 0 (no darkening) to 100 (pitch black).
Returns: 0 if it worked, -1 if it failed.

     Isomot uses a "dirty rectangles" optimisation that can boost it's performance, but can give a few problems if you do some odd things like drawing the isometric view to more than one bitmap, or clear the target bitmap before each call to ism_draw_isom_world. If this happens, you can disable the dirty rectangles using the following function:

void ism_enable_dirty_rect(char enable);
Description: enables or disables the dirty rectangles.
Parameters: enable: tells if you want to enable (value 1) or disable (value 0) the dirty rectangles (by default, they are allways enabled).
Returns: Nothing.


- Errors

     If a function returns an error, you will propably want to know what went wrong. You can get a description of the error calling the following function:

char *ism_error_desc(void);
Description: Returns a string with the error's explanation.
Parameters: None.
Returns: A string with the error's explanation.


- The ism_lng.h file

     As I've said before, Isomot has an Spanish/english bilingual interface. By default, you can use both interfaces withouth any problem, but the most probable thing, as you are reading the English version of these docs, is that you only are going to use the English functions. If that is the case you can avoid the Spanish interface compilation, reducing a bit the executable size and improving it a little too, by editing the ism_lng.h file and removing the ISM_ESP definition. It's true that none of the two advantages is really big, but every little thing helps.


- Functions summary

     The following is an alphabetically ordered list of all Isomot's functions. Click on them to see the detailed description of each one.

ism_change_object_data(ism_id id, unsigned char data, int value, unsigned char mode);
ism_change_object_map(ism_id id, BITMAP *map);
ism_change_object_shadow(ism_id id, BITMAP *shadow);
ism_collided_against(ism_id id);
ism_collisions_number(void);
ism_coords_2d_to_iso(int *xiso, int *yiso, int *ziso, int x2d, int y2d, int fixedv, unsigned char fixedc);
ism_coords_iso_to_2d(int xiso, int yiso, int ziso, int *x2d, int *y2d);
ism_destroy_all(void);
ism_draw_isom_world(BITMAP *map_dest, int x0, int y0);
ism_empty_isom_world(void);
ism_enable_dirty_rect(char enable);
ism_error_desc(void);
ism_get_cell_coords(int x, int y, int *cx, int *cy);
ism_get_coll_id(void);
ism_get_free_coords(int x, int y, int *fx, int *fy);
ism_get_object_coords(ism_id id, int *dstx, int *dsty, int *dstz);
ism_get_object_data(ism_id id, unsigned char data);
ism_move_floor(int offsetx, int offsety);
ism_move_floor_tile(int x, int y, int offsetx, int offsety);
ism_move_object(ism_id id, int x, int y, int z, unsigned char mode);
ism_move_wall(unsigned char axis, int wall, int offsetaxis, int offsetheight);
ism_object_exists(ism_id id);
ism_put_free_object(int x, int y, int z, int widthx, int widthy, int height, BITMAP *map, BITMAP *shadow);
ism_put_grid_object(int x, int y, int z, int height, BITMAP *map, BITMAP *shadow);
ism_remove_object(ism_id id);
ism_set_floor(BITMAP *map);
ism_set_floor_tile(BITMAP *map, int x, int y);
ism_set_gloom_percentage(char gloomper);
ism_set_grid(int x, int y, int width);
ism_set_shadowing_percentage(char shadowper);
ism_set_transp_func(void(*transp_f)(BITMAP *, BITMAP *, int, int, int));
ism_set_wall(BITMAP *map, unsigned char axis, int wall);


- Programming tips

     First of all, try to keep the grid and the number of object as small as possible. This way the engine will use less memory and work faster (obvious, isn't it?).

     You can't move a grid object on the X or Y axis (appart from other restrictions), so you will probably think on setting all your objects as free ones. Don't do that! Grid objects are much faster to handle, and use much less memory than free ones.

     If you are going to handle various objects, don't start with one until you have finished with the previous. For example:
     x1=ism_get_object_data(obj1,D_X);
     ism_modify_object_data(obj1,x1+1,D_X);
     x2=ism_get_object_data(obj2,D_X);
     ism_modify_object_data(obj2,x2+1,D_X);
   will work much faster than:
     x1=ism_get_object_data(obj1,D_X);
     x2=ism_get_object_data(obj2,D_X);
     ism_modify_object_data(obj1,D_X,x1+1);
     ism_modify_object_data(obj2,D_X,x2+1);

     The toutines that draw shadows are optimized for for certain shadowing percentages, so they work much faster with then set. Those percentages are, for 24 and 32 color depth maps, 50%, 75%, 87%, 93%, 97%, 98%, 99% and 100%, and for 15 and 16 bits, 47%, 48%, 49%, 50%, 72%, 73%, 74%, 75%, 85%, 86%, 87%, 91%, 92%, 93%, 97%, 98%, 99%, y 100%. I know they look strange, but there is a perfectly logical reason for those percentages to be the optimized ones, and not others ;)

     The posibilities of the ism_set_transp_func function are much more than improve the drawing of transparent objects; you can write your own function and use the transparency level to apply special effects to the objects, like darkening them, drawing them black and white, draw transparent only certain color components, or anything you can think.


- History

* 18/05/2000 v1.0.
- Added the ism_coords_iso_to_2d and ism_coords_2d_to_iso functions, to convert from 2d bitmap and 3d isometric coordinates.
- Dirty rectangles are now disabled by default. If you want them, you must enable them with ism_enable_dirty_rect.
- Improved C++ compatibility.
- Minor bugfixes here and there.

* 27/05/2005 vb0.5.
- Added the ism_set_floor and ism_move_floor functions, to draw the floor using a single bitmap, appart from a tiled mosaic.
- Added the ism_set_gloom_percentage so you can tell Isomot to draw the room darkened by the given percentage.
- Aadded a dirty rectangles optimisation, and the ism_enable_dirty_rect, to disable it if neccesary.
- Acouple of not very important bugfixes.

* 02/11/2004 vb0.4.
- This version was never made public, as it only had a couple of minor changes.

* 01/08/2003 vb0.3.
- Added the ism_collided_against and ism_collisions_number functions, to make collision detection easier (thanks to Ron Ophir for the idea).
- Support for transparent objects.
- Optimized shadow casting.

* 30/04/2003 vb0.2.
- Support for any color depth bitmaps.
- Added the ism_lng file for interface optimisation.
- Just from the start, Isomot was planned to be a C project, but I has ben compilin it all this time as a C++ project withouth noticing it. This caused a lot of errors to appear when I compiled as plain C. All of them are fixed now (thanks to Neil Walker for the tip).
- A couple of minor optimisations and bugfixes.

* 22/03/2003 vb0.1
- First public beta.


- Acknowledgements

     A lot of people have helped me on this project, my Retrospec colleagues the most: I owe Tomaz Kak the masking shadowing method, Isomot's logo is based on an idea by Dan Condon-Jones, Neil Walker helped me a lot to debug and optimise the code, and all the others, Arif Majothi, Bill Harbison, Graham Goring, Jeff Brayne, John Dow, Matthew Smith, Peter Jovanovic, Richard Jordan and Russ Hoy, have helped me a lot with their comments and support. Iñaki Martínez and Ron Ophir have helped a lot too, with their valuable comments and cooperation. And I want to say thanks too to all people that have e-mailed me about the project, encouraging me to finish it.


©2008 - Ignacio Pérez Gil.
Ignacio's SP Remakes logo by Ric Lumb.

Those pages are kindly hosted by  NETSI