From 183dca563d7a927cc994aabadbf545cf36da9f2e Mon Sep 17 00:00:00 2001 From: Nik Henson Date: Sun, 15 Jul 2012 21:04:46 +0000 Subject: [PATCH] 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 --- Makefiles/Makefile.SDL.OSX.GCC | 4 + Makefiles/Makefile.SDL.UNIX.GCC | 4 + Makefiles/Makefile.SDL.Win32.GCC | 1 + Makefiles/Makefile.SDL.Win32.MSVC | 8 +- Src/CPU/PowerPC/ppc.cpp | 13 +- Src/CPU/PowerPC/ppc.h | 1 + Src/CPU/PowerPC/ppc603.c | 2 + Src/Debugger/CPUDebug.h | 11 +- Src/Graphics/Models.cpp | 44 ++- Src/Graphics/Render2D.h | 2 +- Src/Graphics/Render3D.cpp | 308 ++++++++++++------ Src/Graphics/Render3D.h | 57 ++-- Src/Graphics/Shaders/Fragment.glsl | 18 +- Src/Graphics/Shaders/Fragment_Flat.glsl | 9 +- Src/Graphics/Shaders/Fragment_MultiSheet.glsl | 22 +- .../Shaders/Fragment_NoSpotlight.glsl | 5 +- Src/Graphics/Shaders/Vertex.glsl | 5 +- Src/Graphics/Shaders3D.h | 37 ++- Src/INIFile.cpp | 11 + Src/INIFile.h | 1 + Src/Inputs/Inputs.cpp | 1 + Src/Inputs/Inputs.h | 1 + Src/Model3/DriveBoard.cpp | 14 +- Src/Model3/DriveBoard.h | 8 +- Src/Model3/Model3.cpp | 30 +- Src/Model3/Model3.h | 5 + Src/Model3/TileGen.cpp | 2 +- Src/OSD/Outputs.cpp | 97 ++++++ Src/OSD/Outputs.h | 143 ++++++++ Src/OSD/SDL/Main.cpp | 304 +++++++++++------ Src/OSD/SDL/OSDConfig.h | 34 ++ Src/OSD/Windows/WinOutputs.cpp | 265 +++++++++++++++ Src/OSD/Windows/WinOutputs.h | 175 ++++++++++ Src/Supermodel.h | 1 + VS2008/Supermodel.vcproj | 184 +++++++++++ 35 files changed, 1536 insertions(+), 291 deletions(-) create mode 100755 Src/OSD/Outputs.cpp create mode 100755 Src/OSD/Outputs.h create mode 100755 Src/OSD/Windows/WinOutputs.cpp create mode 100755 Src/OSD/Windows/WinOutputs.h diff --git a/Makefiles/Makefile.SDL.OSX.GCC b/Makefiles/Makefile.SDL.OSX.GCC index 056ba83..1391e4f 100644 --- a/Makefiles/Makefile.SDL.OSX.GCC +++ b/Makefiles/Makefile.SDL.OSX.GCC @@ -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 diff --git a/Makefiles/Makefile.SDL.UNIX.GCC b/Makefiles/Makefile.SDL.UNIX.GCC index ff80255..06985e9 100644 --- a/Makefiles/Makefile.SDL.UNIX.GCC +++ b/Makefiles/Makefile.SDL.UNIX.GCC @@ -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 diff --git a/Makefiles/Makefile.SDL.Win32.GCC b/Makefiles/Makefile.SDL.Win32.GCC index d347ebb..6e48dee 100644 --- a/Makefiles/Makefile.SDL.Win32.GCC +++ b/Makefiles/Makefile.SDL.Win32.GCC @@ -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 diff --git a/Makefiles/Makefile.SDL.Win32.MSVC b/Makefiles/Makefile.SDL.Win32.MSVC index 3c82a8b..2bea668 100644 --- a/Makefiles/Makefile.SDL.Win32.MSVC +++ b/Makefiles/Makefile.SDL.Win32.MSVC @@ -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 diff --git a/Src/CPU/PowerPC/ppc.cpp b/Src/CPU/PowerPC/ppc.cpp index 44d4128..605b819 100644 --- a/Src/CPU/PowerPC/ppc.cpp +++ b/Src/CPU/PowerPC/ppc.cpp @@ -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) diff --git a/Src/CPU/PowerPC/ppc.h b/Src/CPU/PowerPC/ppc.h index e098530..b902eaa 100644 --- a/Src/CPU/PowerPC/ppc.h +++ b/Src/CPU/PowerPC/ppc.h @@ -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); diff --git a/Src/CPU/PowerPC/ppc603.c b/Src/CPU/PowerPC/ppc603.c index 51587e4..fbae6ae 100644 --- a/Src/CPU/PowerPC/ppc603.c +++ b/Src/CPU/PowerPC/ppc603.c @@ -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; } diff --git a/Src/Debugger/CPUDebug.h b/Src/Debugger/CPUDebug.h index b5a3848..8ba4c95 100644 --- a/Src/Debugger/CPUDebug.h +++ b/Src/Debugger/CPUDebug.h @@ -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); diff --git a/Src/Graphics/Models.cpp b/Src/Graphics/Models.cpp index 8c3e9ec..c2228b7 100644 --- a/Src/Graphics/Models.cpp +++ b/Src/Graphics/Models.cpp @@ -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) diff --git a/Src/Graphics/Render2D.h b/Src/Graphics/Render2D.h index ec00edc..ecaa621 100644 --- a/Src/Graphics/Render2D.h +++ b/Src/Graphics/Render2D.h @@ -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 diff --git a/Src/Graphics/Render3D.cpp b/Src/Graphics/Render3D.cpp index 2a5b836..f7f8a05 100644 --- a/Src/Graphics/Render3D.cpp +++ b/Src/Graphics/Render3D.cpp @@ -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(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(1, min(g_Config.maxTexUnits, glMaxTexUnits)); + unsigned maxTexMaps = max(1, min(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(1, min(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(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(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(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; diff --git a/Src/Graphics/Render3D.h b/Src/Graphics/Render3D.h index 293f90e..44339da 100644 --- a/Src/Graphics/Render3D.h +++ b/Src/Graphics/Render3D.h @@ -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 diff --git a/Src/Graphics/Shaders/Fragment.glsl b/Src/Graphics/Shaders/Fragment.glsl index 9735872..fb2804e 100644 --- a/Src/Graphics/Shaders/Fragment.glsl +++ b/Src/Graphics/Shaders/Fragment.glsl @@ -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; } diff --git a/Src/Graphics/Shaders/Fragment_Flat.glsl b/Src/Graphics/Shaders/Fragment_Flat.glsl index bc6c061..d1af1c4 100644 --- a/Src/Graphics/Shaders/Fragment_Flat.glsl +++ b/Src/Graphics/Shaders/Fragment_Flat.glsl @@ -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); } diff --git a/Src/Graphics/Shaders/Fragment_MultiSheet.glsl b/Src/Graphics/Shaders/Fragment_MultiSheet.glsl index 20d3fd4..f49db75 100644 --- a/Src/Graphics/Shaders/Fragment_MultiSheet.glsl +++ b/Src/Graphics/Shaders/Fragment_MultiSheet.glsl @@ -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 diff --git a/Src/Graphics/Shaders/Fragment_NoSpotlight.glsl b/Src/Graphics/Shaders/Fragment_NoSpotlight.glsl index 1a2917c..07e99ac 100644 --- a/Src/Graphics/Shaders/Fragment_NoSpotlight.glsl +++ b/Src/Graphics/Shaders/Fragment_NoSpotlight.glsl @@ -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; } diff --git a/Src/Graphics/Shaders/Vertex.glsl b/Src/Graphics/Shaders/Vertex.glsl index 368001f..5dbbb48 100644 --- a/Src/Graphics/Shaders/Vertex.glsl +++ b/Src/Graphics/Shaders/Vertex.glsl @@ -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; } diff --git a/Src/Graphics/Shaders3D.h b/Src/Graphics/Shaders3D.h index 78344be..4bdaab6 100644 --- a/Src/Graphics/Shaders3D.h +++ b/Src/Graphics/Shaders3D.h @@ -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" diff --git a/Src/INIFile.cpp b/Src/INIFile.cpp index c53f636..3444e23 100644 --- a/Src/INIFile.cpp +++ b/Src/INIFile.cpp @@ -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) { diff --git a/Src/INIFile.h b/Src/INIFile.h index a2cbbcc..52abef2 100644 --- a/Src/INIFile.h +++ b/Src/INIFile.h @@ -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); /* diff --git a/Src/Inputs/Inputs.cpp b/Src/Inputs/Inputs.cpp index 274af10..138998e 100644 --- a/Src/Inputs/Inputs.cpp +++ b/Src/Inputs/Inputs.cpp @@ -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"); diff --git a/Src/Inputs/Inputs.h b/Src/Inputs/Inputs.h index c07f2eb..56b1542 100644 --- a/Src/Inputs/Inputs.h +++ b/Src/Inputs/Inputs.h @@ -94,6 +94,7 @@ public: CSwitchInput *uiExit; CSwitchInput *uiReset; CSwitchInput *uiPause; + CSwitchInput *uiFullScreen; CSwitchInput *uiSaveState; CSwitchInput *uiChangeSlot; CSwitchInput *uiLoadState; diff --git a/Src/Model3/DriveBoard.cpp b/Src/Model3/DriveBoard.cpp index 69500b4..f4ac7f2 100644 --- a/Src/Model3/DriveBoard.cpp +++ b/Src/Model3/DriveBoard.cpp @@ -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"); } diff --git a/Src/Model3/DriveBoard.h b/Src/Model3/DriveBoard.h index fd34f7a..61d8316 100644 --- a/Src/Model3/DriveBoard.h +++ b/Src/Model3/DriveBoard.h @@ -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 diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp index 7df6609..c1ae49d 100644 --- a/Src/Model3/Model3.cpp +++ b/Src/Model3/Model3.cpp @@ -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; diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h index cca2292..f9acc24 100644 --- a/Src/Model3/Model3.h +++ b/Src/Model3/Model3.h @@ -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; diff --git a/Src/Model3/TileGen.cpp b/Src/Model3/TileGen.cpp index 53a0262..e18a6d9 100644 --- a/Src/Model3/TileGen.cpp +++ b/Src/Model3/TileGen.cpp @@ -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 diff --git a/Src/OSD/Outputs.cpp b/Src/OSD/Outputs.cpp new file mode 100755 index 0000000..4c123e9 --- /dev/null +++ b/Src/OSD/Outputs.cpp @@ -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 . + **/ + +/* + * 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); +} diff --git a/Src/OSD/Outputs.h b/Src/OSD/Outputs.h new file mode 100755 index 0000000..7da3157 --- /dev/null +++ b/Src/OSD/Outputs.h @@ -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 . + **/ + +/* + * 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 diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 401d3e4..eb7f468 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -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= Crosshairs configuration for gun games:"); + puts(" 0=none [Default], 1=P1 only, 2=P2 only, 3=P1 & P2"); puts(" -vert-shader= Load 3D vertex shader from external file"); puts(" -frag-shader= 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= Input system [Default: %s]\n", g_Config.GetInputSystem()); + printf(" -outputs= 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) diff --git a/Src/OSD/SDL/OSDConfig.h b/Src/OSD/SDL/OSDConfig.h index 1d5fe87..14befab 100644 --- a/Src/OSD/SDL/OSDConfig.h +++ b/Src/OSD/SDL/OSDConfig.h @@ -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; }; diff --git a/Src/OSD/Windows/WinOutputs.cpp b/Src/OSD/Windows/WinOutputs.cpp new file mode 100755 index 0000000..598a8b5 --- /dev/null +++ b/Src/OSD/Windows/WinOutputs.cpp @@ -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 . + **/ + +/* + * WinOutputs.cpp + */ + +#include +#include +#include + +#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::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 ®Id, 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::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::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)©Data); + + 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; +} \ No newline at end of file diff --git a/Src/OSD/Windows/WinOutputs.h b/Src/OSD/Windows/WinOutputs.h new file mode 100755 index 0000000..28aabde --- /dev/null +++ b/Src/OSD/Windows/WinOutputs.h @@ -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 . + **/ + +/* + * 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 + +#include "OSD/Outputs.h" + +#include + +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 m_clients; + + /* + * AllocateMessageId(regId, str): + * + * Defines a new window message type with the given name and returns the allocated message id. + */ + bool AllocateMessageId(UINT ®Id, 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 diff --git a/Src/Supermodel.h b/Src/Supermodel.h index 01906ff..734fcc6 100644 --- a/Src/Supermodel.h +++ b/Src/Supermodel.h @@ -104,6 +104,7 @@ #include "OSD/Thread.h" #include "OSD/Audio.h" #include "OSD/Video.h" +#include "OSD/Outputs.h" /****************************************************************************** diff --git a/VS2008/Supermodel.vcproj b/VS2008/Supermodel.vcproj index 1b504f8..c59ce67 100755 --- a/VS2008/Supermodel.vcproj +++ b/VS2008/Supermodel.vcproj @@ -805,6 +805,42 @@ /> + + + + + + + + + + + + + + @@ -974,6 +1010,10 @@ + + @@ -1001,6 +1041,10 @@ RelativePath="..\Src\OSD\Windows\DirectInputSystem.cpp" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1927,6 +2107,10 @@ RelativePath="..\Src\OSD\Windows\DirectInputSystem.h" > + +