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" > + +