Aozora technical information Author - David Johnston Date - 5/4/99 This file contains information about some of the wierd stuff that is used to make Aozora. Hopefully at least one person will find this useful/interesting. Map Info The map for an area consists of a 128x128 grid. Each location within this grid has with it: A tile ID which is explained later but basically refers to a 3D model that should occupy this grid square. A height which is in the range 0..15, this refers to how high the 3D model is off the ground in units of one grid square. An orientation in the range 0..3 with 0 being no rotation, 1 being 90 degrees anticlockwise, 2 180 degrees and 3 270 degrees. In memory 2 bytes are used for each grid square. One byte is used for the tile ID and the second byte contains the orientation in the lower 2 bits and the height in the 4 bits above it. The upper two bits of this byte may need to be used to represent an extra two high bits for the tile ID. The maps are stored in memory using a 12-bit LZW algorithm and the map the player is currently in is decompressed into a seperate area of memory. This not only allows more maps to be stored but ensures that the current map is always at the same point in memory. Tile IDs A tile ID refers to polygon data that is used to fill a column of 3D space which is regarded in the data as being 15x15x225 units big. At the beginning of the block of memory containing translations between tile IDs and polys (tileinfo) is a header. This header consists of a list of relative addresses from the beginning of the header. Each address occupies 2 bytes so that the address for polygon data for tile ID n is located at tileinfo+*(tileinfo+(n*2)). The data for each tile ID is built up as follows: number of vertices list of vertices number of polygons list of polygons collision data Each vertex occupies 2 bytes. The lower 4 bits of the first byte contain the X value with the upper 4 containing the Z value. The Y value is stored in the 2nd value. (This is why each model can only be 15x15x225 units big) The first byte of each polygon specifies the type of polygon that it is. The following is a list of possible types along with the information that follows it: 0 - 0/128 (Texture mapped quadrangle, 128 if transparent) 1 - Sprite no. (see below) 2 - Vertex 1 3 - Vertex 2 4 - Vertex 3 5 - Vertex 4 6 - Normal (This isn't used as lighting is never performed) 0 - 1/129 (Texture mapped triangle, 129 if transparent) 1 - Sprite no. 2 - Corner of sprite (see below) 3 - Vertex 1 4 - Vertex 2 5 - Vertex 3 6 - Normal (This isn't used as lighting is never performed) 0 - 2/130 (Coloured quadrangle, 130 if transparent) 1 - red 2 - green 3 - blue 4 - Vertex 1 5 - Vertex 2 6 - Vertex 3 7 - Vertex 4 8 - Normal (This isn't used as lighting is never performed) 0 - 3/131 (Animated texture mapped quadrangle, 131 if transparent) 1 - Animation type 2 - Sprite no. 3 - Vertex 1 4 - Vertex 2 5 - Vertex 3 6 - Vertex 4 7 - Starting position (see below) 0 - 4/132 (Coloured triangle, 132 if transparent) 1 - red 2 - green 3 - blue 4 - Vertex 1 5 - Vertex 2 6 - Vertex 3 A sprite number refers to one of the standard sprites in the game. The u & v coordinates for the sprites position in VRAM is calculated within the game. The corner of sprite value in the texture mapped triangle is needed because the sprite number refers to a square sprite. This value determines wether the top-left, top-right, bottom-left or bottom-right triangle of the sprite is used. The starting position in the animated poly is used to let animated polygons around it be offset from each other and thus retain a fluid animation. A vertex number refers to a point in 3D that the polygon has corner at. If this value is greater or equal to 64 then the value-64th entry in the vertex table is used. However if the number is in the range 0..63 then a standard vertex is used. A standard vertex is one that lies on the corner of a standard block. 0 is the vertex at the bottom-back-left of the 3D column, 1 is back-right, 2 is front-left and 3 is front-right. This sequence continues moving one block up the 3D column at a time so vertex 63 is the vertex at the very top of the column at the front-right. Creating TMD data Within the game only a grid of 24x24 tiles are created as 3D models and displayed at a time. Each time this area needs to be updated all the existing TMD models are scrapped and a pointer moved to the bottom of the area of memory they are stored. Each tile ID is then retrieved and the appropriate polygon information for each tile is found. TMD data is then built up in memory at the pointer using the polygon information and then a model is linked to it so it can be displayed. 6 tiles that are used frequently are stored first so that the many references to their tile ID's don't cause new TMD data to be built up. Each time their ID is encountered the model is linked to the appropriate address. Randomizing textures Some textures in the game appear often (grass, mud wall) so 3 different textures are actually used to break up the monotony. This also helps prevent the problem of a player moving down a passage of the same texture and appearing not to move. However, as the TMD data is recreated each time the area is updated a model that the player can see could get re-made as he watches it. If a different random texture is picked at this point it doesn't look very good. To solve this when the map is decompressed into memory a random number for each tile is also stored. This is used to create the random texture and ensures that the same one gets used each time. Night and Day To create the effect of going from day to night and vica versa 16 CLUTs are stored. CLUT 0 represents the true colours of the sprites and CLUT 15 represents the colours of the sprites at night (a slight blue). When the TMD data is getting built the correct CLUT for the time of day is stored with it. The timer that keeps track of the time of day forces an update of the current area if the CLUT should change. For the coloured polys that have an RGB value the following equation is used to calculate their true colour: displayed_red = red+=(CLUT*((red+green+blue)/3-16))/16); displayed_green = green+=(CLUT*((red+green+blue)/3-16))/16); displayed_blue = blue+=(CLUT*((red+green+blue)/3+24))/16); Well, I hope there was something there that proved to be of interest. David Johnston