Committing various small updates that have been hanging around in my source tree for a while now:

- Added 'crosshairs' command line and config option.
- Added 'vsync' command line and config option (so far only tested on NVidia cards on Windows 7 - other graphics drivers, O/Ss or driver settings may simply chose to ignore this).
- Added fullscreen toggle within game using Alt+Enter key combination.
- Added framework for lamp outputs and 'outputs' command line and config option.  So far only the lamps for driving games are hooked up in the emulator (others to be added later).
- Added an initial outputs implementation for Windows that sends MAMEHooker compatible messages (-outputs=win to enable)
- Fixed fps calculation in Main.cpp that was producing incorrect results and so giving the impression that frame throttling wasn't working properly when in fact it was.
- Fixed palette indexed colours as the index was always off by one, causing incorrect colours in various games, eg drivers' suits and flashing Start sign in Daytona 2.
- Altered caching of models so that models with palette indexed colours use the dynamic cache rather than the static one.  This is so that changes in palette indexed colours appear on screen, eg the flashing Start sign on the advanced course of Daytona 2 (although currently the START message itself is not visible due to other problems with texture decoding).
- Fixed small bug in TileGen.cpp which meant both palettes were being completely recomputed pretty much with every frame.  This was a significant performance hit, particularly as palette recomputation is currently being done in SyncSnapshots (it should be moved out of here at some point, although for now it's no big deal).
- Made sure all OpenGL objects and resources are deleted in Render2D/3D destructors, in particular the deleting of the VBO buffer in DestroyModelCache.
- Made sure that GLSL uniforms are always checked to see if they are bound before using them in order to stop unecessary (but harmless) GL errors.
- Altered the default texture sheet handling to use a single large GL texture holding multiple Model3 texture sheets rather than multiple GL textures as before (if required, the old behaviour can still be selected with the mulisheet fragment shader).  I believe this fixes the disappearing crosshairs/corrupt GL state problem which the multisheet fragment shader seemed to be triggering somehow.
- Fixed a bug in debugger which meant memory watches were not triggering properly
This commit is contained in:
Nik Henson 2012-07-15 21:04:46 +00:00
parent ac29fc1e88
commit 183dca563d
35 changed files with 1536 additions and 291 deletions

View file

@ -97,6 +97,7 @@ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/I
$(OBJ_DIR)/IRQ.o $(OBJ_DIR)/53C810.o $(OBJ_DIR)/PCI.o $(OBJ_DIR)/RTC72421.o $(OBJ_DIR)/DriveBoard.o \
$(OBJ_DIR)/MPC10x.o $(OBJ_DIR)/Input.o $(OBJ_DIR)/Inputs.o $(OBJ_DIR)/InputSource.o $(OBJ_DIR)/InputSystem.o \
$(OBJ_DIR)/InputTypes.o $(OBJ_DIR)/MultiInputSource.o $(OBJ_DIR)/SDLInputSystem.o \
$(OBJ_DIR)/Outputs.o \
$(OBJ_DIR)/amp_audio.o $(OBJ_DIR)/amp_dump.o $(OBJ_DIR)/amp_getbits.o $(OBJ_DIR)/amp_getdata.o $(OBJ_DIR)/amp_huffman.o \
$(OBJ_DIR)/amp_layer2.o $(OBJ_DIR)/amp_layer3.o $(OBJ_DIR)/amp_misc2.o $(OBJ_DIR)/amp_position.o $(OBJ_DIR)/amp_transform.o \
$(OBJ_DIR)/amp_util.o $(OBJ_DIR)/SDLMain_tmpl.o
@ -195,6 +196,9 @@ $(OBJ_DIR)/%.o: Src/Inputs/%.cpp Src/Inputs/%.h $(HEADERS)
$(OBJ_DIR)/%.o: Src/Sound/%.cpp Src/Sound/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/%.cpp Src/OSD/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/SDL/%.cpp $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o

View file

@ -99,6 +99,7 @@ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/I
$(OBJ_DIR)/IRQ.o $(OBJ_DIR)/53C810.o $(OBJ_DIR)/PCI.o $(OBJ_DIR)/RTC72421.o $(OBJ_DIR)/DriveBoard.o \
$(OBJ_DIR)/MPC10x.o $(OBJ_DIR)/Input.o $(OBJ_DIR)/Inputs.o $(OBJ_DIR)/InputSource.o $(OBJ_DIR)/InputSystem.o \
$(OBJ_DIR)/InputTypes.o $(OBJ_DIR)/MultiInputSource.o $(OBJ_DIR)/SDLInputSystem.o \
$(OBJ_DIR)/Outputs.o \
$(OBJ_DIR)/amp_audio.o $(OBJ_DIR)/amp_dump.o $(OBJ_DIR)/amp_getbits.o $(OBJ_DIR)/amp_getdata.o $(OBJ_DIR)/amp_huffman.o \
$(OBJ_DIR)/amp_layer2.o $(OBJ_DIR)/amp_layer3.o $(OBJ_DIR)/amp_misc2.o $(OBJ_DIR)/amp_position.o $(OBJ_DIR)/amp_transform.o \
$(OBJ_DIR)/amp_util.o
@ -201,6 +202,9 @@ $(OBJ_DIR)/%.o: Src/CPU/Z80/%.cpp Src/CPU/Z80/%.h $(HEADERS)
$(OBJ_DIR)/%.o: Src/Inputs/%.cpp Src/Inputs/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/%.cpp Src/OSD/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o
$(OBJ_DIR)/%.o: Src/OSD/SDL/%.cpp $(HEADERS)
$(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o

View file

@ -122,6 +122,7 @@ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/I
$(OBJ_DIR)/IRQ.o $(OBJ_DIR)/53C810.o $(OBJ_DIR)/PCI.o $(OBJ_DIR)/RTC72421.o $(OBJ_DIR)/DriveBoard.o \
$(OBJ_DIR)/MPC10x.o $(OBJ_DIR)/Input.o $(OBJ_DIR)/Inputs.o $(OBJ_DIR)/InputSource.o $(OBJ_DIR)/InputSystem.o \
$(OBJ_DIR)/InputTypes.o $(OBJ_DIR)/MultiInputSource.o $(OBJ_DIR)/SDLInputSystem.o $(OBJ_DIR)/DirectInputSystem.o \
$(OBJ_DIR)/Outputs.o $(OBJ_DIR)/WinOutputs.o \
$(OBJ_DIR)/amp_audio.o $(OBJ_DIR)/amp_dump.o $(OBJ_DIR)/amp_getbits.o $(OBJ_DIR)/amp_getdata.o $(OBJ_DIR)/amp_huffman.o \
$(OBJ_DIR)/amp_layer2.o $(OBJ_DIR)/amp_layer3.o $(OBJ_DIR)/amp_misc2.o $(OBJ_DIR)/amp_position.o $(OBJ_DIR)/amp_transform.o \
$(OBJ_DIR)/amp_util.o

View file

@ -87,7 +87,7 @@ SDL_INCLUDEPATH = Libraries\SDL-1.2.14\include
# ZLib
#
ZLIB_LIBPATH = Libraries\zlib-1.2.4\lib-$(ARCH)-MT
ZLIB_INCLUDEPATH = Libraries\zlib-1.2.4\include
ZLIB_INCLUDEPATH = Libraries\zlib-1.2.4
#
# DirectX
@ -124,7 +124,7 @@ CPPFLAGS = $(COMPILER_FLAGS) /TP /EHsc
LFLAGS = /MACHINE:$(ARCH) $(ARCH_LIBS) /LIBPATH:"$(SDL_LIBPATH)" /LIBPATH:"$(ZLIB_LIBPATH)" /LIBPATH:"$(DIRECTX_LIBPATH)" /OUT:"$(OUTFILE)" \
/MANIFEST:NO /SUBSYSTEM:CONSOLE /NOLOGO /OPT:REF /OPT:ICF /DYNAMICBASE /NXCOMPAT /LTCG /DEBUG
OBJ_LIBS = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib \
odbc32.lib odbccp32.lib OpenGL32.lib GLu32.lib SDL.lib SDLmain.lib zlib.lib dinput8.lib dxguid.lib
odbc32.lib odbccp32.lib OpenGL32.lib GLu32.lib SDL.lib SDLmain.lib zlib.lib dinput8.lib dxguid.lib WbemUuid.lib
#
# Build options...
@ -153,6 +153,7 @@ OBJ = $(OBJ_DIR)/PPCDisasm.obj $(OBJ_DIR)/Games.obj $(OBJ_DIR)/Config.obj $(OBJ_
$(OBJ_DIR)/IRQ.obj $(OBJ_DIR)/53C810.obj $(OBJ_DIR)/PCI.obj $(OBJ_DIR)/RTC72421.obj $(OBJ_DIR)/DriveBoard.obj \
$(OBJ_DIR)/MPC10x.obj $(OBJ_DIR)/Input.obj $(OBJ_DIR)/Inputs.obj $(OBJ_DIR)/InputSource.obj $(OBJ_DIR)/InputSystem.obj \
$(OBJ_DIR)/InputTypes.obj $(OBJ_DIR)/MultiInputSource.obj $(OBJ_DIR)/SDLInputSystem.obj $(OBJ_DIR)/DirectInputSystem.obj \
$(OBJ_DIR)/Outputs.obj $(OBJ_DIR)/WinOutputs.obj \
$(OBJ_DIR)/amp_audio.obj $(OBJ_DIR)/amp_dump.obj $(OBJ_DIR)/amp_getbits.obj $(OBJ_DIR)/amp_getdata.obj $(OBJ_DIR)/amp_huffman.obj \
$(OBJ_DIR)/amp_layer2.obj $(OBJ_DIR)/amp_layer3.obj $(OBJ_DIR)/amp_misc2.obj $(OBJ_DIR)/amp_position.obj $(OBJ_DIR)/amp_transform.obj \
$(OBJ_DIR)/amp_util.obj
@ -263,6 +264,9 @@ $(OBJ_DIR)/%.obj: Src/CPU/Z80/%.cpp Src/CPU/Z80/%.h $(HEADERS)
$(OBJ_DIR)/%.obj: Src/Inputs/%.cpp Src/Inputs/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/$(*F).obj
$(OBJ_DIR)/%.obj: Src/OSD/%.cpp Src/OSD/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/$(*F).obj
$(OBJ_DIR)/%.obj: Src/OSD/SDL/%.cpp Src/OSD/SDL/%.h $(HEADERS)
$(CC) $< $(CPPFLAGS) /Fo$(OBJ_DIR)/$(*F).obj

View file

@ -1117,6 +1117,17 @@ void ppc_detach_debugger()
Bus = PPCDebug->DetachBus();
PPCDebug = NULL;
}
void ppc_break()
{
if (PPCDebug != NULL)
PPCDebug->ForceBreak(true);
}
#else // SUPERMODEL_DEBUGGER
void ppc_break()
{
//
}
#endif // SUPERMODEL_DEBUGGER
void ppc_set_pc(UINT32 pc)
@ -1148,7 +1159,7 @@ void ppc_set_fpr(unsigned num, double val)
void ppc_write_spr(unsigned spr, UINT32 val)
{
// TODO
ppc_set_spr(spr, val);
}
void ppc_write_sr(unsigned num, UINT32 val)

View file

@ -373,6 +373,7 @@ extern UINT32 ppc_read_sr(unsigned num);
extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr);
extern void ppc_detach_debugger();
#endif // SUPERMODEL_DEBUGGER
extern void ppc_break();
extern void ppc_set_pc(UINT32 pc);
extern UINT8 ppc_get_cr(unsigned num);
extern void ppc_set_cr(unsigned num, UINT8 val);

View file

@ -357,5 +357,7 @@ int ppc_execute(int cycles)
ppc.total_cycles += executed;
ppc.cur_cycles = 0;
ppc.icount = 0;
ppc.tb_base_icount = 0;
ppc.dec_base_icount = 0;
return executed;
}

View file

@ -689,8 +689,6 @@ namespace Debugger
else if (mappedIO == NULL)
return;
}
if (m_break)
WaitCommand(HaltUntil);
}
inline void CCPUDebug::CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data)
@ -737,8 +735,6 @@ namespace Debugger
}
else if (mappedIO == NULL)
return;
if (m_break)
WaitCommand(HaltUntil);
}
}
@ -748,6 +744,7 @@ namespace Debugger
debugger->MemWatchTriggered(watch, addr, dataSize, data, read);
watch->Reset();
m_break = true;
UpdateExecMasks();
}
inline void CCPUDebug::IOWatchTriggered(CWatch* watch, CIO *io, UINT64 data, bool read)
@ -756,6 +753,7 @@ namespace Debugger
debugger->IOWatchTriggered(watch, io, data, read);
watch->Reset();
m_break = true;
UpdateExecMasks();
}
inline void CCPUDebug::CheckRead8(UINT32 addr, UINT8 data)
@ -781,8 +779,6 @@ namespace Debugger
CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
if (watch != NULL && watch->CheckRead(addr, dataSize, data))
MemWatchTriggered(watch, addr, dataSize, data, true);
if (m_break)
WaitCommand(HaltUntil);
}
inline void CCPUDebug::CheckRead16(UINT32 addr, UINT16 data)
@ -826,10 +822,9 @@ namespace Debugger
CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
if (watch != NULL && watch->CheckWrite(addr, dataSize, data))
MemWatchTriggered(watch, addr, dataSize, data, false);
if (m_break)
WaitCommand(HaltUntil);
}
inline void CCPUDebug::CheckWrite16(UINT32 addr, UINT16 data)
{
if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0)
CheckWrite(addr, 2, data);

View file

@ -73,7 +73,8 @@
#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 23 // texture format 0-7 (also ==0 indicates contour texture - see also texParams.trans)
#define VBO_VERTEX_SIZE 24 // total size (may include padding for alignment)
#define VBO_VERTEX_OFFSET_TEXMAP 24 // texture map number
#define VBO_VERTEX_SIZE 25 // total size (may include padding for alignment)
/******************************************************************************
@ -156,13 +157,14 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
glNormalPointer(GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_NX*sizeof(GLfloat)));
glTexCoordPointer(2, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_U*sizeof(GLfloat)));
glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat)));
glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat)));
glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat)));
glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*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)));
if (subTextureLoc != -1) glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat)));
if (texParamsLoc != -1) glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat)));
if (texFormatLoc != -1) glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT*sizeof(GLfloat)));
if (texMapLoc != -1) glVertexAttribPointer(texMapLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXMAP*sizeof(GLfloat)));
if (transLevelLoc != -1) glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat)));
if (lightEnableLoc != -1) glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat)));
if (shininessLoc != -1) glVertexAttribPointer(shininessLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_SHININESS*sizeof(GLfloat)));
if (fogIntensityLoc != -1) glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat)));
// Set up state
if (state == POLY_STATE_ALPHA)
@ -185,14 +187,14 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
{
if (!D->next->isViewport)
{
glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams);
glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix);
if (lightingLoc != -1) glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams);
if (projectionMatrixLoc != -1) glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix);
glFogf(GL_FOG_DENSITY, D->Data.Viewport.fogParams[3]);
glFogf(GL_FOG_START, D->Data.Viewport.fogParams[4]);
glFogfv(GL_FOG_COLOR, &(D->Data.Viewport.fogParams[0]));
glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse);
glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange);
glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor);
if (spotEllipseLoc != -1) glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse);
if (spotRangeLoc != -1) glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange);
if (spotColorLoc != -1) glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor);
glViewport(D->Data.Viewport.x, D->Data.Viewport.y, D->Data.Viewport.width, D->Data.Viewport.height);
}
}
@ -210,7 +212,8 @@ void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
glFrontFace(D->Data.Model.frontFace);
}
glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix);
if (modelViewMatrixLoc != -1)
glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix);
glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts);
if (D->Data.Model.frontFace == -GL_CW)
@ -363,6 +366,7 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
GLfloat r, g, b;
GLfloat translucence, fogIntensity, texWidth, texHeight, texBaseX, texBaseY, contourProcessing;
unsigned baseIdx, texFormat, texEnable, lightEnable, modulate, colorIdx;
TexSheet *texSheet;
int s, texPage, shininess;
// Texture selection
@ -371,9 +375,10 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
texWidth = (GLfloat) (32<<((P->header[3]>>3)&7));
texHeight = (GLfloat) (32<<((P->header[3]>>0)&7));
texPage = (P->header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
texBaseX = (GLfloat) (32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + texOffsetXY[0];
texBaseY = (GLfloat) (32*(P->header[5]&0x1F)+texPage) + texOffsetXY[1];
texSheet = fmtToTexSheet[texFormat]; // get X&Y offset of texture sheet within texture map
texBaseX = (GLfloat) (texSheet->xOffset + (((32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1))) + (int)texOffsetXY[0])&2047));
texBaseY = (GLfloat) (texSheet->yOffset + (((32*(P->header[5]&0x1F)+texPage) + (int)texOffsetXY[1])&2047));
/*
* Lighting and Color Modulation:
*
@ -389,7 +394,7 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
// Material color
if ((P->header[1]&2) == 0)
{
colorIdx = (P->header[4]>>20)&0x7FF;
colorIdx = ((P->header[4]>>20)&0x7FF) - 1;
b = (GLfloat) (polyRAM[0x400+colorIdx]&0xFF) * (1.0f/255.0f);
g = (GLfloat) ((polyRAM[0x400+colorIdx]>>8)&0xFF) * (1.0f/255.0f);
r = (GLfloat) ((polyRAM[0x400+colorIdx]>>16)&0xFF) * (1.0f/255.0f);
@ -499,6 +504,7 @@ void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP] = (P->header[2]&2) ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP] = (P->header[2]&1) ? 1.0f : 0.0f;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXFORMAT] = (float)texFormat;
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXMAP] = (float)texSheet->mapNum;
Cache->curVertIdx[s]++;
Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat);
@ -968,6 +974,8 @@ bool CRender3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
void CRender3D::DestroyModelCache(ModelCache *Cache)
{
glDeleteBuffers(1, &(Cache->vboID));
for (int i = 0; i < 2; i++)
{
if (Cache->verts[i] != NULL)

View file

@ -160,7 +160,7 @@ private:
GLuint texID[2]; // IDs for the 2 layer textures (top and bottom)
unsigned xPixels, yPixels; // display surface resolution
unsigned xOffs, yOffs; // offset
unsigned totalXPixels, totalYPixels; // totay display surface resolution
unsigned totalXPixels, totalYPixels; // total display surface resolution
// Shader programs and input data locations
GLuint shaderProgram; // shader program object

View file

@ -184,6 +184,19 @@
Texture Management
******************************************************************************/
// Default mapping from Model3 texture format to texture sheet.
// Currently this is just a simple 1-to-1 mapping but if/when more formats get added, sheets will start to get reused.
int CRender3D::defaultFmtToTexSheetNum[] = {
0, // Fmt 0 -> 0
1, // 1 -> 1
2, // 2 -> 2
3, // 3 -> 3
4, // 4 -> 4
5, // 5 -> 5
6, // 6 -> 6
7 // 7 -> 7
};
void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
{
int xi, yi, i;
@ -201,12 +214,11 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
return;
}
// Map Model3 format to texture unit and texture unit to texture sheet number
unsigned texUnit = fmtToTexUnit[format];
unsigned texNum = texUnit % numTexIDs; // there may be less texture sheets than texture units (due to lack of video memory)
// Map Model3 format to texture sheet
TexSheet *texSheet = fmtToTexSheet[format];
// Check to see if ALL texture tiles have been properly decoded on current texture sheet
if ((textureFormat[texNum][y/32][x/32]==format) && (textureWidth[texNum][y/32][x/32]>=width) && (textureHeight[texNum][y/32][x/32]>=height))
// Check to see if ALL texture tiles have been properly decoded on texture sheet
if ((texSheet->texFormat[y/32][x/32] == format) && (texSheet->texWidth[y/32][x/32] >= width) && (texSheet->texHeight[y/32][x/32] >= height))
return;
//printf("Decoding texture format %u: %u x %u @ (%u, %u) sheet %u\n", format, width, height, x, y, texNum);
@ -385,22 +397,22 @@ void CRender3D::DecodeTexture(int format, int x, int y, int width, int height)
break;
}
// Upload the texture
// Upload texture to correct position within texture map
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glActiveTexture(GL_TEXTURE0 + texUnit); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_FLOAT, textureBuffer);
glActiveTexture(GL_TEXTURE0 + texSheet->mapNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texMapIDs[texSheet->mapNum]); // bind correct texture map
glTexSubImage2D(GL_TEXTURE_2D, 0, texSheet->xOffset + x, texSheet->yOffset + y, width, height, GL_RGBA, GL_FLOAT, textureBuffer);
// Mark as decoded
textureFormat[texNum][y/32][x/32] = format;
textureWidth[texNum][y/32][x/32] = width;
textureHeight[texNum][y/32][x/32] = height;
// Mark texture as decoded
texSheet->texFormat[y/32][x/32] = format;
texSheet->texWidth[y/32][x/32] = width;
texSheet->texHeight[y/32][x/32] = height;
}
// Signals that new textures have been uploaded. Flushes model caches. Be careful not to exceed bounds!
void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned height)
{
unsigned texNum, xi, yi;
unsigned texSheet, xi, yi;
// Make everything red
#ifdef DEBUG
@ -414,15 +426,15 @@ void CRender3D::UploadTextures(unsigned x, unsigned y, unsigned width, unsigned
#endif
// Update all texture sheets
for (texNum = 0; texNum < numTexIDs; texNum++)
for (texSheet = 0; texSheet < numTexSheets; texSheet++)
{
for (xi = x/32; xi < (x+width)/32; xi++)
{
for (yi = y/32; yi < (y+height)/32; yi++)
{
textureFormat[texNum][yi][xi] = -1;
textureWidth[texNum][yi][xi] = -1;
textureHeight[texNum][yi][xi] = -1;
texSheets[texSheet].texFormat[yi][xi] = -1;
texSheets[texSheet].texWidth[yi][xi] = -1;
texSheets[texSheet].texHeight[yi][xi] = -1;
}
}
}
@ -566,6 +578,39 @@ void CRender3D::InitMatrixStack(UINT32 matrixBaseAddr)
Complete scene database traversal and rendering.
******************************************************************************/
static bool IsDynamicModel(const UINT32 *data, UINT32 modelAddr)
{
if (data == NULL)
return false;
// Models in polygon RAM are always dynamic
if (modelAddr < 0x100000)
return true;
// VROM models are only dynamic if they reference polygon RAM via color palette indices
bool done;
do
{
// Check if polygon has color palette reference, if so polygon is dynamic and can return here
if ((data[1]&2) == 0)
return true;
if (data[6] == 0)
break;
// Get number of vertices
unsigned numVerts = (data[0]&0x40 ? 4 : 3);
// Deduct number of reused verts
if (data[0]&1) numVerts--;
if (data[0]&2) numVerts--;
if (data[0]&4) numVerts--;
if (data[0]&8) numVerts--;
done = data[1]&4;
// Skip header and vertices to next polygon
data += 7 + numVerts * 4;
}
while (!done);
return false;
}
/*
* DrawModel():
*
@ -591,7 +636,7 @@ bool CRender3D::DrawModel(UINT32 modelAddr)
model = TranslateModelAddress(modelAddr);
// Determine whether model is in polygon RAM or VROM
if (modelAddr < 0x100000)
if (IsDynamicModel(model, modelAddr))
Cache = &PolyCache;
else
Cache = &VROMCache;
@ -974,13 +1019,11 @@ void CRender3D::RenderFrame(void)
// Bind Real3D shader program and texture maps
glUseProgram(shaderProgram);
for (unsigned fmt = 0; fmt < 8; fmt++)
for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++)
{
// Map Model3 format to texture unit and texture unit to texture sheet number
unsigned texUnit = fmtToTexUnit[fmt];
unsigned texNum = texUnit % numTexIDs; // there may be less texture sheets than texture units (due to lack of video memory)
glActiveTexture(GL_TEXTURE0 + texUnit); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet
glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // bind correct texture sheet
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
@ -990,13 +1033,14 @@ void CRender3D::RenderFrame(void)
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableVertexAttribArray(subTextureLoc);
glEnableVertexAttribArray(texParamsLoc);
glEnableVertexAttribArray(texFormatLoc);
glEnableVertexAttribArray(transLevelLoc);
glEnableVertexAttribArray(lightEnableLoc);
glEnableVertexAttribArray(shininessLoc);
glEnableVertexAttribArray(fogIntensityLoc);
if (subTextureLoc != -1) glEnableVertexAttribArray(subTextureLoc);
if (texParamsLoc != -1) glEnableVertexAttribArray(texParamsLoc);
if (texFormatLoc != -1) glEnableVertexAttribArray(texFormatLoc);
if (texMapLoc != -1) glEnableVertexAttribArray(texMapLoc);
if (transLevelLoc != -1) glEnableVertexAttribArray(transLevelLoc);
if (lightEnableLoc != -1) glEnableVertexAttribArray(lightEnableLoc);
if (shininessLoc != -1) glEnableVertexAttribArray(shininessLoc);
if (fogIntensityLoc != -1) glEnableVertexAttribArray(fogIntensityLoc);
// Draw
//ClearModelCache(&VROMCache); // debug
@ -1016,13 +1060,14 @@ void CRender3D::RenderFrame(void)
glFrontFace(GL_CW); // restore front face
// Disable VBO client states
glDisableVertexAttribArray(fogIntensityLoc);
glDisableVertexAttribArray(shininessLoc);
glDisableVertexAttribArray(lightEnableLoc);
glDisableVertexAttribArray(transLevelLoc);
glDisableVertexAttribArray(texFormatLoc);
glDisableVertexAttribArray(texParamsLoc);
glDisableVertexAttribArray(subTextureLoc);
if (fogIntensityLoc != -1) glDisableVertexAttribArray(fogIntensityLoc);
if (shininessLoc != -1) glDisableVertexAttribArray(shininessLoc);
if (lightEnableLoc != -1) glDisableVertexAttribArray(lightEnableLoc);
if (transLevelLoc != -1) glDisableVertexAttribArray(transLevelLoc);
if (texMapLoc != -1) glDisableVertexAttribArray(texMapLoc);
if (texFormatLoc != -1) glDisableVertexAttribArray(texFormatLoc);
if (texParamsLoc != -1) glDisableVertexAttribArray(texParamsLoc);
if (subTextureLoc != -1) glDisableVertexAttribArray(subTextureLoc);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
@ -1088,7 +1133,7 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
// Create model caches and VBOs
if (CreateModelCache(&VROMCache, NUM_STATIC_VERTS, NUM_LOCAL_VERTS, NUM_STATIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, false))
return FAIL;
if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x400000/4, NUM_DISPLAY_LIST_ITEMS, true))
if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, true))
return FAIL;
// Initialize lighting parameters (updated as viewports are traversed)
@ -1107,95 +1152,149 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
totalXRes = totalXResParam;
totalYRes = totalYResParam;
// Query max number of texture units supported by video card to use as upper limit
// Get ideal number of texture sheets required by default mapping from Model3 texture format to texture sheet
unsigned idealTexSheets = 0;
for (unsigned fmt = 0; fmt < 8; fmt++)
{
int sheetNum = defaultFmtToTexSheetNum[fmt];
idealTexSheets = max<int>(idealTexSheets, sheetNum + 1);
}
// Get upper limit for number of texture maps to use from max number of texture units supported by video card
GLint glMaxTexUnits;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits);
unsigned maxTexUnits = max<int>(1, min<int>(g_Config.maxTexUnits, glMaxTexUnits));
unsigned maxTexMaps = max<int>(1, min<int>(g_Config.maxTexMaps, glMaxTexUnits));
// Load shaders. Use multi-sheet shader if requested and possible.
// Get upper limit for extent of texture maps to use from max texture size supported by video card
GLint maxTexSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
unsigned mapExtent = max<int>(1, min<unsigned>(g_Config.maxTexMapExtent, maxTexSize / 2048));
unsigned mapSize = 2048 * mapExtent;
while (mapExtent > 1)
{
if ((mapExtent - 1) * (mapExtent - 1) < idealTexSheets)
{
// Use a GL proxy texture to double check max texture size returned above
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL);
GLint glTexWidth;
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &glTexWidth);
if (glTexWidth == mapSize)
break;
}
mapExtent--;
mapSize -= 2048;
}
// Load shaders, using multi-sheet shader if requested.
const char *vsFile = g_Config.vertexShaderFile.size() ? g_Config.vertexShaderFile.c_str() : NULL;
const char *fsFile = g_Config.fragmentShaderFile.size() ? g_Config.fragmentShaderFile.c_str() : NULL;
const char *fragmentShaderSource = fragmentShaderSingleSheetSource; // single texture shader
if (g_Config.multiTexture)
{
if (maxTexUnits >= 8) // can we use the multi-sheet shader?
fragmentShaderSource = fragmentShaderMultiSheetSource;
else
ErrorLog("Your system has too few texture units. Reverting to single texture shader.");
}
const char *fragmentShaderSource = (g_Config.multiTexture ? fragmentShaderMultiSheetSource : fragmentShaderSingleSheetSource); // single texture shader
if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,vsFile,fsFile,vertexShaderSource,fragmentShaderSource))
return FAIL;
// Try locating default "textureMap" uniform in shader program
glUseProgram(shaderProgram); // bind program
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
unsigned unitCount = 0;
// If exists, bind to first texture unit
unsigned mapCount = 0;
if (textureMapLoc != -1)
{
// If exists, then bind to first texture unit
unsigned texUnit = unitCount % maxTexUnits;
glUniform1i(textureMapLoc, texUnit++);
unitCount++;
}
glUniform1i(textureMapLoc, mapCount++);
// Try locating "textureMap[0-7]" uniforms in shader program
for (unsigned fmt = 0; fmt < 8; fmt++)
for (unsigned mapNum = 0; mapNum < 8 && mapCount < maxTexMaps; mapNum++)
{
char uniformName[12];
sprintf(uniformName, "textureMap%u", fmt);
textureMapLocs[fmt] = glGetUniformLocation(shaderProgram, uniformName);
if (textureMapLocs[fmt] != -1)
{
// If exists, then bind to next texture unit
unsigned texUnit = unitCount % maxTexUnits;
glUniform1i(textureMapLocs[fmt], texUnit);
fmtToTexUnit[fmt] = texUnit;
unitCount++;
}
else
{
// Otherwise bind to first texture unit by default
fmtToTexUnit[fmt] = 0;
}
sprintf(uniformName, "textureMap%u", mapNum);
textureMapLocs[mapNum] = glGetUniformLocation(shaderProgram, uniformName);
// If exist, bind to remaining texture units
if (textureMapLocs[mapNum] != -1)
glUniform1i(textureMapLocs[mapNum], mapCount++);
}
numTexUnits = min<int>(unitCount, maxTexUnits);
// Check located at least one uniform to bind to a texture unit
if (numTexUnits == 0)
// Check sucessully located at least one "textureMap" uniform in shader program
if (mapCount == 0)
return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform.");
InfoLog("Located and bound %u uniform(s) in GL shader script to %u texture unit(s).", numTexUnits, unitCount);
InfoLog("Located and bound %u 'textureMap' uniform(s) in fragment shader.", mapCount);
// Now try creating texture sheets, one for each texture unit (memory permitting)
numTexIDs = numTexUnits;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(numTexIDs, texIDs);
for (unsigned texNum = 0; texNum < numTexIDs; texNum++)
// Readjust map extent so as to utilise as many texture maps found in shader program as possible
while (mapExtent > 1 && mapCount * (mapExtent - 1) * (mapExtent - 1) >= idealTexSheets)
{
glActiveTexture(GL_TEXTURE0 + texNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texIDs[texNum]); // bind correct texture sheet
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2048, 2048, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0);
if (glGetError() != GL_NO_ERROR)
mapExtent--;
mapSize -= 2048;
}
// Create required number of GL textures for texture maps, decreasing map extent if memory is insufficent
unsigned sheetsPerMap = mapExtent * mapExtent;
while (true)
{
numTexMaps = min<unsigned>(mapCount, 1 + (idealTexSheets - 1) / sheetsPerMap);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(numTexMaps, texMapIDs);
bool okay = true;
for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++)
{
// Probably ran out of video memory, so don't try creating any more texture sheets
numTexIDs = texNum;
glGetError(); // clear error flag
break;
glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // bind correct texture sheet
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0);
if (glGetError() != GL_NO_ERROR)
{
// Ran out of video memory or texture size is too large
numTexMaps = mapNum;
okay = false;
break;
}
}
if (okay || mapExtent == 1)
break;
// Delete textures, decrease extent and try again
glDeleteTextures(numTexMaps, texMapIDs);
mapExtent--;
mapSize -= 2048;
sheetsPerMap = mapExtent * mapExtent;
}
// Check successfully created at least one texture map
if (numTexMaps == 0)
return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps.");
InfoLog("Created %u %ux%u-texel GL texture map(s).", numTexMaps, mapSize, mapSize);
// Create texture sheet objects and assign them to texture maps
numTexSheets = min<unsigned>(numTexMaps * sheetsPerMap, idealTexSheets);
texSheets = new(std::nothrow) TexSheet[numTexSheets];
if (texSheets == NULL)
return ErrorLog("Unable to assign memory for %u texture sheet objects.", numTexSheets);
for (unsigned sheetNum = 0; sheetNum < numTexSheets; sheetNum++)
{
unsigned mapNum = sheetNum / sheetsPerMap;
unsigned posInMap = sheetNum % sheetsPerMap;
texSheets[sheetNum].sheetNum = sheetNum;
texSheets[sheetNum].mapNum = mapNum;
texSheets[sheetNum].xOffset = 2048 * (posInMap % mapExtent);
texSheets[sheetNum].yOffset = 2048 * (posInMap / mapExtent);
}
// Check created at least one texture sheet
if (numTexIDs == 0)
return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps.");
InfoLog("Created %u 2048x2048-texel GL texture map(s)", numTexIDs);
// Assign Model3 texture formats to texture sheets (cannot just use default mapping as may have ended up with fewer
// texture sheets than anticipated)
for (unsigned fmt = 0; fmt < 8; fmt++)
{
int sheetNum = defaultFmtToTexSheetNum[fmt] % numTexSheets;
fmtToTexSheet[fmt] = &texSheets[sheetNum];
}
InfoLog("Mapped %u Model3 texture formats to %u texture sheet(s) in %u %ux%u-texel texture map(s).", 8, numTexSheets, numTexMaps, mapSize, mapSize);
// Get location of the rest of the uniforms
modelViewMatrixLoc = glGetUniformLocation(shaderProgram,"modelViewMatrix");
projectionMatrixLoc = glGetUniformLocation(shaderProgram,"projectionMatrix");
lightingLoc = glGetUniformLocation(shaderProgram, "lighting");
mapSizeLoc = glGetUniformLocation(shaderProgram, "mapSize");
spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse");
spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange");
spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor");
@ -1204,11 +1303,16 @@ bool CRender3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
subTextureLoc = glGetAttribLocation(shaderProgram,"subTexture");
texParamsLoc = glGetAttribLocation(shaderProgram,"texParams");
texFormatLoc = glGetAttribLocation(shaderProgram,"texFormat");
texMapLoc = glGetAttribLocation(shaderProgram,"texMap");
transLevelLoc = glGetAttribLocation(shaderProgram,"transLevel");
lightEnableLoc = glGetAttribLocation(shaderProgram,"lightEnable");
shininessLoc = glGetAttribLocation(shaderProgram,"shininess");
fogIntensityLoc = glGetAttribLocation(shaderProgram,"fogIntensity");
// Set map size
if (mapSizeLoc != -1)
glUniform1f(mapSizeLoc, (GLfloat)mapSize);
// Additional OpenGL stuff
glFrontFace(GL_CW); // polygons are uploaded w/ clockwise winding
glCullFace(GL_BACK);
@ -1233,6 +1337,7 @@ CRender3D::CRender3D(void)
vrom = NULL;
textureRAM = NULL;
textureBuffer = NULL;
texSheets = NULL;
// Clear model cache pointers so we can safely destroy them if init fails
for (int i = 0; i < 2; i++)
@ -1259,7 +1364,7 @@ CRender3D::~CRender3D(void)
DestroyShaderProgram(shaderProgram,vertexShader,fragmentShader);
if (glBindBuffer != NULL) // we may have failed earlier due to lack of OpenGL 2.0 functions
glBindBuffer(GL_ARRAY_BUFFER, 0); // disable VBOs by binding to 0
glDeleteTextures(numTexIDs, texIDs);
glDeleteTextures(numTexMaps, texMapIDs);
DestroyModelCache(&VROMCache);
DestroyModelCache(&PolyCache);
@ -1270,6 +1375,9 @@ CRender3D::~CRender3D(void)
vrom = NULL;
textureRAM = NULL;
if (texSheets != NULL)
delete [] texSheets;
if (textureBuffer != NULL)
delete [] textureBuffer;
textureBuffer = NULL;

View file

@ -185,6 +185,26 @@ struct ModelCache
DisplayList *ListTail[2]; // current tail node for each state
};
struct TexSheet
{
unsigned sheetNum;
unsigned mapNum;
unsigned xOffset;
unsigned yOffset;
/*
* Texture Format Buffer
*
* Records the format that a texture (at a given location within the
* texture sheet) is currently stored in. A negative value indicates the
* texture has not been accessed and converted yet and non-negative values
* correspond to the texture format bits in the polygon headers. They can
* be used to determine whether a texture needs to be updated.
*/
int texWidth[2048/32][2048/32];
int texHeight[2048/32][2048/32];
INT8 texFormat[2048/32][2048/32];
};
/******************************************************************************
CRender3D Classes
@ -200,15 +220,17 @@ class CRender3DConfig
public:
string vertexShaderFile; // path to vertex shader or "" to use internal shader
string fragmentShaderFile; // fragment shader
unsigned maxTexUnits; // maximum number of texture units to use (1-9)
unsigned maxTexMaps; // maximum number of texture maps to use (1-9)
unsigned maxTexMapExtent; // maximum extent of texture maps (where num of tex sheets per map = extent ^ 2)
bool multiTexture; // if enabled and no external fragment shader, select internal shader w/ multiple texture sheet support
// Defaults
CRender3DConfig(void)
{
// strings will be clear to begin with
maxTexUnits = 9;
multiTexture = true;
maxTexMaps = 9;
maxTexMapExtent = 4;
multiTexture = false;
}
};
@ -412,26 +434,30 @@ private:
unsigned totalXRes, totalYRes;
// Texture details
unsigned numTexUnits; // number of texture units
int fmtToTexUnit[8]; // mapping from Model3 texture format to texture unit
unsigned numTexIDs; // number of 2048x2048 texture sheets (maximum 8, one for each Model3 texture format)
GLuint texIDs[8]; // texture IDs of texture sheets
static int defaultFmtToTexSheetNum[8]; // default mapping from Model3 texture format to texture sheet
unsigned numTexMaps; // total number of texture maps
GLuint texMapIDs[9]; // GL texture IDs of texture maps
unsigned numTexSheets; // total number of texture sheets
TexSheet *texSheets; // texture sheet objects
TexSheet *fmtToTexSheet[8]; // final mapping from Model3 texture format to texture sheet
// Shader programs and input data locations
GLuint shaderProgram; // shader program object
GLuint vertexShader; // vertex shader handle
GLuint fragmentShader; // fragment shader
GLint textureMapLoc; // location of "textureMap" uniform (default combined sheet for all Model3 textures formats)
GLint textureMapLocs[8]; // location of "textureMap[0-7]" uniforms (one sheet per Model3 texture format)
GLint textureMapLoc; // location of "textureMap" uniform (if available)
GLint textureMapLocs[8]; // location of "textureMap[0-7]" uniforms (if available)
GLint modelViewMatrixLoc; // uniform
GLint projectionMatrixLoc; // uniform
GLint lightingLoc; // uniform
GLint mapSizeLoc; // uniform
GLint spotEllipseLoc; // uniform
GLint spotRangeLoc; // uniform
GLint spotColorLoc; // uniform
GLint subTextureLoc; // attribute
GLint texParamsLoc; // attribute
GLint texFormatLoc; // attribute
GLint texMapLoc; // attribute
GLint transLevelLoc; // attribute
GLint lightEnableLoc; // attribute
GLint shininessLoc; // attribute
@ -440,19 +466,6 @@ private:
// Model caching
ModelCache VROMCache; // VROM (static) models
ModelCache PolyCache; // polygon RAM (dynamic) models
/*
* Texture Format Buffer
*
* Records the format that a texture (at a given location within the
* texture sheet) is currently stored in. A negative value indicates the
* texture has not been accessed and converted yet and non-negative values
* correspond to the texture format bits in the polygon headers. They can
* be used to determine whether a texture needs to be updated.
*/
int textureWidth[8][2048/32][2048/32];
int textureHeight[8][2048/32][2048/32];
INT8 textureFormat[8][2048/32][2048/32];
/*
* Texture Decode Buffer

View file

@ -1,4 +1,4 @@
/**
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
@ -29,15 +29,17 @@
// Global uniforms
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTexMap; // texture map number
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
varying vec3 fsLightIntensity; // lighting intensity
varying float fsSpecularTerm; // specular highlight
@ -83,7 +85,7 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
return glTexCoord;
}
@ -167,7 +169,7 @@ void main(void)
}
// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency
if (fsTexFormat < 0.5) // contour (T1RGB5) texture map
if (fsTexFormat < 0.5) // contour (T1RGB5) texture
fragColor.a = 1.0;
}

View file

@ -36,6 +36,7 @@ uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
@ -85,12 +86,12 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
/*
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
*/
return glTexCoord;
}
@ -116,8 +117,8 @@ void main(void)
else
// Textured polygons: set fragment color to texel value
{
fragColor = texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw/2.0)/2048.0);
//fragColor += texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw)/2048.0);
fragColor = texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw/2.0)/mapSize);
//fragColor += texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw)/mapSize);
}

View file

@ -41,11 +41,13 @@ uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTexFormat; // T1RGB5 contour texture (if > 0)
varying float fsTexMap; // texture map number
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
varying vec3 fsLightIntensity; // lighting intensity
varying float fsSpecularTerm; // specular highlight
@ -91,7 +93,7 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
return glTexCoord;
}
@ -140,38 +142,38 @@ void main(void)
uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
// Fetch the texels from the texture map that corresponds to the current texture format
if (fsTexFormat < 0.5f) {
// Fetch the texels from the given texture map
if (fsTexMap < 0.5f) {
c[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap0, uv_top.xy); // top-left
c[3]=texture2D(textureMap0, uv_top.zw); // top-right
} else if (fsTexFormat < 1.5f) {
} else if (fsTexMap < 1.5f) {
c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap1, uv_top.xy); // top-left
c[3]=texture2D(textureMap1, uv_top.zw); // top-right
} else if (fsTexFormat < 2.5f) {
} else if (fsTexMap < 2.5f) {
c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap2, uv_top.xy); // top-left
c[3]=texture2D(textureMap2, uv_top.zw); // top-right
} else if (fsTexFormat < 3.5f) {
} else if (fsTexMap < 3.5f) {
c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap3, uv_top.xy); // top-left
c[3]=texture2D(textureMap3, uv_top.zw); // top-right
} else if (fsTexFormat < 4.5f) {
} else if (fsTexMap < 4.5f) {
c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap4, uv_top.xy); // top-left
c[3]=texture2D(textureMap4, uv_top.zw); // top-right
} else if (fsTexFormat < 5.5f) {
} else if (fsTexMap < 5.5f) {
c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap5, uv_top.xy); // top-left
c[3]=texture2D(textureMap5, uv_top.zw); // top-right
} else if (fsTexFormat < 6.5f) {
} else if (fsTexMap < 6.5f) {
c[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap6, uv_top.xy); // top-left

View file

@ -36,6 +36,7 @@ uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform float mapSize; // texture map size (2048,4096,6144 etc)
// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
@ -85,12 +86,12 @@ vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEna
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
/*
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;
) / mapSize;
*/
return glTexCoord;
}

View file

@ -39,6 +39,7 @@ uniform vec3 spotColor; // spotlight RGB color
attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
attribute vec4 texParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
attribute float texFormat; // T1RGB5 contour texture (if > 0)
attribute float texMap; // texture map number
attribute float transLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
attribute float lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
attribute float shininess; // specular shininess (if >= 0.0) or disable specular lighting (negative)
@ -48,6 +49,7 @@ attribute float fogIntensity; // fog intensity (1.0, full fog effect, 0.0, no fo
varying vec4 fsSubTexture;
varying vec4 fsTexParams;
varying float fsTexFormat;
varying float fsTexMap;
varying float fsTransLevel;
varying vec3 fsLightIntensity; // total light intensity for this vertex
varying float fsSpecularTerm; // specular light term (additive)
@ -158,7 +160,7 @@ void main(void)
//vec3 R = normalize(2.0*dot(sunVector,viewNormal)*viewNormal - sunVector);
//vec3 V = normalize(-viewVertex);
//float s = max(2.0,64.0-shininess);
//fsSpecularTerm = pow(max(dot(R,V),0),s);
//fsSpecularTerm = pow(max(dot(R,V),0.0),s);
}
}
@ -175,4 +177,5 @@ void main(void)
fsTexParams = texParams;
fsTransLevel = transLevel;
fsTexFormat = texFormat;
fsTexMap = texMap;
}

View file

@ -72,6 +72,7 @@ static const char vertexShaderSource[] =
"attribute vec4\tsubTexture;\t\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"attribute vec4\ttexParams;\t\t// .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"attribute float\ttexFormat;\t\t// T1RGB5 contour texture (if > 0)\n"
"attribute float\ttexMap;\t\t// texture map number\n"
"attribute float\ttransLevel;\t\t// translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value\n"
"attribute float\tlightEnable;\t// lighting enabled (1.0) or luminous (0.0), drawn at full intensity\n"
"attribute float\tshininess;\t\t// specular shininess (if >= 0.0) or disable specular lighting (negative)\n"
@ -81,6 +82,7 @@ static const char vertexShaderSource[] =
"varying vec4\tfsSubTexture;\n"
"varying vec4\tfsTexParams;\n"
"varying float\tfsTexFormat;\n"
"varying float\tfsTexMap;\n"
"varying float\tfsTransLevel;\n"
"varying vec3\tfsLightIntensity;\t// total light intensity for this vertex\n"
"varying float\tfsSpecularTerm;\t\t// specular light term (additive)\n"
@ -208,6 +210,7 @@ static const char vertexShaderSource[] =
"\tfsTexParams = texParams;\n"
"\tfsTransLevel = transLevel;\n"
"\tfsTexFormat = texFormat;\n"
"\tfsTexMap = texMap;\n"
"}\n"
};
@ -246,15 +249,17 @@ static const char fragmentShaderSingleSheetSource[] =
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// complete texture map, 2048x2048 texels\n"
"uniform vec4\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\tspotColor;\t\t\t// spotlight RGB color\n"
"uniform vec3\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform vec4\t\tspotEllipse;\t\t// spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)\n"
"uniform vec2\t\tspotRange;\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\n"
"\n"
"// Inputs from vertex shader \n"
"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
"varying float\t\tfsTexMap;\t\t// texture map number\n"
"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
@ -300,7 +305,7 @@ static const char fragmentShaderSingleSheetSource[] =
"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
"\t\t\t\t\ttexOffset\n"
"\t\t\t\t ) / 2048.0;\n"
"\t\t\t\t ) / mapSize;\n"
"\treturn glTexCoord;\n"
"}\n"
"\n"
@ -384,7 +389,7 @@ static const char fragmentShaderSingleSheetSource[] =
"\t\t}\n"
"\t\t\n"
"\t\t// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency\n"
"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture map\n"
"\t\tif (fsTexFormat < 0.5)\t\t// contour (T1RGB5) texture\n"
"\t\t\tfragColor.a = 1.0;\n"
"\t}\n"
"\n"
@ -456,11 +461,13 @@ static const char fragmentShaderMultiSheetSource[] =
"uniform vec2\t\tspotRange;\t\t\t// spotlight Z range: .x=start (viewspace coordinates), .y=limit\n"
"uniform vec3\t\tspotColor;\t\t\t// spotlight RGB color\n"
"uniform vec3\t\tlighting[2];\t\t// lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)\n"
"uniform float\t\tmapSize;\t\t// texture map size (2048,4096,6144 etc)\n"
"\n"
"// Inputs from vertex shader \n"
"varying vec4\t\tfsSubTexture;\t// .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)\n"
"varying vec4\t\tfsTexParams;\t// .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode\n"
"varying float\t\tfsTexFormat;\t// T1RGB5 contour texture (if > 0)\n"
"varying float\t\tfsTexMap;\t// texture map number\n"
"varying float\t\tfsTransLevel;\t// translucence level, 0.0 (transparent) to 1.0 (opaque)\n"
"varying vec3\t\tfsLightIntensity;\t// lighting intensity \n"
"varying float\t\tfsSpecularTerm;\t// specular highlight\n"
@ -506,7 +513,7 @@ static const char fragmentShaderMultiSheetSource[] =
"\tglTexCoord = (\tmirror*(texSize-clampedCoord) +\n"
"\t\t\t\t\t(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +\n"
"\t\t\t\t\ttexOffset\n"
"\t\t\t\t ) / 2048.0;\n"
"\t\t\t\t ) / mapSize;\n"
"\treturn glTexCoord;\n"
"}\n"
"\n"
@ -555,38 +562,38 @@ static const char fragmentShaderMultiSheetSource[] =
"\t\tuv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\t\tuv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));\n"
"\n"
"\t\t// Fetch the texels from the texture map that corresponds to the current texture format\n"
"\t\tif (fsTexFormat < 0.5f)\t{\n"
"\t\t// Fetch the texels from the given texture map\n"
"\t\tif (fsTexMap < 0.5f)\t{\n"
"\t\t\tc[0]=texture2D(textureMap0, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap0, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap0, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap0, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 1.5f) {\n"
"\t\t} else if (fsTexMap < 1.5f) {\n"
" c[0]=texture2D(textureMap1, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap1, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap1, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap1, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 2.5f) {\n"
"\t\t} else if (fsTexMap < 2.5f) {\n"
" c[0]=texture2D(textureMap2, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap2, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap2, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap2, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 3.5f) {\n"
"\t\t} else if (fsTexMap < 3.5f) {\n"
" c[0]=texture2D(textureMap3, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap3, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap3, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap3, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 4.5f) {\n"
"\t\t} else if (fsTexMap < 4.5f) {\n"
" c[0]=texture2D(textureMap4, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap4, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap4, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap4, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 5.5f) {\n"
"\t\t} else if (fsTexMap < 5.5f) {\n"
" c[0]=texture2D(textureMap5, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap5, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap5, uv_top.xy); // top-left\n"
"\t\t\tc[3]=texture2D(textureMap5, uv_top.zw); // top-right\n"
"\t\t} else if (fsTexFormat < 6.5f) {\n"
"\t\t} else if (fsTexMap < 6.5f) {\n"
"\t\t\tc[0]=texture2D(textureMap6, uv_bot.xy); // bottom-left (base texel)\n"
"\t\t\tc[1]=texture2D(textureMap6, uv_bot.zw); // bottom-right\n"
"\t\t\tc[2]=texture2D(textureMap6, uv_top.xy); // top-left\n"

View file

@ -320,6 +320,17 @@ bool CINIFile::Get(string SectionName, string SettingName, unsigned& value)
return OKAY;
}
bool CINIFile::Get(string SectionName, string SettingName, bool& value)
{
int intVal;
if (Get(SectionName, SettingName, intVal) == FAIL)
return FAIL;
value = (bool)intVal;
return OKAY;
}
// Obtains a string setting, if it exists, otherwise does nothing.
bool CINIFile::Get(string SectionName, string SettingName, string& String)
{

View file

@ -68,6 +68,7 @@ public:
*/
bool Get(string SectionName, string SettingName, int& value);
bool Get(string SectionName, string SettingName, unsigned& value);
bool Get(string SectionName, string SettingName, bool& value);
bool Get(string SectionName, string SettingName, string& String);
/*

View file

@ -43,6 +43,7 @@ CInputs::CInputs(CInputSystem *system) : m_system(system)
uiExit = AddSwitchInput("UIExit", "Exit UI", GAME_INPUT_UI, "KEY_ESCAPE");
uiReset = AddSwitchInput("UIReset", "Reset", GAME_INPUT_UI, "KEY_ALT+KEY_R");
uiPause = AddSwitchInput("UIPause", "Pause", GAME_INPUT_UI, "KEY_ALT+KEY_P");
uiFullScreen = AddSwitchInput("UIFullScreen", "Toggle Fullscreen", GAME_INPUT_UI, "KEY_ALT+KEY_RETURN");
uiSaveState = AddSwitchInput("UISaveState", "Save State", GAME_INPUT_UI, "KEY_F5");
uiChangeSlot = AddSwitchInput("UIChangeSlot", "Change Save Slot", GAME_INPUT_UI, "KEY_F6");
uiLoadState = AddSwitchInput("UILoadState", "Load State", GAME_INPUT_UI, "KEY_F7");

View file

@ -94,6 +94,7 @@ public:
CSwitchInput *uiExit;
CSwitchInput *uiReset;
CSwitchInput *uiPause;
CSwitchInput *uiFullScreen;
CSwitchInput *uiSaveState;
CSwitchInput *uiChangeSlot;
CSwitchInput *uiLoadState;

View file

@ -221,14 +221,21 @@ bool CDriveBoard::Init(const UINT8 *romPtr)
return OKAY;
}
void CDriveBoard::AttachInputs(CInputs *InputsPtr, unsigned gameInputFlags)
void CDriveBoard::AttachInputs(CInputs *inputs, unsigned gameInputFlags)
{
m_inputs = InputsPtr;
m_inputs = inputs;
m_inputFlags = gameInputFlags;
DebugLog("DriveBoard attached inputs\n");
}
void CDriveBoard::AttachOutputs(COutputs *outputs)
{
m_outputs = outputs;
DebugLog("DriveBoard attached outputs\n");
}
void CDriveBoard::Reset(void)
{
m_tmpDisabled = false;
@ -814,7 +821,7 @@ void CDriveBoard::SendVibrate(UINT8 val)
}
CDriveBoard::CDriveBoard() : m_attached(false), m_tmpDisabled(false), m_simulated(false),
m_rom(NULL), m_ram(NULL), m_inputs(NULL), m_dip1(0xCF), m_dip2(0xFF)
m_rom(NULL), m_ram(NULL), m_inputs(NULL), m_outputs(NULL), m_dip1(0xCF), m_dip2(0xFF)
{
DebugLog("Built Drive Board\n");
}
@ -828,6 +835,7 @@ CDriveBoard::~CDriveBoard(void)
}
m_rom = NULL;
m_inputs = NULL;
m_outputs = NULL;
DebugLog("Destroyed Drive Board\n");
}

View file

@ -180,10 +180,12 @@ public:
* position).
*
* Parameters:
* InputsPtr Pointer to the input object.
* inputs Pointer to the input object.
* gameInputFlags The current game's input flags.
*/
void AttachInputs(CInputs *InputsPtr, unsigned gameInputFlags);
void AttachInputs(CInputs *inputs, unsigned gameInputFlags);
void AttachOutputs(COutputs *outputs);
/*
* Reset(void):
@ -273,6 +275,8 @@ private:
CInputs *m_inputs;
unsigned m_inputFlags;
COutputs *m_outputs;
// Emulation state
bool m_initialized; // True if drive board has finished initialization

View file

@ -474,6 +474,18 @@ void CModel3::WriteInputs(unsigned reg, UINT8 data)
DriveBoard.Write(data);
break;
case 0x14: // Lamp outputs (Daytona/Scud Race/Sega Rally/Le Mans 24)
if (NULL != Outputs) // TODO - check gameInputs
{
Outputs->SetValue(OutputLampStart, !!(data&0x04));
Outputs->SetValue(OutputLampView1, !!(data&0x08));
Outputs->SetValue(OutputLampView2, !!(data&0x10));
Outputs->SetValue(OutputLampView3, !!(data&0x20));
Outputs->SetValue(OutputLampView4, !!(data&0x40));
Outputs->SetValue(OutputLampLeader, !!(data&0x80));
}
break;
case 0x24: // Serial FIFO 1
switch (data) // Command
{
@ -3028,11 +3040,23 @@ void CModel3::AttachInputs(CInputs *InputsPtr)
Inputs = InputsPtr;
if (DriveBoard.IsAttached())
DriveBoard.AttachInputs(InputsPtr, Game->inputFlags);
DriveBoard.AttachInputs(Inputs, Game->inputFlags);
DebugLog("Model 3 attached inputs\n");
}
void CModel3::AttachOutputs(COutputs *OutputsPtr)
{
Outputs = OutputsPtr;
Outputs->SetGame(Game);
Outputs->Attached();
if (DriveBoard.IsAttached())
DriveBoard.AttachOutputs(Outputs);
DebugLog("Model 3 attached outputs\n");
}
// Model 3 initialization. Some initialization is deferred until ROMs are loaded in LoadROMSet()
bool CModel3::Init(void)
{
@ -3097,6 +3121,8 @@ CModel3::CModel3(void)
// Various uninitialized pointers
Game = NULL;
Inputs = NULL;
Outputs = NULL;
ram = NULL;
crom = NULL;
vrom = NULL;
@ -3162,6 +3188,8 @@ CModel3::~CModel3(void)
}
Game = NULL;
Inputs = NULL;
Outputs = NULL;
ram = NULL;
crom = NULL;
vrom = NULL;

View file

@ -269,6 +269,8 @@ public:
*/
void AttachInputs(CInputs *InputsPtr);
void AttachOutputs(COutputs *OutputsPtr);
/*
* Init(void):
*
@ -380,6 +382,9 @@ private:
// Game inputs
CInputs *Inputs;
// Game outputs
COutputs *Outputs;
// Input registers (game controls)
UINT8 inputBank;

View file

@ -369,7 +369,7 @@ void CTileGen::WriteRegister(unsigned reg, UINT32 data)
// We only have a mechanism to recompute both palettes simultaneously.
// These regs are often written together in the same frame. To avoid
// needlessly recomputing both palettes twice, we defer the operation.
if (regs[reg] != data) // only if changed
if (regs[reg/4] != data) // only if changed
recomputePalettes = true;
break;
case 0x10: // IRQ acknowledge

97
Src/OSD/Outputs.cpp Executable file
View file

@ -0,0 +1,97 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** 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/>.
**/
/*
* Outputs.cpp
*/
#include "Supermodel.h"
const char *COutputs::s_outputNames[] =
{
"Pause",
"LampStart",
"LampView1",
"LampView2",
"LampView3",
"LampView4",
"LampLeader"
};
const char *COutputs::GetOutputName(EOutputs output)
{
int idx = (int)output;
if (idx < 0 || idx >= NUM_OUTPUTS)
return NULL;
return s_outputNames[idx];
}
EOutputs COutputs::GetOutputByName(const char *name)
{
for (unsigned i = 0; i < NUM_OUTPUTS; i++)
{
if (stricmp(name, s_outputNames[i]) == 0)
return (EOutputs)i;
}
return OutputUnknown;
}
COutputs::COutputs()
{
memset(m_first, true, sizeof(m_first));
memset(m_values, 0, sizeof(m_values));
}
COutputs::~COutputs()
{
//
}
const GameInfo *COutputs::GetGame() const
{
return m_game;
}
void COutputs::SetGame(const GameInfo *game)
{
m_game = game;
}
UINT8 COutputs::GetValue(EOutputs output) const
{
int idx = (unsigned)output;
if (idx < 0 || idx >= NUM_OUTPUTS)
return 0;
return m_values[idx];
}
void COutputs::SetValue(EOutputs output, UINT8 value)
{
int idx = (unsigned)output;
if (idx < 0 || idx >= NUM_OUTPUTS)
return;
bool firstSet = m_first[idx];
UINT8 prevValue = m_values[idx];
m_first[idx] = false;
m_values[idx] = value;
if (firstSet || value != prevValue)
SendOutput(output, prevValue, value);
}

143
Src/OSD/Outputs.h Executable file
View file

@ -0,0 +1,143 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** 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/>.
**/
/*
* Outputs.h
*
* Base class for outputs.
*/
#ifndef INCLUDED_OUTPUTS_H
#define INCLUDED_OUTPUTS_H
/*
* EOutputs enumeration of all available outputs.
* Currently just contains the outputs for the driving games - more will need to be added for the other games.
*/
enum EOutputs
{
OutputUnknown = -1,
OutputPause = 0,
OutputLampStart,
OutputLampView1,
OutputLampView2,
OutputLampView3,
OutputLampView4,
OutputLampLeader
};
#define NUM_OUTPUTS 7
struct GameInfo;
class COutputs
{
public:
/*
* GetOutputName(output):
*
* Returns the name of the given output as a string.
*/
static const char *GetOutputName(EOutputs output);
/*
* GetOutputByName(name):
*
* Returns the output with the given name (if any).
*/
static EOutputs GetOutputByName(const char *name);
/*
* ~COutputs():
*
* Destructor.
*/
virtual ~COutputs();
/*
* Initialize():
*
* Initializes the outputs. Must be called before the outputs are attached.
* To be implemented by the subclass.
*/
virtual bool Initialize() = 0;
/*
* Attached():
*
* Lets the outputs know they have been attached to the emulator.
* To be implemented by the subclass.
*/
virtual void Attached() = 0;
/*
* GetGame():
*
* Returns the currently running game.
*/
const GameInfo *GetGame() const;
/*
* SetGame(game):
*
* Sets the currently running game.
*/
void SetGame(const GameInfo *game);
/*
* GetValue(output):
*
* Returns the current value of the given output.
*/
UINT8 GetValue(EOutputs output) const;
/*
* SetValue(output, value):
*
* Sets the current value of the given output.
*/
void SetValue(EOutputs output, UINT8 value);
protected:
/*
* COutputs():
*
* Constructor.
*/
COutputs();
/*
* SendOutput():
*
* Called when an output's value changes so that the subclass can handle it appropriately.
* To be implemented by the subclass.
*/
virtual void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value) = 0;
private:
static const char* s_outputNames[]; // Static array of output names
const GameInfo *m_game; // Currently running game
bool m_first[NUM_OUTPUTS]; // For each output, true if an initial value has been set
UINT8 m_values[NUM_OUTPUTS]; // Current value of each output
};
#endif // INCLUDED_OUTPUTS_H

View file

@ -53,6 +53,7 @@
#include "SDLInputSystem.h"
#ifdef SUPERMODEL_WIN32
#include "DirectInputSystem.h"
#include "WinOutputs.h"
#endif
@ -123,44 +124,13 @@ unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport
unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport)
unsigned totalXRes, totalYRes; // total resolution (the whole GL viewport)
/*
* CreateGLScreen():
*
* Creates an OpenGL display surface of the requested size. xOffset and yOffset
* are used to return a display surface offset (for OpenGL viewport commands)
* because the actual drawing area may need to be adjusted to preserve the
* Model 3 aspect ratio. The new resolution will be passed back as well -- both
* the adjusted viewable area resolution and the total resolution.
*
* NOTE: keepAspectRatio should always be true. It has not yet been tested with
* the wide screen hack.
*/
static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio)
{
const SDL_VideoInfo *VideoInfo;
GLenum err;
float model3Ratio, ratio;
float xRes, yRes;
float model3Ratio, ratio;
float xRes, yRes;
// Initialize video subsystem
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0)
return ErrorLog("Unable to initialize SDL video subsystem: %s\n", SDL_GetError());
// Important GL attributes
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,5); // need at least RGB555 for Model 3 textures
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
// Set video mode
if (SDL_SetVideoMode(*xResPtr,*yResPtr,0,SDL_OPENGL|(fullScreen?SDL_FULLSCREEN|SDL_HWSURFACE:0)) == NULL)
{
ErrorLog("Unable to create an OpenGL display: %s\n", SDL_GetError());
return FAIL;
}
// What resolution did we actually get?
// What resolution did we actually get?
VideoInfo = SDL_GetVideoInfo();
*totalXResPtr = VideoInfo->current_w;
*totalYResPtr = VideoInfo->current_h;
@ -188,17 +158,6 @@ static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
if (*yResPtr < VideoInfo->current_h)
*yOffsetPtr += (VideoInfo->current_h - *yResPtr)/2;
// Create window caption
SDL_WM_SetCaption(caption,NULL);
// Initialize GLEW, allowing us to use features beyond OpenGL 1.2
err = glewInit();
if (GLEW_OK != err)
{
ErrorLog("OpenGL initialization failed: %s\n", glewGetErrorString(err));
return FAIL;
}
// OpenGL initialization
glViewport(0,0,*xResPtr,*yResPtr);
glClearColor(0.0,0.0,0.0,0.0);
@ -233,8 +192,75 @@ static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *
glScissor(*xOffsetPtr, *yOffsetPtr, *xResPtr, *yResPtr);
}
}
return 0;
return OKAY;
}
/*
* CreateGLScreen():
*
* Creates an OpenGL display surface of the requested size. xOffset and yOffset
* are used to return a display surface offset (for OpenGL viewport commands)
* because the actual drawing area may need to be adjusted to preserve the
* Model 3 aspect ratio. The new resolution will be passed back as well -- both
* the adjusted viewable area resolution and the total resolution.
*
* NOTE: keepAspectRatio should always be true. It has not yet been tested with
* the wide screen hack.
*/
static bool CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
{
GLenum err;
// Initialize video subsystem
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0)
return ErrorLog("Unable to initialize SDL video subsystem: %s\n", SDL_GetError());
// Important GL attributes
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,5); // need at least RGB555 for Model 3 textures
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
// Set vsync
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, (g_Config.vsync ? 1 : 0));
// Set video mode
if (SDL_SetVideoMode(*xResPtr,*yResPtr,0,SDL_OPENGL|(fullScreen?SDL_FULLSCREEN|SDL_HWSURFACE:0)) == NULL)
{
ErrorLog("Unable to create an OpenGL display: %s\n", SDL_GetError());
return FAIL;
}
// Create window caption
SDL_WM_SetCaption(caption,NULL);
// Initialize GLEW, allowing us to use features beyond OpenGL 1.2
err = glewInit();
if (GLEW_OK != err)
{
ErrorLog("OpenGL initialization failed: %s\n", glewGetErrorString(err));
return FAIL;
}
return SetGLGeometry(xOffsetPtr, yOffsetPtr, xResPtr, yResPtr, totalXResPtr, totalYResPtr, keepAspectRatio);
}
static bool ResizeGLScreen(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio, bool fullScreen)
{
const SDL_VideoInfo *VideoInfo;
GLenum err;
float model3Ratio, ratio;
float xRes, yRes;
// Set video mode
if (SDL_SetVideoMode(*xResPtr,*yResPtr,0,SDL_OPENGL|(fullScreen?SDL_FULLSCREEN|SDL_HWSURFACE:0)) == NULL)
{
ErrorLog("Unable to create an OpenGL display: %s\n", SDL_GetError());
return FAIL;
}
return SetGLGeometry(xOffsetPtr, yOffsetPtr, xResPtr, yResPtr, totalXResPtr, totalYResPtr, keepAspectRatio);
}
/*
@ -294,6 +320,10 @@ static void PrintGLInfo(bool createScreen, bool infoLog, bool printExtensions)
if (infoLog) InfoLog(" Maximum Vertex Uniforms: %d", value);
else printf(" Maximum Vertex Uniforms: %d\n", value);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &value);
if (infoLog) InfoLog("Maximum Texture Img Units: %d", value);
else printf("Maximum Texture Img Units: %d\n", value);
if (printExtensions)
{
str = glGetString(GL_EXTENSIONS);
@ -378,10 +408,8 @@ static void ApplySettings(CINIFile *INI, const char *section)
string String;
// Model 3
if (OKAY == INI->Get(section, "MultiThreaded", x))
g_Config.multiThreaded = x ? true : false;
if (OKAY == INI->Get(section, "GPUMultiThreaded", x))
g_Config.gpuMultiThreaded = x ? true : false;
INI->Get(section, "MultiThreaded", g_Config.multiThreaded);
INI->Get(section, "GPUMultiThreaded", g_Config.gpuMultiThreaded);
if (OKAY == INI->Get(section, "PowerPCFrequency", x))
g_Config.SetPowerPCFrequency(x);
@ -396,32 +424,25 @@ static void ApplySettings(CINIFile *INI, const char *section)
g_Config.SetMusicVolume(x);
if (OKAY == INI->Get(section, "Balance", y))
g_Config.SetSCSPBalance(y);
if (OKAY == INI->Get(section, "EmulateSound", x))
g_Config.emulateSound = x ? true : false;
if (OKAY == INI->Get(section, "EmulateDSB", x))
g_Config.emulateDSB = x ? true : false;
INI->Get(section, "EmulateSound", g_Config.emulateSound);
INI->Get(section, "EmulateDSB", g_Config.emulateDSB);
// Drive board
#ifdef SUPERMODEL_WIN32
if (OKAY == INI->Get(section, "ForceFeedback", x))
g_Config.forceFeedback = x ? true : false;
INI->Get(section, "ForceFeedback", g_Config.forceFeedback);
#endif // SUPERMODEL_WIN32
// OSD
INI->Get(section, "XResolution", g_Config.xRes);
INI->Get(section, "YResolution", g_Config.yRes);
if (OKAY == INI->Get(section, "FullScreen", x))
g_Config.fullScreen = x ? true : false;
if (OKAY == INI->Get(section, "WideScreen", x))
g_Config.wideScreen = x ? true : false;
if (OKAY == INI->Get(section, "MultiTexture", x))
g_Config.multiTexture = x ? true : false;
if (OKAY == INI->Get(section, "Throttle", x))
g_Config.throttle = x ? true : false;
if (OKAY == INI->Get(section, "ShowFrameRate", x))
g_Config.showFPS = x ? true : false;
if (OKAY == INI->Get(section, "FlipStereo", x))
g_Config.flipStereo = x ? true : false;
INI->Get(section, "FullScreen", g_Config.fullScreen);
INI->Get(section, "WideScreen", g_Config.wideScreen);
INI->Get(section, "MultiTexture", g_Config.multiTexture);
INI->Get(section, "VSync", g_Config.vsync);
INI->Get(section, "Throttle", g_Config.throttle);
INI->Get(section, "ShowFrameRate", g_Config.showFPS);
INI->Get(section, "Crosshairs", g_Config.crosshairs);
INI->Get(section, "FlipStereo", g_Config.flipStereo);
#ifdef SUPERMODEL_WIN32
// DirectInput ForceFeedback
@ -462,13 +483,16 @@ static void LogConfig(void)
InfoLog("\tYResolution = %d", g_Config.yRes);
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
InfoLog("\tWideScreen = %d", g_Config.wideScreen);
InfoLog("\tVSync = %d", g_Config.vsync);
InfoLog("\tMultiTexture = %d", g_Config.multiTexture);
InfoLog("\tThrottle = %d", g_Config.throttle);
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
InfoLog("\tCrosshairs = %d", g_Config.crosshairs);
#ifdef SUPERMODEL_DEBUGGER
InfoLog("\tDisableDebugger = %d", g_Config.disableDebugger);
#endif
InfoLog("\tInputSystem = %s", g_Config.GetInputSystem());
InfoLog("\tOutputs = %s", g_Config.GetOutputs());
InfoLog("\tFlipStereo = %d", g_Config.flipStereo);
#ifdef SUPERMODEL_WIN32
@ -699,12 +723,12 @@ static void PrintGLError(GLenum error)
}
}
static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
static void UpdateCrosshairs(CInputs *Inputs, unsigned crosshairs)
{
float x[2], y[2];
showCrosshairs &= 3;
if (!showCrosshairs)
crosshairs &= 3;
if (!crosshairs)
return;
// Set up the viewport and orthogonal projection
@ -730,9 +754,9 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
// Draw visible crosshairs
glBegin(GL_TRIANGLES);
if ((showCrosshairs & 1) && !Inputs->trigger[0]->offscreenValue) // Player 1
if ((crosshairs & 1) && !Inputs->trigger[0]->offscreenValue) // Player 1
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
if ((showCrosshairs & 2) && !Inputs->trigger[1]->offscreenValue) // Player 2
if ((crosshairs & 2) && !Inputs->trigger[1]->offscreenValue) // Player 2
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
glEnd();
@ -745,7 +769,6 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
******************************************************************************/
static CInputs *videoInputs = NULL;
static unsigned videoShowCrosshairs = 0; // bit 1: player 1 crosshair, bit 0: player 2
bool BeginFrameVideo()
{
@ -756,7 +779,7 @@ void EndFrameVideo()
{
// Show crosshairs for light gun games
if (videoInputs)
UpdateCrosshairs(videoInputs, videoShowCrosshairs);
UpdateCrosshairs(videoInputs, g_Config.crosshairs);
// Swap the buffers
SDL_GL_SwapBuffers();
@ -768,11 +791,11 @@ void EndFrameVideo()
******************************************************************************/
#ifdef SUPERMODEL_DEBUGGER
int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, CINIFile *CmdLine)
int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, COutputs *Outputs, Debugger::CDebugger *Debugger, CINIFile *CmdLine)
{
CLogger *oldLogger;
#else
int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
int Supermodel(const char *zipFile, CInputs *Inputs, COutputs *Outputs, CINIFile *CmdLine)
{
CModel3 *Model3 = new CModel3();
#endif // SUPERMODEL_DEBUGGER
@ -818,16 +841,16 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
Inputs->GetInputSystem()->SetMouseVisibility(!g_Config.fullScreen);
gameHasLightguns = !!(Model3->GetGameInfo()->inputFlags & (GAME_INPUT_GUN1|GAME_INPUT_GUN2));
if (gameHasLightguns)
{
videoInputs = Inputs;
if (g_Config.fullScreen && gameHasLightguns)
videoShowCrosshairs = 1; // show player 1 cursor only by default (TODO: add an IsMapped() member to CInput to allow testing for both lightguns)
}
else
videoInputs = NULL;
// Attach the inputs to the emulator
Model3->AttachInputs(Inputs);
// Attach the outputs to the emulator
if (Outputs != NULL)
Model3->AttachOutputs(Outputs);
// Initialize the renderer
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
@ -938,6 +961,38 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
SetAudioEnabled(true);
SDL_WM_SetCaption(baseTitleStr,NULL);
}
// Send paused value as output
if (Outputs != NULL)
Outputs->SetValue(OutputPause, paused);
}
else if (Inputs->uiFullScreen->Pressed())
{
// Toggle emulator fullscreen
g_Config.fullScreen = !g_Config.fullScreen;
// Delete renderers and recreate them afterwards since GL context will most likely be lost when switching from/to fullscreen
delete Render2D;
delete Render3D;
Render2D = NULL;
Render3D = NULL;
// Resize screen
totalXRes = xRes = g_Config.xRes;
totalYRes = yRes = g_Config.yRes;
if (OKAY != ResizeGLScreen(&xOffset,&yOffset,&xRes,&yRes,&totalXRes,&totalYRes,true,g_Config.fullScreen))
goto QuitError;
// Recreate renderers and attach to the emulator
Render2D = new CRender2D();
Render3D = new CRender3D();
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError;
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
Inputs->GetInputSystem()->SetMouseVisibility(!g_Config.fullScreen);
}
else if (Inputs->uiSaveState->Pressed())
{
@ -1059,8 +1114,8 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
}
else if (Inputs->uiSelectCrosshairs->Pressed() && gameHasLightguns)
{
videoShowCrosshairs++;
switch ((videoShowCrosshairs&3))
g_Config.crosshairs++;
switch (g_Config.crosshairs&3)
{
case 0: puts("Crosshairs disabled."); break;
case 3: puts("Crosshairs enabled."); break;
@ -1099,7 +1154,7 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
++fpsFramesElapsed;
if((currentFPSTicks-prevFPSTicks) >= 1000) // update FPS every 1 second (each tick is 1 ms)
{
sprintf(titleStr, "%s - %1.1f FPS%s", baseTitleStr, (float)fpsFramesElapsed*(float)(currentFPSTicks-prevFPSTicks)/1000.0f, paused ? " (Paused)" : "");
sprintf(titleStr, "%s - %1.1f FPS%s", baseTitleStr, (float)fpsFramesElapsed/((float)(currentFPSTicks-prevFPSTicks)/1000.0f), paused ? " (Paused)" : "");
SDL_WM_SetCaption(titleStr,NULL);
prevFPSTicks = currentFPSTicks; // reset tick count
fpsFramesElapsed = 0; // reset frame count
@ -1285,10 +1340,12 @@ static void Help(void)
puts(" -window Windowed mode [Default]");
puts(" -fullscreen Full screen mode");
puts(" -wide-screen Expand 3D field of view to screen width");
puts(" -multi-texture Use 8 texture maps for accurate decoding [Default]");
puts(" -no-multi-texture Decode to a single texture map");
puts(" -multi-texture Use 8 texture maps for decoding");
puts(" -no-multi-texture Decode to a single texture map [Default]");
puts(" -no-throttle Disable 60 Hz frame rate lock");
puts(" -show-fps Display frame rate in window title bar");
puts(" -crosshairs=<n> Crosshairs configuration for gun games:");
puts(" 0=none [Default], 1=P1 only, 2=P2 only, 3=P1 & P2");
puts(" -vert-shader=<file> Load 3D vertex shader from external file");
puts(" -frag-shader=<file> Load 3D fragment shader from external file");
puts(" -print-gl-info Print OpenGL driver information and quit");
@ -1308,6 +1365,7 @@ static void Help(void)
puts(" -config-inputs Configure keyboards, mice, and game controllers");
#ifdef SUPERMODEL_WIN32
printf(" -input-system=<s> Input system [Default: %s]\n", g_Config.GetInputSystem());
printf(" -outputs=<s> Outputs [Default: %s]\n", g_Config.GetOutputs());
#endif
puts(" -print-inputs Prints current input configuration");
puts("");
@ -1353,6 +1411,7 @@ int main(int argc, char **argv)
int cmdEnterDebugger=false;
#endif // SUPERMODEL_DEBUGGER
char *inputSystem = NULL; // use default input system
char *outputs = NULL;
unsigned n;
int m;
UINT32 addr;
@ -1482,8 +1541,12 @@ int main(int argc, char **argv)
ret = sscanf(&argv[i][4],"=%d,%d",&x,&y);
if (ret != 2)
ErrorLog("'-res' requires both a width and a height.");
else
{
ret = sscanf(&argv[i][4],"=%dx%d",&x,&y);
if (ret != 2)
ErrorLog("'-res' requires both a width and a height.");
}
if (ret == 2)
{
CmdLine.Set("Global", "XResolution", x);
CmdLine.Set("Global", "YResolution", y);
@ -1514,6 +1577,16 @@ int main(int argc, char **argv)
n = 0;
CmdLine.Set("Global", "MultiTexture", n);
}
else if (!strcmp(argv[i],"-vsync"))
{
n = 1;
CmdLine.Set("Global", "VSync", n);
}
else if (!strcmp(argv[i],"-no-vsync"))
{
n = 0;
CmdLine.Set("Global", "VSync", n);
}
else if (!strcmp(argv[i],"-no-throttle"))
{
n = 0;
@ -1524,6 +1597,16 @@ int main(int argc, char **argv)
n = 1;
CmdLine.Set("Global", "ShowFrameRate", n);
}
else if (!strncmp(argv[i],"-crosshairs",11))
{
unsigned x;
ret = sscanf(&argv[i][11],"=%d",&x);
if (ret != 1 || x > 3)
ErrorLog("'-crosshairs' requires a number 0-3");
else
CmdLine.Set("Global", "Crosshairs", x);
}
else if (!strncmp(argv[i],"-vert-shader",12))
{
if (argv[i][12] == '\0')
@ -1558,6 +1641,17 @@ int main(int argc, char **argv)
else
inputSystem = &argv[i][14];
}
else if (!strncmp(argv[i],"-outputs", 8)) // this setting is not written to the config file!
{
if (argv[i][8] == '\0')
ErrorLog("'-outputs' requires an outputs name.");
else if (argv[i][8] != '=')
ErrorLog("Ignoring unrecognized option: %s", argv[i]);
else if (argv[i][9] == '\0')
ErrorLog("'-outputs' requires an outputs name.");
else
outputs = &argv[i][9];
}
#endif // SUPERMODEL_WIN32
else if (!strcmp(argv[i],"-print-inputs"))
cmdPrintInputs = true;
@ -1602,6 +1696,7 @@ int main(int argc, char **argv)
// Create input system (default is SDL) and debugger
CInputSystem *InputSystem = NULL;
CInputs *Inputs = NULL;
COutputs *Outputs = NULL;
int exitCode = 0;
#ifdef SUPERMODEL_DEBUGGER
CModel3 *Model3 = NULL;
@ -1647,6 +1742,29 @@ int main(int argc, char **argv)
Inputs->PrintInputs(NULL);
InputSystem->PrintSettings();
}
// Create outputs
#ifdef SUPERMODEL_WIN32
g_Config.SetOutputs(outputs);
if (stricmp(g_Config.GetOutputs(), "none") == 0)
Outputs = NULL;
else if (stricmp(g_Config.GetOutputs(), "win") == 0)
Outputs = new CWinOutputs();
else
{
ErrorLog("Unknown outputs: %s\n", g_Config.GetOutputs());
exitCode = 1;
goto Exit;
}
#endif // SUPERMODEL_WIN32
// Initialize outputs
if (Outputs != NULL && !Outputs->Initialize())
{
ErrorLog("Unable to initialize outputs.\n");
exitCode = 1;
goto Exit;
}
// From this point onwards, a ROM set is needed
if (fileIdx == 0)
@ -1675,13 +1793,13 @@ int main(int argc, char **argv)
Debugger->ForceBreak(true);
}
// Fire up Supermodel with debugger
exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,&CmdLine);
exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Outputs,Debugger,&CmdLine);
if (Debugger != NULL)
delete Debugger;
delete Model3;
#else
// Fire up Supermodel
exitCode = Supermodel(argv[fileIdx],Inputs,&CmdLine);
exitCode = Supermodel(argv[fileIdx],Inputs,Outputs,&CmdLine);
#endif // SUPERMODEL_DEBUGGER
Exit:
@ -1689,6 +1807,8 @@ Exit:
delete Inputs;
if (InputSystem != NULL)
delete InputSystem;
if (Outputs != NULL)
delete Outputs;
SDL_Quit();
if (exitCode)

View file

@ -46,8 +46,10 @@ public:
unsigned xRes, yRes; // X and Y resolution, in pixels
bool fullScreen; // Full screen mode (if true)
bool wideScreen; // Wide screen hack
bool vsync; // Enable v-sync
bool throttle; // 60 Hz frame limiting
bool showFPS; // Show frame rate
unsigned crosshairs; // For game guns: 0 = no crosshairs, 1 = player 1 only, 2 = player 2 only, 3 = both players
bool flipStereo; // Flip stereo channels
#ifdef SUPERMODEL_DEBUGGER
@ -101,6 +103,34 @@ public:
{
return inputSystem.c_str();
}
// Outputs
inline void SetOutputs(const char *outputsName)
{
if (outputsName == NULL)
{
outputs = "none";
return;
}
if (stricmp(outputsName, "none")
#ifdef SUPERMODEL_WIN32
&& stricmp(outputsName, "win")
#endif
)
{
ErrorLog("Unknown outputs '%s', defaulting to None.", outputsName);
outputs = "none";
return;
}
outputs = outputsName;
}
inline const char *GetOutputs(void)
{
return outputs.c_str();
}
// Defaults
COSDConfig(void)
@ -109,8 +139,10 @@ public:
yRes = 384;
fullScreen = false;
wideScreen = false;
vsync = true;
throttle = true;
showFPS = false;
crosshairs = 0;
flipStereo = false;
#ifdef SUPERMODEL_DEBUGGER
disableDebugger = false;
@ -128,10 +160,12 @@ public:
#else
inputSystem = "sdl";
#endif
outputs = "none";
}
private:
string inputSystem;
string outputs;
};

265
Src/OSD/Windows/WinOutputs.cpp Executable file
View file

@ -0,0 +1,265 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** 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/>.
**/
/*
* WinOutputs.cpp
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "OSD/Windows/WinOutputs.h"
#include "Supermodel.h"
#define OUTPUT_WINDOW_CLASS TEXT("MAMEOutput")
#define OUTPUT_WINDOW_NAME TEXT("MAMEOutput")
// Messages sent from emulator to external clients
#define START TEXT("MAMEOutputStart")
#define STOP TEXT("MAMEOutputStop")
#define UPDATE_STATE TEXT("MAMEOutputUpdateState")
// Messages sent from external clients to emulator
#define REGISTER_CLIENT TEXT("MAMEOutputRegister")
#define UNREGISTER_CLIENT TEXT("MAMEOutputUnregister")
#define GET_ID_STRING TEXT("MAMEOutputGetIDString")
#define COPYDATA_MESSAGE_ID_STRING 1
bool CWinOutputs::s_createdClass = false;
CWinOutputs::CWinOutputs() : m_hwnd(NULL)
{
//
}
CWinOutputs::~CWinOutputs()
{
// Broadcast a shutdown message
if (m_hwnd)
PostMessage(HWND_BROADCAST, m_onStop, (WPARAM)m_hwnd, 0);
}
bool CWinOutputs::Initialize()
{
// Create window class
if (!CreateWindowClass())
{
ErrorLog("Unable to register window class for Windows outputs");
return false;
}
// Create window
m_hwnd = CreateWindowEx(0,
OUTPUT_WINDOW_CLASS,
OUTPUT_WINDOW_NAME,
WS_OVERLAPPEDWINDOW,
0, 0,
1, 1,
NULL,
NULL,
GetModuleHandle(NULL),
NULL);
if (!m_hwnd)
{
ErrorLog("Unable to create window handle for Windows outputs");
return false;
}
// Allocate message ids
if (!AllocateMessageId(m_onStart, START)) return false;
if (!AllocateMessageId(m_onStop, STOP)) return false;
if (!AllocateMessageId(m_updateState, UPDATE_STATE)) return false;
if (!AllocateMessageId(m_regClient, REGISTER_CLIENT)) return false;
if (!AllocateMessageId(m_unregClient, UNREGISTER_CLIENT)) return false;
if (!AllocateMessageId(m_getIdString, GET_ID_STRING)) return false;
// Set pointer to this object
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
return true;
}
void CWinOutputs::Attached()
{
// Broadcast a startup message
PostMessage(HWND_BROADCAST, m_onStart, (WPARAM)m_hwnd, 0);
}
void CWinOutputs::SendOutput(EOutputs output, UINT8 prevValue, UINT8 value)
{
//printf("LAMP OUTPUT %s = %u -> %u\n", GetOutputName(output), prevValue, value);
// Loop through all registered clients and send them new output value
LPARAM param = (LPARAM)output + 1;
for (vector<RegisteredClient>::iterator it = m_clients.begin(), end = m_clients.end(); it != end; it++)
PostMessage(it->hwnd, m_updateState, param, value);
}
LRESULT CALLBACK CWinOutputs::OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
CWinOutputs *outputs = (CWinOutputs*)ptr;
if (!outputs)
return 1;
return outputs->OutputWindowProc(hwnd, msg, wParam, lParam);
}
bool CWinOutputs::CreateWindowClass()
{
if (s_createdClass)
return true;
// Setup description of window class
WNDCLASS wc = { 0 };
wc.lpszClassName = OUTPUT_WINDOW_CLASS;
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = OutputWindowProcCallback;
// Register class
if (RegisterClass(&wc))
{
s_createdClass = true;
return true;
}
return false;
}
bool CWinOutputs::AllocateMessageId(UINT &regId, LPCSTR str)
{
regId = RegisterWindowMessage(str);
if (regId != 0)
return true;
ErrorLog("Unable to register window message '%s' for Windows outputs", str);
return false;
}
LRESULT CWinOutputs::OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Process message sent to emulator window
if (msg == m_regClient) return RegisterClient((HWND)wParam, lParam);
else if (msg == m_unregClient) return UnregisterClient((HWND)wParam, lParam);
else if (msg == m_getIdString) return SendIdString((HWND)wParam, lParam);
else return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CWinOutputs::RegisterClient(HWND hwnd, LPARAM id)
{
// Check that given client is not already registered
for (vector<RegisteredClient>::iterator it = m_clients.begin(), end = m_clients.end(); it != end; it++)
{
if (it->id == id)
{
it->hwnd = hwnd;
// If so, just send it current state of all outputs
SendAllToClient(*it);
return 1;
}
}
// If not, store details about client and send it current state of all outputs
RegisteredClient client;
client.id = id;
client.hwnd = hwnd;
m_clients.push_back(client);
SendAllToClient(client);
return 0;
}
void CWinOutputs::SendAllToClient(RegisteredClient &client)
{
// Loop through all known outputs and send their current state to given client
for (unsigned i = 0; i < NUM_OUTPUTS; i++)
{
EOutputs output = (EOutputs)i;
LPARAM param = (LPARAM)output + 1;
PostMessage(client.hwnd, m_updateState, param, GetValue(output));
}
}
LRESULT CWinOutputs::UnregisterClient(HWND hwnd, LPARAM id)
{
// Find any matching clients and remove them
bool found = false;
vector<RegisteredClient>::iterator it = m_clients.begin();
while (it != m_clients.end())
{
if (it->id == id)
{
it = m_clients.erase(it);
found = true;
}
else
it++;
}
// Return error if no matches found
return (found ? 0 : 1);
}
LRESULT CWinOutputs::SendIdString(HWND hwnd, LPARAM id)
{
// Id 0 is the name of the game
const char *name;
if (id == 0)
name = GetGame()->id;
else
name = MapIdToName(id);
// NULL name is an empty string
if (name == NULL)
name = "";
// Allocate memory for message
int dataLen = sizeof(CopyDataIdString) + strlen(name);
CopyDataIdString *data = (CopyDataIdString*)new(nothrow) UINT8[dataLen];
if (!data)
return 1;
data->id = id;
strcpy(data->string, name);
// Reply by using SendMessage with WM_COPYDATA
COPYDATASTRUCT copyData;
copyData.dwData = COPYDATA_MESSAGE_ID_STRING;
copyData.cbData = dataLen;
copyData.lpData = data;
SendMessage(hwnd, WM_COPYDATA, (WPARAM)m_hwnd, (LPARAM)&copyData);
delete[] data;
return 0;
}
const char *CWinOutputs::MapIdToName(LPARAM id)
{
EOutputs output = (EOutputs)(id - 1);
return GetOutputName(output);
}
LPARAM CWinOutputs::MapNameToId(const char *name)
{
EOutputs output = GetOutputByName(name);
if (output == OutputUnknown)
return 0;
return (LPARAM)output + 1;
}

175
Src/OSD/Windows/WinOutputs.h Executable file
View file

@ -0,0 +1,175 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** 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/>.
**/
/*
* WinOutputs.h
*
* Implementation of COutputs that sends MAMEHooker compatible messages via Windows messages.
*/
#ifndef INCLUDED_WINOUTPUTS_H
#define INCLUDED_WINOUTPUTS_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "OSD/Outputs.h"
#include <vector>
using namespace std;
// Struct that represents a client (eg MAMEHooker) currently registered with the emulator
struct RegisteredClient
{
LPARAM id; // Client-specified id
HWND hwnd; // Client HWND
};
struct CopyDataIdString
{
UINT32 id; // Id that was requested
char string[1]; // String containing data
};
class CWinOutputs : public COutputs
{
public:
/*
* CWinOutputs():
* ~CWinOutputs():
*
* Constructor and destructor.
*/
CWinOutputs();
virtual ~CWinOutputs();
/*
* Initialize():
*
* Initializes this class.
*/
bool Initialize();
/*
* Attached():
*
* Lets the class know that it has been attached to the emulator.
*/
void Attached();
protected:
/*
* SendOutput():
*
* Sends the appropriate output message to all registered clients.
*/
void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value);
private:
static bool s_createdClass;
/*
* CreateWindowClass():
*
* Registers the window class and sets up OutputWindowProcCallback to process all messages sent to the emulator window.
*/
static bool CreateWindowClass();
/*
* OutputWindowProcCallback(hwnd, msg, wParam, lParam):
*
* Receives all messages sent to the emulator window and passes them on to the CWinOutputs object (whose pointer is passed
* via GWLP_USERDATA).
*/
static LRESULT CALLBACK OutputWindowProcCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND m_hwnd;
UINT m_onStart;
UINT m_onStop;
UINT m_updateState;
UINT m_regClient;
UINT m_unregClient;
UINT m_getIdString;
vector<RegisteredClient> m_clients;
/*
* AllocateMessageId(regId, str):
*
* Defines a new window message type with the given name and returns the allocated message id.
*/
bool AllocateMessageId(UINT &regId, LPCSTR str);
/*
* OutputWindowProc(hwnd, msg, wParam, lParam):
*
* Processes the messages sent to the emulator window.
*/
LRESULT OutputWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
/*
* RegisterClient(hwnd, id):
*
* Registers a client (eg MAMEHooker) with the emulator.
*/
LRESULT RegisterClient(HWND hwnd, LPARAM id);
/*
* SendAllToClient(client):
*
* Sends the current state of all the outputs to the given registered client.
* Called whenever a client is registered with the emulator.
*/
void SendAllToClient(RegisteredClient &client);
/*
* UnregisterClient(hwnd, id):
*
* Unregisters a client from the emulator.
*/
LRESULT UnregisterClient(HWND hwnd, LPARAM id);
/*
* SendIdString(hwnd, id):
*
* Sends the name of the requested output back to a client, or the name of the current running game if an id of zero is requested.
*/
LRESULT SendIdString(HWND hwnd, LPARAM id);
/*
* MapIdToName(id):
*
* Maps the given id to an output's name.
*/
const char *MapIdToName(LPARAM id);
/*
* MapNameToId(name):
*
* Maps the given name to an output's id.
*/
LPARAM MapNameToId(const char *name);
};
#endif // INCLUDED_WINOUTPUTS_H

View file

@ -104,6 +104,7 @@
#include "OSD/Thread.h"
#include "OSD/Audio.h"
#include "OSD/Video.h"
#include "OSD/Outputs.h"
/******************************************************************************

View file

@ -805,6 +805,42 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Src\Graphics\Shaders\Fragment_MultiSheet.glsl"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Src\Graphics\Shaders\Fragment_NoSpotlight.glsl"
>
@ -974,6 +1010,10 @@
<Filter
Name="OSD"
>
<File
RelativePath="..\Src\OSD\Outputs.cpp"
>
</File>
<Filter
Name="SDL"
>
@ -1001,6 +1041,10 @@
RelativePath="..\Src\OSD\Windows\DirectInputSystem.cpp"
>
</File>
<File
RelativePath="..\Src\OSD\Windows\WinOutputs.cpp"
>
</File>
</Filter>
</Filter>
<Filter
@ -1673,14 +1717,146 @@
<File
RelativePath="..\Makefiles\Makefile.SDL.OSX.GCC"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Makefiles\Makefile.SDL.UNIX.GCC"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Makefiles\Makefile.SDL.Win32.GCC"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\Makefiles\Makefile.SDL.Win32.MSVC"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
@ -1896,6 +2072,10 @@
RelativePath="..\Src\OSD\Logger.h"
>
</File>
<File
RelativePath="..\Src\OSD\Outputs.h"
>
</File>
<File
RelativePath="..\Src\OSD\Thread.h"
>
@ -1927,6 +2107,10 @@
RelativePath="..\Src\OSD\Windows\DirectInputSystem.h"
>
</File>
<File
RelativePath="..\Src\OSD\Windows\WinOutputs.h"
>
</File>
</Filter>
</Filter>
<Filter