diff options
Diffstat (limited to 'src/mame/video/hng64.c')
-rw-r--r-- | src/mame/video/hng64.c | 2083 |
1 files changed, 2083 insertions, 0 deletions
diff --git a/src/mame/video/hng64.c b/src/mame/video/hng64.c new file mode 100644 index 00000000000..8a0af6c22ce --- /dev/null +++ b/src/mame/video/hng64.c @@ -0,0 +1,2083 @@ +#include "driver.h" + +#include <math.h> + + +// !!! I'm sure this isn't right !!! +UINT32 hng64_dls[2][0x81] ; + +static int frameCount = 0 ; + +UINT32* hng64_videoram; +tilemap *hng64_tilemap0; +tilemap *hng64_tilemap1; +tilemap *hng64_tilemap2; +tilemap *hng64_tilemap3; + +UINT32 *hng64_spriteram; +UINT32 *hng64_videoregs; + +UINT32 *hng64_tcram ; + +/* HAAAACK to make the floor 'work' */ +UINT32 hng64_hackTilemap3, hng64_hackTm3Count, hng64_rowScrollOffset ; + + +static void matmul4( float *product, const float *a, const float *b ) ; +static void vecmatmul4( float *product, const float *a, const float *b) ; +//static float vecDotProduct( const float *a, const float *b) ; +//static void normalize(float* x) ; + +// 3d helpers +static float uToF(UINT16 input) ; +static void SetIdentity(float *matrix) ; + + +//static void plot(INT32 x, INT32 y, INT32 color, mame_bitmap *bitmap) ; +//static void drawline2d(INT32 x0, INT32 y0, INT32 x1, INT32 y1, INT32 color, mame_bitmap *bitmap) ; + +static float *depthBuffer ; +static struct polygon *polys ; + +//static int tilemap2Offset = 0x10000 ; + + +#define WORD_AT(BUFFER,OFFSET) ( (BUFFER[OFFSET] << 8) | BUFFER[OFFSET+1] ) + + +/* + * Sprite Format + * ------------------ + * + * UINT32 | Bytes | Use + * -------+-76543210-+---------------- + * 0 | xxxx---- | y position + * 0 | ----xxxx | x position + * 1 | xxxx---- | y zoom + * 1 | ----xxxx | x zoom + * 2 | ------x- | x chain + * 2 | -------x | y chain + * 2 | xxxx---- | end of sprite list - though it surely contains more info ! + * 2 | ----oo-- | not used ?? + * 3 | --xx---- | palette entry + * 3 | --x----- | (bit 0x8) - maybe a graphics bank selector + * 3 | oo--oooo | not used ? + * 4 | --xxxxxx | tile number + * 4 | --x----- | (bit 0x2) - x flip + * 4 | --x----- | (bit 0x1) - y flip + * 4 | oo------ | not used ?? + * 5 | oooooooo | not used ?? + * 6 | oooooooo | not used ?? + * 7 | oooooooo | not used ?? + */ + +/* xxxx---- | I think this part of UINT32 2 is interesting as more than just a list end marker (AJG) + */ + +static void draw_sprites(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect) +{ + const gfx_element *gfx; + UINT32 *source = hng64_spriteram; + UINT32 *finish = hng64_spriteram + 0xb000/4; + + /* find end of list? */ + while( source<finish) + { + int endlist; + + endlist=(source[2]&0xffff0000)>>16; + if (endlist == 0x07ff) break; + source+=8; + } + +// for (int iii = 0; iii < 0x0f; iii++) +// mame_printf_debug("%.8x ", hng64_videoregs[iii]) ; + +// mame_printf_debug("\n") ; + + finish = hng64_spriteram; + + /* draw backwards .. */ + while( source>finish ) + { + int xpos, ypos, tileno,chainx,chainy,xflip; + int xdrw,ydrw,pal,xinc,yinc,yflip; + UINT32 zoomx,zoomy; + float foomX, foomY; + source-=8; + + ypos = (source[0]&0xffff0000)>>16; + xpos = (source[0]&0x0000ffff)>>0; + tileno=(source[4]&0x00ffffff); + chainx=(source[2]&0x000000f0)>>4; + chainy=(source[2]&0x0000000f); + + zoomy = (source[1]&0xffff0000)>>16; + zoomx = (source[1]&0x0000ffff)>>0; + + pal =(source[3]&0x00ff0000)>>16; + xflip=(source[4]&0x02000000)>>25; + yflip=(source[4]&0x01000000)>>24; + + if(xpos&0x8000) xpos -=0x10000; + if(ypos&0x8000) ypos -=0x10000; + + +// if (!(source[4] == 0x00000000 || source[4] == 0x000000aa)) +// mame_printf_debug("unknown : %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x \n", source[0], source[1], source[2], source[3], +// source[4], source[5], source[6], source[7]) ; + + /* Calculate the zoom */ + /* First, prevent any possible divide by zero errors */ + if(!zoomx) zoomx=0x1000; + if(!zoomy) zoomy=0x1000; + + foomX = (float)(0x1000) / (float)zoomx ; + foomY = (float)(0x1000) / (float)zoomy ; + + zoomx = ((int)foomX) << 16 ; + zoomy = ((int)foomY) << 16 ; + + zoomx += (int)((foomX - floor(foomX)) * (float)0x10000) ; + zoomy += (int)((foomY - floor(foomY)) * (float)0x10000) ; + + + if (source[3]&0x00800000) // maybe .. + { + gfx= machine->gfx[4]; + } + else + { + gfx= machine->gfx[5]; + tileno>>=1; + + // Just a big hack to make the top and bottom tiles in the intro not crash (a pal value of 0x70 is bad) + // Is there a problem with draw_sprites? Doubtful... + if (source[2] == 0x00080000) + pal >>=4; + } + + // Accomodate for chaining and flipping + if(xflip) + { + xinc=-(int)(16.0f*foomX); + xpos-=xinc*chainx; + } + else + { + xinc=(int)(16.0f*foomX); + } + + if(yflip) + { + yinc=-(int)(16.0f*foomY); + ypos-=yinc*chainy; + } + else + { + yinc=(int)(16.0f*foomY); + } + + +// if (((source[2] & 0xffff0000) >> 16) == 0x0001) +// { +// usrintf_showmessage("T %.8x %.8x %.8x %.8x %.8x", source[0], source[1], source[2], source[3], source[4]) ; +// // usrintf_showmessage("T %.8x %.8x %.8x %.8x %.8x", source[0], source[1], source[2], source[3], source[4]) ; +// } + + for(ydrw=0;ydrw<=chainy;ydrw++) + { + for(xdrw=0;xdrw<=chainx;xdrw++) + { + drawgfxzoom(bitmap,gfx,tileno,pal,xflip,yflip,xpos+(xinc*xdrw),ypos+(yinc*ydrw),cliprect,TRANSPARENCY_PEN,0,zoomx,zoomy/*0x10000*/); + tileno++; + } + } + } +} + + +/* Transition_Control Memory Region Map + * ------------------------------ + * + * UINT32 | Bytes | Use + * -------+-76543210-+---------- + * 0 | | + * 1 | | + * 2 | | + * 3 | | + * 4 | | + * 5 | | + * 6 | --xxxxxx | I popped into Buriki and saw some of these values changing to the same as 7. hmmmm... + * 7 | --xxxxxx | Almost certainly RGB darkening + * 8 | | + * 9 | | + * 10 | --xxxxxx | Almost certainly RGB brightening + * 11 | xxxxxxxx | Unknown - looks like an ARGB value - it seems to change when the scene changes + * 12 | | + * 13 | | + * 14 | | + * 15 | | + * + * Various bits change depending on what is happening in the scene. + * These bits may set which 'layer' is affected by the blending. + * Or maybe they adjust the scale of the lightening and darkening... + * Or maybe it switches from fading by scaling to fading using absolute addition and subtraction... + * Or maybe they set transition type (there seems to be a cute scaling-squares transition in there somewhere)... + */ +static void transition_control(running_machine *machine, mame_bitmap *bitmap) +{ + int i, j ; + +// float colorScaleR, colorScaleG, colorScaleB ; +// float finR, finG, finB ; + INT32 finR, finG, finB ; + + INT32 darkR, darkG, darkB ; + INT32 brigR, brigG, brigB ; + + // If either of the fading memory regions is non-zero... + if (hng64_tcram[0x00000007] != 0x00000000 || hng64_tcram[0x0000000a] != 0x00000000) + { + darkR = (INT32)( hng64_tcram[0x00000007] & 0xff) ; + darkG = (INT32)((hng64_tcram[0x00000007] >> 8) & 0xff) ; + darkB = (INT32)((hng64_tcram[0x00000007] >> 16) & 0xff) ; + + brigR = (INT32)( hng64_tcram[0x0000000a] & 0xff) ; + brigG = (INT32)((hng64_tcram[0x0000000a] >> 8) & 0xff) ; + brigB = (INT32)((hng64_tcram[0x0000000a] >> 16) & 0xff) ; + + for (i = machine->screen[0].visarea.min_x; i < machine->screen[0].visarea.max_x; i++) + { + for (j = machine->screen[0].visarea.min_y; j < machine->screen[0].visarea.max_y; j++) + { + UINT32* thePixel = BITMAP_ADDR32(bitmap, j, i); + + finR = (INT32)RGB_RED(*thePixel) ; + finG = (INT32)RGB_GREEN(*thePixel) ; + finB = (INT32)RGB_BLUE(*thePixel) ; + + /* + // Apply the darkening pass (0x07)... + colorScaleR = 1.0f - (float)( hng64_tcram[0x00000007] & 0xff) / 255.0f ; + colorScaleG = 1.0f - (float)((hng64_tcram[0x00000007] >> 8) & 0xff) / 255.0f ; + colorScaleB = 1.0f - (float)((hng64_tcram[0x00000007] >> 16) & 0xff) / 255.0f ; + + finR = ((float)RGB_RED(*thePixel) * colorScaleR) ; + finG = ((float)RGB_GREEN(*thePixel) * colorScaleG) ; + finB = ((float)RGB_BLUE(*thePixel) * colorScaleB) ; + + + // Apply the lightening pass (0x0a)... + colorScaleR = 1.0f + (float)( hng64_tcram[0x0000000a] & 0xff) / 255.0f ; + colorScaleG = 1.0f + (float)((hng64_tcram[0x0000000a] >> 8) & 0xff) / 255.0f ; + colorScaleB = 1.0f + (float)((hng64_tcram[0x0000000a] >> 16) & 0xff) / 255.0f ; + + finR *= colorScaleR ; + finG *= colorScaleG ; + finB *= colorScaleB ; + + + // Clamp + if (finR > 255.0f) finR = 255.0f ; + if (finG > 255.0f) finG = 255.0f ; + if (finB > 255.0f) finB = 255.0f ; + */ + + + // Subtractive fading + if (hng64_tcram[0x00000007] != 0x00000000) + { + finR -= darkR ; + finG -= darkG ; + finB -= darkB ; + } + + // Additive fading + if (hng64_tcram[0x0000000a] != 0x00000000) + { + finR += brigR ; + finG += brigG ; + finB += brigB ; + } + + // Clamp the high end + if (finR > 255) finR = 255 ; + if (finG > 255) finG = 255 ; + if (finB > 255) finB = 255 ; + + // Clamp the low end + if (finR < 0) finR = 0 ; + if (finG < 0) finG = 0 ; + if (finB < 0) finB = 0 ; + + *thePixel = MAKE_ARGB(255, (UINT8)finR, (UINT8)finG, (UINT8)finB) ; + } + } + } +} + + +/* + * 3d 'Sprite' Format + * ------------------ + * + * (documented below) + * + */ + +#define MAX_ONSCREEN_POLYS (10000) + +struct polyVert +{ + float worldCoords[4] ; // World space coordinates (X Y Z 1.0) + + float texCoords[4] ; // Texture coordinates (U V 0 1.0) -> OpenGL style... + + float normal[4] ; // Normal (X Y Z 1.0) + float clipCoords[4] ; // Homogeneous screen space coordinates (X Y Z W) + + float light[3] ; // The intensity of the illumination at this point +} ; + +struct polygon +{ + int n ; // Number of sides + struct polyVert vert[10] ; // Vertices (maximum number per polygon is 10 -> 3+6) + + float faceNormal[4] ; // Normal of the face overall - for calculating visibility and flat-shading... + int visible ; // Polygon visibility in scene + + INT8 texIndex ; // Which texture to draw from (0x00-0x0f) + INT8 texType ; // How to index into the texture + UINT8 palIndex ; // Which palette to use when rasterizing +} ; + + +static void PerformFrustumClip(struct polygon *p) ; + +//static void DrawWireframe(struct polygon *p, mame_bitmap *bitmap) ; +static void DrawShaded(running_machine *machine, struct polygon *p, mame_bitmap *bitmap) ; + + +static void draw3d(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect ) +{ + int i,j,k,l,m ; + + float projectionMatrix[16] ; + float modelViewMatrix[16] ; + float cameraMatrix[16] ; + float objectMatrix[16] ; + + int paletteState = 0x00 ; + + UINT32 numPolys = 0 ; + + struct polygon lastPoly = { 0 } ; + + // Set some matrices to the identity... + SetIdentity(projectionMatrix) ; + SetIdentity(modelViewMatrix) ; + SetIdentity(cameraMatrix) ; + SetIdentity(objectMatrix) ; + + // Display list 2 comes after display list 1. Go figure. + for (j = 1; j >= 0; j--) + { + UINT32 *workingList = hng64_dls[j] ; + + for (i = 0; i < 0x80; i += 0x08) + { + float left, right, top, bottom, near_, far_ ; + UINT8 *threeDRoms ; + UINT8 *threeDPointer ; + UINT32 threeDOffset ; + UINT32 size[4] ; + UINT32 address[4] ; + UINT32 megaOffset ; + float eyeCoords[4] ; // objectCoords transformed by the modelViewMatrix + // float clipCoords[4] ; // eyeCoords transformed by the projectionMatrix + float ndCoords[4] ; // normalized device coordinates/clipCoordinates (x/w, y/w, z/w) + float windowCoords[4] ; // mapped ndCoordinates to screen space + float cullRay[4] ; + + // Debug... +// mame_printf_debug("Element %.2d (%d) : %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", i/0x08, j, +// (UINT32)hng64_dls[j][i+0], (UINT32)hng64_dls[j][i+1], (UINT32)hng64_dls[j][i+2], (UINT32)hng64_dls[j][i+3], +// (UINT32)hng64_dls[j][i+4], (UINT32)hng64_dls[j][i+5], (UINT32)hng64_dls[j][i+6], (UINT32)hng64_dls[j][i+7]) ; + + // Depending on what the initial flags are, do sumthin'... + switch((workingList[i+0] & 0xffff0000) >> 16) + { + case 0x0012: + // UNKNOWN - seems an awful lot parameters for a projection matrix though... + + ; + + // It changes when 'How to play' is on the screen... not too much, but if this is right, the aspect + // ratio is different... + + // Heisted from GLFrustum - 6 parameters... + left = uToF( workingList[i+5] & 0x0000ffff) ; + right = uToF((workingList[i+5] & 0xffff0000) >> 16) ; + top = uToF((workingList[i+6] & 0xffff0000) >> 16) ; + bottom = uToF( workingList[i+6] & 0x0000ffff) ; + near_ = uToF((workingList[i+3] & 0xffff0000) >> 16) ; + far_ = uToF( workingList[i+3] & 0x0000ffff) ; + + // It's almost .always. these values in fatfurwa... + // 0.070313 0.000000 [0] (scaled by 128) + // 0.000000 10.000000 [1] + // 0.000000 0.500000 [2] + // 2.000000 11.062500 [3] + // 10.000000 11.000000 [4] + // 1.000000 -1.000000 [5] + // 0.875000 -0.875000 [6] + // 0.000000 0.000000 [7] + + projectionMatrix[0] = (2.0f*near_)/(right-left) ; + projectionMatrix[1] = 0.0f ; + projectionMatrix[2] = 0.0f ; + projectionMatrix[3] = 0.0f ; + + projectionMatrix[4] = 0.0f ; + projectionMatrix[5] = (2.0f*near_)/(top-bottom) ; + projectionMatrix[6] = 0.0f ; + projectionMatrix[7] = 0.0f ; + + projectionMatrix[8] = (right+left)/(right-left) ; + projectionMatrix[9] = (top+bottom)/(top-bottom) ; + projectionMatrix[10] = -((far_+near_)/(far_-near_)) ; + projectionMatrix[11] = -1.0f ; + + projectionMatrix[12] = 0.0f ; + projectionMatrix[13] = 0.0f ; + projectionMatrix[14] = -((2.0f*far_*near_)/(far_-near_)) ; + projectionMatrix[15] = 0.0f ; + + /* + int xxx ; + for (xxx = 0; xxx < 16; xxx++) + mame_printf_debug("%f ", projectionMatrix[xxx]) ; + mame_printf_debug("\n") ; + + mame_printf_debug("Vars : %f %f %f %f %f %f\n", left, right, top, bottom, near, far) ; + mame_printf_debug("Camera : %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n", + uToF((workingList[i+0] & 0xffff0000) >> 16)*128, uToF( workingList[i+0] & 0x0000ffff)*128, + uToF((workingList[i+1] & 0xffff0000) >> 16)*128, uToF( workingList[i+1] & 0x0000ffff)*128, + + uToF((workingList[i+2] & 0xffff0000) >> 16)*128, uToF( workingList[i+2] & 0x0000ffff)*128, + uToF((workingList[i+3] & 0xffff0000) >> 16)*128, uToF( workingList[i+3] & 0x0000ffff)*128, + + uToF((workingList[i+4] & 0xffff0000) >> 16)*128, uToF( workingList[i+4] & 0x0000ffff)*128, + uToF((workingList[i+5] & 0xffff0000) >> 16)*128, uToF( workingList[i+5] & 0x0000ffff)*128, + + uToF((workingList[i+6] & 0xffff0000) >> 16)*128, uToF( workingList[i+6] & 0x0000ffff)*128, + uToF((workingList[i+7] & 0xffff0000) >> 16)*128, uToF( workingList[i+7] & 0x0000ffff)*128) ; + */ + + break ; + + case 0x0001: + // CAMERA TRANSFORMATION MATRIX + cameraMatrix[0] = uToF( workingList[i+0] & 0x0000ffff) ; + cameraMatrix[4] = uToF((workingList[i+1] & 0xffff0000) >> 16) ; + cameraMatrix[8] = uToF( workingList[i+1] & 0x0000ffff) ; + cameraMatrix[3] = 0.0f ; + + cameraMatrix[1] = uToF((workingList[i+2] & 0xffff0000) >> 16) ; + cameraMatrix[5] = uToF( workingList[i+2] & 0x0000ffff) ; + cameraMatrix[9] = uToF((workingList[i+3] & 0xffff0000) >> 16) ; + cameraMatrix[7] = 0.0f ; + + cameraMatrix[2] = uToF( workingList[i+3] & 0x0000ffff) ; + cameraMatrix[6] = uToF((workingList[i+4] & 0xffff0000) >> 16) ; + cameraMatrix[10] = uToF( workingList[i+4] & 0x0000ffff) ; + cameraMatrix[11] = 0.0f ; + + cameraMatrix[12] = uToF((workingList[i+5] & 0xffff0000) >> 16) ; + cameraMatrix[13] = uToF( workingList[i+5] & 0x0000ffff) ; + cameraMatrix[14] = uToF((workingList[i+6] & 0xffff0000) >> 16) ; + cameraMatrix[15] = 1.0f ; + break ; + + case 0x0010: + // UNKNOWN - light maybe + break ; + + case 0x0011: + // Model Flags? + // 00110000 00000000 00000100 01000100 0400-0000 00007fff 00000000 00000020 + // ---- pal ---- -------- -------- -------- not used (known) + paletteState = (workingList[i+4] & 0xff000000) >> 24 ; + + break ; + + case 0x0100: + // GEOMETRY + ; + + threeDRoms = memory_region(REGION_GFX4) ; + + ///////////////////////// + // GET THE HEADER INFO // + ///////////////////////// + + // 3d ROM Offset + // !!! This might be more than just 20 bits... + threeDOffset = workingList[i+1] & 0x000fffff ; + threeDOffset = (threeDOffset<<1) * 3 ; + + threeDPointer = &threeDRoms[threeDOffset] ; + + + // 66 byte versus 48 byte chunk flag + // WRONG ! I think it's something to do with lighting... + // it's 0 for the 66-byte lit globe in the character select and 0 for something in terry's hand... + // if (workingList[i+0] & 0x00000010) + + + ////////////////////////////////////////// + // GET THE OBJECT TRANSFORMATION MATRIX // + ////////////////////////////////////////// + + objectMatrix[8 ] = uToF( workingList[i+3] & 0x0000ffff) ; + objectMatrix[4 ] = uToF((workingList[i+4] & 0xffff0000) >> 16) ; + objectMatrix[0 ] = uToF( workingList[i+4] & 0x0000ffff) ; + objectMatrix[3] = 0.0f ; + + objectMatrix[9 ] = uToF((workingList[i+5] & 0xffff0000) >> 16) ; + objectMatrix[5 ] = uToF( workingList[i+5] & 0x0000ffff) ; + objectMatrix[1 ] = uToF((workingList[i+6] & 0xffff0000) >> 16) ; + objectMatrix[7] = 0.0f ; + + objectMatrix[10] = uToF( workingList[i+6] & 0x0000ffff) ; + objectMatrix[6 ] = uToF((workingList[i+7] & 0xffff0000) >> 16) ; + objectMatrix[2 ] = uToF( workingList[i+7] & 0x0000ffff) ; + objectMatrix[11] = 0.0f ; + + objectMatrix[12] = uToF((workingList[i+2] & 0xffff0000) >> 16) ; + objectMatrix[13] = uToF( workingList[i+2] & 0x0000ffff) ; + objectMatrix[14] = uToF((workingList[i+3] & 0xffff0000) >> 16) ; + objectMatrix[15] = 1.0f ; + + + + ////////////////////////////////////////////////////////// + // EXTRACT DATA FROM THE ADDRESS POINTED TO IN THE FILE // + ////////////////////////////////////////////////////////// + + // Okay, there are 4 hunks per address. They all seem good too... + + // First tell me how many 'chunks' are at each address... + size[0] = WORD_AT( threeDPointer, ( 6<<1) ) ; + size[1] = WORD_AT( threeDPointer, ( 7<<1) ) ; + size[2] = WORD_AT( threeDPointer, ( 9<<1) ) ; + size[3] = WORD_AT( threeDPointer, (10<<1) ) ; + + megaOffset = ( WORD_AT(threeDRoms, (threeDOffset + 4)) ) << 16 ; + address[0] = megaOffset | WORD_AT(threeDPointer, (0<<1)) ; + address[1] = megaOffset | WORD_AT(threeDPointer, (1<<1)) ; + address[2] = megaOffset | WORD_AT(threeDPointer, (3<<1)) ; + address[3] = megaOffset | WORD_AT(threeDPointer, (4<<1)) ; + + // DEBUG + // mame_printf_debug("%.5x %.3x %.5x %.3x %.5x %.3x %.5x %.3x\n", address[0], size[0], address[1], size[1], address[2], size[2], address[3], size[3]) ; + // !! END DEBUG !! + + + + //////////////////////////////////// + // A FEW 'GLOBAL' TRANSFORMATIONS // + //////////////////////////////////// + + // Now perform the world transformations... + // !! Can eliminate this step with a matrix stack (maybe necessary?) !! + SetIdentity(modelViewMatrix) ; + matmul4(modelViewMatrix, modelViewMatrix, cameraMatrix) ; + matmul4(modelViewMatrix, modelViewMatrix, objectMatrix) ; + + for (k = 0; k < 4; k++) // For all 4 chunks + { + threeDPointer = &threeDRoms[(address[k]<<1) * 3] ; + + for (l = 0; l < size[k]; l++) + { + //////////////////////////////////////////// + // GATHER A SINGLE TRIANGLE'S INFORMATION // + //////////////////////////////////////////// + + UINT8 triangleType = threeDPointer[1] ; + + UINT8 numVertices = 3 ; + UINT8 chunkLength = 0 ; + + // Some chunks only have 1 vertex (they act as a vertex fan) + if (triangleType == 0x97 || + triangleType == 0x87 || + triangleType == 0xd7 || + triangleType == 0x96) + numVertices = 1 ; + + // Get which texture this polygon refers to... + // In fatfur it's 0xc for the smooth-shaded earth - maybe this is for all things with alpha - check the hair at some point... + // 0x9 for the untextured buildings + // 0xd for the 'explosion' of the HNG64 + // and 0x8 everywhere else... + // they're 0x8 in the buriki intro too (those are 66-byte chunks!) + polys[numPolys].texType = ((threeDPointer[2] & 0xf0) >> 4); + + if (polys[numPolys].texType == 0x8 || polys[numPolys].texType == 0xc) // || polys[numPolys].texType == 0x9 + polys[numPolys].texIndex = threeDPointer[3] & 0x0f ; + else + polys[numPolys].texIndex = -1 ; + + // Set the polygon's palette + polys[numPolys].palIndex = paletteState ; + + for (m = 0; m < numVertices; m++) // For all vertices of the chunk + { + switch(triangleType) + { + // 42 byte chunk + case 0x04: + case 0x0e: + polys[numPolys].vert[m].worldCoords[0] = uToF(WORD_AT(threeDPointer, ((3<<1) + (6<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[1] = uToF(WORD_AT(threeDPointer, ((4<<1) + (6<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[2] = uToF(WORD_AT(threeDPointer, ((5<<1) + (6<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[3] = 1.0f ; + polys[numPolys].n = 3 ; + + // !! What is the first coordinate here (6) - maybe denotes size of chunk? !! + polys[numPolys].vert[m].texCoords[0] = uToF(WORD_AT(threeDPointer, ((7<<1) + (6<<1)*m)) ) ; + polys[numPolys].vert[m].texCoords[1] = uToF(WORD_AT(threeDPointer, ((8<<1) + (6<<1)*m)) ) ; + polys[numPolys].vert[m].texCoords[2] = 0.0f ; + polys[numPolys].vert[m].texCoords[3] = 1.0f ; + + polys[numPolys].vert[m].normal[0] = uToF(WORD_AT(threeDPointer, (21<<1) )) ; + polys[numPolys].vert[m].normal[1] = uToF(WORD_AT(threeDPointer, (22<<1) )) ; + polys[numPolys].vert[m].normal[2] = uToF(WORD_AT(threeDPointer, (23<<1) )) ; + polys[numPolys].vert[m].normal[3] = 0.0f ; + + // !!! DUMB !!! + polys[numPolys].vert[m].light[0] = polys[numPolys].vert[m].texCoords[0] * 255.0f ; + polys[numPolys].vert[m].light[1] = polys[numPolys].vert[m].texCoords[1] * 255.0f ; + polys[numPolys].vert[m].light[2] = polys[numPolys].vert[m].texCoords[2] * 255.0f ; + + // Redundantly called, but it works... + polys[numPolys].faceNormal[0] = polys[numPolys].vert[m].normal[0] ; + polys[numPolys].faceNormal[1] = polys[numPolys].vert[m].normal[1] ; + polys[numPolys].faceNormal[2] = polys[numPolys].vert[m].normal[2] ; + polys[numPolys].faceNormal[3] = 0.0f ; + + chunkLength = (24<<1) ; + break ; + + // 66 byte chunk + case 0x05: + case 0x0f: + polys[numPolys].vert[m].worldCoords[0] = uToF(WORD_AT(threeDPointer, ((3<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[1] = uToF(WORD_AT(threeDPointer, ((4<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[2] = uToF(WORD_AT(threeDPointer, ((5<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].worldCoords[3] = 1.0f ; + polys[numPolys].n = 3 ; + + // !! See above - (6) - why? !! + polys[numPolys].vert[m].texCoords[0] = uToF(WORD_AT(threeDPointer, ((7<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].texCoords[1] = uToF(WORD_AT(threeDPointer, ((8<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].texCoords[2] = 0.0f ; + polys[numPolys].vert[m].texCoords[3] = 1.0f ; + + polys[numPolys].vert[m].normal[0] = uToF(WORD_AT(threeDPointer, ((9<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].normal[1] = uToF(WORD_AT(threeDPointer, ((10<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].normal[2] = uToF(WORD_AT(threeDPointer, ((11<<1) + (9<<1)*m)) ) ; + polys[numPolys].vert[m].normal[3] = 0.0f ; + + // !!! DUMB !!! + polys[numPolys].vert[m].light[0] = polys[numPolys].vert[m].texCoords[0] * 255.0f ; + polys[numPolys].vert[m].light[1] = polys[numPolys].vert[m].texCoords[1] * 255.0f ; + polys[numPolys].vert[m].light[2] = polys[numPolys].vert[m].texCoords[2] * 255.0f ; + + // Redundantly called, but it works... + polys[numPolys].faceNormal[0] = uToF(WORD_AT(threeDPointer, (30<<1) )) ; + polys[numPolys].faceNormal[1] = uToF(WORD_AT(threeDPointer, (31<<1) )) ; + polys[numPolys].faceNormal[2] = uToF(WORD_AT(threeDPointer, (32<<1) )) ; + polys[numPolys].faceNormal[3] = 0.0f ; + + chunkLength = (33<<1) ; + break ; + + // 30 byte chunk + case 0x97: + case 0x87: + case 0xd7: + + // Copy over the proper vertices from the previous triangle... + memcpy(&polys[numPolys].vert[1], &lastPoly.vert[0], sizeof(struct polyVert)) ; + memcpy(&polys[numPolys].vert[2], &lastPoly.vert[2], sizeof(struct polyVert)) ; + + // Fill in the appropriate data... + polys[numPolys].vert[0].worldCoords[0] = uToF(WORD_AT(threeDPointer, (3<<1) )) ; + polys[numPolys].vert[0].worldCoords[1] = uToF(WORD_AT(threeDPointer, (4<<1) )) ; + polys[numPolys].vert[0].worldCoords[2] = uToF(WORD_AT(threeDPointer, (5<<1) )) ; + polys[numPolys].vert[0].worldCoords[3] = 1.0f ; + polys[numPolys].n = 3 ; + + // !! See above - (6) - why? !! + polys[numPolys].vert[0].texCoords[0] = uToF(WORD_AT(threeDPointer, (7<<1) )) ; + polys[numPolys].vert[0].texCoords[1] = uToF(WORD_AT(threeDPointer, (8<<1) )) ; + polys[numPolys].vert[0].texCoords[2] = 0.0f ; + polys[numPolys].vert[0].texCoords[3] = 1.0f ; + + polys[numPolys].vert[0].normal[0] = uToF(WORD_AT(threeDPointer, (9<<1) )) ; + polys[numPolys].vert[0].normal[1] = uToF(WORD_AT(threeDPointer, (10<<1) )) ; + polys[numPolys].vert[0].normal[2] = uToF(WORD_AT(threeDPointer, (11<<1) )) ; + polys[numPolys].vert[0].normal[3] = 0.0f ; + + polys[numPolys].vert[0].light[0] = polys[numPolys].vert[0].texCoords[0] * 255.0f ; + polys[numPolys].vert[0].light[1] = polys[numPolys].vert[0].texCoords[1] * 255.0f ; + polys[numPolys].vert[0].light[2] = polys[numPolys].vert[0].texCoords[2] * 255.0f ; + + polys[numPolys].faceNormal[0] = uToF(WORD_AT(threeDPointer, (12<<1) )) ; + polys[numPolys].faceNormal[1] = uToF(WORD_AT(threeDPointer, (13<<1) )) ; + polys[numPolys].faceNormal[2] = uToF(WORD_AT(threeDPointer, (14<<1) )) ; + polys[numPolys].faceNormal[3] = 0.0f ; + + chunkLength = (15<<1) ; + break ; + + // 18 byte chunk + case 0x96: + + // Copy over the proper vertices from the previous triangle... + memcpy(&polys[numPolys].vert[1], &lastPoly.vert[0], sizeof(struct polyVert)) ; + memcpy(&polys[numPolys].vert[2], &lastPoly.vert[2], sizeof(struct polyVert)) ; + + // !!! Too lazy to have finished this yet !!! + + polys[numPolys].vert[0].worldCoords[0] = uToF(WORD_AT(threeDPointer, (3<<1))) ; + polys[numPolys].vert[0].worldCoords[1] = uToF(WORD_AT(threeDPointer, (4<<1))) ; + polys[numPolys].vert[0].worldCoords[2] = uToF(WORD_AT(threeDPointer, (5<<1))) ; + polys[numPolys].vert[0].worldCoords[3] = 1.0f ; + polys[numPolys].n = 3 ; + + // !! See above - (6) - why? !! + polys[numPolys].vert[0].texCoords[0] = uToF(WORD_AT(threeDPointer, (7<<1))) ; + polys[numPolys].vert[0].texCoords[1] = uToF(WORD_AT(threeDPointer, (8<<1))) ; + polys[numPolys].vert[0].texCoords[2] = 0.0f ; + polys[numPolys].vert[0].texCoords[3] = 1.0f ; + + // !!! DUMB !!! + polys[numPolys].vert[0].light[0] = polys[numPolys].vert[0].texCoords[0] * 255.0f ; + polys[numPolys].vert[0].light[1] = polys[numPolys].vert[0].texCoords[1] * 255.0f ; + polys[numPolys].vert[0].light[2] = polys[numPolys].vert[0].texCoords[2] * 255.0f ; + + // This normal could be right, but I'm not entirely sure - there is no normal in the 18 bytes! + polys[numPolys].vert[0].normal[0] = lastPoly.faceNormal[0] ; + polys[numPolys].vert[0].normal[1] = lastPoly.faceNormal[1] ; + polys[numPolys].vert[0].normal[2] = lastPoly.faceNormal[2] ; + polys[numPolys].vert[0].normal[3] = lastPoly.faceNormal[3] ; + + polys[numPolys].faceNormal[0] = lastPoly.faceNormal[0] ; + polys[numPolys].faceNormal[1] = lastPoly.faceNormal[1] ; + polys[numPolys].faceNormal[2] = lastPoly.faceNormal[2] ; + polys[numPolys].faceNormal[3] = lastPoly.faceNormal[3] ; + + chunkLength = (9<<1) ; + break ; + + default: + mame_printf_debug("UNKNOWN geometry CHUNK TYPE : %x\n", triangleType) ; + break ; + } + } + + polys[numPolys].visible = 1 ; + + memcpy(&lastPoly, &polys[numPolys], sizeof(struct polygon)) ; + + + + + // THE HNG64 HARDWARE DOES NOT SEEM TO BACKFACE CULL // + + /////////////////// + // BACKFACE CULL // + /////////////////// +/* + float cullRay[4] ; + float cullNorm[4] ; + + // Cast a ray out of the camera towards the polygon's point in eyespace. + vecmatmul4(cullRay, modelViewMatrix, polys[numPolys].vert[0].worldCoords) ; + normalize(cullRay) ; + // Dot product that with the normal to see if you're negative... + vecmatmul4(cullNorm, modelViewMatrix, polys[numPolys].faceNormal) ; + + float result = vecDotProduct(cullRay, cullNorm) ; + + if (result < 0.0f) + polys[numPolys].visible = 1 ; + else + polys[numPolys].visible = 0 ; +*/ + + //////////////////////////// + // BEHIND-THE-CAMERA CULL // + //////////////////////////// + vecmatmul4(cullRay, modelViewMatrix, polys[numPolys].vert[0].worldCoords) ; + + if (cullRay[2] > 0.0f) // Camera is pointing down -Z + { + polys[numPolys].visible = 0 ; + } + + + ////////////////////////////////////////////////////////// + // TRANSFORM THE TRIANGLE INTO HOMOGENEOUS SCREEN SPACE // + ////////////////////////////////////////////////////////// + + if (polys[numPolys].visible) + { + for (m = 0; m < polys[numPolys].n; m++) + { + // Transform and project the vertex into pre-divided homogeneous coordinates... + vecmatmul4(eyeCoords, modelViewMatrix, polys[numPolys].vert[m].worldCoords) ; + vecmatmul4(polys[numPolys].vert[m].clipCoords, projectionMatrix, eyeCoords) ; + } + + if (polys[numPolys].visible) + { + // Clip the triangles to the view frustum... + PerformFrustumClip(&polys[numPolys]) ; + + for (m = 0; m < polys[numPolys].n; m++) + { + // Convert into normalized device coordinates... + ndCoords[0] = polys[numPolys].vert[m].clipCoords[0] / polys[numPolys].vert[m].clipCoords[3] ; + ndCoords[1] = polys[numPolys].vert[m].clipCoords[1] / polys[numPolys].vert[m].clipCoords[3] ; + ndCoords[2] = polys[numPolys].vert[m].clipCoords[2] / polys[numPolys].vert[m].clipCoords[3] ; + ndCoords[3] = polys[numPolys].vert[m].clipCoords[3] ; + + // Final pixel values are garnered here : + windowCoords[0] = (ndCoords[0]+1.0f) * ((float)(machine->screen[0].visarea.max_x) / 2.0f) + 0.0f ; + windowCoords[1] = (ndCoords[1]+1.0f) * ((float)(machine->screen[0].visarea.max_y) / 2.0f) + 0.0f ; + windowCoords[2] = (ndCoords[2]+1.0f) * 0.5f ; + + windowCoords[1] = (float)machine->screen[0].visarea.max_y - windowCoords[1] ; // Flip Y + + // Store the points in a list for later use... + polys[numPolys].vert[m].clipCoords[0] = windowCoords[0] ; + polys[numPolys].vert[m].clipCoords[1] = windowCoords[1] ; + polys[numPolys].vert[m].clipCoords[2] = windowCoords[2] ; + polys[numPolys].vert[m].clipCoords[3] = ndCoords[3] ; + } + } + } + + +/* + // DEBUG + if (chunkLength == (9 << 1)) + { + mame_printf_debug("Chunk : ") ; + for (int ajg = 0; ajg < chunkLength; ajg+=2) + mame_printf_debug("%.2x%.2x ", threeDPointer[ajg], threeDPointer[ajg+1]) ; + mame_printf_debug("\n") ; + } + // END DEBUG +*/ + + // Advance to the next polygon chunk... + threeDPointer += chunkLength ; + + numPolys++ ; // Add one more to the display list... + } + } + + break ; + + default: + break ; + } + } + + // Don't forget about this !!! + //mame_printf_debug(" %.8x\n\n", (UINT32)hng64_dls[j][0x80]) ; + + } + + + ///////////////////////////////////////////////// + // FINALLY RENDER THE TRIANGLES INTO THE FRAME // + ///////////////////////////////////////////////// + + // Reset the depth buffer... + for (i = 0; i < (machine->screen[0].visarea.max_x)*(machine->screen[0].visarea.max_y); i++) + depthBuffer[i] = 100.0f ; + + for (i = 0; i < numPolys; i++) + { + if (polys[i].visible) + { + //DrawWireframe(&polys[i], bitmap) ; + DrawShaded(machine, &polys[i], bitmap) ; + } + } + + // usrintf_showmessage("%d", numPolys) ; + + // Clear each of the display list buffers after drawing... + for (i = 0; i < 0x81; i++) + { + hng64_dls[0][i] = 0 ; + hng64_dls[1][i] = 0 ; + } +} + + +/* 8x8 tiles, 4bpp layer */ +static TILE_GET_INFO( get_hng64_tile0_info ) +{ + int tileno,pal; + tileno = hng64_videoram[tile_index]; + pal = hng64_videoram[tile_index]>>24; + SET_TILE_INFO((tileno&1)?0:1,(tileno&0x3fffff)>>1,pal,TILE_FLIPYX((tileno&0xc00000)>>22)); +} + +/* 16x16 tiles, 8bpp layer */ +static TILE_GET_INFO( get_hng64_tile1_info ) +{ + int tileno,pal; + tileno = hng64_videoram[tile_index+(0x10000/4)]; + pal = hng64_videoram[tile_index+(0x10000/4)]>>24; + + SET_TILE_INFO(3,(tileno&0x3fffff)>>3,pal>>4, TILE_FLIPYX((tileno&0xc00000)>>22) ); +} + +/* 16x16 tiles, 8bpp layer */ +static TILE_GET_INFO( get_hng64_tile2_info ) +{ + int tileno,pal; + tileno = hng64_videoram[tile_index+(0x20000/4)]; + pal = hng64_videoram[tile_index+(0x20000/4)]>>24; + SET_TILE_INFO(3,(tileno&0x3fffff)>>3,pal>>4,TILE_FLIPYX((tileno&0xc00000)>>22)); +} + +/* 16x16 tiles, 8bpp layer */ +static TILE_GET_INFO( get_hng64_tile3_info ) +{ + int tileno,pal; + tileno = hng64_videoram[tile_index+(0x30000/4)]; + pal = hng64_videoram[tile_index+(0x30000/4)]>>24; + SET_TILE_INFO(3,(tileno&0x3fffff)>>3,pal>>4,TILE_FLIPYX((tileno&0xc00000)>>22)); +} + + + +/* Tilemaps zoom, and probably rotate.. they can have linescroll/lineselect, +looks like the zoom center can move too.. +not sure how these features are enabled up yet */ + +static int gatherPixelsForLine(mame_bitmap *tilemapBitmap, + INT32 startX, INT32 startY, INT32 endX, INT32 endY, + UINT16 *penList) +{ + int retVal = 0 ; + + // !! BRESENHAM'S AGAIN - I REALLY SHOULD GENERALIZE THIS !! + +#define SWAP(a,b) tmpswap = a; a = b; b = tmpswap; + + INT32 i; + INT32 steep = 1; + INT32 sx, sy; /* step positive or negative (1 or -1) */ + INT32 dx, dy; /* delta (difference in X and Y between points) */ + INT32 e; + + /* + * inline swap. On some architectures, the XOR trick may be faster + */ + INT32 tmpswap; + + /* + * optimize for vertical and horizontal lines here + */ + + dx = abs(endX - startX); + sx = ((endX - startX) > 0) ? 1 : -1; + dy = abs(endY - startY); + sy = ((endY - startY) > 0) ? 1 : -1; + + if (dy > dx) + { + steep = 0; + SWAP(startX, startY); + SWAP(dx, dy); + SWAP(sx, sy); + } + + e = (dy << 1) - dx; + + for (i = 0; i < dx; i++) + { + if (steep) + { + penList[retVal] = *BITMAP_ADDR16(tilemapBitmap, startY, startX); + retVal++ ; + } + else + { + penList[retVal] = *BITMAP_ADDR16(tilemapBitmap, startX, startY) ; + retVal++ ; + } + while (e >= 0) + { + startY += sy; + e -= (dx << 1); + } + + startX += sx; + e += (dy << 1); + } +#undef SWAP + + + return retVal ; +} + +static void plotTilemap3Line(running_machine *machine, + mame_bitmap *tilemapBitmap, + INT32 startX, INT32 startY, INT32 endX, INT32 endY, + INT32 screenY, mame_bitmap *bitmap) +{ + int i ; + + int numPix ; + UINT16 penList[0x1000] ; // 4k of pixels to be safe + + float pixStride, pixOffset ; + +// mame_printf_debug("(%d,%d) (%d,%d)\n", startX, startY, endX, endY) ; + + // CLAMP - BUT I'M PRETTY SURE THIS ISN'T QUITE RIGHT !!! ??? + startX += 1024 ; if (startX < 0) startX = 0 ; else if (startX >= 2048) startX = 2048-1 ; + startY += 1024 ; if (startY < 0) startY = 0 ; else if (startY >= 2048) startY = 2048-1 ; + endX += 1024 ; if (endX < 0) endX = 0 ; else if (endX >= 2048) endX = 2048-1 ; + endY += 1024 ; if (endY < 0) endY = 0 ; else if (endY >= 2048) endY = 2048-1 ; + + numPix = gatherPixelsForLine(tilemapBitmap, startX, startY, endX, endY, penList) ; + + pixStride = (float)numPix / (float)(machine->screen[0].visarea.max_x-1) ; + pixOffset = 0 ; + + if (numPix == 0) + penList[0] = *BITMAP_ADDR16(tilemapBitmap, 1024, 1024); + +// mame_printf_debug("numpix %d ps %f po %f s(%d,%d) e(%d,%d)\n", numPix, pixStride, pixOffset, startX, startY, endX, endY) ; + + // Draw out the screen's line... + for (i = machine->screen[0].visarea.min_x; i < machine->screen[0].visarea.max_x; i++) + { + // Nearest-neighbor interpolation for now (but i doubt it does linear) + UINT16 tmPen = penList[(int)pixOffset] ; + + *BITMAP_ADDR32(bitmap, screenY, i) = palette_get_color(machine, tmPen) | 0xff000000; + + pixOffset += pixStride ; + } +} + +static void hng64_drawtilemap3(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect ) +{ + int i ; + + mame_bitmap *srcbitmap = tilemap_get_pixmap( hng64_tilemap3 ); + +// usrintf_showmessage("%d", hng64_hackTm3Count) ; + + if (hng64_hackTm3Count/4 < Machine->screen[0].visarea.max_y) + { + for (i = 0; i < hng64_hackTm3Count/4; i++) + { + UINT32 address = hng64_rowScrollOffset + 0xbf0 ; + address -= (i * 0x10) ; + address /= 4 ; + //mame_printf_debug("nums : %.4x %.4x %.4x %.4x\n", (INT16)((hng64_videoram[address+0x0]&0xffff0000) >> 16), + // (INT16)((hng64_videoram[address+0x1]&0xffff0000) >> 16), + // (INT16)((hng64_videoram[address+0x2]&0xffff0000) >> 16), + // (INT16)((hng64_videoram[address+0x3]&0xffff0000) >> 16)) ; + + plotTilemap3Line(machine, srcbitmap, + (INT16)((hng64_videoram[address+0x0]&0xffff0000) >> 16), + (INT16)((hng64_videoram[address+0x2]&0xffff0000) >> 16), + (INT16)((hng64_videoram[address+0x1]&0xffff0000) >> 16), + (INT16)((hng64_videoram[address+0x3]&0xffff0000) >> 16), + (machine->screen[0].visarea.max_y-1)-i, + bitmap) ; + } + } + + // Maybe the scrollbase should be used instead of my hacky hng64_rowScrollOffset :)? +// scrollbase = (hng64_videoregs[0x05]&0x00003fff)>>0; + +// xscroll = hng64_videoram[(0x40000+(scrollbase<<4))/4]>>16; +// yscroll = hng64_videoram[(0x40008+(scrollbase<<4))/4]>>16; +// xzoom = hng64_videoram[(0x40010+(scrollbase<<4))/4]>>16; +// yzoom = hng64_videoram[(0x4000c+(scrollbase<<4))/4]>>16; +// xzoom = xzoom-xscroll; +// yzoom = yzoom-yscroll; +// xzoom &=0xffff; +// yzoom &=0xffff; + +// xscroll <<=16; +// yscroll <<=16; +// xzoom <<=8; +// yzoom <<=8; + +// tilemap_set_scrollx(hng64_tilemap3,0, xscroll); +// tilemap_set_scrolly(hng64_tilemap3,0, yscroll); + +// tilemap_draw(bitmap,cliprect,hng64_tilemap3,0,0); + +// tilemap_draw_roz(bitmap,cliprect,hng64_tilemap3,xscroll,yscroll, +// xzoom,0,0,yzoom, +// 1, +// 0,0); +} + +static void hng64_drawtilemap2( mame_bitmap *bitmap, const rectangle *cliprect ) +{ + int scrollbase,xscroll,yscroll,xzoom,yzoom; + + scrollbase = (hng64_videoregs[0x05]&0x3fff0000)>>16; + xscroll = (INT16)(hng64_videoram[(0x40000+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40004+(scrollbase<<4))/4]>>16); + yscroll = (INT16)(hng64_videoram[(0x40008+(scrollbase<<4))/4]>>16); + yzoom = (INT16)(hng64_videoram[(0x4000c+(scrollbase<<4))/4]>>16); + xzoom = (INT16)(hng64_videoram[(0x40010+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40014+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40018+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x4001c+(scrollbase<<4))/4]>>16); + xzoom = xzoom-xscroll; + yzoom = yzoom-yscroll; + xzoom &=0xffff; + yzoom &=0xffff; + + xscroll <<=16; + yscroll <<=16; + xzoom <<=8; + yzoom <<=8; + + tilemap_draw_roz(bitmap,cliprect,hng64_tilemap2,xscroll,yscroll, + xzoom,0,0,yzoom, + 1, + 0,0); +} + +static void hng64_drawtilemap1( mame_bitmap *bitmap, const rectangle *cliprect ) +{ + int scrollbase,xscroll,yscroll,xzoom,yzoom; + + scrollbase = (hng64_videoregs[0x04]&0x00003fff)>>0; + xscroll = (INT16)(hng64_videoram[(0x40000+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40004+(scrollbase<<4))/4]>>16); + yscroll = (INT16)(hng64_videoram[(0x40008+(scrollbase<<4))/4]>>16); + xzoom = (INT16)(hng64_videoram[(0x40010+(scrollbase<<4))/4]>>16); + yzoom = (INT16)(hng64_videoram[(0x4000c+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40014+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x40018+(scrollbase<<4))/4]>>16); + // ??? = (INT16)(hng64_videoram[(0x4001c+(scrollbase<<4))/4]>>16); + xzoom = xzoom-xscroll; + yzoom = yzoom-yscroll; + xzoom &=0xffff; + yzoom &=0xffff; + + xscroll <<=16; + yscroll <<=16; + xzoom <<=8; + yzoom <<=8; + + tilemap_draw_roz(bitmap,cliprect,hng64_tilemap1,xscroll,yscroll, + xzoom,0,0,yzoom, + 1, + 0,0); +} + + + +/* + * Video Regs Format + * ------------------ + * + * UINT32 | Bytes | Use + * -------+-76543210-+---------------- + * 0 | oooooooo | unknown - always seems to be 04060000 (fatfurwa) and 00060000 (buriki) + * 1 | xxxx---- | looks like it's 0001 most (all) of the time - turns off in buriki intro + * 1 | ----oooo | unknown - always seems to be 0000 (fatfurwa) + * 2 | oooooooo | unknown - likes to change sometimes though... (looks like bit-flags) + * 3 | oooooooo | unknown - change a lot - maybe mixer flags? + * 4 | xxxx---- | tilemap0 offset into tilemap RAM? + * 4 | ----xxxx | tilemap1 offset into tilemap RAM + * 5 | xxxx---- | tilemap3 offset into tilemap RAM + * 5 | ----xxxx | tilemap4 offset into tilemap RAM? + * 6 | oooooooo | unknown - always seems to be 000001ff (fatfurwa) + * 7 | oooooooo | unknown - always seems to be 000001ff (fatfurwa) + * 8 | oooooooo | unknown - always seems to be 80008000 (fatfurwa) + * 9 | oooooooo | unknown - always seems to be 00000000 (fatfurwa) + * a | oooooooo | unknown - always seems to be 00000000 (fatfurwa) + * b | oooooooo | unknown - 00000000 in intro - 00007ff8 when fight is going on (looks like &'ing it with 0x3fff is a good thing to do - tilemap stuff?) + * c | xxxxxxxx | I'm almost positive this is some form of offset to produce animations in Tilemap1 - or maybe all tilemaps? + * - 00xxx000 are used in fatfurwa during fights + * - 00000?xx are used in buriki during the intro + * d | oooooooo | not used ?? + * e | oooooooo | not used ?? + */ + + +VIDEO_UPDATE( hng64 ) +{ + fillbitmap(bitmap, get_black_pen(machine), 0); + + // Debug +// for (int iii = 0; iii < 0x0f; iii++) +// mame_printf_debug("%.8x ", hng64_videoregs[iii]) ; +// mame_printf_debug("\n") ; +// usrintf_showmessage("%.8x %.8x %.8x %.8x", hng64_videoregs[0x1], hng64_videoregs[0x3], hng64_videoregs[0xb], hng64_videoregs[0xc]) ; + + // I think there's something to this, but I'm not doing it right... + // Interestingly enough, this turns the bootup screen blue :) + /* + if (hng64_videoregs[0xc] != tilemap2Offset) + { + tilemap2Offset = hng64_videoregs[0xc] ; + + tilemap_dispose(hng64_tilemap1) ; + hng64_tilemap1 = tilemap_create(get_hng64_tile2_info,tilemap_scan_rows,TILEMAP_TYPE_PEN, 16, 16, 128,128); // 128x128x4 = 0x10000 + } + */ + + // Rowscroll variables... + // usrintf_showmessage("%.8x %.8x x %.8x %.8x", hng64_videoram[0x00057bf0/4], hng64_videoram[0x00057bf4/4], hng64_videoram[0x00057bf8/4], hng64_videoram[0x00057bfc/4]) ; + + // All of this priority stuff is probably done with some funky layer mixing chip - + // (there are neato alpha effects on a real board, etc) + hng64_drawtilemap2(bitmap,cliprect); + hng64_drawtilemap1(bitmap,cliprect); + hng64_drawtilemap3(machine, bitmap,cliprect); // Draw the ground last... + + // !!! This tilemap has the same flags as the 'previous' three, but they're not used in fatfurwa !!! + // (in other words, we should make a similar hng64_drawtilemap0() function for this tilemap) + tilemap_draw(bitmap,cliprect,hng64_tilemap0,0,0); + + draw_sprites(machine, bitmap,cliprect); + + // 3d really shouldn't be last, but you don't see some cool stuff right now if it's put before sprites :)... + draw3d(machine, bitmap, cliprect); + + /* hack to enable 2nd cpu when key is pressed */ +// if ( input_code_pressed_once(KEYCODE_L) ) +// { +// cpunum_set_input_line(1, INPUT_LINE_HALT, CLEAR_LINE); +// cpunum_set_input_line(1, INPUT_LINE_RESET, CLEAR_LINE); +// } + + /* AJG */ + // if(input_code_pressed(KEYCODE_D)) + + transition_control(machine, bitmap) ; + +// mame_printf_debug("FRAME DONE %d\n", frameCount) ; + frameCount++ ; + + hng64_hackTilemap3 = hng64_rowScrollOffset = hng64_hackTm3Count = 0 ; + return 0; +} + +VIDEO_START( hng64 ) +{ + hng64_tilemap0 = tilemap_create(get_hng64_tile0_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 8, 8, 128,128); /* 128x128x4 = 0x10000 */ + hng64_tilemap1 = tilemap_create(get_hng64_tile1_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 16, 16, 128,128); /* 128x128x4 = 0x10000 */ + hng64_tilemap2 = tilemap_create(get_hng64_tile2_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 16, 16, 128,128); /* 128x128x4 = 0x10000 */ + hng64_tilemap3 = tilemap_create(get_hng64_tile3_info, tilemap_scan_rows, TILEMAP_TYPE_PEN, 16, 16, 128,128); /* 128x128x4 = 0x10000 */ + tilemap_set_transparent_pen(hng64_tilemap0,0); + tilemap_set_transparent_pen(hng64_tilemap1,0); + tilemap_set_transparent_pen(hng64_tilemap2,0); + tilemap_set_transparent_pen(hng64_tilemap3,0); + + // 3d Buffer Allocation + depthBuffer = (float*)auto_malloc((machine->screen[0].visarea.max_x)*(machine->screen[0].visarea.max_y)*sizeof(float)) ; + + // The general display list of polygons in the scene... + // !! This really should be a dynamic array !! + polys = auto_malloc(MAX_ONSCREEN_POLYS * sizeof(struct polygon)) ; +} + +/////////////// +// UTILITIES // +/////////////// + +/* 4x4 matrix multiplication */ +static void matmul4( float *product, const float *a, const float *b ) +{ + int i; + for (i = 0; i < 4; i++) + { + const float ai0 = a[0 + i] ; + const float ai1 = a[4 + i] ; + const float ai2 = a[8 + i] ; + const float ai3 = a[12 + i] ; + + product[0 + i] = ai0 * b[0 ] + ai1 * b[1 ] + ai2 * b[2 ] + ai3 * b[3 ] ; + product[4 + i] = ai0 * b[4 ] + ai1 * b[5 ] + ai2 * b[6 ] + ai3 * b[7 ] ; + product[8 + i] = ai0 * b[8 ] + ai1 * b[9 ] + ai2 * b[10] + ai3 * b[11] ; + product[12 + i] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15] ; + } +} + +/* vector by 4x4 matrix multiply */ +static void vecmatmul4( float *product, const float *a, const float *b) +{ + const float bi0 = b[0] ; + const float bi1 = b[1] ; + const float bi2 = b[2] ; + const float bi3 = b[3] ; + + product[0] = bi0 * a[0] + bi1 * a[4] + bi2 * a[8 ] + bi3 * a[12]; + product[1] = bi0 * a[1] + bi1 * a[5] + bi2 * a[9 ] + bi3 * a[13]; + product[2] = bi0 * a[2] + bi1 * a[6] + bi2 * a[10] + bi3 * a[14]; + product[3] = bi0 * a[3] + bi1 * a[7] + bi2 * a[11] + bi3 * a[15]; +} + +#ifdef UNUSED_FUNCTION +static float vecDotProduct( const float *a, const float *b) +{ + return ((a[0]*b[0]) + (a[1]*b[1]) + (a[2]*b[2])) ; +} +#endif + +static void SetIdentity(float *matrix) +{ + int i ; + + for (i = 0; i < 16; i++) + { + matrix[i] = 0.0f ; + } + + matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.0f ; +} + +static float uToF(UINT16 input) +{ + float retVal ; + + retVal = (float)((INT16)input) / 32768.0f ; + +/* + if ((INT16)input < 0) + retVal = (float)((INT16)input) / 32768.0f ; + else + retVal = (float)((INT16)input) / 32767.0f ; +*/ + + return retVal ; +} + +#ifdef UNUSED_FUNCTION +static void normalize(float* x) +{ + double l2 = (x[0]*x[0]) + (x[1]*x[1]) + (x[2]*x[2]); + double l=sqrt(l2) ; + + x[0] = (float)(x[0] / l) ; + x[1] = (float)(x[1] / l) ; + x[2] = (float)(x[2] / l) ; +} +#endif + + + +/////////////////////////// +// POLYGON CLIPPING CODE // +/////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////// +// The remainder of the code in this file is heavily // +// influenced by, and sometimes copied verbatim from Andrew Zaferakis' SoftGL // +// rasterizing system. http://www.cs.unc.edu/~andrewz/comp236/ // +// // +// Andrew granted permission for its use in MAME in October of 2004. // +/////////////////////////////////////////////////////////////////////////////////// + +// Refer to the clipping planes as numbers +#define HNG64_LEFT 0 +#define HNG64_RIGHT 1 +#define HNG64_TOP 2 +#define HNG64_BOTTOM 3 +#define HNG64_NEAR 4 +#define HNG64_FAR 5 + + +static int Inside(struct polyVert *v, int plane) +{ + switch(plane) + { + case HNG64_LEFT: + return (v->clipCoords[0] >= -v->clipCoords[3]) ? 1 : 0 ; + case HNG64_RIGHT: + return (v->clipCoords[0] <= v->clipCoords[3]) ? 1 : 0 ; + + case HNG64_TOP: + return (v->clipCoords[1] <= v->clipCoords[3]) ? 1 : 0 ; + case HNG64_BOTTOM: + return (v->clipCoords[1] >= -v->clipCoords[3]) ? 1 : 0 ; + + case HNG64_NEAR: + return (v->clipCoords[2] <= v->clipCoords[3]) ? 1 : 0; + return 1 ; + case HNG64_FAR: + return (v->clipCoords[2] >= -v->clipCoords[3]) ? 1 : 0; + return 1 ; + } + + return 0 ; +} + +static void Intersect(struct polyVert *input0, struct polyVert *input1, struct polyVert *output, int plane) +{ + float t = 0.0f ; + + float *Iv0 = input0->clipCoords ; + float *Iv1 = input1->clipCoords ; + float *Ov = output->clipCoords ; + + float *It0 = input0->texCoords ; + float *It1 = input1->texCoords ; + float *Ot = output->texCoords ; + + float *Il0 = input0->light ; + float *Il1 = input1->light ; + float *Ol = output->light ; + + switch(plane) + { + case HNG64_LEFT: + t = (Iv0[0]+Iv0[3]) / (-Iv1[3]+Iv0[3]-Iv1[0]+Iv0[0]); + break; + case HNG64_RIGHT: + t = (Iv0[0]-Iv0[3]) / (Iv1[3]-Iv0[3]-Iv1[0]+Iv0[0]); + break; + case HNG64_TOP: + t = (Iv0[1]-Iv0[3]) / (Iv1[3]-Iv0[3]-Iv1[1]+Iv0[1]); + break; + case HNG64_BOTTOM: + t = (Iv0[1]+Iv0[3]) / (-Iv1[3]+Iv0[3]-Iv1[1]+Iv0[1]); + break; + case HNG64_NEAR: + t = (Iv0[2]-Iv0[3]) / (Iv1[3]-Iv0[3]-Iv1[2]+Iv0[2]); + break; + case HNG64_FAR: + t = (Iv0[2]+Iv0[3]) / (-Iv1[3]+Iv0[3]-Iv1[2]+Iv0[2]); + break; + } + + Ov[0] = Iv0[0] + (Iv1[0] - Iv0[0]) * t ; + Ov[1] = Iv0[1] + (Iv1[1] - Iv0[1]) * t ; + Ov[2] = Iv0[2] + (Iv1[2] - Iv0[2]) * t ; + Ov[3] = Iv0[3] + (Iv1[3] - Iv0[3]) * t ; + + Ot[0] = It0[0] + (It1[0] - It0[0]) * t ; + Ot[1] = It0[1] + (It1[1] - It0[1]) * t ; + Ot[2] = It0[2] + (It1[2] - It0[2]) * t ; + Ot[3] = It0[3] + (It1[3] - It0[3]) * t ; + + Ol[0] = Il0[0] + (Il1[0] - Il0[0]) * t ; + Ol[1] = Il0[1] + (Il1[1] - Il0[1]) * t ; + Ol[2] = Il0[2] + (Il1[2] - Il0[2]) * t ; +} + + + +static void PerformFrustumClip(struct polygon *p) +{ + int i, j ; + int k ; + + ////////////////////////////////////////////////////////////////////////// + // Clip against the volumes defined by the homogeneous clip coordinates // + ////////////////////////////////////////////////////////////////////////// + + struct polygon temp ; + + struct polyVert *v0 ; + struct polyVert *v1 ; + struct polyVert *tv ; + + temp.n = 0; + + // Skip near and far clipping planes ? + for (j = 0; j <= HNG64_BOTTOM; j++) + { + for (i = 0; i < p->n; i++) + { + k = (i+1) % p->n; // Index of next vertex + + v0 = &p->vert[i] ; + v1 = &p->vert[k] ; + + tv = &temp.vert[temp.n] ; + + if (Inside(v0, j) && Inside(v1, j)) // Edge is completely inside the volume... + { + memcpy(tv, v1, sizeof(struct polyVert)) ; + temp.n++; + } + + else if (Inside(v0, j) && !Inside(v1, j)) // Edge goes from in to out... + { + Intersect(v0, v1, tv, j) ; + temp.n++; + } + + else if (!Inside(v0, j) && Inside(v1, j)) // Edge goes from out to in... + { + Intersect(v0, v1, tv, j) ; + memcpy(&temp.vert[temp.n+1], v1, sizeof(struct polyVert)) ; + temp.n+=2; + } + } + + p->n = temp.n; + + for (i = 0; i < temp.n; i++) + { + memcpy(&p->vert[i], &temp.vert[i], sizeof(struct polyVert)) ; + } + + temp.n = 0 ; + } +} + + +////////////////////////////// +// POLYGON RASTERIZING CODE // +////////////////////////////// + +///////////////////////// +// wireframe rendering // +///////////////////////// + +#ifdef UNUSED_FUNCTION +static void plot(INT32 x, INT32 y, INT32 color, mame_bitmap *bitmap) +{ + *BITMAP_ADDR32(bitmap, y, x) = MAKE_ARGB((UINT8)255, (UINT8)color, (UINT8)color, (UINT8)color) ; +} + +// Stolen from http://en.wikipedia.org/wiki/Bresenham's_line_algorithm (no copyright denoted) - the non-optimized version +static void drawline2d(INT32 x0, INT32 y0, INT32 x1, INT32 y1, INT32 color, mame_bitmap *bitmap) +{ +#define SWAP(a,b) tmpswap = a; a = b; b = tmpswap; + + INT32 i; + INT32 steep = 1; + INT32 sx, sy; /* step positive or negative (1 or -1) */ + INT32 dx, dy; /* delta (difference in X and Y between points) */ + INT32 e; + + /* + * inline swap. On some architectures, the XOR trick may be faster + */ + INT32 tmpswap; + + /* + * optimize for vertical and horizontal lines here + */ + + dx = abs(x1 - x0); + sx = ((x1 - x0) > 0) ? 1 : -1; + dy = abs(y1 - y0); + sy = ((y1 - y0) > 0) ? 1 : -1; + + if (dy > dx) + { + steep = 0; + SWAP(x0, y0); + SWAP(dx, dy); + SWAP(sx, sy); + } + + e = (dy << 1) - dx; + + for (i = 0; i < dx; i++) + { + if (steep) + { + plot(x0,y0,color, bitmap); + } + else + { + plot(y0,x0,color, bitmap); + } + while (e >= 0) + { + y0 += sy; + e -= (dx << 1); + } + + x0 += sx; + e += (dy << 1); + } +#undef SWAP +} + + +static void DrawWireframe(struct polygon *p, mame_bitmap *bitmap) +{ + int j; + for (j = 0; j < p->n; j++) + { + // mame_printf_debug("now drawing : %f %f %f, %f %f %f\n", p->vert[j].clipCoords[0], p->vert[j].clipCoords[1], p->vert[j].clipCoords[2], p->vert[(j+1)%p->n].clipCoords[0], p->vert[(j+1)%p->n].clipCoords[1], p->vert[(j+1)%p->n].clipCoords[2]) ; + // mame_printf_debug("%f %f %f %f\n", p->vert[j].clipCoords[0], p->vert[j].clipCoords[1], p->vert[(j+1)%p->n].clipCoords[0], p->vert[(j+1)%p->n].clipCoords[1]) ; + drawline2d(p->vert[j].clipCoords[0], p->vert[j].clipCoords[1], p->vert[(j+1)%p->n].clipCoords[0], p->vert[(j+1)%p->n].clipCoords[1], 255, bitmap) ; + } + + // SHOWS THE CLIPPING // + /* + for (int j = 1; j < p->n-1; j++) + { + drawline2d(p->vert[0].clipCoords[0], p->vert[0].clipCoords[1], p->vert[j].clipCoords[0], p->vert[j].clipCoords[1], 255, bitmap) ; + drawline2d(p->vert[j].clipCoords[0], p->vert[j].clipCoords[1], p->vert[j+1].clipCoords[0], p->vert[j+1].clipCoords[1], 255, bitmap) ; + drawline2d(p->vert[j+1].clipCoords[0], p->vert[j+1].clipCoords[1], p->vert[0].clipCoords[0], p->vert[0].clipCoords[1], 255, bitmap) ; + } + */ +} +#endif + + +/////////////////////// +// polygon rendering // +/////////////////////// + +static void RasterizeTriangle_SMOOTH_TEX_PC(running_machine *machine, mame_bitmap *Color, + float A[4], float B[4], float C[4], + float Ca[3], float Cb[3], float Cc[3], // PER-VERTEX RGB COLORS + float Ta[2], float Tb[2], float Tc[2], // PER-VERTEX (S,T) TEX-COORDS + int Wrapping, int Filtering, int Function) ; + +static void DrawShaded(running_machine *machine, struct polygon *p, mame_bitmap *bitmap) +{ + // The perspective-correct texture divide... + // !!! There is a very good chance the HNG64 hardware does not do perspective-correct texture-mapping !!! + int j; + for (j = 0; j < p->n; j++) + { + p->vert[j].clipCoords[3] = 1.0f / p->vert[j].clipCoords[3] ; + p->vert[j].light[0] = p->vert[j].light[0] * p->vert[j].clipCoords[3] ; + p->vert[j].light[1] = p->vert[j].light[1] * p->vert[j].clipCoords[3] ; + p->vert[j].light[2] = p->vert[j].light[2] * p->vert[j].clipCoords[3] ; + p->vert[j].texCoords[0] = p->vert[j].texCoords[0] * p->vert[j].clipCoords[3] ; + p->vert[j].texCoords[1] = p->vert[j].texCoords[1] * p->vert[j].clipCoords[3] ; + } + + for (j = 1; j < p->n-1; j++) + { + RasterizeTriangle_SMOOTH_TEX_PC(machine, bitmap, + p->vert[0].clipCoords, p->vert[j].clipCoords, p->vert[j+1].clipCoords, + p->vert[0].light, p->vert[j].light, p->vert[j+1].light, + p->vert[0].texCoords, p->vert[j].texCoords, p->vert[j+1].texCoords, + p->texType, p->palIndex, p->texIndex) ; + + } +} + + +/*********************************************************************/ +/** FillSmoothTexPCHorizontalLine **/ +/** Input: Color Buffer (framebuffer), depth buffer, width and **/ +/** height of framebuffer, starting, and ending values **/ +/** for x and y, constant y. Fills horizontally with **/ +/** z,r,g,b interpolation. **/ +/** **/ +/** Output: none **/ +/*********************************************************************/ +INLINE void FillSmoothTexPCHorizontalLine(running_machine *machine, mame_bitmap *Color, + int Wrapping, int Filtering, int Function, + int x_start, int x_end, int y, float z_start, float z_delta, + float w_start, float w_delta, float r_start, float r_delta, + float g_start, float g_delta, float b_start, float b_delta, + float s_start, float s_delta, float t_start, float t_delta) +{ + float *dp = &(depthBuffer[y*machine->screen[0].visarea.max_x+x_start]); + + const UINT8 *gfx = memory_region(REGION_GFX3); + const UINT8 *textureOffset ; + UINT8 paletteEntry ; + float t_coord, s_coord ; + + if (Function >= 0) textureOffset = &gfx[Function * 1024 * 1024] ; + else textureOffset = 0x00 ; + + + for ( ; x_start <= x_end; x_start++) + { + if (z_start < (*dp)) + { + // MULTIPLY BACK THROUGH BY W + t_coord = t_start / w_start; + s_coord = s_start / w_start; + + // GET THE TEXTURE INDEX + if (Function >= 0) + { + if (Wrapping == 0x8 || Wrapping == 0xc) + paletteEntry = textureOffset[(((int)(s_coord*1024.0f))*1024 + (int)(t_coord*1024.0f))] ; + else + paletteEntry = textureOffset[(((int)(s_coord*512.0f))*1024 + (int)(t_coord*512.0f))] ; + + // Naieve Alpha Implementation (?) - don't draw if you're at texture index 0... + if (paletteEntry != 0) + { + // Greyscale texture - for Buriki... + // *BITMAP_ADDR32(Color, y, x_start) = MAKE_ARGB(255, (UINT8)paletteEntry, (UINT8)paletteEntry, (UINT8)paletteEntry) ; + + *BITMAP_ADDR32(Color, y, x_start) = machine->pens[(128*(Filtering))+paletteEntry] ; + *dp = z_start ; + } + } + else + { + *BITMAP_ADDR32(Color, y, x_start) = MAKE_ARGB(255, (UINT8)(r_start/w_start), (UINT8)(g_start/w_start), (UINT8)(b_start/w_start)) ; + *dp = z_start; + } + } + dp++; + z_start += z_delta; + w_start += w_delta; + r_start += r_delta; + g_start += g_delta; + b_start += b_delta; + s_start += s_delta; + t_start += t_delta; + } +} + +//---------------------------------------------------------------------------- +// Given 3D triangle ABC in screen space with clipped coordinates within the following +// bounds: x in [0,W], y in [0,H], z in [0,1]. The origin for (x,y) is in the bottom +// left corner of the pixel grid. z=0 is the near plane and z=1 is the far plane, +// so lesser values are closer. The coordinates of the pixels are evenly spaced +// in x and y 1 units apart starting at the bottom-left pixel with coords +// (0.5,0.5). In other words, the pixel sample point is in the center of the +// rectangular grid cell containing the pixel sample. The framebuffer has +// dimensions width x height (WxH). The Color buffer is a 1D array (row-major +// order) with 3 unsigned chars per pixel (24-bit color). The Depth buffer is +// a 1D array (also row-major order) with a float value per pixel +// For a pixel location (x,y) we can obtain +// the Color and Depth array locations as: Color[(((int)y)*W+((int)x))*3] +// (for the red value, green is offset +1, and blue is offset +2 and +// Depth[((int)y)*W+((int)x)]. Fills the pixels contained in the triangle +// with the global current color and the properly linearly interpolated depth +// value (performs Z-buffer depth test before writing new pixel). +// Pixel samples that lie inside the triangle edges are filled with +// a bias towards the minimum values (samples that lie exactly on a triangle +// edge are filled only for minimum x values along a horizontal span and for +// minimum y values, samples lying on max values are not filled). +// Per-vertex colors are RGB floating point triplets in [0.0,255.0]. The vertices +// include their w-components for use in linearly interpolating perspectively +// correct color (RGB) and texture-coords (st) across the face of the triangle. +// A texture image of RGB floating point triplets of size TWxWH is also given. +// Texture colors are normalized RGB values in [0,1]. +// clamp and repeat wrapping modes : Wrapping={0,1} +// nearest and bilinear filtering: Filtering={0,1} +// replace and modulate application modes: Function={0,1} +//--------------------------------------------------------------------------- +static void RasterizeTriangle_SMOOTH_TEX_PC(running_machine *machine, mame_bitmap *Color, + float A[4], float B[4], float C[4], + float Ca[3], float Cb[3], float Cc[3], // PER-VERTEX RGB COLORS + float Ta[2], float Tb[2], float Tc[2], // PER-VERTEX (S,T) TEX-COORDS + int Wrapping, int Filtering, int Function) +{ + // Get our order of points by increasing y-coord + float *p_min = ((A[1] <= B[1]) && (A[1] <= C[1])) ? A : ((B[1] <= A[1]) && (B[1] <= C[1])) ? B : C; + float *p_max = ((A[1] >= B[1]) && (A[1] >= C[1])) ? A : ((B[1] >= A[1]) && (B[1] >= C[1])) ? B : C; + float *p_mid = ((A != p_min) && (A != p_max)) ? A : ((B != p_min) && (B != p_max)) ? B : C; + + // Perspectively correct color interpolation, interpolate r/w, g/w, b/w, then divide by 1/w at each pixel (A[3] = 1/w) + float ca[3], cb[3], cc[3]; + float ta[2], tb[2], tc[2]; + + float *c_min; + float *c_mid; + float *c_max; + + // We must keep the tex coords straight with the point ordering + float *t_min; + float *t_mid; + float *t_max; + + // Find out control points for y, this divides the triangle into upper and lower + int y_min; + int y_max; + int y_mid; + + // Compute the slopes of each line, and color this is used to determine the interpolation + float x1_slope; + float x2_slope; + float z1_slope; + float z2_slope; + float w1_slope; + float w2_slope; + float r1_slope; + float r2_slope; + float g1_slope; + float g2_slope; + float b1_slope; + float b2_slope; + float s1_slope; + float s2_slope; + float t1_slope; + float t2_slope; + + // Compute the t values used in the equation Ax = Ax + (Bx - Ax)*t + // We only need one t, because it is only used to compute the start. + // Create storage for the interpolated x and z values for both lines + // also for the RGB interpolation + float t; + float x1_interp; + float z1_interp; + float w1_interp; + float r1_interp; + float g1_interp; + float b1_interp; + float s1_interp; + float t1_interp; + + float x2_interp; + float z2_interp; + float w2_interp; + float r2_interp; + float g2_interp; + float b2_interp; + float s2_interp; + float t2_interp; + + // Create storage for the horizontal interpolation of z and RGB color and its starting points + // This is used to fill the triangle horizontally + int x_start, x_end; + float z_interp_x, z_delta_x; + float w_interp_x, w_delta_x; + float r_interp_x, r_delta_x; + float g_interp_x, g_delta_x; + float b_interp_x, b_delta_x; + float s_interp_x, s_delta_x; + float t_interp_x, t_delta_x; + + ca[0] = Ca[0]; ca[1] = Ca[1]; ca[2] = Ca[2]; + cb[0] = Cb[0]; cb[1] = Cb[1]; cb[2] = Cb[2]; + cc[0] = Cc[0]; cc[1] = Cc[1]; cc[2] = Cc[2]; + + // Perspectively correct tex interpolation, interpolate s/w, t/w, then divide by 1/w at each pixel (A[3] = 1/w) + ta[0] = Ta[0]; ta[1] = Ta[1]; + tb[0] = Tb[0]; tb[1] = Tb[1]; + tc[0] = Tc[0]; tc[1] = Tc[1]; + + // We must keep the colors straight with the point ordering + c_min = (p_min == A) ? ca : (p_min == B) ? cb : cc; + c_mid = (p_mid == A) ? ca : (p_mid == B) ? cb : cc; + c_max = (p_max == A) ? ca : (p_max == B) ? cb : cc; + + // We must keep the tex coords straight with the point ordering + t_min = (p_min == A) ? ta : (p_min == B) ? tb : tc; + t_mid = (p_mid == A) ? ta : (p_mid == B) ? tb : tc; + t_max = (p_max == A) ? ta : (p_max == B) ? tb : tc; + + // Find out control points for y, this divides the triangle into upper and lower + y_min = (((int)p_min[1]) + 0.5 >= p_min[1]) ? p_min[1] : ((int)p_min[1]) + 1; + y_max = (((int)p_max[1]) + 0.5 < p_max[1]) ? p_max[1] : ((int)p_max[1]) - 1; + y_mid = (((int)p_mid[1]) + 0.5 >= p_mid[1]) ? p_mid[1] : ((int)p_mid[1]) + 1; + + // Compute the slopes of each line, and color this is used to determine the interpolation + x1_slope = (p_max[0] - p_min[0]) / (p_max[1] - p_min[1]); + x2_slope = (p_mid[0] - p_min[0]) / (p_mid[1] - p_min[1]); + z1_slope = (p_max[2] - p_min[2]) / (p_max[1] - p_min[1]); + z2_slope = (p_mid[2] - p_min[2]) / (p_mid[1] - p_min[1]); + w1_slope = (p_max[3] - p_min[3]) / (p_max[1] - p_min[1]); + w2_slope = (p_mid[3] - p_min[3]) / (p_mid[1] - p_min[1]); + r1_slope = (c_max[0] - c_min[0]) / (p_max[1] - p_min[1]); + r2_slope = (c_mid[0] - c_min[0]) / (p_mid[1] - p_min[1]); + g1_slope = (c_max[1] - c_min[1]) / (p_max[1] - p_min[1]); + g2_slope = (c_mid[1] - c_min[1]) / (p_mid[1] - p_min[1]); + b1_slope = (c_max[2] - c_min[2]) / (p_max[1] - p_min[1]); + b2_slope = (c_mid[2] - c_min[2]) / (p_mid[1] - p_min[1]); + s1_slope = (t_max[0] - t_min[0]) / (p_max[1] - p_min[1]); + s2_slope = (t_mid[0] - t_min[0]) / (p_mid[1] - p_min[1]); + t1_slope = (t_max[1] - t_min[1]) / (p_max[1] - p_min[1]); + t2_slope = (t_mid[1] - t_min[1]) / (p_mid[1] - p_min[1]); + + // Compute the t values used in the equation Ax = Ax + (Bx - Ax)*t + // We only need one t, because it is only used to compute the start. + // Create storage for the interpolated x and z values for both lines + // also for the RGB interpolation + t = (((float)y_min) + 0.5 - p_min[1]) / (p_max[1] - p_min[1]); + x1_interp = p_min[0] + (p_max[0] - p_min[0]) * t; + z1_interp = p_min[2] + (p_max[2] - p_min[2]) * t; + w1_interp = p_min[3] + (p_max[3] - p_min[3]) * t; + r1_interp = c_min[0] + (c_max[0] - c_min[0]) * t; + g1_interp = c_min[1] + (c_max[1] - c_min[1]) * t; + b1_interp = c_min[2] + (c_max[2] - c_min[2]) * t; + s1_interp = t_min[0] + (t_max[0] - t_min[0]) * t; + t1_interp = t_min[1] + (t_max[1] - t_min[1]) * t; + + t = (((float)y_min) + 0.5 - p_min[1]) / (p_mid[1] - p_min[1]); + x2_interp = p_min[0] + (p_mid[0] - p_min[0]) * t; + z2_interp = p_min[2] + (p_mid[2] - p_min[2]) * t; + w2_interp = p_min[3] + (p_mid[3] - p_min[3]) * t; + r2_interp = c_min[0] + (c_mid[0] - c_min[0]) * t; + g2_interp = c_min[1] + (c_mid[1] - c_min[1]) * t; + b2_interp = c_min[2] + (c_mid[2] - c_min[2]) * t; + s2_interp = t_min[0] + (t_mid[0] - t_min[0]) * t; + t2_interp = t_min[1] + (t_mid[1] - t_min[1]) * t; + + // First work on the bottom half of the triangle + // I'm using y_min as the incrementer because it saves space and we don't need it anymore + for ( ; y_min < y_mid; y_min++) { + + // We always want to fill left to right, so we have 2 main cases + // Compute the integer starting and ending points and the appropriate z by + // interpolating. Remember the pixels are in the middle of the grid, i.e. (0.5,0.5,0.5) + if (x1_interp < x2_interp) { + x_start = ((((int)x1_interp) + 0.5) >= x1_interp) ? x1_interp : ((int)x1_interp) + 1; + x_end = ((((int)x2_interp) + 0.5) < x2_interp) ? x2_interp : ((int)x2_interp) - 1; + z_delta_x = (z2_interp - z1_interp) / (x2_interp - x1_interp); + w_delta_x = (w2_interp - w1_interp) / (x2_interp - x1_interp); + r_delta_x = (r2_interp - r1_interp) / (x2_interp - x1_interp); + g_delta_x = (g2_interp - g1_interp) / (x2_interp - x1_interp); + b_delta_x = (b2_interp - b1_interp) / (x2_interp - x1_interp); + s_delta_x = (s2_interp - s1_interp) / (x2_interp - x1_interp); + t_delta_x = (t2_interp - t1_interp) / (x2_interp - x1_interp); + t = (x_start + 0.5 - x1_interp) / (x2_interp - x1_interp); + z_interp_x = z1_interp + (z2_interp - z1_interp) * t; + w_interp_x = w1_interp + (w2_interp - w1_interp) * t; + r_interp_x = r1_interp + (r2_interp - r1_interp) * t; + g_interp_x = g1_interp + (g2_interp - g1_interp) * t; + b_interp_x = b1_interp + (b2_interp - b1_interp) * t; + s_interp_x = s1_interp + (s2_interp - s1_interp) * t; + t_interp_x = t1_interp + (t2_interp - t1_interp) * t; + + } else { + x_start = ((((int)x2_interp) + 0.5) >= x2_interp) ? x2_interp : ((int)x2_interp) + 1; + x_end = ((((int)x1_interp) + 0.5) < x1_interp) ? x1_interp : ((int)x1_interp) - 1; + z_delta_x = (z1_interp - z2_interp) / (x1_interp - x2_interp); + w_delta_x = (w1_interp - w2_interp) / (x1_interp - x2_interp); + r_delta_x = (r1_interp - r2_interp) / (x1_interp - x2_interp); + g_delta_x = (g1_interp - g2_interp) / (x1_interp - x2_interp); + b_delta_x = (b1_interp - b2_interp) / (x1_interp - x2_interp); + s_delta_x = (s1_interp - s2_interp) / (x1_interp - x2_interp); + t_delta_x = (t1_interp - t2_interp) / (x1_interp - x2_interp); + t = (x_start + 0.5 - x2_interp) / (x1_interp - x2_interp); + z_interp_x = z2_interp + (z1_interp - z2_interp) * t; + w_interp_x = w2_interp + (w1_interp - w2_interp) * t; + r_interp_x = r2_interp + (r1_interp - r2_interp) * t; + g_interp_x = g2_interp + (g1_interp - g2_interp) * t; + b_interp_x = b2_interp + (b1_interp - b2_interp) * t; + s_interp_x = s2_interp + (s1_interp - s2_interp) * t; + t_interp_x = t2_interp + (t1_interp - t2_interp) * t; + } + + // Pass the horizontal line to the filler, this could be put in the routine + // then interpolate for the next values of x and z + FillSmoothTexPCHorizontalLine(machine, Color, Wrapping, Filtering, Function, + x_start, x_end, y_min, z_interp_x, z_delta_x, w_interp_x, w_delta_x, + r_interp_x, r_delta_x, g_interp_x, g_delta_x, b_interp_x, b_delta_x, + s_interp_x, s_delta_x, t_interp_x, t_delta_x); + x1_interp += x1_slope; z1_interp += z1_slope; + x2_interp += x2_slope; z2_interp += z2_slope; + r1_interp += r1_slope; r2_interp += r2_slope; + g1_interp += g1_slope; g2_interp += g2_slope; + b1_interp += b1_slope; b2_interp += b2_slope; + w1_interp += w1_slope; w2_interp += w2_slope; + s1_interp += s1_slope; s2_interp += s2_slope; + t1_interp += t1_slope; t2_interp += t2_slope; + } + + // Now do the same thing for the top half of the triangle. + // We only need to recompute the x2 line because it changes at the midpoint + x2_slope = (p_max[0] - p_mid[0]) / (p_max[1] - p_mid[1]); + z2_slope = (p_max[2] - p_mid[2]) / (p_max[1] - p_mid[1]); + w2_slope = (p_max[3] - p_mid[3]) / (p_max[1] - p_mid[1]); + r2_slope = (c_max[0] - c_mid[0]) / (p_max[1] - p_mid[1]); + g2_slope = (c_max[1] - c_mid[1]) / (p_max[1] - p_mid[1]); + b2_slope = (c_max[2] - c_mid[2]) / (p_max[1] - p_mid[1]); + s2_slope = (t_max[0] - t_mid[0]) / (p_max[1] - p_mid[1]); + t2_slope = (t_max[1] - t_mid[1]) / (p_max[1] - p_mid[1]); + + t = (((float)y_mid) + 0.5 - p_mid[1]) / (p_max[1] - p_mid[1]); + x2_interp = p_mid[0] + (p_max[0] - p_mid[0]) * t; + z2_interp = p_mid[2] + (p_max[2] - p_mid[2]) * t; + w2_interp = p_mid[3] + (p_max[3] - p_mid[3]) * t; + r2_interp = c_mid[0] + (c_max[0] - c_mid[0]) * t; + g2_interp = c_mid[1] + (c_max[1] - c_mid[1]) * t; + b2_interp = c_mid[2] + (c_max[2] - c_mid[2]) * t; + s2_interp = t_mid[0] + (t_max[0] - t_mid[0]) * t; + t2_interp = t_mid[1] + (t_max[1] - t_mid[1]) * t; + + // We've seen this loop before haven't we? + // I'm using y_mid as the incrementer because it saves space and we don't need it anymore + for ( ; y_mid <= y_max; y_mid++) { + + if (x1_interp < x2_interp) { + x_start = ((((int)x1_interp) + 0.5) >= x1_interp) ? x1_interp : ((int)x1_interp) + 1; + x_end = ((((int)x2_interp) + 0.5) < x2_interp) ? x2_interp : ((int)x2_interp) - 1; + z_delta_x = (z2_interp - z1_interp) / (x2_interp - x1_interp); + w_delta_x = (w2_interp - w1_interp) / (x2_interp - x1_interp); + r_delta_x = (r2_interp - r1_interp) / (x2_interp - x1_interp); + g_delta_x = (g2_interp - g1_interp) / (x2_interp - x1_interp); + b_delta_x = (b2_interp - b1_interp) / (x2_interp - x1_interp); + s_delta_x = (s2_interp - s1_interp) / (x2_interp - x1_interp); + t_delta_x = (t2_interp - t1_interp) / (x2_interp - x1_interp); + t = (x_start + 0.5 - x1_interp) / (x2_interp - x1_interp); + z_interp_x = z1_interp + (z2_interp - z1_interp) * t; + w_interp_x = w1_interp + (w2_interp - w1_interp) * t; + r_interp_x = r1_interp + (r2_interp - r1_interp) * t; + g_interp_x = g1_interp + (g2_interp - g1_interp) * t; + b_interp_x = b1_interp + (b2_interp - b1_interp) * t; + s_interp_x = s1_interp + (s2_interp - s1_interp) * t; + t_interp_x = t1_interp + (t2_interp - t1_interp) * t; + + } else { + x_start = ((((int)x2_interp) + 0.5) >= x2_interp) ? x2_interp : ((int)x2_interp) + 1; + x_end = ((((int)x1_interp) + 0.5) < x1_interp) ? x1_interp : ((int)x1_interp) - 1; + z_delta_x = (z1_interp - z2_interp) / (x1_interp - x2_interp); + w_delta_x = (w1_interp - w2_interp) / (x1_interp - x2_interp); + r_delta_x = (r1_interp - r2_interp) / (x1_interp - x2_interp); + g_delta_x = (g1_interp - g2_interp) / (x1_interp - x2_interp); + b_delta_x = (b1_interp - b2_interp) / (x1_interp - x2_interp); + s_delta_x = (s1_interp - s2_interp) / (x1_interp - x2_interp); + t_delta_x = (t1_interp - t2_interp) / (x1_interp - x2_interp); + t = (x_start + 0.5 - x2_interp) / (x1_interp - x2_interp); + z_interp_x = z2_interp + (z1_interp - z2_interp) * t; + w_interp_x = w2_interp + (w1_interp - w2_interp) * t; + r_interp_x = r2_interp + (r1_interp - r2_interp) * t; + g_interp_x = g2_interp + (g1_interp - g2_interp) * t; + b_interp_x = b2_interp + (b1_interp - b2_interp) * t; + s_interp_x = s2_interp + (s1_interp - s2_interp) * t; + t_interp_x = t2_interp + (t1_interp - t2_interp) * t; + } + + // Pass the horizontal line to the filler, this could be put in the routine + // then interpolate for the next values of x and z + FillSmoothTexPCHorizontalLine(machine, Color, Wrapping, Filtering, Function, + x_start, x_end, y_mid, z_interp_x, z_delta_x, w_interp_x, w_delta_x, + r_interp_x, r_delta_x, g_interp_x, g_delta_x, b_interp_x, b_delta_x, + s_interp_x, s_delta_x, t_interp_x, t_delta_x); + x1_interp += x1_slope; z1_interp += z1_slope; + x2_interp += x2_slope; z2_interp += z2_slope; + r1_interp += r1_slope; r2_interp += r2_slope; + g1_interp += g1_slope; g2_interp += g2_slope; + b1_interp += b1_slope; b2_interp += b2_slope; + w1_interp += w1_slope; w2_interp += w2_slope; + s1_interp += s1_slope; s2_interp += s2_slope; + t1_interp += t1_slope; t2_interp += t2_slope; + } +} |