2012-01-27 05:52:59 +00:00
//TODO: organize memory pool more tightly: 2 512x384 layers plus 4 extra lines
2011-04-24 01:14:00 +00:00
/**
* * Supermodel
* * A Sega Model 3 Arcade Emulator .
2011-09-14 19:08:43 +00:00
* * Copyright 2011 Bart Trzynadlowski , Nik Henson
2011-04-24 01:14:00 +00:00
* *
* * This file is part of Supermodel .
* *
* * Supermodel is free software : you can redistribute it and / or modify it under
* * the terms of the GNU General Public License as published by the Free
* * Software Foundation , either version 3 of the License , or ( at your option )
* * any later version .
* *
* * Supermodel is distributed in the hope that it will be useful , but WITHOUT
* * ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* * FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* * more details .
* *
* * You should have received a copy of the GNU General Public License along
* * with Supermodel . If not , see < http : //www.gnu.org/licenses/>.
* */
/*
* Render2D . cpp
*
* Implementation of the CRender2D class : OpenGL tile generator graphics .
*
* To - Do List
* - - - - - - - - - -
2012-01-26 02:48:16 +00:00
* - Add dirty rectangles ?
2011-04-24 01:14:00 +00:00
* - Are v - scroll values 9 or 10 bits ?
* - Add fast paths for no scrolling ( including unclipped tile rendering ) .
* - Inline the loops in the tile renderers .
* - Update description of tile generator before you forget : )
* - A proper shut - down function is needed ! OpenGL might not be available when
* the destructor for this class is called .
*/
# include <string.h>
# include "Pkgs/glew.h"
# include "Supermodel.h"
# include "Graphics/Shaders2D.h" // fragment and vertex shaders
/******************************************************************************
Definitions and Constants
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Shader program files (for use in development builds only)
# define VERTEX_2D_SHADER_FILE "Src / Graphics / Vertex2D.glsl"
# define FRAGMENT_2D_SHADER_FILE "Src / Graphics / Fragment2D.glsl"
/******************************************************************************
Tile Drawing Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Draw 4-bit tile line, no clipping performed
void CRender2D : : DrawTileLine4BitNoClip ( UINT32 * buf , UINT16 tile , int tileLine )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 8 pattern pixels fetched at once
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
tileOffset = ( ( tile & 0x3FFF ) < < 1 ) | ( ( tile > > 15 ) & 1 ) ;
tileOffset * = 32 ;
tileOffset / = 4 ; // VRAM is a UINT32 array
// Upper color bits; the lower 4 bits come from the tile pattern
palette = tile & 0x7FF0 ;
// Draw 8 pixels
pattern = vram [ tileOffset + tileLine ] ;
* buf + + = pal [ ( ( pattern > > 28 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 24 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 20 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 16 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 12 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 8 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 4 ) & 0xF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 0 ) & 0xF ) | palette ] ;
}
// Draw 8-bit tile line, clipped at left edge
void CRender2D : : DrawTileLine8BitNoClip ( UINT32 * buf , UINT16 tile , int tileLine )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 4 pattern pixels fetched at once
tileLine * = 2 ; // 8-bit pixels, each line is two words
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
tileOffset = tile & 0x3FFF ;
tileOffset * = 64 ;
tileOffset / = 4 ;
// Upper color bits
palette = tile & 0x7F00 ;
// Draw 4 pixels at a time
pattern = vram [ tileOffset + tileLine ] ;
* buf + + = pal [ ( ( pattern > > 24 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 16 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 8 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 0 ) & 0xFF ) | palette ] ;
pattern = vram [ tileOffset + tileLine + 1 ] ;
* buf + + = pal [ ( ( pattern > > 24 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 16 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 8 ) & 0xFF ) | palette ] ;
* buf + + = pal [ ( ( pattern > > 0 ) & 0xFF ) | palette ] ;
}
// Draw 4-bit tile line, clipped at left edge
void CRender2D : : DrawTileLine4Bit ( UINT32 * buf , int offset , UINT16 tile , int tileLine )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 8 pattern pixels fetched at once
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
tileOffset = ( ( tile & 0x3FFF ) < < 1 ) | ( ( tile > > 15 ) & 1 ) ;
tileOffset * = 32 ;
tileOffset / = 4 ; // VRAM is a UINT32 array
// Upper color bits; the lower 4 bits come from the tile pattern
palette = tile & 0x7FF0 ;
// Draw 8 pixels
pattern = vram [ tileOffset + tileLine ] ;
for ( int bitPos = 28 ; bitPos > = 0 ; bitPos - = 4 )
{
if ( offset > = 0 )
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xF ) | palette ] ;
+ + offset ;
}
}
// Draw 4-bit tile line, clipped at right edge
void CRender2D : : DrawTileLine4BitRightClip ( UINT32 * buf , int offset , UINT16 tile , int tileLine , int numPixels )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 8 pattern pixels fetched at once
int bitPos ;
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
tileOffset = ( ( tile & 0x3FFF ) < < 1 ) | ( ( tile > > 15 ) & 1 ) ;
tileOffset * = 32 ;
tileOffset / = 4 ; // VRAM is a UINT32 array
// Upper color bits; the lower 4 bits come from the tile pattern
palette = tile & 0x7FF0 ;
// Draw 8 pixels
pattern = vram [ tileOffset + tileLine ] ;
bitPos = 28 ;
for ( int i = 0 ; i < numPixels ; i + + )
{
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xF ) | palette ] ;
+ + offset ;
bitPos - = 4 ;
}
}
// Draw 8-bit tile line, clipped at left edge
void CRender2D : : DrawTileLine8Bit ( UINT32 * buf , int offset , UINT16 tile , int tileLine )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 4 pattern pixels fetched at once
tileLine * = 2 ; // 8-bit pixels, each line is two words
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
tileOffset = tile & 0x3FFF ;
tileOffset * = 64 ;
tileOffset / = 4 ;
// Upper color bits
palette = tile & 0x7F00 ;
// Draw 4 pixels at a time
pattern = vram [ tileOffset + tileLine ] ;
for ( int bitPos = 24 ; bitPos > = 0 ; bitPos - = 8 )
{
if ( offset > = 0 )
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xFF ) | palette ] ;
+ + offset ;
}
pattern = vram [ tileOffset + tileLine + 1 ] ;
for ( int bitPos = 24 ; bitPos > = 0 ; bitPos - = 8 )
{
if ( offset > = 0 )
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xFF ) | palette ] ;
+ + offset ;
}
}
// Draw 8-bit tile line, clipped at right edge
void CRender2D : : DrawTileLine8BitRightClip ( UINT32 * buf , int offset , UINT16 tile , int tileLine , int numPixels )
{
unsigned tileOffset ; // offset of tile pattern within VRAM
unsigned palette ; // color palette bits obtained from tile
UINT32 pattern ; // 4 pattern pixels fetched at once
int bitPos ;
tileLine * = 2 ; // 8-bit pixels, each line is two words
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
tileOffset = tile & 0x3FFF ;
tileOffset * = 64 ;
tileOffset / = 4 ;
// Upper color bits
palette = tile & 0x7F00 ;
// Draw 4 pixels at a time
pattern = vram [ tileOffset + tileLine ] ;
bitPos = 24 ;
for ( int i = 0 ; ( i < 4 ) & & ( i < numPixels ) ; i + + )
{
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xFF ) | palette ] ;
+ + offset ;
bitPos - = 8 ;
}
pattern = vram [ tileOffset + tileLine + 1 ] ;
bitPos = 24 ;
for ( int i = 0 ; ( i < 4 ) & & ( i < numPixels ) ; i + + )
{
buf [ offset ] = pal [ ( ( pattern > > bitPos ) & 0xFF ) | palette ] ;
+ + offset ;
bitPos - = 8 ;
}
}
/******************************************************************************
Layer Rendering
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
2012-01-27 05:52:59 +00:00
* DrawLine ( ) :
2011-04-24 01:14:00 +00:00
*
2012-01-27 05:52:59 +00:00
* 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 .
2011-04-24 01:14:00 +00:00
*/
2012-01-27 05:52:59 +00:00
void CRender2D : : DrawLine ( UINT32 * dest , int layerNum , int y , const UINT16 * nameTableBase )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
// Determine the layer color depth (4 or 8-bit pixels)
bool is4Bit = regs [ 0x20 / 4 ] & ( 1 < < ( 12 + layerNum ) ) ;
2011-04-24 01:14:00 +00:00
2012-01-27 05:52:59 +00:00
// 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
2011-04-24 01:14:00 +00:00
2012-01-27 05:52:59 +00:00
// Render 512 pixels (64 tiles) w/out any horizontal scrolling or masking
if ( is4Bit )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
for ( int tx = 0 ; tx < 64 ; tx + = 4 )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
// 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
2011-04-24 01:14:00 +00:00
}
2012-01-27 05:52:59 +00:00
}
else
{
for ( int tx = 0 ; tx < 64 ; tx + = 4 )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
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 ;
2011-04-24 01:14:00 +00:00
nameTable + = 4 ;
}
2012-01-27 05:52:59 +00:00
}
}
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
*/
2011-04-24 01:14:00 +00:00
2012-01-27 05:52:59 +00:00
// 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
2011-04-24 01:14:00 +00:00
2012-01-27 05:52:59 +00:00
// 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 )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
// Only copy pixels if the mask bit is appropriate for this layer type
if ( ( mask & 0x8000 ) = = doCopy )
2011-04-24 01:14:00 +00:00
{
if ( i < = ( 512 - 32 ) ) // safe to use memcpy for fast blit?
{
2012-01-27 05:52:59 +00:00
memcpy ( dest , & src [ i ] , 32 * sizeof ( UINT32 ) ) ;
2011-04-24 01:14:00 +00:00
i + = 32 ;
dest + = 32 ;
}
else // slow copy, wrap line boundary
{
for ( int k = 0 ; k < 32 ; k + + )
{
i & = 511 ;
2012-01-27 05:52:59 +00:00
* dest + + = src [ i + + ] ;
2011-04-24 01:14:00 +00:00
}
}
2012-01-27 05:52:59 +00:00
}
else
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
// 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 + + )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
i & = 511 ;
p = src [ i + + ] ;
if ( ( p > > 24 ) ! = 0 ) // opaque pixel, put it down
* dest = p ;
dest + + ;
2011-04-24 01:14:00 +00:00
}
}
2012-01-27 05:52:59 +00:00
else
{
i + = 32 ;
i & = 511 ;
dest + = 32 ;
}
2011-04-24 01:14:00 +00:00
}
2012-01-27 05:52:59 +00:00
mask < < = 1 ;
}
// Mix last two tiles
if ( isBottom )
{
if ( ( mask & 0x8000 ) = = doCopy )
2011-04-24 01:14:00 +00:00
{
for ( int k = 0 ; k < 16 ; k + + )
{
i & = 511 ;
2012-01-27 05:52:59 +00:00
* dest + + = src [ i + + ] ;
2011-04-24 01:14:00 +00:00
}
}
2012-01-27 05:52:59 +00:00
else // clear
2011-04-24 01:14:00 +00:00
{
for ( int k = 0 ; k < 16 ; k + + )
{
2012-01-27 05:52:59 +00:00
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 + + ;
2011-04-24 01:14:00 +00:00
}
}
}
}
2012-01-27 05:52:59 +00:00
void CRender2D : : DrawTilemaps ( UINT32 * destBottom , UINT32 * destTop )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
// 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 ;
}
2011-04-24 01:14:00 +00:00
}
2012-01-27 05:52:59 +00:00
2011-04-24 01:14:00 +00:00
/******************************************************************************
Frame Display Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-01-27 05:52:59 +00:00
// Draws a surface to the screen (0 is top and 1 is bottom)
void CRender2D : : DisplaySurface ( int surface , GLfloat z )
2011-04-24 01:14:00 +00:00
{
2012-01-27 05:52:59 +00:00
glBindTexture ( GL_TEXTURE_2D , texID [ surface ] ) ;
2011-04-24 01:14:00 +00:00
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 ) ;
glTexCoord2f ( 496.0f / 512.0f , 384.0f / 512.0f ) ; glVertex3f ( 1.0f , 1.0f , z ) ;
glTexCoord2f ( 0.0f / 512.0f , 384.0f / 512.0f ) ; glVertex3f ( 0.0f , 1.0f , z ) ;
glEnd ( ) ;
}
// Set up viewport and OpenGL state for 2D rendering (sets up blending function but disables blending)
void CRender2D : : Setup2D ( void )
{
// Set up the viewport and orthogonal projection
glViewport ( xOffs , yOffs , xPixels , yPixels ) ;
glMatrixMode ( GL_PROJECTION ) ;
glLoadIdentity ( ) ;
gluOrtho2D ( 0.0 , 1.0 , 1.0 , 0.0 ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glLoadIdentity ( ) ;
// Enable texture mapping and blending
glEnable ( GL_TEXTURE_2D ) ;
glTexEnvf ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ; // alpha of 1.0 is opaque, 0 is transparent
glDisable ( GL_BLEND ) ;
// Disable Z-buffering
glDisable ( GL_DEPTH_TEST ) ;
// Shader program
glUseProgram ( shaderProgram ) ;
}
// Convert color offset register data to RGB
void CRender2D : : ColorOffset ( GLfloat colorOffset [ 3 ] , UINT32 reg )
{
INT8 ir , ig , ib ;
ib = ( reg > > 16 ) & 0xFF ;
ig = ( reg > > 8 ) & 0xFF ;
ir = ( reg > > 0 ) & 0xFF ;
/*
* Uncertain how these should be interpreted . It appears to be signed ,
* which means the values range from - 128 to + 127. The division by 128
* normalizes this to roughly - 1 , + 1.
*/
colorOffset [ 0 ] = ( GLfloat ) ir * ( 1.0f / 128.0f ) ;
colorOffset [ 1 ] = ( GLfloat ) ig * ( 1.0f / 128.0f ) ;
colorOffset [ 2 ] = ( GLfloat ) ib * ( 1.0f / 128.0f ) ;
//printf("%08X -> %g,%g,%g\n", reg, colorOffset[2], colorOffset[1], colorOffset[0]);
}
// Bottom layers
void CRender2D : : BeginFrame ( void )
{
GLfloat colorOffset [ 3 ] ;
// Update all layers
2012-01-27 05:52:59 +00:00
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 ) ;
2011-04-24 01:14:00 +00:00
2012-01-27 05:52:59 +00:00
// Display bottom surface
2011-04-24 01:14:00 +00:00
Setup2D ( ) ;
ColorOffset ( colorOffset , regs [ 0x44 / 4 ] ) ;
glUniform3fv ( colorOffsetLoc , 1 , colorOffset ) ;
2012-01-27 05:52:59 +00:00
DisplaySurface ( 1 , 0.0 ) ;
2011-04-24 01:14:00 +00:00
}
// Top layers
void CRender2D : : EndFrame ( void )
{
GLfloat colorOffset [ 3 ] ;
2012-01-27 05:52:59 +00:00
// Display top surface
2011-04-24 01:14:00 +00:00
Setup2D ( ) ;
glEnable ( GL_BLEND ) ;
ColorOffset ( colorOffset , regs [ 0x40 / 4 ] ) ;
glUniform3fv ( colorOffsetLoc , 1 , colorOffset ) ;
2012-01-27 05:52:59 +00:00
DisplaySurface ( 0 , - 0.5 ) ;
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
Emulation Callbacks
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-07-04 20:53:37 +00:00
void CRender2D : : WriteVRAM ( unsigned addr , UINT32 data )
{
2011-04-24 01:14:00 +00:00
if ( vram [ addr / 4 ] = = data ) // do nothing if no changes
return ;
2011-07-04 20:53:37 +00:00
}
2011-04-24 01:14:00 +00:00
/******************************************************************************
Configuration , Initialization , and Shutdown
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CRender2D : : AttachRegisters ( const UINT32 * regPtr )
{
regs = regPtr ;
DebugLog ( " Render2D attached registers \n " ) ;
}
2012-01-16 23:21:14 +00:00
void CRender2D : : AttachPalette ( const UINT32 * palPtr )
{
pal = palPtr ;
DebugLog ( " Render2D attached palette \n " ) ;
}
2011-04-24 01:14:00 +00:00
void CRender2D : : AttachVRAM ( const UINT8 * vramPtr )
{
vram = ( UINT32 * ) vramPtr ;
DebugLog ( " Render2D attached VRAM \n " ) ;
}
2012-01-27 05:52:59 +00:00
// 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)
2011-04-24 01:14:00 +00:00
2011-09-08 06:34:18 +00:00
bool CRender2D : : Init ( unsigned xOffset , unsigned yOffset , unsigned xRes , unsigned yRes )
2011-04-24 01:14:00 +00:00
{
float memSizeMB = ( float ) MEMORY_POOL_SIZE / ( float ) 0x100000 ;
// Load shaders
if ( OKAY ! = LoadShaderProgram ( & shaderProgram , & vertexShader , & fragmentShader , NULL , NULL , vertexShaderSource , fragmentShaderSource ) )
return FAIL ;
// Get locations of the uniforms
glUseProgram ( shaderProgram ) ; // bind program
textureMapLoc = glGetUniformLocation ( shaderProgram , " textureMap " ) ;
glUniform1i ( textureMapLoc , 0 ) ; // attach it to texture unit 0
colorOffsetLoc = glGetUniformLocation ( shaderProgram , " colorOffset " ) ;
2012-01-27 05:52:59 +00:00
// Allocate memory for layer surfaces
2011-04-24 01:14:00 +00:00
memoryPool = new ( std : : nothrow ) UINT8 [ MEMORY_POOL_SIZE ] ;
if ( NULL = = memoryPool )
2012-01-27 05:52:59 +00:00
return ErrorLog ( " Insufficient memory for tilemap surfaces (need %1.1f MB) . " , memSizeMB) ;
memset ( memoryPool , 0 , MEMORY_POOL_SIZE ) ; // clear textures
2011-04-24 01:14:00 +00:00
// Set up pointers to memory regions
2012-01-27 05:52:59 +00:00
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 ] ;
2011-04-24 01:14:00 +00:00
// Resolution
xPixels = xRes ;
yPixels = yRes ;
xOffs = xOffset ;
yOffs = yOffset ;
2012-01-26 02:48:16 +00:00
2011-04-24 01:14:00 +00:00
// Create textures
glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ;
glGenTextures ( 2 , texID ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
glBindTexture ( GL_TEXTURE_2D , texID [ i ] ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_REPEAT ) ;
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 ) ;
2012-01-27 05:52:59 +00:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , 512 , 512 , 0 , GL_RGBA , GL_UNSIGNED_BYTE , surfTop ) ;
2011-04-24 01:14:00 +00:00
if ( glGetError ( ) ! = GL_NO_ERROR )
2012-01-27 05:52:59 +00:00
return ErrorLog ( " OpenGL was unable to provide 512x512-texel texture maps for tilemap layers. " ) ;
2011-04-24 01:14:00 +00:00
}
DebugLog ( " Render2D initialized (allocated %1.1f MB) \n " , memSizeMB ) ;
return OKAY ;
}
CRender2D : : CRender2D ( void )
{
xPixels = 496 ;
yPixels = 384 ;
xOffs = 0 ;
yOffs = 0 ;
memoryPool = NULL ;
vram = NULL ;
2012-01-27 05:52:59 +00:00
surfTop = NULL ;
surfBottom = NULL ;
for ( int i = 0 ; i < 4 ; i + + )
lineBuffer [ i ] = NULL ;
2011-04-24 01:14:00 +00:00
DebugLog ( " Built Render2D \n " ) ;
}
CRender2D : : ~ CRender2D ( void )
{
DestroyShaderProgram ( shaderProgram , vertexShader , fragmentShader ) ;
glDeleteTextures ( 2 , texID ) ;
if ( memoryPool ! = NULL )
{
delete [ ] memoryPool ;
memoryPool = NULL ;
}
vram = NULL ;
2012-01-27 05:52:59 +00:00
surfTop = NULL ;
surfBottom = NULL ;
for ( int i = 0 ; i < 4 ; i + + )
lineBuffer [ i ] = NULL ;
2011-04-24 01:14:00 +00:00
DebugLog ( " Destroyed Render2D \n " ) ;
}