diff --git a/Src/Graphics/Models.cpp b/Src/Graphics/Models.cpp index fffa5d2..21db8fa 100644 --- a/Src/Graphics/Models.cpp +++ b/Src/Graphics/Models.cpp @@ -45,30 +45,31 @@ * All vertex information is stored in an array of GLfloats. Offset and size * information is defined here for now. */ -#define VBO_VERTEX_OFFSET_X 0 // vertex X * -#define VBO_VERTEX_OFFSET_Y 1 // vertex Y * -#define VBO_VERTEX_OFFSET_Z 2 // vertex Z * -#define VBO_VERTEX_OFFSET_NX 3 // normal X * -#define VBO_VERTEX_OFFSET_NY 4 // normal Y * -#define VBO_VERTEX_OFFSET_NZ 5 // normal Z * -#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R c (w/ texenable?) +#define VBO_VERTEX_OFFSET_X 0 // vertex X +#define VBO_VERTEX_OFFSET_Y 1 // vertex Y +#define VBO_VERTEX_OFFSET_Z 2 // vertex Z +#define VBO_VERTEX_OFFSET_NX 3 // normal X +#define VBO_VERTEX_OFFSET_NY 4 // normal Y +#define VBO_VERTEX_OFFSET_NZ 5 // normal Z +#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R #define VBO_VERTEX_OFFSET_G 7 // color and material G #define VBO_VERTEX_OFFSET_B 8 // color and material B -#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque) * -#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled) * -#define VBO_VERTEX_OFFSET_FOGINTENSITY 11 // fog intensity (0.0 no fog applied, 1.0 all fog applied) * (combine w/ lightenable, making one negative) -#define VBO_VERTEX_OFFSET_U 12 // texture U coordinate (in texels, relative to sub-texture) * -#define VBO_VERTEX_OFFSET_V 13 // texture V coordinate * -#define VBO_VERTEX_OFFSET_TEXTURE_X 14 // sub-texture parameters, X (position in overall texture map, in texels) * -#define VBO_VERTEX_OFFSET_TEXTURE_Y 15 // "" Y "" * -#define VBO_VERTEX_OFFSET_TEXTURE_W 16 // sub-texture parameters, width of texture in texels * -#define VBO_VERTEX_OFFSET_TEXTURE_H 17 // "" height of texture in texels * -#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 18 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon) c (w/ R?) -#define VBO_VERTEX_OFFSET_TEXPARAMS_TRANS 19 // texture parameter: >=0 use transparency bit, <0 no transparency (per-polygon) c (w/ contour?) -#define VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP 20 // texture parameters: U wrap mode: ==1 mirrored repeat, ==0 normal repeat -#define VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP 21 // "" V wrap mode "" -#define VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR 22 // contour texture: >0 indicates contour texture (see also texParams.trans) c (w/ trans?) -#define VBO_VERTEX_SIZE 23 // total size (may include padding for alignment) +#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque) +#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled) +#define VBO_VERTEX_OFFSET_SHININESS 11 // shininess (if negative, disables specular lighting) +#define VBO_VERTEX_OFFSET_FOGINTENSITY 12 // fog intensity (0.0 no fog applied, 1.0 all fog applied) +#define VBO_VERTEX_OFFSET_U 13 // texture U coordinate (in texels, relative to sub-texture) +#define VBO_VERTEX_OFFSET_V 14 // texture V coordinate +#define VBO_VERTEX_OFFSET_TEXTURE_X 15 // sub-texture parameters, X (position in overall texture map, in texels) +#define VBO_VERTEX_OFFSET_TEXTURE_Y 16 // "" Y "" +#define VBO_VERTEX_OFFSET_TEXTURE_W 17 // sub-texture parameters, width of texture in texels +#define VBO_VERTEX_OFFSET_TEXTURE_H 18 // "" height of texture in texels +#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 19 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon) +#define VBO_VERTEX_OFFSET_TEXPARAMS_TRANS 20 // texture parameter: >=0 use transparency bit, <0 no transparency (per-polygon) +#define VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP 21 // texture parameters: U wrap mode: ==1 mirrored repeat, ==0 normal repeat +#define VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP 22 // "" V wrap mode "" +#define VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR 23 // contour texture: >0 indicates contour texture (see also texParams.trans) +#define VBO_VERTEX_SIZE 24 // total size (may include padding for alignment) /****************************************************************************** @@ -156,6 +157,7 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state) glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR*sizeof(GLfloat))); glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat))); glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat))); + glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat))); glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat))); // Set up state @@ -351,13 +353,13 @@ void CRender3D::ClearDisplayList(ModelCache *Cache) Vertices are copied in batches sorted by state when the model is complete. ******************************************************************************/ -// Inserts a vertex into the local vertex buffer, incrementing both the local and VBO pointers. The normal is scaled by normFlip. +// Inserts a vertex into the local vertex buffer, incrementing both the local and VBO pointers. The normal is scaled by normFlip. void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, float normFlip) { GLfloat r, g, b; GLfloat translucence, fogIntensity, texWidth, texHeight, texBaseX, texBaseY, contourProcessing; unsigned baseIdx, texFormat, texEnable, lightEnable, modulate, colorIdx; - int s, texPage; + int s, texPage, shininess; // Texture selection texEnable = P->header[6]&0x04000000; @@ -377,9 +379,8 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, */ lightEnable = !(P->header[6]&0x00010000); - modulate = !(P->header[4]&0x80); + //modulate = !(P->header[4]&0x80); modulate = P->header[3]&0x80; // seems to work better - // Material color if ((P->header[1]&2) == 0) @@ -404,6 +405,14 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, if (!modulate) r = g = b = 1.0f; } + + // Specular shininess + shininess = (P->header[0]>>26)&0x3F; + //shininess = (P->header[0]>>28)&0xF; + //if (shininess) + // printf("%X\n", shininess); + if (!(P->header[0]&0x80) || (shininess == 0)) // bit 0x80 seems to enable specular lighting + shininess = -1; // disable #if 0 if (texFormat==5)//texFormat==6||texFormat==2) @@ -415,13 +424,18 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, } #endif #if 0 - if ((P->header[testWord]&(1<header[testWord]&(1<header[0]>>24) & 0x3) != 0) + //if (!((P->header[0]>>26) & 0x3F) && (P->header[0]&0x80)) { texEnable = 0; r=b=0; g=1.0f; - if (!lightEnable) - b=1.0f; + g = ((P->header[0]>>26)&0x3F) * (1.0f/64.0f); + //if (!lightEnable) + // b=1.0f; lightEnable=0; } #endif @@ -454,6 +468,7 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_B] = b; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TRANSLUCENCE] = translucence; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_LIGHTENABLE] = lightEnable ? 1.0f : 0.0f; + Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_SHININESS] = (GLfloat) shininess; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_FOGINTENSITY] = fogIntensity; Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NX] = V->n[0]*normFlip; diff --git a/Src/Graphics/Render2D.cpp b/Src/Graphics/Render2D.cpp index ec94b2e..c7a0390 100644 --- a/Src/Graphics/Render2D.cpp +++ b/Src/Graphics/Render2D.cpp @@ -1,3 +1,4 @@ +//TODO: organize memory pool more tightly: 2 512x384 layers plus 4 extra lines /** ** Supermodel ** A Sega Model 3 Arcade Emulator. @@ -243,154 +244,111 @@ void CRender2D::DrawTileLine8BitRightClip(UINT32 *buf, int offset, UINT16 tile, ******************************************************************************/ /* - * DrawCompleteLayer(): + * DrawLine(): * - * Updates the complete layer. + * Draws a single scanline of single layer. Vertical (but not horizontal) + * scrolling is applied here. + * + * Parametes: + * dest Destination of 512-pixel wide output buffer to draw + * to. + * layerNum Layer number: + * 0 = Layer A (@ 0xF8000) + * 1 = Layer A' (@ 0xFA000) + * 2 = Layer B (@ 0xFC000) + * 3 = Layer B' (@ 0xFE000) + * y Line number (0-495). + * nameTableBase Pointer to VRAM name table (see above addresses) + * for this layer. + * hScrollTable Pointer to the line-by-line horizontal scroll value + * table for this layer. */ -void CRender2D::DrawCompleteLayer(int layerNum, const UINT16 *nameTableBase) +void CRender2D::DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase) { - UINT32 *dest = surf; // destination surface to write to - UINT32 *lineBufferPri = &surf[512*496]; // line buffer for primary and alternate layer - UINT32 *lineBufferAlt = &surf[512*497]; - UINT32 *buf; - const UINT16 *maskTable; // pointer to start of mask table - const UINT16 *hScrollTablePri, *hScrollTableAlt; // pointers to line scroll tables - const UINT16 *nameTablePri = nameTableBase; // primary (this layer) name table - const UINT16 *nameTableAlt = &nameTableBase[64*64]; // alternate layer's name table - const UINT16 *nameTable; - int colorDepthPri, colorDepthAlt; // primary and alternate layer color depths - int hScrollPri, hScrollAlt; // primary and alternate layer scroll offsets - int vScrollPri, vScrollAlt; - int hFullScrollPri, hFullScrollAlt; // full-screen horizontal scroll values (from registers) - int vOffset; // vertical pixel offset within tile - int tx, i, j; - bool lineScrollPri, lineScrollAlt; // line scrolling enable/disable - UINT16 mask; - // Determine layer color depths (1 if 4-bit, 0 if 8-bit) - colorDepthPri = regs[0x20/4] & (1<<(12+layerNum*2)); - colorDepthAlt = regs[0x20/4] & (1<<(12+layerNum*2+1)); + // Determine the layer color depth (4 or 8-bit pixels) + bool is4Bit = regs[0x20/4] & (1<<(12+layerNum)); - // Line scroll tables - hScrollTablePri = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400)/4]; - hScrollTableAlt = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400+0x400)/4]; + // Compute offsets due to vertical scrolling + int vScroll = (regs[0x60/4+layerNum]>>16)&0x1FF; + const UINT16 *nameTable = &nameTableBase[(64*((y+vScroll)/8)) & 0xFFF]; // clamp to 64x64=0x1000 + int vOffset = (y+vScroll)&7; // vertical pixel offset within 8x8 tile - // Get correct offset into mask table - maskTable = (UINT16 *) &vram[0xF7000/4]; - if (layerNum == 0) - ++maskTable; // little endian, layer 0 is second word in each pair - - // Load horizontal full-screen scroll values and scroll mode - hFullScrollPri = regs[0x60/4+layerNum*2]&0x3FF; - hFullScrollAlt = regs[0x60/4+layerNum*2+1]&0x3FF; - lineScrollPri = regs[0x60/4+layerNum*2]&0x8000; - lineScrollAlt = regs[0x60/4+layerNum*2+1]&0x8000; - - // Load vertical scroll values - vScrollPri = (regs[0x60/4+layerNum*2]>>16)&0x1FF; - vScrollAlt = (regs[0x60/4+layerNum*2+1]>>16)&0x1FF; - - // Iterate over all displayed lines - for (int y = 0; y < 384; y++) + // Render 512 pixels (64 tiles) w/out any horizontal scrolling or masking + if (is4Bit) { - /* - * Draw all tiles from primary layer first. Horizontal scrolling is not - * applied yet, but vertical scrolling is taken into account. An entire - * 512-pixel line is rendered so that it can be scrolled during mixing. - */ - nameTable = &nameTablePri[(64*((y+vScrollPri)/8)) & 0xFFF]; // clamp to 64x64=0x1000 - vOffset = (y+vScrollPri)&7; - buf = lineBufferPri; // output to primary line buffer - for (tx = 0; tx < 64; tx += 4) // 4 tiles at a time (for masking) + for (int tx = 0; tx < 64; tx += 4) { - if (colorDepthPri) //TODO: move this test outside of loop - { - DrawTileLine4BitNoClip(buf, nameTable[1], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[0], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[3], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[2], vOffset); - buf += 8; - } - else - { - DrawTileLine8BitNoClip(buf, nameTable[1], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[0], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[3], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[2], vOffset); - buf += 8; - } - - // Next set of 4 tiles + // Little endian: offsets 0,1,2,3 become 1,0,3,2 + DrawTileLine4BitNoClip(dest, nameTable[1], vOffset); dest += 8; + DrawTileLine4BitNoClip(dest, nameTable[0], vOffset); dest += 8; + DrawTileLine4BitNoClip(dest, nameTable[3], vOffset); dest += 8; + DrawTileLine4BitNoClip(dest, nameTable[2], vOffset); dest += 8; + nameTable += 4; // next set of 4 tiles + } + } + else + { + for (int tx = 0; tx < 64; tx += 4) + { + DrawTileLine8BitNoClip(dest, nameTable[1], vOffset); dest += 8; + DrawTileLine8BitNoClip(dest, nameTable[0], vOffset); dest += 8; + DrawTileLine8BitNoClip(dest, nameTable[3], vOffset); dest += 8; + DrawTileLine8BitNoClip(dest, nameTable[2], vOffset); dest += 8; nameTable += 4; } - - /* - * Draw the alternate layer wherever the primary layer was masked - */ - nameTable = &nameTableAlt[(64*((y+vScrollAlt)/8))&0xFFF]; - vOffset = (y+vScrollAlt)&7; - buf = lineBufferAlt; // output to alternate line buffer - for (tx = 0; tx < 64; tx += 4) // 4 tiles at a time (for masking) - { - if (colorDepthAlt) //TODO: move this test outside of loop - { - DrawTileLine4BitNoClip(buf, nameTable[1], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[0], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[3], vOffset); - buf += 8; - DrawTileLine4BitNoClip(buf, nameTable[2], vOffset); - buf += 8; - } - else - { - DrawTileLine8BitNoClip(buf, nameTable[1], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[0], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[3], vOffset); - buf += 8; - DrawTileLine8BitNoClip(buf, nameTable[2], vOffset); - buf += 8; - } - - // Next set of 4 tiles - nameTable += 4; - } - - /* - * Mix the two layers into the current line under control of the - * stencil mask, applying scrolling in the process. - */ + } +} + +void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bool isBottom) +{ + /* + * Mix in the appropriate layer under control of the stencil mask, applying + * horizontal scrolling in theprocess + */ - // Load horizontal scroll values - if (lineScrollPri) - hScrollPri = hScrollTablePri[y]; - else - hScrollPri = hFullScrollPri; - if (lineScrollAlt) - hScrollAlt = hScrollTableAlt[y]; - else - hScrollAlt = hFullScrollAlt; + // Line scroll table + const UINT16 *hScrollTable = (UINT16 *) &vram[(0xF6000+layerNum*0x400)/4]; + + // Load horizontal full-screen scroll values and scroll mode + int hFullScroll = regs[0x60/4+layerNum]&0x3FF; + bool lineScrollMode = regs[0x60/4+layerNum]&0x8000; + + // Load horizontal scroll values + int hScroll; + if (lineScrollMode) + hScroll = hScrollTable[y]; + else + hScroll = hFullScroll; + + // Get correct offset into mask table + const UINT16 *maskTable = (UINT16 *) &vram[0xF7000/4]; + maskTable += 2*y; + if (layerNum < 2) // little endian: layers A and A' use second word in each pair + ++maskTable; + + // Figure out what mask bit should be to mix in this layer + UINT16 doCopy; + if ((layerNum & 1)) // layers 1 and 3 are A' and B': alternates + doCopy = 0x0000; // if mask is clear, copy alternate layer + else + doCopy = 0x8000; // copy primary layer when mask is set - // Mix first 60 tiles (4 at a time) - mask = *maskTable; - i = hScrollPri&511; // primary line index - j = hScrollAlt&511; // alternate line index - for (tx = 0; tx < 60; tx += 4) + // Mix first 60 tiles (4 at a time) + UINT16 mask = *maskTable; // mask for this line (each bit covers 4 tiles) + int i = hScroll&511; // line index (where to copy from) + for (int tx = 0; tx < 60; tx += 4) + { + // If bottom layer, we can copy without worrying about transparency, and must also write blank values when this layer is not showing + //TODO: move this test outside of loop + if (isBottom) { - if ((mask&0x8000)) // copy tiles from primary layer + // Only copy pixels if the mask bit is appropriate for this layer type + if ((mask&0x8000) == doCopy) { if (i <= (512-32)) // safe to use memcpy for fast blit? { - memcpy(dest, &lineBufferPri[i], 32*sizeof(UINT32)); + memcpy(dest, &src[i], 32*sizeof(UINT32)); i += 32; dest += 32; } @@ -399,76 +357,148 @@ void CRender2D::DrawCompleteLayer(int layerNum, const UINT16 *nameTableBase) for (int k = 0; k < 32; k++) { i &= 511; - *dest++ = lineBufferPri[i++]; + *dest++ = src[i++]; } } - j += 32; // update alternate pointer as well - } - else // copy tiles from alternate layer - { - if (j <= (512-32)) - { - memcpy(dest, &lineBufferAlt[j], 32*sizeof(UINT32)); - j += 32; - dest += 32; - } - else - { - for (int k = 0; k < 32; k++) - { - j &= 511; - *dest++ = lineBufferAlt[j++]; - } - } - - i += 32; // update primary } - - mask <<= 1; + else + { + // Write blank pixels + memset(dest, 0, 32*sizeof(UINT32)); + i += 32; + i &= 511; // wrap line boundaries + dest += 32; + } + } + else + { + // Copy while testing for transparencies + if ((mask&0x8000) == doCopy) + { + UINT32 p; + for (int k = 0; k < 32; k++) + { + i &= 511; + p = src[i++]; + if ((p>>24) != 0) // opaque pixel, put it down + *dest = p; + dest++; + } + } + else + { + i += 32; + i &= 511; + dest += 32; + } } - // Mix last two tiles - if ((mask&0x8000)) // copy tiles from primary layer + mask <<= 1; + } + + // Mix last two tiles + if (isBottom) + { + if ((mask&0x8000) == doCopy) { for (int k = 0; k < 16; k++) { i &= 511; - *dest++ = lineBufferPri[i++]; + *dest++ = src[i++]; } } - else // copy from alternate + else // clear { for (int k = 0; k < 16; k++) { - j &= 511; - *dest++ = lineBufferAlt[j++]; + i &= 511; + *dest++ = 0; + } + } + } + else + { + if ((mask&0x8000) == doCopy) + { + UINT32 p; + for (int k = 0; k < 16; k++) + { + i &= 511; + p = src[i++]; + if ((p>>24) != 0) + *dest = p; + dest++; } } - - // Next line - maskTable += 2; // next mask line } - - // Upload - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, surf); } -// Updates any changed portions of a layer -void CRender2D::UpdateLayer(int layerNum) + +void CRender2D::DrawTilemaps(UINT32 *destBottom, UINT32 *destTop) { - glBindTexture(GL_TEXTURE_2D, texID[layerNum]); - DrawCompleteLayer(layerNum, (UINT16 *) &vram[(0xF8000+layerNum*2*0x2000)/4]); -} + // Base address of all 4 name tables + const UINT16 *nameTableBase[4]; + nameTableBase[0] = (UINT16 *) &vram[(0xF8000+0*0x2000)/4]; // A + nameTableBase[1] = (UINT16 *) &vram[(0xF8000+1*0x2000)/4]; // A' + nameTableBase[2] = (UINT16 *) &vram[(0xF8000+2*0x2000)/4]; // B + nameTableBase[3] = (UINT16 *) &vram[(0xF8000+3*0x2000)/4]; // B' + + // Render and mix each line + for (int y = 0; y < 384; y++) + { + // Draw each layer + DrawLine(lineBuffer[0], 0, y, nameTableBase[0]); + DrawLine(lineBuffer[1], 1, y, nameTableBase[1]); + DrawLine(lineBuffer[2], 2, y, nameTableBase[2]); + DrawLine(lineBuffer[3], 3, y, nameTableBase[3]); + //TODO: could probably further optimize: only have a single layer clear masked-out areas, then if alt. layer is being written to same place, don't bother worrying about transparencies if directly on top + // Combine according to priority settings + // NOTE: question mark indicates unobserved and therefore unknown + switch ((regs[0x20/4]>>8)&0xF) + { + case 0x5: // top: A, B, A'? bottom: B' + MixLine(destBottom, lineBuffer[3], 3, y, true); + MixLine(destTop, lineBuffer[2], 2, y, true); + MixLine(destTop, lineBuffer[0], 0, y, false); + MixLine(destTop, lineBuffer[1], 1, y, false); + break; + case 0xF: // all on top + memset(destBottom, 0, 496*sizeof(UINT32)); //TODO: use glClear(GL_COLOR_BUFFER_BIT) if there is no bottom layer + MixLine(destTop, lineBuffer[2], 2, y, true); + MixLine(destTop, lineBuffer[3], 3, y, false); + MixLine(destTop, lineBuffer[0], 0, y, false); + MixLine(destTop, lineBuffer[1], 1, y, false); + break; + case 0x7: // top: A, B bottom: A'?, B' + MixLine(destBottom, lineBuffer[3], 3, y, true); + MixLine(destBottom, lineBuffer[1], 1, y, false); + MixLine(destTop, lineBuffer[2], 2, y, true); + MixLine(destTop, lineBuffer[0], 0, y, false); + break; + default: // unknown, use A and A' on top, B and B' on the bottom + MixLine(destBottom, lineBuffer[2], 2, y, true); + MixLine(destBottom, lineBuffer[3], 3, y, false); + MixLine(destTop, lineBuffer[0], 0, y, true); + MixLine(destTop, lineBuffer[1], 1, y, false); + break; + } + + // Advance to next line in output surfaces + destBottom += 496; + destTop += 496; + } +} + /****************************************************************************** Frame Display Functions ******************************************************************************/ -// Draws a layer to the screen -void CRender2D::DisplayLayer(int layerNum, GLfloat z) +// Draws a surface to the screen (0 is top and 1 is bottom) +void CRender2D::DisplaySurface(int surface, GLfloat z) { - glBindTexture(GL_TEXTURE_2D, texID[layerNum]); + glBindTexture(GL_TEXTURE_2D, texID[surface]); glBegin(GL_QUADS); glTexCoord2f(0.0f/512.0f, 0.0f); glVertex3f(0.0f, 0.0f, z); glTexCoord2f(496.0f/512.0f, 0.0f); glVertex3f(1.0f, 0.0f, z); @@ -527,14 +557,17 @@ void CRender2D::BeginFrame(void) GLfloat colorOffset[3]; // Update all layers - for (int i = 0; i < 2; i++) - UpdateLayer(i); + DrawTilemaps(surfBottom, surfTop); + glBindTexture(GL_TEXTURE_2D, texID[0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, surfTop); + glBindTexture(GL_TEXTURE_2D, texID[1]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, surfBottom); - // Draw bottom layer + // Display bottom surface Setup2D(); ColorOffset(colorOffset, regs[0x44/4]); glUniform3fv(colorOffsetLoc, 1, colorOffset); - DisplayLayer(1, 0.0); + DisplaySurface(1, 0.0); } // Top layers @@ -542,12 +575,12 @@ void CRender2D::EndFrame(void) { GLfloat colorOffset[3]; - // Draw top layer + // Display top surface Setup2D(); glEnable(GL_BLEND); ColorOffset(colorOffset, regs[0x40/4]); glUniform3fv(colorOffsetLoc, 1, colorOffset); - DisplayLayer(0, -0.5); + DisplaySurface(0, -0.5); } @@ -555,8 +588,6 @@ void CRender2D::EndFrame(void) Emulation Callbacks ******************************************************************************/ - - void CRender2D::WriteVRAM(unsigned addr, UINT32 data) { if (vram[addr/4] == data) // do nothing if no changes @@ -586,7 +617,11 @@ void CRender2D::AttachVRAM(const UINT8 *vramPtr) DebugLog("Render2D attached VRAM\n"); } -#define MEMORY_POOL_SIZE (512*512*4) +// Memory pool and offsets within it +#define MEMORY_POOL_SIZE (2*512*384*4 + 4*512*4) +#define OFFSET_TOP_SURFACE 0 // 512*384*4 bytes +#define OFFSET_BOTTOM_SURFACE (512*384*4) // 512*384*4 +#define OFFSET_LINE_BUFFERS (2*512*384*4) // 4*512*4 (4 lines) bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes) { @@ -602,23 +637,23 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned glUniform1i(textureMapLoc,0); // attach it to texture unit 0 colorOffsetLoc = glGetUniformLocation(shaderProgram, "colorOffset"); - // Allocate memory for layer surfaces and palette + // Allocate memory for layer surfaces memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE]; if (NULL == memoryPool) - return ErrorLog("Insufficient memory for tile layer surfaces (need %1.1f MB).", memSizeMB); - memset(memoryPool,0,MEMORY_POOL_SIZE); + return ErrorLog("Insufficient memory for tilemap surfaces (need %1.1f MB).", memSizeMB); + memset(memoryPool,0,MEMORY_POOL_SIZE); // clear textures // Set up pointers to memory regions - surf = (UINT32 *) memoryPool; + surfTop = (UINT32 *) &memoryPool[OFFSET_TOP_SURFACE]; + surfBottom = (UINT32 *) &memoryPool[OFFSET_BOTTOM_SURFACE]; + for (int i = 0; i < 4; i++) + lineBuffer[i] = (UINT32 *) &memoryPool[OFFSET_LINE_BUFFERS + i*512*4]; // Resolution xPixels = xRes; yPixels = yRes; xOffs = xOffset; yOffs = yOffset; - - // Clear textures - memset(memoryPool, 0, MEMORY_POOL_SIZE); // Create textures glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -630,9 +665,9 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, surfTop); if (glGetError() != GL_NO_ERROR) - return ErrorLog("OpenGL was unable to provide 512x512-texel texture maps for tile map layers."); + return ErrorLog("OpenGL was unable to provide 512x512-texel texture maps for tilemap layers."); } DebugLog("Render2D initialized (allocated %1.1f MB)\n", memSizeMB); @@ -648,7 +683,10 @@ CRender2D::CRender2D(void) memoryPool = NULL; vram = NULL; - surf = NULL; + surfTop = NULL; + surfBottom = NULL; + for (int i = 0; i < 4; i++) + lineBuffer[i] = NULL; DebugLog("Built Render2D\n"); } @@ -664,8 +702,11 @@ CRender2D::~CRender2D(void) memoryPool = NULL; } - surf = NULL; vram = NULL; + surfTop = NULL; + surfBottom = NULL; + for (int i = 0; i < 4; i++) + lineBuffer[i] = NULL; DebugLog("Destroyed Render2D\n"); } diff --git a/Src/Graphics/Render2D.h b/Src/Graphics/Render2D.h index 73b20d5..287511a 100644 --- a/Src/Graphics/Render2D.h +++ b/Src/Graphics/Render2D.h @@ -31,10 +31,6 @@ #include "Pkgs/glew.h" -// Dirty rectangle size, must be multiples of 4 because masking code relies on this -#define DIRTY_RECT_WIDTH 8 // width of a dirty rectangle in 8-pixel tiles (multiples of 4, divisible into 64) -#define DIRTY_RECT_HEIGHT 8 // height (multiples of 4, divisible into 48) - /* * CRender2D: * @@ -141,9 +137,10 @@ private: void DrawTileLine4BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels); void DrawTileLine8Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine); void DrawTileLine8BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels); - void DrawCompleteLayer(int layerNum, const UINT16 *nameTableBase); - void UpdateLayer(int layerNum); - void DisplayLayer(int layerNum, GLfloat z); + void DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase); + void MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bool isBottom); + void DrawTilemaps(UINT32 *destBottom, UINT32 *destTop); + void DisplaySurface(int surface, GLfloat z); void Setup2D(void); void ColorOffset(GLfloat colorOffset[3], UINT32 reg); @@ -166,7 +163,9 @@ private: // Buffers UINT8 *memoryPool; // all memory is allocated here - UINT32 *surf; // 512x512x32bpp pixel surface + UINT32 *surfTop; // 512x384x32bpp pixel surface for top layers + UINT32 *surfBottom; // bottom layers + UINT32 *lineBuffer[4]; // 512 32bpp pixel line buffers for layer composition }; diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp index aa38a0c..31c2b6d 100644 --- a/Src/Graphics/Render3D.cpp +++ b/Src/Graphics/Render3D.cpp @@ -953,6 +953,7 @@ void CRender3D::RenderFrame(void) glEnableVertexAttribArray(texFormatLoc); glEnableVertexAttribArray(transLevelLoc); glEnableVertexAttribArray(lightEnableLoc); + glEnableVertexAttribArray(shininessLoc); glEnableVertexAttribArray(fogIntensityLoc); // Draw @@ -974,6 +975,7 @@ void CRender3D::RenderFrame(void) // Disable VBO client states glDisableVertexAttribArray(fogIntensityLoc); + glDisableVertexAttribArray(shininessLoc); glDisableVertexAttribArray(lightEnableLoc); glDisableVertexAttribArray(transLevelLoc); glDisableVertexAttribArray(texFormatLoc); @@ -1098,6 +1100,7 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned texFormatLoc = glGetAttribLocation(shaderProgram,"texFormat"); transLevelLoc = glGetAttribLocation(shaderProgram,"transLevel"); lightEnableLoc = glGetAttribLocation(shaderProgram,"lightEnable"); + shininessLoc = glGetAttribLocation(shaderProgram,"shininess"); fogIntensityLoc = glGetAttribLocation(shaderProgram,"fogIntensity"); // Additional OpenGL stuff diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h index ba2a4d6..155acfe 100644 --- a/Src/Graphics/Render3D.h +++ b/Src/Graphics/Render3D.h @@ -398,6 +398,7 @@ private: GLuint texFormatLoc; // attribute GLuint transLevelLoc; // attribute GLuint lightEnableLoc; // attribute + GLuint shininessLoc; // attribute GLuint fogIntensityLoc; // attribute // Model caching