diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afbeaa1e7b1fc9b65d4a49c1348ff1ad139f31ad..a7735449381802897a058bb734008e3a8fce156d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -255,6 +255,10 @@ else() endif() set(SRB2_CONFIG_HWRENDER ON CACHE BOOL "Enable hardware rendering through OpenGL.") +set(SRB2_CONFIG_HAVE_GLES ON CACHE BOOL + "Enable hardware rendering through OpenGL ES 1.1.") +set(SRB2_CONFIG_HAVE_GLES2 ON CACHE BOOL + "Enable hardware rendering through OpenGL ES 2.0.") set(SRB2_CONFIG_USEASM OFF CACHE BOOL "Enable NASM tmap implementation for software mode speedup.") set(SRB2_CONFIG_YASM OFF CACHE BOOL @@ -529,19 +533,35 @@ if(${SRB2_CONFIG_HWRENDER}) ) set(SRB2_R_OPENGL_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.c ${CMAKE_CURRENT_SOURCE_DIR}/hardware/shaders/gl_shaders.c ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_glcommon/r_glcommon.c ) set(SRB2_R_OPENGL_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.h ${CMAKE_CURRENT_SOURCE_DIR}/hardware/shaders/gl_shaders.h ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_glcommon/r_glcommon.h ) endif() +if(${SRB2_CONFIG_HAVE_GLES}) + set(SRB2_HAVE_GLES ON) + set(SRB2_HWRENDER_HEADERS ${SRB2_HWRENDER_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_gles/r_gles.h) + set(SRB2_HWRENDER_HEADERS ${SRB2_HWRENDER_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_gles/r_gleslib.h) + add_definitions(-DHAVE_GLES) + if(${SRB2_CONFIG_HAVE_GLES2}) + set(SRB2_HAVE_GLES2 ON) + set(SRB2_HWRENDER_SOURCES ${SRB2_HWRENDER_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_gles/r_gles2.c) + set(SRB2_HWRENDER_HEADERS ${SRB2_HWRENDER_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_gles/lzml.h) + add_definitions(-DHAVE_GLES2) + else() + set(SRB2_HWRENDER_SOURCES ${SRB2_HWRENDER_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_gles/r_gles1.c) + endif() +else() + set(SRB2_HWRENDER_SOURCES ${SRB2_HWRENDER_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.c) + set(SRB2_HWRENDER_HEADERS ${SRB2_HWRENDER_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.h) +endif() + if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL}) find_package(OpenGL) if(${OPENGL_FOUND}) diff --git a/src/Makefile b/src/Makefile index 58e7428faf80c4a004c30c42000da4a46e2270c3..1b59099feb8092f469b65ec741b03d7ac4d833c2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -270,7 +270,16 @@ else OPTS+=-DHWRENDER OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \ $(OBJDIR)/hw_main.o $(OBJDIR)/hw_gpu.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o \ - $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o + $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o \ + $(r_opengl_o) $(OBJDIR)/r_glcommon.o $(OBJDIR)/gl_shaders.o + + ifdef HAVE_GLES2 + OPTS+=-DHAVE_GLES2 -DHAVE_GLES + else + ifdef HAVE_GLES + OPTS+=-DHAVE_GLES + endif + endif endif OPTS += -DCOMPVERSION @@ -653,97 +662,6 @@ endif $(OBJDIR): -$(MKDIR) $(OBJDIR) -ifndef SDL -ifdef NOHW -dll : -else -dll : opengl_dll -endif -ifdef MINGW -all_dll: opengl_dll ds3d_dll fmod_dll openal_dll - -opengl_dll: $(BIN)/r_opengl.dll -$(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o $(OBJDIR)/r_glcommon.o $(OBJDIR)/gl_shaders.o - -$(MKDIR) $(BIN) - @echo Linking R_OpenGL.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lgdi32 -static-libgcc -ifndef NOUPX - -$(UPX) $(UPX_OPTS) $@ -endif - -ds3d_dll: $(BIN)/s_ds3d.dll -$(BIN)/s_ds3d.dll: $(OBJDIR)/s_ds3d.o - @echo Linking S_DS3d.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -ldsound -luuid - -fmod_dll: $(BIN)/s_fmod.dll -$(BIN)/s_fmod.dll: $(OBJDIR)/s_fmod.o - -$(MKDIR) $(BIN) - @echo Linking S_FMOD.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lfmod - -openal_dll: $(BIN)/s_openal.dll -$(BIN)/s_openal.dll: $(OBJDIR)/s_openal.o - -$(MKDIR) $(BIN) - @echo Linking S_OpenAL.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lopenal32 -else -all_dll: fmod_so openal_so - -fmod_so: $(BIN)/s_fmod.so -$(BIN)/s_fmod.so: $(OBJDIR)/s_fmod.o - -$(MKDIR) $(BIN) - @echo Linking S_FMOD.so... - $(CC) --shared $^ -o $@ -g --nostartfiles -lm -lfmod - -openal_so: $(BIN)/s_openal.so -$(BIN)/s_openal.so: $(OBJDIR)/s_openal.o - -$(MKDIR) $(BIN) - @echo Linking S_OpenAL.so... - $(CC) --shared $^ -o $@ -g --nostartfiles -lm -lopenal -endif - -else -ifdef SDL -ifdef MINGW -$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \ - hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \ - doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(echoName) - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -else -$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \ - hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@ -$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \ - doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@ -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(echoName) - $(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@ -endif -endif - -endif - #dependecy made by gcc itself ! $(OBJS): ifndef DUMMY @@ -758,6 +676,9 @@ $(OBJDIR)/depend.dep: $(CC) $(CFLAGS) -MM $(INTERFACE)/*.c >> $(OBJDIR)/depend.ped ifndef NOHW $(CC) $(CFLAGS) -MM hardware/*.c >> $(OBJDIR)/depend.ped + $(CC) $(CFLAGS) -MM hardware/shaders/*.c >> $(OBJDIR)/depend.ped + $(CC) $(CFLAGS) -MM hardware/r_glcommon/*.c >> $(OBJDIR)/depend.ped + $(CC) $(CFLAGS) -MM hardware/$(OPENGL_API)/*.c >> $(OBJDIR)/depend.ped endif $(CC) $(CFLAGS) -MM blua/*.c >> $(OBJDIR)/depend.ped @sed -e 's,\(.*\)\.o: ,$(subst /,\/,$(OBJDIR))\/&,g' < $(OBJDIR)/depend.ped > $(OBJDIR)/depend.dep @@ -797,6 +718,18 @@ $(OBJDIR)/%.o: hardware/%.c $(echoName) $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ +$(OBJDIR)/%.o: hardware/shaders/%.c + $(echoName) + $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ + +$(OBJDIR)/%.o: hardware/r_glcommon/%.c + $(echoName) + $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ + +$(OBJDIR)/%.o: hardware/$(OPENGL_API)/%.c + $(echoName) + $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ + $(OBJDIR)/%.o: blua/%.c $(echoName) $(CC) $(CFLAGS) $(LUA_CFLAGS) $(WFLAGS) -c $< -o $@ @@ -818,40 +751,6 @@ $(OBJDIR)/SRB2.res: win32/Srb2win.rc win32/afxres.h win32/resource.h $(WINDRES) -i $< -O rc $(WINDRESFLAGS) --include-dir=win32 -o $@ -O coff -ifdef MINGW -ifndef SDL -ifndef NOHW -$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \ - hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \ - doomdef.h doomtype.h doomdata.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(echoName) - $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ - -$(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(echoName) - $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ -endif - -endif -endif - ifdef SDL ifdef MINGW diff --git a/src/Makefile.cfg b/src/Makefile.cfg index db7230bb4b42ffe4a21f27d94fddf73174153e1c..7368c3105ad1ff183a2a17e6a46ffbe1756f8261 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -451,6 +451,19 @@ ifdef SDL OBJDIR:=$(OBJDIR)/SDL endif +r_opengl_o=$(OBJDIR)/r_opengl.o +OPENGL_API=r_opengl + +ifdef HAVE_GLES2 + r_opengl_o=$(OBJDIR)/r_gles2.o + OPENGL_API=r_gles +else + ifdef HAVE_GLES + r_opengl_o=$(OBJDIR)/r_gles1.o + OPENGL_API=r_gles + endif +endif + ifndef DUMMY ifdef DEBUGMODE OBJDIR:=$(OBJDIR)/Debug diff --git a/src/doomdef.h b/src/doomdef.h index d0b7ea0c2391334c703051d02e0dae693dfdfe19..1b77280176204f96b53949f4c348f78926bf1dc0 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -624,6 +624,10 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// OpenGL shaders #define GL_SHADERS +#if defined(HAVE_GLES) && !defined(HAVE_GLES2) +#undef GL_SHADERS +#endif + /// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink. /// \note Required for proper collision with moving sloped surfaces that have sector specials on them. #define SECTORSPECIALSAFTERTHINK diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index 7aecad8e3e29a1baa458a63b0a244ea39c1ca3e7..f981e22b3a6d6a0eb970a46334e650b816f23caa 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -213,19 +213,20 @@ enum EFilterMode // Vertex count for post processing effects #define GPU_POSTIMGVERTS 10 -#ifdef GL_SHADERS // Predefined shader types -enum +enum EShaderType { SHADER_DEFAULT = 0, - SHADER_FLOOR, + SHADER_LEVEL_FIRST, + SHADER_FLOOR = SHADER_LEVEL_FIRST, SHADER_WALL, SHADER_SPRITE, SHADER_MODEL, SHADER_MODEL_LIGHTING, SHADER_WATER, SHADER_FOG, SHADER_SKY, + SHADER_LEVEL_LAST = SHADER_SKY, #ifdef HAVE_GLES2 SHADER_FADEMASK, @@ -235,6 +236,8 @@ enum NUMBASESHADERS, }; +#ifdef GL_SHADERS + // Maximum amount of shader programs // Must be higher than NUMBASESHADERS #define HWR_MAXSHADERS 16 @@ -247,6 +250,8 @@ struct FShaderSource }; typedef struct FShaderSource FShaderSource; +#endif + // Custom shader reference table struct FShaderReferenceArray { @@ -255,8 +260,6 @@ struct FShaderReferenceArray }; typedef struct FShaderReferenceArray FShaderReferenceArray; -#endif - struct FSkyVertex { float x, y, z; diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 0a0184adf77b2fad90248ef02d896f76bcb64b66..ea145265ef7877265d1488e3185d348d7b7f8842 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -1483,39 +1483,40 @@ static inline boolean saveTGA(const char *file_name, void *buffer, #endif // -------------------------------------------------------------------------- -// screen shot +// Screen buffer and screenshots +// Saves or returns either 24bit 888 RGB or 32bit 8888 RGBA // -------------------------------------------------------------------------- -UINT8 *HWR_GetScreenshot(void) +UINT8 *HWR_GetScreenBuffer(void) { - UINT8 *buf = malloc(vid.width * vid.height * 3 * sizeof (*buf)); + UINT8 *buf = malloc(vid.width * vid.height * SCREENSHOT_BITS * sizeof (*buf)); if (!buf) return NULL; - // returns 24bit 888 RGB - GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf); + + GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * SCREENSHOT_BITS, (void *)buf); return buf; } -boolean HWR_Screenshot(const char *pathname) +boolean HWR_TakeScreenshot(const char *pathname) { boolean ret; - UINT8 *buf = malloc(vid.width * vid.height * 3 * sizeof (*buf)); + UINT8 *buf = malloc(vid.width * vid.height * SCREENSHOT_BITS * sizeof (*buf)); if (!buf) { - CONS_Debug(DBG_RENDER, "HWR_Screenshot: Failed to allocate memory\n"); + CONS_Debug(DBG_RENDER, "HWR_TakeScreenshot: Failed to allocate memory\n"); return false; } - // returns 24bit 888 RGB - GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf); + GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * SCREENSHOT_BITS, (void *)buf); #ifdef USE_PNG ret = M_SavePNG(pathname, buf, vid.width, vid.height, NULL); #else ret = saveTGA(pathname, buf, vid.width, vid.height); #endif + free(buf); return ret; } diff --git a/src/hardware/hw_gpu.c b/src/hardware/hw_gpu.c index 5052148931ac0c16450e3d82d73201093b6591fd..bf277dc54a41eef54abf44806051ef8fdc289fa8 100644 --- a/src/hardware/hw_gpu.c +++ b/src/hardware/hw_gpu.c @@ -23,4 +23,16 @@ void GPUInterface_Load(struct GPURenderingAPI **api) *api = &GLInterfaceAPI; } +const char *GPUInterface_GetAPIName(void) +{ + return +#if defined(HAVE_GLES2) + "OpenGL ES 2.0"; +#elif defined(HAVE_GLES) + "OpenGL ES 1.1"; +#else + "OpenGL"; +#endif +} + #endif // HWRENDER diff --git a/src/hardware/hw_gpu.h b/src/hardware/hw_gpu.h index 03f522cfe87c1aa169c601119da7174c355bfe1a..a1b2230fe235718ed5ca54200c2f4ed88cae78c2 100644 --- a/src/hardware/hw_gpu.h +++ b/src/hardware/hw_gpu.h @@ -20,13 +20,14 @@ struct GPURenderingAPI { boolean (*Init) (void); - void (*FinishUpdate) (INT32 waitvbl); + void (*SetInitialStates) (void); + void (*SetModelView) (INT32 w, INT32 h); void (*SetState) (INT32 State, INT32 Value); void (*SetTransform) (FTransform *ptransform); void (*SetBlend) (UINT32 PolyFlags); void (*SetPalette) (RGBA_t *palette); - void (*ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor); + void (*SetDepthBuffer) (void); void (*DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags); void (*DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray); @@ -45,6 +46,7 @@ struct GPURenderingAPI void (*ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data); void (*GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip); + void (*ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor); void (*MakeScreenTexture) (void); void (*MakeScreenFinalTexture) (void); @@ -53,6 +55,7 @@ struct GPURenderingAPI void (*StartScreenWipe) (void); void (*EndScreenWipe) (void); void (*DoScreenWipe) (void); + void (*DoTintedWipe) (boolean istowhite, boolean isfadingin); void (*DrawIntermissionBG) (void); void (*DrawScreenFinalTexture) (int width, int height); @@ -70,5 +73,6 @@ struct GPURenderingAPI extern struct GPURenderingAPI *GPU; void GPUInterface_Load(struct GPURenderingAPI **api); +const char *GPUInterface_GetAPIName(void); #endif // __HWR_GPU_H__ diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index b63d0df61a260772312f92b2d682500c4b7fe0dc..11b4eda3614c7d122d80a590d41829c3f534b80a 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6203,6 +6203,9 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) GPU->SetTransform(NULL); GPU->UnSetShader(); +#ifdef HAVE_GLES + GPU->SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest); +#endif HWR_DoPostProcessor(player); @@ -6242,7 +6245,7 @@ static CV_PossibleValue_t glshearing_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Thi static void CV_glfiltermode_OnChange(void); static void CV_glanisotropic_OnChange(void); -static CV_PossibleValue_t glfiltermode_cons_t[]= {{GPU_TEXFILTER_POINTSAMPLED, "Nearest"}, +static CV_PossibleValue_t glfiltermode_cons_t[] = {{GPU_TEXFILTER_POINTSAMPLED, "Nearest"}, {GPU_TEXFILTER_BILINEAR, "Bilinear"}, {GPU_TEXFILTER_TRILINEAR, "Trilinear"}, {GPU_TEXFILTER_MIXED1, "Linear_Nearest"}, {GPU_TEXFILTER_MIXED2, "Nearest_Linear"}, @@ -6623,8 +6626,16 @@ void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum) void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum) { +#ifdef HAVE_GLES2 + if (!HWR_WipeCheck(wipenum, scrnnum)) + return; + + HWR_GetFadeMask(wipelumpnum); + GPU->DoTintedWipe((wipestyleflags & WSF_FADEIN), (wipestyleflags & WSF_TOWHITE)); +#else // It does the same thing HWR_DoWipe(wipenum, scrnnum); +#endif } void HWR_MakeScreenFinalTexture(void) @@ -6666,6 +6677,10 @@ FShaderReferenceArray shaderxlat[] = {"WaterRipple", SHADER_WATER}, {"Fog", SHADER_FOG}, {"Sky", SHADER_SKY}, +#ifdef HAVE_GLES2 + {"Wipe", SHADER_FADEMASK}, + {"WipeAdditiveAndSubtractive", SHADER_FADEMASK_ADDITIVEANDSUBTRACTIVE}, +#endif {NULL, 0}, }; @@ -6748,7 +6763,13 @@ skip_lump: for (i = 0; shaderxlat[i].type; i++) { - if (!stricmp(shaderxlat[i].type, stoken)) + FShaderReferenceArray *xlat = &shaderxlat[i]; + INT32 id = xlat->id; + + if (id < SHADER_LEVEL_FIRST || id > SHADER_LEVEL_LAST) + continue; + + if (!stricmp(xlat->type, stoken)) { size_t shader_size; char *shader_source; @@ -6781,7 +6802,7 @@ skip_lump: shader_source = Z_Malloc(shader_size, PU_STATIC, NULL); W_ReadLumpPwad(wadnum, shader_lumpnum, shader_source); - GPU->LoadCustomShader(shaderxlat[i].id, shader_source, shader_size, (shadertype == 2)); + GPU->LoadCustomShader(id, shader_source, shader_size, (shadertype == 2)); Z_Free(shader_source); Z_Free(shader_lumpname); diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index d1de5c328447cfbccfd4a7d63d61e9a0ef5dcbe8..03c37aef715cd45cc30a1e3e3f886f952ddc2318 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -48,8 +48,8 @@ void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 ac void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT32 actualcolor); // Lat: separate flags from color since color needs to be an uint to work right. void HWR_DrawPic(INT32 x,INT32 y,lumpnum_t lumpnum); -UINT8 *HWR_GetScreenshot(void); -boolean HWR_Screenshot(const char *pathname); +UINT8 *HWR_GetScreenBuffer(void); +boolean HWR_TakeScreenshot(const char *pathname); void HWR_AddCommands(void); void HWR_AddSessionCommands(void); diff --git a/src/hardware/r_glcommon/r_glcommon.c b/src/hardware/r_glcommon/r_glcommon.c index cf552d2fcf69cd5195273a56e6294a3422f2773d..3e1c9ee33feb5e6bfafe31a96d72f43255348c43 100644 --- a/src/hardware/r_glcommon/r_glcommon.c +++ b/src/hardware/r_glcommon/r_glcommon.c @@ -1,13 +1,13 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 1998-2020 by Sonic Team Junior. +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file r_glcommon.c -/// \brief Common OpenGL functions shared by OpenGL backends +/// \brief Common OpenGL functions shared by all of the OpenGL backends #include "r_glcommon.h" @@ -53,6 +53,7 @@ GLuint WipeEndTexture = 0; UINT32 CurrentPolyFlags; GLboolean MipmappingEnabled = GL_FALSE; +GLboolean MipmappingSupported = GL_FALSE; GLboolean ModelLightingEnabled = GL_FALSE; GLint MipmapMinFilter = GL_LINEAR; @@ -70,6 +71,7 @@ boolean GLExtension_vertex_buffer_object; boolean GLExtension_texture_filter_anisotropic; boolean GLExtension_vertex_program; boolean GLExtension_fragment_program; +boolean GLExtension_framebuffer_object; boolean GLExtension_shaders; // Not an extension on its own, but it is set if multiple extensions are available. static FExtensionList const ExtensionList[] = { @@ -79,6 +81,7 @@ static FExtensionList const ExtensionList[] = { {"GL_EXT_texture_filter_anisotropic", &GLExtension_texture_filter_anisotropic}, {"GL_ARB_vertex_program", &GLExtension_vertex_program}, {"GL_ARB_fragment_program", &GLExtension_fragment_program}, + {"GL_ARB_framebuffer_object", &GLExtension_framebuffer_object}, {NULL, NULL} }; @@ -150,20 +153,16 @@ PFNglClientActiveTexture pglClientActiveTexture; // Mipmapping // -#ifdef HAVE_GLES PFNglGenerateMipmap pglGenerateMipmap; -#endif // // Depth functions // -#ifndef HAVE_GLES - PFNglClearDepth pglClearDepth; - PFNglDepthRange pglDepthRange; -#else - PFNglClearDepthf pglClearDepthf; - PFNglDepthRangef pglDepthRangef; -#endif +PFNglClearDepth pglClearDepth; +PFNglDepthRange pglDepthRange; + +PFNglClearDepthf pglClearDepthf; +PFNglDepthRangef pglDepthRangef; // // Legacy functions @@ -202,11 +201,8 @@ PFNglTexEnvi pglTexEnvi; #endif // HAVE_GLES2 // Color -#ifdef HAVE_GLES PFNglColor4f pglColor4f; -#else PFNglColor4ubv pglColor4ubv; -#endif /* 1.5 functions for buffers */ PFNglGenBuffers pglGenBuffers; @@ -221,8 +217,16 @@ PFNglBlendEquation pglBlendEquation; // FUNCTIONS // ========================================================================== +#ifdef GL_SHADERS +static boolean ShadersInit = false; +#endif + boolean GLBackend_Init(void) { +#if defined(__ANDROID__) + GLBackend_InitContext(); +#endif + GPUTextureFormat = GL_RGBA; return GLBackend_LoadFunctions(); } @@ -237,6 +241,7 @@ boolean GLBackend_InitContext(void) pglGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion); GL_DBG_Printf("OpenGL %s\n", GLVersion); + GL_DBG_Printf("Specification: %s\n", GPUInterface_GetAPIName()); GL_DBG_Printf("Version: %d.%d\n", GLMajorVersion, GLMinorVersion); GL_DBG_Printf("GPU: %s\n", GLRenderer); @@ -261,16 +266,25 @@ boolean GLBackend_InitContext(void) return true; } -boolean GLBackend_LoadCommonFunctions(void) +boolean GLBackend_InitShaders(void) { -#define GETOPENGLFUNC(func) \ - p ## gl ## func = GLBackend_GetFunction("gl" #func); \ - if (!(p ## gl ## func)) \ - { \ - GL_MSG_Error("failed to get OpenGL function: %s", #func); \ - return false; \ - } \ +#ifdef GL_SHADERS + if (ShadersInit) + return true; + + Shader_LoadFunctions(); + if (!Shader_Compile()) + return false; + + ShadersInit = true; +#endif + + return true; +} + +boolean GLBackend_LoadCommonFunctions(void) +{ GETOPENGLFUNC(ClearColor) GETOPENGLFUNC(Clear) @@ -327,13 +341,13 @@ boolean GLBackend_LoadLegacyFunctions(void) GETOPENGLFUNC(Lightfv) GETOPENGLFUNC(LightModelfv) GETOPENGLFUNC(Materialfv) + + GETOPENGLFUNC(TexEnvi) #endif return true; } -#undef GETOPENGLFUNC - INT32 GLBackend_GetShaderType(INT32 type) { #ifdef GL_SHADERS @@ -344,9 +358,110 @@ INT32 GLBackend_GetShaderType(INT32 type) return SHADER_MODEL_LIGHTING; #endif +#ifdef HAVE_GLES2 + if (ShadersAllowed == GPU_SHADEROPTION_OFF) + { + switch (type) + { + case SHADER_FLOOR: + case SHADER_WALL: + case SHADER_SPRITE: + case SHADER_MODEL: + case SHADER_MODEL_LIGHTING: + case SHADER_WATER: + case SHADER_FOG: + case SHADER_SKY: + return SHADER_DEFAULT; + default: + break; + } + } +#endif + return type; } +void GLState_SetSurface(INT32 w, INT32 h) +{ + GPU->SetModelView(w, h); + GPU->SetInitialStates(); + pglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void GLState_SetPalette(RGBA_t *palette) +{ + size_t palsize = (sizeof(RGBA_t) * 256); + // on a palette change, you have to reload all of the textures + if (memcmp(&GPUTexturePalette, palette, palsize)) + { + memcpy(&GPUTexturePalette, palette, palsize); + GLTexture_Flush(); + } +} + +// Sets the texture filtering mode. +void GLState_SetFilterMode(INT32 mode) +{ + switch (mode) + { + case GPU_TEXFILTER_TRILINEAR: + MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR; + MipmapMagFilter = GL_LINEAR; + MipmappingEnabled = GL_TRUE; + break; + case GPU_TEXFILTER_BILINEAR: + MipmapMinFilter = MipmapMagFilter = GL_LINEAR; + MipmappingEnabled = GL_FALSE; + break; + case GPU_TEXFILTER_POINTSAMPLED: + MipmapMinFilter = MipmapMagFilter = GL_NEAREST; + MipmappingEnabled = GL_FALSE; + break; + case GPU_TEXFILTER_MIXED1: + MipmapMinFilter = GL_NEAREST; + MipmapMagFilter = GL_LINEAR; + MipmappingEnabled = GL_FALSE; + break; + case GPU_TEXFILTER_MIXED2: + MipmapMinFilter = GL_LINEAR; + MipmapMagFilter = GL_NEAREST; + MipmappingEnabled = GL_FALSE; + break; + case GPU_TEXFILTER_MIXED3: + MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR; + MipmapMagFilter = GL_NEAREST; + MipmappingEnabled = GL_TRUE; + break; + default: + MipmapMagFilter = GL_LINEAR; + MipmapMinFilter = GL_NEAREST; + } +} + +void GLState_SetClamp(GLenum pname) +{ +#ifndef HAVE_GLES + pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP); // fallback clamp +#endif + pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP_TO_EDGE); +} + +void GLState_SetDepthBuffer(void) +{ + // Hurdler: all that are permanent states + if (pglClearDepthf) + pglClearDepthf(1.0f); + else + pglClearDepth(1.0f); + + if (pglDepthRangef) + pglDepthRangef(0.0f, 1.0f); + else + pglDepthRange(0.0f, 1.0f); + + pglDepthFunc(GL_LEQUAL); +} + static void SetBlendEquation(GLenum mode) { if (pglBlendEquation) @@ -405,7 +520,7 @@ static void SetBlendMode(UINT32 flags) break; } - // Alpha test + // Alpha testing switch (flags) { case PF_Masked & PF_Blending: @@ -432,7 +547,7 @@ static void SetBlendMode(UINT32 flags) // PF_Masked - we could use an ALPHA_TEST of GL_EQUAL, and alpha ref of 0, // is it faster when pixels are discarded ? -void SetBlendingStates(UINT32 PolyFlags) +void GLState_SetBlend(UINT32 PolyFlags) { UINT32 Xor = CurrentPolyFlags^PolyFlags; @@ -470,7 +585,7 @@ void SetBlendingStates(UINT32 PolyFlags) if (Xor & PF_RemoveYWrap) { if (PolyFlags & PF_RemoveYWrap) - SetClamp(GL_TEXTURE_WRAP_T); + GLState_SetClamp(GL_TEXTURE_WRAP_T); } if (Xor & PF_ForceWrapX) @@ -523,18 +638,51 @@ void SetBlendingStates(UINT32 PolyFlags) } if (PolyFlags & PF_NoTexture) { - SetNoTexture(); + GLTexture_SetNoTexture(); } } CurrentPolyFlags = PolyFlags; } -void SetSurface(INT32 w, INT32 h) +void GLState_SetColor(const GLfloat red, const GLfloat green, const GLfloat blue, const GLfloat alpha) { - SetModelView(w, h); - SetStates(); - pglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#if defined(__ANDROID__) + if (pglColor4f) + pglColor4f(red, green, blue, alpha); + else if (pglColor4ubv) +#endif + { + GLubyte color[4]; + + color[0] = (unsigned char)(red * 255); + color[1] = (unsigned char)(green * 255); + color[2] = (unsigned char)(blue * 255); + color[3] = (unsigned char)(alpha * 255); + + pglColor4ubv(color); + } +} + +void GLState_SetColorUBV(const GLubyte *v) +{ +#if defined(__ANDROID__) + if (pglColor4f) + { + INT32 i = 0; + GLfloat color[4]; + + for (; i < 4; i++) + { + color[i] = byte2float(*v); + v++; + } + + pglColor4f(color[0], color[1], color[2], color[3]); + } + else if (pglColor4ubv) +#endif + pglColor4ubv(v); } void GLExtension_Init(void) @@ -821,67 +969,43 @@ void GLModel_GenerateVBOs(model_t *model) } } -// Deletes all textures. -void GLTexture_Flush(void) +// Sets the current texture. +void GLTexture_Set(HWRTexture_t *pTexInfo) { - while (TexCacheHead) + if (!pTexInfo) { - FTextureInfo *pTexInfo = TexCacheHead; - HWRTexture_t *texture = pTexInfo->texture; - - if (pTexInfo->name) + GLTexture_SetNoTexture(); + return; + } + else if (pTexInfo->downloaded) + { + if (pTexInfo->downloaded != CurrentTexture) { - pglDeleteTextures(1, (GLuint *)&pTexInfo->name); - pTexInfo->name = 0; + pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded); + CurrentTexture = pTexInfo->downloaded; } - - if (texture) - texture->downloaded = 0; - - TexCacheHead = pTexInfo->next; - free(pTexInfo); } + else + { + FTextureInfo *newTex = calloc(1, sizeof (*newTex)); - TexCacheTail = TexCacheHead = NULL; // Hurdler: well, TexCacheHead is already NULL - CurrentTexture = 0; -} + GPU->UpdateTexture(pTexInfo); -// Sets texture filtering mode. -void GLTexture_SetFilterMode(INT32 mode) -{ - switch (mode) - { - case GPU_TEXFILTER_TRILINEAR: - MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR; - MipmapMagFilter = GL_LINEAR; - MipmappingEnabled = GL_TRUE; - break; - case GPU_TEXFILTER_BILINEAR: - MipmapMinFilter = MipmapMagFilter = GL_LINEAR; - MipmappingEnabled = GL_FALSE; - break; - case GPU_TEXFILTER_POINTSAMPLED: - MipmapMinFilter = MipmapMagFilter = GL_NEAREST; - MipmappingEnabled = GL_FALSE; - break; - case GPU_TEXFILTER_MIXED1: - MipmapMinFilter = GL_NEAREST; - MipmapMagFilter = GL_LINEAR; - MipmappingEnabled = GL_FALSE; - break; - case GPU_TEXFILTER_MIXED2: - MipmapMinFilter = GL_LINEAR; - MipmapMagFilter = GL_NEAREST; - MipmappingEnabled = GL_FALSE; - break; - case GPU_TEXFILTER_MIXED3: - MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR; - MipmapMagFilter = GL_NEAREST; - MipmappingEnabled = GL_TRUE; - break; - default: - MipmapMagFilter = GL_LINEAR; - MipmapMinFilter = GL_NEAREST; + newTex->texture = pTexInfo; + newTex->name = (UINT32)pTexInfo->downloaded; + newTex->width = (UINT32)pTexInfo->width; + newTex->height = (UINT32)pTexInfo->height; + newTex->format = (UINT32)pTexInfo->format; + + // insertion at the tail + if (TexCacheTail) + { + newTex->prev = TexCacheTail; + TexCacheTail->next = newTex; + TexCacheTail = newTex; + } + else // initialization of the linked list + TexCacheTail = TexCacheHead = newTex; } } @@ -906,28 +1030,249 @@ void GLTexture_Delete(HWRTexture_t *pTexInfo) } } -// Calculates total memory usage by textures, excluding mipmaps. -INT32 GLTexture_GetMemoryUsage(FTextureInfo *head) +// Updates a texture. +void GLTexture_Update(HWRTexture_t *pTexInfo) { - INT32 res = 0; + boolean update = true; + static RGBA_t textureBuffer[2048 * 2048]; + const GLvoid *pTextureBuffer = textureBuffer; + GLuint textureName = 0; - while (head) + if (!pTexInfo->downloaded) { - // Figure out the correct bytes-per-pixel for this texture - // This follows format2bpp in hw_cache.c - INT32 bpp = 1; - UINT32 format = head->format; - if (format == GPU_TEXFMT_RGBA) - bpp = 4; - else if (format == GPU_TEXFMT_ALPHA_INTENSITY_88 || format == GPU_TEXFMT_AP_88) - bpp = 2; + pglGenTextures(1, &textureName); + pTexInfo->downloaded = textureName; + update = false; + } + else + textureName = pTexInfo->downloaded; + + if (pTexInfo->format == GPU_TEXFMT_RGBA) + pTextureBuffer = pTexInfo->data; + else if ((pTexInfo->format == GPU_TEXFMT_P_8) || (pTexInfo->format == GPU_TEXFMT_AP_88)) + GLTexture_WritePalette(pTexInfo, textureBuffer); + else if (pTexInfo->format == GPU_TEXFMT_ALPHA_INTENSITY_88) + GLTexture_WriteLuminanceAlpha(pTexInfo, textureBuffer); + else if (pTexInfo->format == GPU_TEXFMT_ALPHA_8) // Used for fade masks + { +#ifdef HAVE_GLES2 + GLTexture_WriteFadeMaskR(pTexInfo, textureBuffer); +#else + GLTexture_WriteFadeMaskA(pTexInfo, textureBuffer); +#endif + } + else + GL_MSG_Warning("GLTexture_Update: bad format %d\n", pTexInfo->format); - // Add it up! - res += head->height*head->width*bpp; - head = head->next; + // the texture number was already generated by pglGenTextures + pglBindTexture(GL_TEXTURE_2D, textureName); + CurrentTexture = textureName; + + // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues + if (pTexInfo->flags & TF_TRANSPARENT) + { + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else + { + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MipmapMagFilter); + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MipmapMinFilter); } - return res; +#ifdef HAVE_GLES + GLTexture_UploadData(pTexInfo, pTextureBuffer, GPUTextureFormat, update); +#else + if (pTexInfo->format == GPU_TEXFMT_ALPHA_INTENSITY_88) + GLTexture_UploadData(pTexInfo, pTextureBuffer, GL_LUMINANCE_ALPHA, update); + else if (pTexInfo->format == GPU_TEXFMT_ALPHA_8) + GLTexture_UploadData(pTexInfo, pTextureBuffer, GL_ALPHA, update); + else + GLTexture_UploadData(pTexInfo, pTextureBuffer, GPUTextureFormat, update); +#endif + + if (pTexInfo->flags & TF_WRAPX) + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + else + GLState_SetClamp(GL_TEXTURE_WRAP_S); + + if (pTexInfo->flags & TF_WRAPY) + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + else + GLState_SetClamp(GL_TEXTURE_WRAP_T); + + if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy) + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, AnisotropicFilter); + + if (GLTexture_CanGenerateMipmaps(pTexInfo) && pglGenerateMipmap) + pglGenerateMipmap(GL_TEXTURE_2D); +} + +// Uploads texture data. +void GLTexture_UploadData(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer, GLenum format, boolean update) +{ + INT32 w = pTexInfo->width; + INT32 h = pTexInfo->height; + +#ifndef HAVE_GLES + if (GLTexture_CanGenerateMipmaps(pTexInfo) && !pglGenerateMipmap) + pglTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); +#endif + + if (update) + pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pTextureBuffer); + else + pglTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTextureBuffer); +} + +// Writes a palettized texture. +void GLTexture_WritePalette(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer) +{ + const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; + INT32 w = pTexInfo->width; + INT32 h = pTexInfo->height; + INT32 i, j; + + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + if ((*pImgData == GPU_PATCHES_CHROMAKEY_COLORINDEX) && (pTexInfo->flags & TF_CHROMAKEYED)) + { + textureBuffer[w*j+i].s.red = 0; + textureBuffer[w*j+i].s.green = 0; + textureBuffer[w*j+i].s.blue = 0; + textureBuffer[w*j+i].s.alpha = 0; + pTexInfo->flags |= TF_TRANSPARENT; // there is a hole in it + } + else + { + textureBuffer[w*j+i].s.red = GPUTexturePalette[*pImgData].s.red; + textureBuffer[w*j+i].s.green = GPUTexturePalette[*pImgData].s.green; + textureBuffer[w*j+i].s.blue = GPUTexturePalette[*pImgData].s.blue; + textureBuffer[w*j+i].s.alpha = GPUTexturePalette[*pImgData].s.alpha; + } + + pImgData++; + + if (pTexInfo->format == GPU_TEXFMT_AP_88) + { + if (!(pTexInfo->flags & TF_CHROMAKEYED)) + textureBuffer[w*j+i].s.alpha = *pImgData; + pImgData++; + } + } + } +} + +// Writes a texture where every pixel is a luminance/alpha pair. +void GLTexture_WriteLuminanceAlpha(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer) +{ + const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; + INT32 w = pTexInfo->width; + INT32 h = pTexInfo->height; + INT32 i, j; + + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + textureBuffer[w*j+i].s.red = *pImgData; + textureBuffer[w*j+i].s.green = *pImgData; + textureBuffer[w*j+i].s.blue = *pImgData; + pImgData++; + textureBuffer[w*j+i].s.alpha = *pImgData; + pImgData++; + } + } +} + +// Writes a fade mask texture where every pixel is stored in the alpha channel. +void GLTexture_WriteFadeMaskA(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer) +{ + const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; + INT32 w = pTexInfo->width; + INT32 h = pTexInfo->height; + INT32 i, j; + + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + textureBuffer[w*j+i].s.red = 255; // 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't + textureBuffer[w*j+i].s.green = 255; + textureBuffer[w*j+i].s.blue = 255; + textureBuffer[w*j+i].s.alpha = *pImgData; + pImgData++; + } + } +} + +// Writes a fade mask texture where every pixel is stored in the red channel. +void GLTexture_WriteFadeMaskR(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer) +{ + const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; + INT32 w = pTexInfo->width; + INT32 h = pTexInfo->height; + INT32 i, j; + + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + textureBuffer[w*j+i].s.red = *pImgData; + textureBuffer[w*j+i].s.green = 255; + textureBuffer[w*j+i].s.blue = 255; + textureBuffer[w*j+i].s.alpha = 255; + pImgData++; + } + } +} + +// Deletes all textures. +void GLTexture_Flush(void) +{ + while (TexCacheHead) + { + FTextureInfo *pTexInfo = TexCacheHead; + HWRTexture_t *texture = pTexInfo->texture; + + if (pTexInfo->name) + { + pglDeleteTextures(1, (GLuint *)&pTexInfo->name); + pTexInfo->name = 0; + } + + if (texture) + texture->downloaded = 0; + + TexCacheHead = pTexInfo->next; + free(pTexInfo); + } + + TexCacheTail = TexCacheHead = NULL; // Hurdler: well, TexCacheHead is already NULL + CurrentTexture = 0; +} + +// Binds the current texture to a 1x1 white pixel. +void GLTexture_SetNoTexture(void) +{ + // Disable texture. + if (CurrentTexture != BlankTexture) + { + if (BlankTexture == 0) + { + // Generate a 1x1 white pixel for the blank texture + UINT8 white[4] = {255, 255, 255, 255}; + pglGenTextures(1, &BlankTexture); + pglBindTexture(GL_TEXTURE_2D, BlankTexture); + pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, white); + } + else + pglBindTexture(GL_TEXTURE_2D, BlankTexture); + + CurrentTexture = BlankTexture; + } } // Generates a screen texture. @@ -951,8 +1296,8 @@ void GLTexture_GenerateScreenTexture(GLuint *name) { pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - SetClamp(GL_TEXTURE_WRAP_S); - SetClamp(GL_TEXTURE_WRAP_T); + GLState_SetClamp(GL_TEXTURE_WRAP_S); + GLState_SetClamp(GL_TEXTURE_WRAP_T); pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0); } else @@ -961,6 +1306,58 @@ void GLTexture_GenerateScreenTexture(GLuint *name) CurrentTexture = *name; } +// Sryder: This needs to be called whenever the screen changes resolution in order to reset the screen textures to use +// a new size +void GLTexture_FlushScreenTextures(void) +{ + pglDeleteTextures(1, &ScreenTexture); + pglDeleteTextures(1, &FinalScreenTexture); + pglDeleteTextures(1, &WipeStartTexture); + pglDeleteTextures(1, &WipeEndTexture); + + ScreenTexture = FinalScreenTexture = 0; + WipeStartTexture = WipeEndTexture = 0; +} + +// Initializes mipmapping. +boolean GLTexture_InitMipmapping(void) +{ + if (GLExtension_framebuffer_object) + pglGenerateMipmap = GLBackend_GetFunction("glGenerateMipmap"); + + return (pglGenerateMipmap != NULL); +} + +// Returns true if the texture can have mipmaps generated for it. +boolean GLTexture_CanGenerateMipmaps(HWRTexture_t *pTexInfo) +{ + return (MipmappingEnabled && !(pTexInfo->flags & TF_TRANSPARENT)); +} + +// Calculates total memory usage by textures, excluding mipmaps. +int GLTexture_GetMemoryUsage(FTextureInfo *head) +{ + int res = 0; + + while (head) + { + // Figure out the correct bytes-per-pixel for this texture + // This follows format2bpp in hw_cache.c + INT32 bpp = 1; + UINT32 format = head->format; + if (format == GPU_TEXFMT_RGBA) + bpp = 4; + else if (format == GPU_TEXFMT_ALPHA_INTENSITY_88 || format == GPU_TEXFMT_AP_88) + bpp = 2; + + // Add it up! + res += head->height*head->width*bpp; + head = head->next; + } + + return res; +} + // -----------------+ // GL_DBG_Printf : Output debug messages to debug log if DEBUG_TO_FILE is defined, // : else do nothing diff --git a/src/hardware/r_glcommon/r_glcommon.h b/src/hardware/r_glcommon/r_glcommon.h index 3900b79055ef188ecaa4ad58546a9aa35108371a..96856b96b5282c8df2b5b3d107858c4955c93cfa 100644 --- a/src/hardware/r_glcommon/r_glcommon.h +++ b/src/hardware/r_glcommon/r_glcommon.h @@ -1,60 +1,53 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 1998-2020 by Sonic Team Junior. +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file r_glcommon.h -/// \brief Common OpenGL functions and structs shared by OpenGL backends +/// \brief Common OpenGL functions shared by all of the OpenGL backends #ifndef _R_GLCOMMON_H_ #define _R_GLCOMMON_H_ #define GL_GLEXT_PROTOTYPES -#if defined(HAVE_GLES2) - #include <GLES2/gl2.h> - #include <GLES2/gl2ext.h> -#elif defined(HAVE_GLES) - #include <GLES/gl.h> - #include <GLES/glext.h> -#else - #ifdef HAVE_SDL - #define _MATH_DEFINES_DEFINED +#ifdef HAVE_SDL + #define _MATH_DEFINES_DEFINED - #ifdef _MSC_VER - #pragma warning(disable : 4214 4244) - #endif + #ifdef _MSC_VER + #pragma warning(disable : 4214 4244) + #endif - #include "SDL_opengl.h" //Alam_GBC: Simple, yes? + #include "SDL.h" // For GLExtension_Available + #include "SDL_opengl.h" // Alam_GBC: Simple, yes? - #ifdef _MSC_VER - #pragma warning(default : 4214 4244) - #endif - #else - #include <GL/gl.h> - #include <GL/glu.h> - #if defined(STATIC_OPENGL) - #include <GL/glext.h> - #endif + #ifdef _MSC_VER + #pragma warning(default : 4214 4244) + #endif +#else + #include <GL/gl.h> + #include <GL/glu.h> + #if defined(STATIC_OPENGL) + #include <GL/glext.h> #endif #endif -// For GLExtension_Available -#ifdef HAVE_SDL -#define _MATH_DEFINES_DEFINED -#include "SDL.h" +#ifdef HAVE_GLES +#include "../r_gles/r_gleslib.h" #endif #include "../../doomdata.h" #include "../../doomtype.h" #include "../../doomdef.h" -#include "../../hardware/hw_data.h" // HWRTexture_s +#include "../../hardware/hw_gpu.h" // GPURenderingAPI +#include "../../hardware/hw_data.h" // HWRTexture_t #include "../../hardware/hw_defs.h" // FTextureInfo #include "../../hardware/hw_model.h" // model_t / mesh_t / mdlframe_t + #include "../r_opengl/r_vbo.h" void GL_DBG_Printf(const char *format, ...); @@ -216,37 +209,27 @@ extern PFNglActiveTexture pglActiveTexture; // Mipmapping // -#ifdef HAVE_GLES -/* Texture mapping */ typedef void (R_GL_APIENTRY * PFNglGenerateMipmap) (GLenum target); extern PFNglGenerateMipmap pglGenerateMipmap; -#endif // // Depth functions // -#ifndef HAVE_GLES - #ifdef STATIC_OPENGL - #define pglClearDepth glClearDepth - #define pglDepthFunc glDepthFunc - #else - typedef void (R_GL_APIENTRY * PFNglClearDepth) (GLclampd depth); - extern PFNglClearDepth pglClearDepth; - typedef void (R_GL_APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val); - extern PFNglDepthRange pglDepthRange; - #endif +#ifdef STATIC_OPENGL + #define pglClearDepth glClearDepth + #define pglDepthFunc glDepthFunc #else - #ifdef STATIC_OPENGL - #define pglClearDepthf glClearDepthf - #define pglDepthFuncf glDepthFuncf - #else - typedef void (R_GL_APIENTRY * PFNglClearDepthf) (GLclampf depth); - extern PFNglClearDepthf pglClearDepthf; - typedef void (R_GL_APIENTRY * PFNglDepthRangef) (GLclampf near_val, GLclampf far_val); - extern PFNglDepthRangef pglDepthRangef; - #endif + typedef void (R_GL_APIENTRY * PFNglClearDepth) (GLclampd depth); + extern PFNglClearDepth pglClearDepth; + typedef void (R_GL_APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val); + extern PFNglDepthRange pglDepthRange; #endif +typedef void (R_GL_APIENTRY * PFNglClearDepthf) (GLclampf depth); +extern PFNglClearDepthf pglClearDepthf; +typedef void (R_GL_APIENTRY * PFNglDepthRangef) (GLclampf near_val, GLclampf far_val); +extern PFNglDepthRangef pglDepthRangef; + // // Legacy functions // @@ -331,20 +314,18 @@ extern PFNglTexEnvi pglTexEnvi; #endif // HAVE_GLES2 // Color -#ifdef HAVE_GLES - #ifdef STATIC_OPENGL - #define pglColor4f glColor4f - #else - typedef void (*PFNglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); - extern PFNglColor4f pglColor4f; - #endif +#ifdef STATIC_OPENGL + #define pglColor4f glColor4f #else - #ifdef STATIC_OPENGL - #define pglColor4ubv glColor4ubv - #else - typedef void (R_GL_APIENTRY * PFNglColor4ubv) (const GLubyte *v); - extern PFNglColor4ubv pglColor4ubv; - #endif + typedef void (*PFNglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + extern PFNglColor4f pglColor4f; +#endif + +#ifdef STATIC_OPENGL + #define pglColor4ubv glColor4ubv +#else + typedef void (R_GL_APIENTRY * PFNglColor4ubv) (const GLubyte *v); + extern PFNglColor4ubv pglColor4ubv; #endif /* 1.5 functions for buffers */ @@ -365,36 +346,77 @@ extern PFNglBlendEquation pglBlendEquation; // FUNCTIONS // ========================================================================== -void GLModel_GenerateVBOs(model_t *model); -void GLModel_AllocLerpBuffer(size_t size); -void GLModel_AllocLerpTinyBuffer(size_t size); - -void GLTexture_Flush(void); -void GLTexture_SetFilterMode(INT32 mode); -void GLTexture_Delete(HWRTexture_t *pTexInfo); -INT32 GLTexture_GetMemoryUsage(FTextureInfo *head); -void GLTexture_GenerateScreenTexture(GLuint *name); - boolean GLBackend_Init(void); boolean GLBackend_InitContext(void); +boolean GLBackend_InitShaders(void); boolean GLBackend_LoadLibrary(void); boolean GLBackend_LoadFunctions(void); boolean GLBackend_LoadContextFunctions(void); boolean GLBackend_LoadCommonFunctions(void); boolean GLBackend_LoadLegacyFunctions(void); void *GLBackend_GetFunction(const char *proc); - -INT32 GLBackend_GetShaderType(INT32 type); +INT32 GLBackend_GetShaderType(INT32 type); + +#define GLBackend_GetFunctionPointer(func) \ + p ## gl ## func = GLBackend_GetFunction("gl" #func); \ + if (!(p ## gl ## func)) \ + { \ + GL_MSG_Error("Failed to get OpenGL function %s", #func); \ + return false; \ + } + +#define GLBackend_GetFunctionPointerOrFallback(func1, func2) \ + p ## gl ## func1 = GLBackend_GetFunction("gl" #func1); \ + if (!(p ## gl ## func1)) \ + { \ + GL_DBG_Printf("Failed to get OpenGL function %s, trying %s instead\n", #func1, #func2); \ + p ## gl ## func2 = GLBackend_GetFunction("gl" #func2); \ + if (!(p ## gl ## func2)) \ + { \ + GL_MSG_Error("Failed to get OpenGL function %s\n", #func2); \ + return false; \ + } \ + } + +#define GETOPENGLFUNC GLBackend_GetFunctionPointer +#define GETOPENGLFUNCALT GLBackend_GetFunctionPointerOrFallback + +void GLState_SetSurface(INT32 w, INT32 h); +void GLState_SetPalette(RGBA_t *palette); +void GLState_SetFilterMode(INT32 mode); +void GLState_SetClamp(GLenum pname); +void GLState_SetDepthBuffer(void); +void GLState_SetBlend(UINT32 PolyFlags); +void GLState_SetColor(const GLfloat red, const GLfloat green, const GLfloat blue, const GLfloat alpha); +void GLState_SetColorUBV(const GLubyte *v); void GLExtension_Init(void); boolean GLExtension_Available(const char *extension); -void SetSurface(INT32 w, INT32 h); -void SetModelView(GLint w, GLint h); -void SetStates(void); -void SetBlendingStates(UINT32 PolyFlags); -void SetNoTexture(void); -void SetClamp(GLenum pname); +void GLTexture_Set(HWRTexture_t *pTexInfo); +void GLTexture_Delete(HWRTexture_t *pTexInfo); + +void GLTexture_Update(HWRTexture_t *pTexInfo); +void GLTexture_UploadData(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer, GLenum format, boolean update); + +void GLTexture_WritePalette(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer); +void GLTexture_WriteLuminanceAlpha(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer); +void GLTexture_WriteFadeMaskA(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer); +void GLTexture_WriteFadeMaskR(HWRTexture_t *pTexInfo, RGBA_t *textureBuffer); + +void GLTexture_Flush(void); +void GLTexture_FlushScreenTextures(void); +void GLTexture_SetNoTexture(void); + +boolean GLTexture_InitMipmapping(void); +boolean GLTexture_CanGenerateMipmaps(HWRTexture_t *pTexInfo); + +void GLTexture_GenerateScreenTexture(GLuint *name); +int GLTexture_GetMemoryUsage(FTextureInfo *head); + +void GLModel_GenerateVBOs(model_t *model); +void GLModel_AllocLerpBuffer(size_t size); +void GLModel_AllocLerpTinyBuffer(size_t size); // ========================================================================== // CONSTANTS @@ -524,6 +546,7 @@ extern GLuint WipeEndTexture; extern UINT32 CurrentPolyFlags; extern GLboolean MipmappingEnabled; +extern GLboolean MipmappingSupported; extern GLboolean ModelLightingEnabled; extern GLint MipmapMinFilter; diff --git a/src/hardware/r_gles/lzml.h b/src/hardware/r_gles/lzml.h new file mode 100644 index 0000000000000000000000000000000000000000..ab2f11fa6e41e8ed9569eca4a49456018c35566a --- /dev/null +++ b/src/hardware/r_gles/lzml.h @@ -0,0 +1,885 @@ +/** + MIT License + + Copyright (c) 2020 Jaime Passos + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**/ + +#ifndef _LZML_H_ +#define _LZML_H_ + +#include <stdint.h> +#include <stddef.h> +#include <math.h> +#include <float.h> + +void lzml_vector3_add(float a[3], float b[3]); +void lzml_vector3_add_by_scalar(float vec[3], float scalar); +void lzml_vector3_subtract(float a[3], float b[3]); +void lzml_vector3_subtract_by_scalar(float vec[3], float scalar); +void lzml_vector3_multiply(float a[3], float b[3]); +void lzml_vector3_multiply_by_scalar(float vec[3], float scalar); +#define lzml_vector3_scale lzml_vector3_multiply_by_scalar +void lzml_vector3_divide(float a[3], float b[3]); +void lzml_vector3_divide_by_scalar(float vec[3], float scalar); +void lzml_vector3_add_into(float dest[3], float a[3], float b[3]); +void lzml_vector3_add_by_scalar_into(float dest[3], float vec[3], float scalar); +void lzml_vector3_subtract_into(float dest[3], float a[3], float b[3]); +void lzml_vector3_subtract_by_scalar_into(float dest[3], float vec[3], float scalar); +void lzml_vector3_multiply_into(float dest[3], float a[3], float b[3]); +void lzml_vector3_multiply_by_scalar_into(float dest[3], float vec[3], float scalar); +#define lzml_vector3_scale_into lzml_vector3_multiply_by_scalar_into +void lzml_vector3_divide_into(float dest[3], float a[3], float b[3]); +void lzml_vector3_divide_by_scalar_into(float dest[3], float vec[3], float scalar); +float lzml_vector3_dot_product(float a[3], float b[3]); +float lzml_vector3_magnitude(float vec[3]); +void lzml_vector3_normalize(float vec[3]); +void lzml_vector3_normalize_into(float dest[3], float vec[3]); +void lzml_vector3_copy(float dest[3], float src[3]); + +void lzml_vector4_add(float a[4], float b[4]); +void lzml_vector4_add_by_scalar(float vec[4], float scalar); +void lzml_vector4_subtract(float a[4], float b[4]); +void lzml_vector4_subtract_by_scalar(float vec[4], float scalar); +void lzml_vector4_multiply(float a[4], float b[4]); +void lzml_vector4_multiply_by_scalar(float vec[4], float scalar); +#define lzml_vector4_scale lzml_vector4_multiply_by_scalar +void lzml_vector4_divide(float a[4], float b[4]); +void lzml_vector4_divide_by_scalar(float vec[4], float scalar); +void lzml_vector4_add_into(float dest[4], float a[4], float b[4]); +void lzml_vector4_add_by_scalar_into(float dest[4], float vec[4], float scalar); +void lzml_vector4_subtract_into(float dest[4], float a[4], float b[4]); +void lzml_vector4_subtract_by_scalar_into(float dest[4], float vec[4], float scalar); +void lzml_vector4_multiply_into(float dest[4], float a[4], float b[4]); +void lzml_vector4_multiply_by_scalar_into(float dest[4], float vec[4], float scalar); +#define lzml_vector4_scale_into lzml_vector4_multiply_by_scalar_into +void lzml_vector4_divide_into(float dest[4], float a[4], float b[4]); +void lzml_vector4_divide_by_scalar_into(float dest[4], float vec[4], float scalar); +float lzml_vector4_dot_product(float a[4], float b[4]); +float lzml_vector4_magnitude(float vec[4]); +void lzml_vector4_normalize(float vec[4]); +void lzml_vector4_normalize_into(float dest[4], float vec[4]); +void lzml_vector4_copy(float dest[4], float src[4]); + +static float lzml_zeromatrix[4][4] = { + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}}; + +static float lzml_identitymatrix[4][4] = { + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}; + +void lzml_matrix4_copy(float dest[4][4], float src[4][4]); +void lzml_matrix4_clear(float mat[4][4]); +void lzml_matrix4_identity(float mat[4][4]); +void lzml_matrix4_multiply(float a[4][4], float b[4][4]); +void lzml_matrix4_multiply_into(float dest[4][4], float a[4][4], float b[4][4]); +void lzml_matrix4_translate(float mat[4][4], float vec[3]); +void lzml_matrix4_translate_x(float mat[4][4], float x); +void lzml_matrix4_translate_y(float mat[4][4], float y); +void lzml_matrix4_translate_z(float mat[4][4], float z); +void lzml_matrix4_rotate_x(float mat[4][4], float angle); +void lzml_matrix4_rotate_y(float mat[4][4], float angle); +void lzml_matrix4_rotate_z(float mat[4][4], float angle); +void lzml_matrix4_rotate_by_vector(float mat[4][4], float vec[3], float angle); +void lzml_matrix4_scale(float mat[4][4], float vec[3]); +void lzml_matrix4_rotation(float rot[4][4], float vec[3], float angle); +void lzml_matrix4_perspective(float mat[4][4], float fovy, float aspect_ratio, float near_clip, float far_clip); + +/** + == VECTOR3 OPERATIONS == +**/ + +/** Adds a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_add(float a[3], float b[3]) +{ + lzml_vector3_add_into(a, a, b); +} + +/** Adds a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector3_add_by_scalar(float vec[3], float scalar) +{ + lzml_vector3_add_by_scalar_into(vec, vec, scalar); +} + +/** Subtracts a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_subtract(float a[3], float b[3]) +{ + lzml_vector3_subtract_into(a, a, b); +} + +/** Subtracts a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector3_subtract_by_scalar(float vec[3], float scalar) +{ + lzml_vector3_subtract_by_scalar_into(vec, vec, scalar); +} + +/** Multiplies a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_multiply(float a[3], float b[3]) +{ + lzml_vector3_multiply_into(a, a, b); +} + +/** Multiplies a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector3_multiply_by_scalar(float vec[3], float scalar) +{ + lzml_vector3_multiply_by_scalar_into(vec, vec, scalar); +} + +/** Divides a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_divide(float a[3], float b[3]) +{ + lzml_vector3_divide_into(a, a, b); +} + +/** Divides a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector3_divide_by_scalar(float vec[3], float scalar) +{ + lzml_vector3_divide_by_scalar_into(vec, vec, scalar); +} + +/** Adds a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_add_into(float dest[3], float a[3], float b[3]) +{ + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; +} + +/** Adds a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector3_add_by_scalar_into(float dest[3], float vec[3], float scalar) +{ + dest[0] = vec[0] + scalar; + dest[1] = vec[1] + scalar; + dest[2] = vec[2] + scalar; +} + +/** Subtracts a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_subtract_into(float dest[3], float a[3], float b[3]) +{ + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; +} + +/** Subtracts a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector3_subtract_by_scalar_into(float dest[3], float vec[3], float scalar) +{ + dest[0] = vec[0] - scalar; + dest[1] = vec[1] - scalar; + dest[2] = vec[2] - scalar; +} + +/** Multiplies a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_multiply_into(float dest[3], float a[3], float b[3]) +{ + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; +} + +/** Multiplies a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector3_multiply_by_scalar_into(float dest[3], float vec[3], float scalar) +{ + dest[0] = vec[0] * scalar; + dest[1] = vec[1] * scalar; + dest[2] = vec[2] * scalar; +} + +/** Divides a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector3_divide_into(float dest[3], float a[3], float b[3]) +{ + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; + dest[2] = a[2] / b[2]; +} + +/** Divides a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector3_divide_by_scalar_into(float dest[3], float vec[3], float scalar) +{ + lzml_vector3_multiply_by_scalar_into(dest, vec, (1.0f / scalar)); +} + +/** Calculates a dot product. + * + * \param a The first vector. + * \param b The second vector. + */ +float lzml_vector3_dot_product(float a[3], float b[3]) +{ + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); +} + +/** Calculates the magnitude of a vector. + * + * \param vec The vector. + */ +float lzml_vector3_magnitude(float vec[3]) +{ + return sqrtf(lzml_vector3_dot_product(vec, vec)); +} + +/** Normalizes a vector. + * + * \param vec The vector to be normalized. + */ +void lzml_vector3_normalize(float vec[3]) +{ + lzml_vector3_normalize_into(vec, vec); +} + +/** Normalizes a vector, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector to be normalized. + */ +void lzml_vector3_normalize_into(float dest[3], float vec[3]) +{ + float normal = lzml_vector3_magnitude(vec); + if (fpclassify(normal) == FP_ZERO) + dest[0] = dest[1] = dest[2] = 0.0f; + else + lzml_vector3_multiply_by_scalar_into(dest, vec, (1.0f / normal)); +} + +/** Copies a vector into another. + * + * \param dest The destination vector. + * \param src The source vector. + */ +void lzml_vector3_copy(float dest[3], float src[3]) +{ + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; +} + +/** + == VECTOR4 OPERATIONS == +**/ + +/** Adds a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_add(float a[4], float b[4]) +{ + lzml_vector4_add_into(a, a, b); +} + +/** Adds a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector4_add_by_scalar(float vec[4], float scalar) +{ + lzml_vector4_add_by_scalar_into(vec, vec, scalar); +} + +/** Subtracts a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_subtract(float a[4], float b[4]) +{ + lzml_vector4_subtract_into(a, a, b); +} + +/** Subtracts a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector4_subtract_by_scalar(float vec[4], float scalar) +{ + lzml_vector4_subtract_by_scalar_into(vec, vec, scalar); +} + +/** Multiplies a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_multiply(float a[4], float b[4]) +{ + lzml_vector4_multiply_into(a, a, b); +} + +/** Multiplies a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector4_multiply_by_scalar(float vec[4], float scalar) +{ + lzml_vector4_multiply_by_scalar_into(vec, vec, scalar); +} + +/** Divides a vector by another. + * + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_divide(float a[4], float b[4]) +{ + lzml_vector4_divide_into(a, a, b); +} + +/** Divides a vector by a scalar. + * + * \param vec The destination vector. + * \param scalar The scalar. + */ +void lzml_vector4_divide_by_scalar(float vec[4], float scalar) +{ + lzml_vector4_multiply_by_scalar_into(vec, vec, scalar); +} + +/** Adds a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_add_into(float dest[4], float a[4], float b[4]) +{ + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; + dest[3] = a[3] + b[3]; +} + +/** Adds a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector4_add_by_scalar_into(float dest[4], float vec[4], float scalar) +{ + dest[0] = vec[0] + scalar; + dest[1] = vec[1] + scalar; + dest[2] = vec[2] + scalar; + dest[3] = vec[3] + scalar; +} + +/** Subtracts a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_subtract_into(float dest[4], float a[4], float b[4]) +{ + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; + dest[3] = a[3] - b[3]; +} + +/** Subtracts a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector4_subtract_by_scalar_into(float dest[4], float vec[4], float scalar) +{ + dest[0] = vec[0] - scalar; + dest[1] = vec[1] - scalar; + dest[2] = vec[2] - scalar; + dest[3] = vec[3] - scalar; +} + +/** Multiplies a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_multiply_into(float dest[4], float a[4], float b[4]) +{ + dest[0] = a[0] * b[0]; + dest[1] = a[1] * b[1]; + dest[2] = a[2] * b[2]; + dest[3] = a[3] * b[3]; +} + +/** Multiplies a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector4_multiply_by_scalar_into(float dest[4], float vec[4], float scalar) +{ + dest[0] = vec[0] * scalar; + dest[1] = vec[1] * scalar; + dest[2] = vec[2] * scalar; + dest[3] = vec[3] * scalar; +} + +/** Divides a vector by another, and stores the result in dest. + * + * \param dest The destination vector. + * \param a The first vector. + * \param b The second vector. + */ +void lzml_vector4_divide_into(float dest[4], float a[4], float b[4]) +{ + dest[0] = a[0] / b[0]; + dest[1] = a[1] / b[1]; + dest[2] = a[2] / b[2]; + dest[3] = a[3] / b[3]; +} + +/** Divides a vector by a scalar, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector. + * \param scalar The scalar. + */ +void lzml_vector4_divide_by_scalar_into(float dest[4], float vec[4], float scalar) +{ + lzml_vector4_multiply_by_scalar_into(dest, vec, (1.0f / scalar)); +} + +/** Calculates a dot product. + * + * \param a The first vector. + * \param b The second vector. + */ +float lzml_vector4_dot_product(float a[4], float b[4]) +{ + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]) + (a[3] * b[3]); +} + +/** Calculates the magnitude of a vector. + * + * \param vec The vector. + */ +float lzml_vector4_magnitude(float vec[4]) +{ + return sqrtf(lzml_vector4_dot_product(vec, vec)); +} + +/** Normalizes a vector. + * + * \param vec The vector to be normalized. + */ +void lzml_vector4_normalize(float vec[4]) +{ + lzml_vector4_normalize_into(vec, vec); +} + +/** Normalizes a vector, and stores the result in dest. + * + * \param dest The destination vector. + * \param vec The vector to be normalized. + */ +void lzml_vector4_normalize_into(float dest[4], float vec[4]) +{ + float normal = lzml_vector4_magnitude(vec); + if (fpclassify(normal) == FP_ZERO) + dest[0] = dest[1] = dest[2] = dest[3] = 0.0f; + else + lzml_vector4_multiply_by_scalar_into(dest, vec, (1.0f / normal)); +} + +/** Copies a vector into another. + * + * \param dest The destination vector. + * \param src The source vector. + */ +void lzml_vector4_copy(float dest[4], float src[4]) +{ + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +} + +/** + == MATRIX OPERATIONS == +**/ + +/** Copies a matrix into another. + * + * \param dest The destination matrix. + * \param src The source matrix. + */ +void lzml_matrix4_copy(float dest[4][4], float src[4][4]) +{ + dest[0][0] = src[0][0]; + dest[0][1] = src[0][1]; + dest[0][2] = src[0][2]; + dest[0][3] = src[0][3]; + dest[1][0] = src[1][0]; + dest[1][1] = src[1][1]; + dest[1][2] = src[1][2]; + dest[1][3] = src[1][3]; + dest[2][0] = src[2][0]; + dest[2][1] = src[2][1]; + dest[2][2] = src[2][2]; + dest[2][3] = src[2][3]; + dest[3][0] = src[3][0]; + dest[3][1] = src[3][1]; + dest[3][2] = src[3][2]; + dest[3][3] = src[3][3]; +} + +/** Clears a matrix. + * + * \param mat The matrix to be cleared. + */ +void lzml_matrix4_clear(float mat[4][4]) +{ + lzml_matrix4_copy(mat, lzml_zeromatrix); +} + +/** Makes an identity matrix. + * + * \param mat The destination matrix. + */ +void lzml_matrix4_identity(float mat[4][4]) +{ + lzml_matrix4_copy(mat, lzml_identitymatrix); +} + +/** Multiplies a matrix by another, and stores the result into the first matrix. + * + * \param a The first matrix. + * \param b The second matrix. + */ +void lzml_matrix4_multiply(float a[4][4], float b[4][4]) +{ + float mul[4][4]; + lzml_matrix4_multiply_into(mul, a, b); + lzml_matrix4_copy(a, mul); +} + +/** Multiplies a matrix by another, and stores the result into dest. + * + * \param dest The destination matrix. + * \param a The first matrix. + * \param b The second matrix. + */ +void lzml_matrix4_multiply_into(float dest[4][4], float a[4][4], float b[4][4]) +{ +#define MULTMAT(i, j) dest[i][j] = (a[0][j] * b[i][0]) + (a[1][j] * b[i][1]) + (a[2][j] * b[i][2]) + (a[3][j] * b[i][3]); + MULTMAT(0, 0) + MULTMAT(0, 1) + MULTMAT(0, 2) + MULTMAT(0, 3) + MULTMAT(1, 0) + MULTMAT(1, 1) + MULTMAT(1, 2) + MULTMAT(1, 3) + MULTMAT(2, 0) + MULTMAT(2, 1) + MULTMAT(2, 2) + MULTMAT(2, 3) + MULTMAT(3, 0) + MULTMAT(3, 1) + MULTMAT(3, 2) + MULTMAT(3, 3) +#undef MULTMAT +} + +/** Translates a matrix in the X axis. + * + * \param mat The matrix. + * \param x The X translation. + */ +void lzml_matrix4_translate_x(float mat[4][4], float x) +{ + float vec[4]; + lzml_vector4_multiply_by_scalar_into(vec, mat[0], x); + lzml_vector4_add(mat[3], vec); +} + +/** Translates a matrix in the Y axis. + * + * \param mat The matrix. + * \param y The Y translation. + */ +void lzml_matrix4_translate_y(float mat[4][4], float y) +{ + float vec[4]; + lzml_vector4_multiply_by_scalar_into(vec, mat[1], y); + lzml_vector4_add(mat[3], vec); +} + +/** Translates a matrix in the Z axis. + * + * \param mat The matrix. + * \param z The Y translation. + */ +void lzml_matrix4_translate_z(float mat[4][4], float z) +{ + float vec[4]; + lzml_vector4_multiply_by_scalar_into(vec, mat[2], z); + lzml_vector4_add(mat[3], vec); +} + +/** Translates a matrix by a vector. + * + * \param mat The matrix. + * \param vec The translation vector. + */ +void lzml_matrix4_translate(float mat[4][4], float vec[3]) +{ + lzml_matrix4_translate_x(mat, vec[0]); + lzml_matrix4_translate_y(mat, vec[1]); + lzml_matrix4_translate_z(mat, vec[2]); +} + +/** Rotates a matrix in the X axis. + * + * \param mat The matrix. + * \param angle The rotation angle. + */ +void lzml_matrix4_rotate_x(float mat[4][4], float angle) +{ + float sine = sinf(angle); + float cosine = cosf(angle); + + float rot[4][4]; + lzml_matrix4_identity(rot); + + rot[1][1] = cosine; + rot[1][2] = sine; + rot[2][1] = -sine; + rot[2][2] = cosine; + + lzml_matrix4_multiply(mat, rot); +} + +/** Rotates a matrix in the Y axis. + * + * \param mat The matrix. + * \param angle The rotation angle. + */ +void lzml_matrix4_rotate_y(float mat[4][4], float angle) +{ + float sine = sinf(angle); + float cosine = cosf(angle); + + float rot[4][4]; + lzml_matrix4_identity(rot); + + rot[0][0] = cosine; + rot[0][2] = -sine; + rot[2][0] = sine; + rot[2][2] = cosine; + + lzml_matrix4_multiply(mat, rot); +} + +/** Rotates a matrix in the Z axis. + * + * \param mat The matrix. + * \param angle The rotation angle. + */ +void lzml_matrix4_rotate_z(float mat[4][4], float angle) +{ + float sine = sinf(angle); + float cosine = cosf(angle); + + float rot[4][4]; + lzml_matrix4_identity(rot); + + rot[0][0] = cosine; + rot[0][1] = sine; + rot[1][0] = -sine; + rot[1][1] = cosine; + + lzml_matrix4_multiply(mat, rot); +} + +/** Rotates a matrix by an axis. + * + * \param mat The matrix. + * \param vec The rotation vector. + * \param angle The rotation angle. + */ +void lzml_matrix4_rotate_by_vector(float mat[4][4], float vec[3], float angle) +{ + float rot[4][4]; + lzml_matrix4_rotation(rot, vec, angle); + lzml_matrix4_multiply(mat, rot); +} + +/** Scales a matrix by a vector. + * + * \param mat The matrix. + * \param vec The scaling vector. + */ +void lzml_matrix4_scale(float mat[4][4], float vec[3]) +{ + lzml_vector4_multiply_by_scalar(mat[0], vec[0]); + lzml_vector4_multiply_by_scalar(mat[1], vec[1]); + lzml_vector4_multiply_by_scalar(mat[2], vec[2]); +} + +/** Creates a rotation matrix. + * + * \param mat The destination rotation matrix. + * \param vec The rotation axis. + * \param angle The rotation angle. + */ +void lzml_matrix4_rotation(float rot[4][4], float vec[3], float angle) +{ + float sine = sinf(angle); + float cosine = cosf(angle); + float normalized[3]; + float x, y, z; + + lzml_vector3_normalize_into(normalized, vec); + x = normalized[0]; + y = normalized[1]; + z = normalized[2]; + + rot[0][0] = cosine + x * x * (1.0f - cosine); + rot[0][1] = x * y * (1.0f - cosine) - z * sine; + rot[0][2] = x * z * (1.0f - cosine) + y * sine; + rot[0][3] = 0.0f; + + rot[1][0] = y * x * (1.0f - cosine) + z * sine; + rot[1][1] = cosine + y * y * (1.0f - cosine); + rot[1][2] = y * z * (1.0f - cosine) - x * sine; + rot[1][3] = 0.0f; + + rot[2][0] = z * x * (1.0f - cosine) - y * sine; + rot[2][1] = z * y * (1.0f - cosine) + x * sine; + rot[2][2] = cosine + z * z * (1.0f - cosine); + rot[2][3] = 0.0f; + + rot[3][0] = 0.0f; + rot[3][1] = 0.0f; + rot[3][2] = 0.0f; + rot[3][3] = 1.0f; +} + +/** Makes a perspective matrix. + * + * \param mat The destination matrix. + * \param fovy The field of view value, in radians. + * \param aspect_ratio The aspect ratio. + * \param near The near clipping plane value. + * \param far The far clipping plane value. + */ +void lzml_matrix4_perspective(float mat[4][4], float fovy, float aspect_ratio, float near_clip, float far_clip) +{ + float tangent = 1.0f / tanf(fovy * 0.5f); + float delta = (far_clip - near_clip); + + if (fpclassify(aspect_ratio) == FP_ZERO) + return; + + lzml_matrix4_copy(mat, lzml_zeromatrix); + + mat[0][0] = tangent / aspect_ratio; + mat[1][1] = tangent; + mat[2][2] = -(far_clip / delta); + mat[2][3] = -1.0f; + mat[3][2] = -((far_clip * near_clip) / delta); +} + +#endif // _LZML_H_ diff --git a/src/hardware/r_gles/r_gles.h b/src/hardware/r_gles/r_gles.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5041134166b5d418b9e7627f25ab12ccc5262f --- /dev/null +++ b/src/hardware/r_gles/r_gles.h @@ -0,0 +1,33 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1998-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_gles.h +/// \brief OpenGL ES API for Sonic Robo Blast 2 + +#ifndef _R_GLES_H_ +#define _R_GLES_H_ + +#define GL_GLEXT_PROTOTYPES + +#include "r_gleslib.h" + +#define _CREATE_DLL_ +#undef DEBUG_TO_FILE + +#include "../../doomdef.h" +#include "../hw_gpu.h" + +// ========================================================================== +// DEFINITIONS +// ========================================================================== + +#include "../r_glcommon/r_glcommon.h" + +#endif diff --git a/src/hardware/r_gles/r_gles1.c b/src/hardware/r_gles/r_gles1.c new file mode 100644 index 0000000000000000000000000000000000000000..96ff6665b467b70854cd19897427189e0045091d --- /dev/null +++ b/src/hardware/r_gles/r_gles1.c @@ -0,0 +1,1297 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1998-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_gles1.c +/// \brief OpenGL ES 1.1 API for Sonic Robo Blast 2 + +#include <stdarg.h> +#include <math.h> + +#include "r_gles.h" +#include "../r_opengl/r_vbo.h" + +#if defined (HWRENDER) && !defined (NOROPENGL) + +static const GLfloat white[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + +boolean GLBackend_LoadFunctions(void) +{ + if (!GLBackend_LoadCommonFunctions()) + return false; + + GETOPENGLFUNCALT(ClearDepthf, ClearDepth) + GETOPENGLFUNCALT(DepthRangef, DepthRange) + + GETOPENGLFUNC(Color4f) +#if !defined(__ANDROID__) + GETOPENGLFUNC(Color4ubv) +#endif + + GETOPENGLFUNC(VertexPointer) + GETOPENGLFUNC(NormalPointer) + GETOPENGLFUNC(TexCoordPointer) + GETOPENGLFUNC(ColorPointer) + GETOPENGLFUNC(EnableClientState) + GETOPENGLFUNC(DisableClientState) + +#if defined(__ANDROID__) + GLBackend_LoadContextFunctions(); +#endif + + if (!GLBackend_LoadLegacyFunctions()) + return false; + + return true; +} + +boolean GLBackend_LoadContextFunctions(void) +{ + GETOPENGLFUNC(ActiveTexture) + GETOPENGLFUNC(ClientActiveTexture) + + GETOPENGLFUNC(GenBuffers) + GETOPENGLFUNC(BindBuffer) + GETOPENGLFUNC(BufferData) + GETOPENGLFUNC(DeleteBuffers) + + GETOPENGLFUNC(BlendEquation) + + if (GLTexture_InitMipmapping()) + MipmappingSupported = GL_TRUE; + + return true; +} + +static void GLPerspective(GLfloat fovy, GLfloat aspect) +{ + GLfloat m[4][4] = + { + { 1.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.0f, 0.0f, 0.0f}, + { 0.0f, 0.0f, 1.0f,-1.0f}, + { 0.0f, 0.0f, 0.0f, 0.0f}, + }; + const GLfloat zNear = NearClippingPlane; + const GLfloat zFar = FAR_CLIPPING_PLANE; + const GLfloat radians = (GLfloat)(fovy / 2.0f * M_PIl / 180.0f); + const GLfloat sine = sinf(radians); + const GLfloat deltaZ = zFar - zNear; + GLfloat cotangent; + + if ((fabsf((float)deltaZ) < 1.0E-36f) || fpclassify(sine) == FP_ZERO || fpclassify(aspect) == FP_ZERO) + { + return; + } + cotangent = cosf(radians) / sine; + + m[0][0] = cotangent / aspect; + m[1][1] = cotangent; + m[2][2] = -(zFar + zNear) / deltaZ; + m[3][2] = -2.0f * zNear * zFar / deltaZ; + + pglMultMatrixf(&m[0][0]); +} + +// ========================================================================== +// API +// ========================================================================== + +// -----------------+ +// Init : Initializes the OpenGL ES interface API +// -----------------+ +static boolean Init(void) +{ + return GLBackend_Init(); +} + +// -----------------+ +// SetInitialStates : Set permanent states +// -----------------+ +static void SetInitialStates(void) +{ +#ifdef GL_LIGHT_MODEL_AMBIENT + GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; +#endif + + pglEnableClientState(GL_VERTEX_ARRAY); // We always use this one + + pglEnable(GL_TEXTURE_2D); // two-dimensional texturing + pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + pglEnable(GL_BLEND); // enable color blending + pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + pglEnable(GL_ALPHA_TEST); + pglAlphaFunc(GL_NOTEQUAL, 0.0f); + + pglEnable(GL_DEPTH_TEST); // check the depth buffer + pglDepthMask(GL_TRUE); // enable writing to depth buffer + GPU->SetDepthBuffer(); + + // Hurdler: not necessary, is it? + pglShadeModel(GL_SMOOTH); // iterate vertex colors + + // this sets CurrentPolyFlags to the actual configuration + CurrentPolyFlags = 0xFFFFFFFF; + GPU->SetBlend(0); + + CurrentTexture = 0; + GLTexture_SetNoTexture(); + + pglPolygonOffset(-1.0f, -1.0f); + + pglLoadIdentity(); + pglScalef(1.0f, 1.0f, -1.0f); + + // Lighting for models +#ifdef GL_LIGHT_MODEL_AMBIENT + pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse); + pglEnable(GL_LIGHT0); +#endif +} + +// -----------------+ +// SetModelView : Resets the viewport state +// -----------------+ +static void SetModelView(INT32 w, INT32 h) +{ + // The screen textures need to be flushed if the width or height change so that they be remade for the correct size + if (GPUScreenWidth != w || GPUScreenHeight != h) + GPU->FlushScreenTextures(); + + GPUScreenWidth = (GLint)w; + GPUScreenHeight = (GLint)h; + + pglViewport(0, 0, w, h); + + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + + pglMatrixMode(GL_MODELVIEW); + pglLoadIdentity(); + + GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO); +} + +// -----------------+ +// SetBlend : Set blend mode +// -----------------+ +static void SetBlend(UINT32 PolyFlags) +{ + GLState_SetBlend(PolyFlags); +} + +// -----------------+ +// SetPalette : Changes the current texture palette +// -----------------+ +static void SetPalette(RGBA_t *palette) +{ + GLState_SetPalette(palette); +} + +// -----------------+ +// SetDepthBuffer : Set depth buffer state +// -----------------+ +static void SetDepthBuffer(void) +{ + GLState_SetDepthBuffer(); +} + +// -----------------+ +// ClearBuffer : Clear the color/alpha/depth buffer(s) +// -----------------+ +static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor) +{ + GLbitfield ClearMask = 0; + + if (ColorMask) + { + if (ClearColor) + pglClearColor(ClearColor->red, ClearColor->green, ClearColor->blue, ClearColor->alpha); + ClearMask |= GL_COLOR_BUFFER_BIT; + } + + if (DepthMask) + { + SetDepthBuffer(); + ClearMask |= GL_DEPTH_BUFFER_BIT; + } + + SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude); + + pglClear(ClearMask); + pglEnableClientState(GL_TEXTURE_COORD_ARRAY); // We mostly use this one +} + +// -----------------+ +// ReadRect : Read a rectangle region of the truecolor framebuffer +// : store pixels as RGBA8888 +// Returns : RGBA8888 pixel array stored in dst_data +// -----------------+ +static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 * dst_data) +{ + INT32 i; + GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1); + GLubyte *row = malloc(dst_stride); + if (!row) return; + pglPixelStorei(GL_PACK_ALIGNMENT, 1); + pglReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dst_data); + pglPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for(i = 0; i < height/2; i++) + { + memcpy(row, top, dst_stride); + memcpy(top, bottom, dst_stride); + memcpy(bottom, row, dst_stride); + top += dst_stride; + bottom -= dst_stride; + } + free(row); +} + +// -----------------+ +// GClipRect : Defines the 2D clipping window +// -----------------+ +static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip) +{ + pglViewport(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny); + NearClippingPlane = nearclip; + + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO); + pglMatrixMode(GL_MODELVIEW); +} + +// -----------------+ +// Draw2DLine : Render a 2D line +// -----------------+ +static void Draw2DLine(F2DCoord *v1, F2DCoord *v2, RGBA_t Color) +{ + GLfloat fcolor[4]; + GLfloat p[12]; + GLfloat dx, dy; + GLfloat angle; + + pglDisable(GL_TEXTURE_2D); + + // This is the preferred, 'modern' way of rendering lines -- creating a polygon. + if (fabsf(v2->x - v1->x) > FLT_EPSILON) + angle = (float)atan((v2->y-v1->y)/(v2->x-v1->x)); + else + angle = (float)N_PI_DEMI; + dx = (float)sin(angle) / (float)GPUScreenWidth; + dy = (float)cos(angle) / (float)GPUScreenHeight; + + p[0] = v1->x - dx; p[1] = -(v1->y + dy); p[2] = 1; + p[3] = v2->x - dx; p[4] = -(v2->y + dy); p[5] = 1; + p[6] = v2->x + dx; p[7] = -(v2->y - dy); p[8] = 1; + p[9] = v1->x + dx; p[10] = -(v1->y - dy); p[11] = 1; + + fcolor[0] = (Color.s.red/255.0f); + fcolor[1] = (Color.s.green/255.0f); + fcolor[2] = (Color.s.blue/255.0f); + fcolor[3] = (Color.s.alpha/255.0f); + + pglDisableClientState(GL_TEXTURE_COORD_ARRAY); + GLState_SetColor(fcolor[0], fcolor[1], fcolor[2], fcolor[3]); + pglVertexPointer(3, GL_FLOAT, 0, p); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + pglEnableClientState(GL_TEXTURE_COORD_ARRAY); + pglEnable(GL_TEXTURE_2D); +} + +// -----------------+ +// UpdateTexture : Updates the texture data. +// -----------------+ +static void UpdateTexture(HWRTexture_t *pTexInfo) +{ + GLTexture_Update(pTexInfo); +} + +// -----------------+ +// SetTexture : Uploads the texture, and sets it as the current one. +// -----------------+ +static void SetTexture(HWRTexture_t *pTexInfo) +{ + GLTexture_Set(pTexInfo); +} + +// -----------------+ +// DeleteTexture : Deletes a texture from the GPU, and frees its data. +// -----------------+ +static void DeleteTexture(HWRTexture_t *pTexInfo) +{ + if (!pTexInfo) + return; + else if (pTexInfo->downloaded) + pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); + + GLTexture_Delete(pTexInfo); + pTexInfo->downloaded = 0; +} + +// -----------------+ +// ClearTextureCache: Flush OpenGL textures from memory +// -----------------+ +static void ClearTextureCache(void) +{ + GLTexture_Flush(); +} + +// code that is common between DrawPolygon and DrawIndexedTriangles +// the corona thing is there too, i have no idea if that stuff works with DrawIndexedTriangles and batching +static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 PolyFlags) +{ + (void)pOutVerts; + if (PolyFlags & PF_Corona) + PolyFlags &= ~(PF_NoDepthTest|PF_Corona); + + SetBlend(PolyFlags); //TODO: inline (#pragma..) + + // If Modulated, mix the surface colour to the texture + if (pSurf && (CurrentPolyFlags & PF_Modulated)) + { + GLRGBAFloat poly; + + poly.red = (pSurf->PolyColor.s.red/255.0f); + poly.green = (pSurf->PolyColor.s.green/255.0f); + poly.blue = (pSurf->PolyColor.s.blue/255.0f); + poly.alpha = (pSurf->PolyColor.s.alpha/255.0f); + + GLState_SetColor(poly.red, poly.green, poly.blue, poly.alpha); + } +} + +// -----------------+ +// DrawPolygon : Render a polygon, set the texture, set render mode +// -----------------+ +static void DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags) +{ + PreparePolygon(pSurf, pOutVerts, PolyFlags); + + pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x); + pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s); + pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts); + + if (PolyFlags & PF_RemoveYWrap) + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + if (PolyFlags & PF_ForceWrapX) + GLState_SetClamp(GL_TEXTURE_WRAP_S); + + if (PolyFlags & PF_ForceWrapY) + GLState_SetClamp(GL_TEXTURE_WRAP_T); +} + +// ---------------------+ +// DrawIndexedTriangles : Renders indexed triangles +// ---------------------+ +static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray) +{ + PreparePolygon(pSurf, pOutVerts, PolyFlags); + + pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x); + pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s); + pglDrawElements(GL_TRIANGLES, iNumPts, GL_UNSIGNED_INT, IndexArray); + + // the DrawPolygon variant of this has some code about polyflags and wrapping here but havent noticed any problems from omitting it? +} + +// -----------------+ +// DrawSkyDome : Renders a sky dome +// -----------------+ +static void DrawSkyDome(FSkyDome *sky) +{ + int i, j; + + // Build the sky dome! Yes! + if (sky->rebuild) + { + if (GLExtension_vertex_buffer_object) + { + // delete VBO when already exists + if (sky->vbo) + pglDeleteBuffers(1, &sky->vbo); + + // generate a new VBO and get the associated ID + pglGenBuffers(1, &sky->vbo); + + // bind VBO in order to use + pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo); + + // upload data to VBO + pglBufferData(GL_ARRAY_BUFFER, sky->vertex_count * sizeof(sky->data[0]), sky->data, GL_STATIC_DRAW); + } + + sky->rebuild = false; + } + + // bind VBO in order to use + if (GLExtension_vertex_buffer_object) + pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo); + + // activate and specify pointers to arrays + pglVertexPointer(3, GL_FLOAT, sizeof(sky->data[0]), sky_vbo_x); + pglTexCoordPointer(2, GL_FLOAT, sizeof(sky->data[0]), sky_vbo_u); + pglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(sky->data[0]), sky_vbo_r); + + // activate color arrays + pglEnableClientState(GL_COLOR_ARRAY); + + // set transforms + pglScalef(1.0f, (float)sky->height / 200.0f, 1.0f); + pglRotatef(270.0f, 0.0f, 1.0f, 0.0f); + + for (j = 0; j < 2; j++) + { + for (i = 0; i < sky->loopcount; i++) + { + FSkyLoopDef *loop = &sky->loops[i]; + unsigned int mode = 0; + + if (j == 0 ? loop->use_texture : !loop->use_texture) + continue; + + switch (loop->mode) + { + case GPU_SKYLOOP_FAN: + mode = GL_TRIANGLE_FAN; + break; + case GPU_SKYLOOP_STRIP: + mode = GL_TRIANGLE_STRIP; + break; + default: + continue; + } + + pglDrawArrays(mode, loop->vertexindex, loop->vertexcount); + } + } + + pglScalef(1.0f, 1.0f, 1.0f); + GLState_SetColor(white[0], white[1], white[2], white[3]); + + // bind with 0, so, switch back to normal pointer operation + if (GLExtension_vertex_buffer_object) + pglBindBuffer(GL_ARRAY_BUFFER, 0); + + // deactivate color array + pglDisableClientState(GL_COLOR_ARRAY); +} + +static void SetState(INT32 State, INT32 Value) +{ + switch (State) + { + case GPU_STATE_MODEL_LIGHTING: + ModelLightingEnabled = Value ? GL_TRUE : GL_FALSE; + break; + + case GPU_STATE_TEXTUREFILTERMODE: + if (MipmappingSupported) + { + GLState_SetFilterMode(Value); + GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this + } + else + { + MipmappingEnabled = GL_FALSE; + MipmapMinFilter = GL_LINEAR; + } + break; + + case GPU_STATE_TEXTUREANISOTROPICMODE: + if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy) + { + AnisotropicFilter = min(Value, GPUMaximumAnisotropy); + GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this + } + break; + + default: + break; + } +} + +static void CreateModelVBOs(model_t *model) +{ + GLModel_GenerateVBOs(model); +} + +#define BUFFER_OFFSET(i) ((char*)NULL + (i)) + +static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +{ + static GLRGBAFloat poly = {0,0,0,0}; + + float pol = 0.0f; + float scalex, scaley, scalez; + + boolean useTinyFrames; + + boolean useVBO = GLExtension_vertex_buffer_object; + + UINT32 flags; + int i; + + // Because otherwise, scaling the screen negatively vertically breaks the lighting + GLfloat LightPos[4] = {0.0f, 1.0f, 0.0f, 0.0f}; +#ifdef GL_LIGHT_MODEL_AMBIENT + float ambient[4]; + float diffuse[4]; +#endif + + // Affect input model scaling + scale *= 0.5f; + scalex = scale; + scaley = scale; + scalez = scale; + + if (duration != 0 && duration != -1 && tics != -1) // don't interpolate if instantaneous or infinite in length + { + UINT32 newtime = (duration - tics); // + 1; + + pol = (newtime)/(float)duration; + + if (pol > 1.0f) + pol = 1.0f; + + if (pol < 0.0f) + pol = 0.0f; + } + + poly.red = (Surface->PolyColor.s.red/255.0f); + poly.green = (Surface->PolyColor.s.green/255.0f); + poly.blue = (Surface->PolyColor.s.blue/255.0f); + poly.alpha = (Surface->PolyColor.s.alpha/255.0f); + +#ifdef GL_LIGHT_MODEL_AMBIENT + if (ModelLightingEnabled) + { + ambient[0] = poly.red; + ambient[1] = poly.green; + ambient[2] = poly.blue; + ambient[3] = poly.alpha; + + diffuse[0] = poly.red; + diffuse[1] = poly.green; + diffuse[2] = poly.blue; + diffuse[3] = poly.alpha; + + if (ambient[0] > 0.75f) + ambient[0] = 0.75f; + if (ambient[1] > 0.75f) + ambient[1] = 0.75f; + if (ambient[2] > 0.75f) + ambient[2] = 0.75f; + + pglLightfv(GL_LIGHT0, GL_POSITION, LightPos); + pglShadeModel(GL_SMOOTH); + + pglEnable(GL_LIGHTING); + pglMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient); + pglMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); + } +#endif + else + GLState_SetColor(poly.red, poly.green, poly.blue, poly.alpha); + + flags = (Surface->PolyFlags | PF_Modulated); + if (Surface->PolyFlags & (PF_Additive|PF_AdditiveSource|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative)) + flags |= PF_Occlude; + else if (Surface->PolyColor.s.alpha == 0xFF) + flags |= (PF_Occlude | PF_Masked); + SetBlend(flags); + + pglEnable(GL_CULL_FACE); + pglEnable(GL_NORMALIZE); + +#ifdef USE_FTRANSFORM_MIRROR + // flipped is if the object is vertically flipped + // hflipped is if the object is horizontally flipped + // pos->flip is if the screen is flipped vertically + // pos->mirror is if the screen is flipped horizontally + // XOR all the flips together to figure out what culling to use! + { + boolean reversecull = (flipped ^ hflipped ^ pos->flip ^ pos->mirror); + if (reversecull) + pglCullFace(GL_FRONT); + else + pglCullFace(GL_BACK); + } +#else + // pos->flip is if the screen is flipped too + if (flipped ^ hflipped ^ pos->flip) // If one or three of these are active, but not two, invert the model's culling + { + pglCullFace(GL_FRONT); + } + else + { + pglCullFace(GL_BACK); + } +#endif + + pglPushMatrix(); // should be the same as glLoadIdentity + //Hurdler: now it seems to work + pglTranslatef(pos->x, pos->z, pos->y); + if (flipped) + scaley = -scaley; + if (hflipped) + scalez = -scalez; + +#ifdef USE_FTRANSFORM_ANGLEZ + pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart +#endif + pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f); + pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f); + + if (pos->roll) + { + float roll = (1.0f * pos->rollflip); + pglTranslatef(pos->centerx, pos->centery, 0); + if (pos->rotaxis == 2) // Z + pglRotatef(pos->rollangle, 0.0f, 0.0f, roll); + else if (pos->rotaxis == 1) // Y + pglRotatef(pos->rollangle, 0.0f, roll, 0.0f); + else // X + pglRotatef(pos->rollangle, roll, 0.0f, 0.0f); + pglTranslatef(-pos->centerx, -pos->centery, 0); + } + + pglScalef(scalex, scaley, scalez); + + useTinyFrames = model->meshes[0].tinyframes != NULL; + + if (useTinyFrames) + pglScalef(1 / 64.0f, 1 / 64.0f, 1 / 64.0f); + + // Don't use the VBO if it does not have the correct texture coordinates. + // (Can happen when model uses a sprite as a texture and the sprite changes) + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(model->vbo_max_s), &(model->max_s), sizeof(model->max_s)) != 0 || + memcmp(&(model->vbo_max_t), &(model->max_t), sizeof(model->max_t)) != 0) + useVBO = false; + + pglEnableClientState(GL_NORMAL_ARRAY); + + for (i = 0; i < model->numMeshes; i++) + { + mesh_t *mesh = &model->meshes[i]; + + if (useTinyFrames) + { + tinyframe_t *frame = &mesh->tinyframes[frameIndex % mesh->numFrames]; + tinyframe_t *nextframe = NULL; + + if (nextFrameIndex != -1) + nextframe = &mesh->tinyframes[nextFrameIndex % mesh->numFrames]; + + if (!nextframe || fpclassify(pol) == FP_ZERO) + { + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); + + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexPointer(3, GL_SHORT, 0, frame->vertices); + pglNormalPointer(GL_BYTE, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } + } + else + { + short *vertPtr; + char *normPtr; + int j; + + // Dangit, I soooo want to do this in a GLSL shader... + GLModel_AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3); + vertPtr = vertTinyBuffer; + normPtr = normTinyBuffer; + j = 0; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = (short)(frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j]))); + *normPtr++ = (char)(frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j]))); + } + + pglVertexPointer(3, GL_SHORT, 0, vertTinyBuffer); + pglNormalPointer(GL_BYTE, 0, normTinyBuffer); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } + } + else + { + mdlframe_t *frame = &mesh->frames[frameIndex % mesh->numFrames]; + mdlframe_t *nextframe = NULL; + + if (nextFrameIndex != -1) + nextframe = &mesh->frames[nextFrameIndex % mesh->numFrames]; + + if (!nextframe || fpclassify(pol) == FP_ZERO) + { + if (useVBO) + { + // Zoom! Take advantage of just shoving the entire arrays to the GPU. + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); + + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + // No tinyframes, no mesh indices + //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); + pglNormalPointer(GL_FLOAT, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + } + } + else + { + float *vertPtr; + float *normPtr; + int j = 0; + + // Dangit, I soooo want to do this in a GLSL shader... + GLModel_AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3); + vertPtr = vertBuffer; + normPtr = normBuffer; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j])); + *normPtr++ = frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j])); + } + + pglVertexPointer(3, GL_FLOAT, 0, vertBuffer); + pglNormalPointer(GL_FLOAT, 0, normBuffer); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numVertices); + } + } + } + + pglDisableClientState(GL_NORMAL_ARRAY); + + pglPopMatrix(); // should be the same as glLoadIdentity + pglDisable(GL_CULL_FACE); + pglDisable(GL_NORMALIZE); + +#ifdef GL_LIGHT_MODEL_AMBIENT + if (ModelLightingEnabled) + { + pglDisable(GL_LIGHTING); + pglShadeModel(GL_FLAT); + } +#endif +} + +// -----------------+ +// DrawModel : Renders a model +// -----------------+ +static void DrawModel(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +{ + DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface); +} + +// -----------------+ +// SetTransform : +// -----------------+ +static void SetTransform(FTransform *stransform) +{ + static boolean special_splitscreen; + boolean shearing = false; + float used_fov; + + pglLoadIdentity(); + + if (stransform) + { + used_fov = stransform->fovxangle; +#ifdef USE_FTRANSFORM_MIRROR + // mirroring from Kart + if (stransform->mirror) + pglScalef(-stransform->scalex, stransform->scaley, -stransform->scalez); + else +#endif + if (stransform->flip) + pglScalef(stransform->scalex, -stransform->scaley, -stransform->scalez); + else + pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez); + + if (stransform->roll) + pglRotatef(stransform->rollangle, 0.0f, 0.0f, 1.0f); + pglRotatef(stransform->anglex , 1.0f, 0.0f, 0.0f); + pglRotatef(stransform->angley+270.0f, 0.0f, 1.0f, 0.0f); + pglTranslatef(-stransform->x, -stransform->z, -stransform->y); + + special_splitscreen = stransform->splitscreen; + shearing = stransform->shearing; + } + else + { + used_fov = FIELD_OF_VIEW; + pglScalef(1.0f, 1.0f, -1.0f); + } + + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + + // jimita 14042019 + // Simulate Software's y-shearing + // https://zdoom.org/wiki/Y-shearing + if (shearing) + { + float fdy = stransform->viewaiming * 2; + pglTranslatef(0.0f, -fdy/BASEVIDHEIGHT, 0.0f); + } + + if (special_splitscreen) + { + used_fov = atan(tan(used_fov*M_PI/360)*0.8)*360/M_PI; + GLPerspective(used_fov, 2*ASPECT_RATIO); + } + else + GLPerspective(used_fov, ASPECT_RATIO); + + pglMatrixMode(GL_MODELVIEW); +} + +static INT32 GetTextureUsed(void) +{ + return GLTexture_GetMemoryUsage(TexCacheHead); +} + +static void PostImgRedraw(float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2]) +{ + INT32 x, y; + float float_x, float_y, float_nextx, float_nexty; + float xfix, yfix; + INT32 texsize = 2048; + + const float blackBack[16] = + { + -16.0f, -16.0f, 6.0f, + -16.0f, 16.0f, 6.0f, + 16.0f, 16.0f, 6.0f, + 16.0f, -16.0f, 6.0f + }; + + // Use a power of two texture, dammit + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + // X/Y stretch fix for all resolutions(!) + xfix = (float)(texsize)/((float)((GPUScreenWidth)/(float)(GPU_POSTIMGVERTS-1))); + yfix = (float)(texsize)/((float)((GPUScreenHeight)/(float)(GPU_POSTIMGVERTS-1))); + + pglDisable(GL_DEPTH_TEST); + pglDisable(GL_BLEND); + + // const float blackBack[16] + + // Draw a black square behind the screen texture, + // so nothing shows through the edges + GLState_SetColor(white[0], white[1], white[2], white[3]); + + pglVertexPointer(3, GL_FLOAT, 0, blackBack); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + for(x=0;x<GPU_POSTIMGVERTS-1;x++) + { + for(y=0;y<GPU_POSTIMGVERTS-1;y++) + { + float stCoords[8]; + float vertCoords[12]; + + // Used for texture coordinates + // Annoying magic numbers to scale the square texture to + // a non-square screen.. + float_x = (float)(x/(xfix)); + float_y = (float)(y/(yfix)); + float_nextx = (float)(x+1)/(xfix); + float_nexty = (float)(y+1)/(yfix); + + // float stCoords[8]; + stCoords[0] = float_x; + stCoords[1] = float_y; + stCoords[2] = float_x; + stCoords[3] = float_nexty; + stCoords[4] = float_nextx; + stCoords[5] = float_nexty; + stCoords[6] = float_nextx; + stCoords[7] = float_y; + + pglTexCoordPointer(2, GL_FLOAT, 0, stCoords); + + // float vertCoords[12]; + vertCoords[0] = points[x][y][0]; + vertCoords[1] = points[x][y][1]; + vertCoords[2] = 4.4f; + vertCoords[3] = points[x][y + 1][0]; + vertCoords[4] = points[x][y + 1][1]; + vertCoords[5] = 4.4f; + vertCoords[6] = points[x + 1][y + 1][0]; + vertCoords[7] = points[x + 1][y + 1][1]; + vertCoords[8] = 4.4f; + vertCoords[9] = points[x + 1][y][0]; + vertCoords[10] = points[x + 1][y][1]; + vertCoords[11] = 4.4f; + + pglVertexPointer(3, GL_FLOAT, 0, vertCoords); + + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + + pglEnable(GL_DEPTH_TEST); + pglEnable(GL_BLEND); +} + +static void FlushScreenTextures(void) +{ + GLTexture_FlushScreenTextures(); +} + +// Create screen to fade from +static void StartScreenWipe(void) +{ + GLTexture_GenerateScreenTexture(&WipeStartTexture); +} + +// Create screen to fade to +static void EndScreenWipe(void) +{ + GLTexture_GenerateScreenTexture(&WipeEndTexture); +} + +// Draw the last scene under the intermission +static void DrawIntermissionBG(void) +{ + float xfix, yfix; + INT32 texsize = 2048; + + const float screenVerts[12] = + { + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + float fix[8]; + + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + // const float screenVerts[12] + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + pglBindTexture(GL_TEXTURE_2D, ScreenTexture); + GLState_SetColor(white[0], white[1], white[2], white[3]); + + pglTexCoordPointer(2, GL_FLOAT, 0, fix); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + CurrentTexture = ScreenTexture; +} + +// Do screen fades! +static void DoScreenWipe(void) +{ + INT32 texsize = 2048; + float xfix, yfix; + + INT32 fademaskdownloaded = CurrentTexture; // the fade mask that has been set + + const float screenVerts[12] = + { + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + float fix[8]; + + const float defaultST[8] = + { + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f + }; + + // Use a power of two texture, dammit + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + // const float screenVerts[12] + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + SetBlend(PF_Modulated|PF_NoDepthTest); + pglEnable(GL_TEXTURE_2D); + + // Draw the original screen + pglBindTexture(GL_TEXTURE_2D, WipeStartTexture); + GLState_SetColor(white[0], white[1], white[2], white[3]); + pglTexCoordPointer(2, GL_FLOAT, 0, fix); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest); + + // Draw the end screen that fades in + pglActiveTexture(GL_TEXTURE0); + pglEnable(GL_TEXTURE_2D); + pglBindTexture(GL_TEXTURE_2D, WipeEndTexture); + pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + pglActiveTexture(GL_TEXTURE1); + pglEnable(GL_TEXTURE_2D); + pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded); + + pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + // const float defaultST[8] + + pglClientActiveTexture(GL_TEXTURE0); + pglTexCoordPointer(2, GL_FLOAT, 0, fix); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); + pglClientActiveTexture(GL_TEXTURE1); + pglEnableClientState(GL_TEXTURE_COORD_ARRAY); + pglTexCoordPointer(2, GL_FLOAT, 0, defaultST); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + pglDisable(GL_TEXTURE_2D); // disable the texture in the 2nd texture unit + pglDisableClientState(GL_TEXTURE_COORD_ARRAY); + + pglActiveTexture(GL_TEXTURE0); + pglClientActiveTexture(GL_TEXTURE0); + CurrentTexture = WipeEndTexture; +} + +// Create a texture from the screen. +static void MakeScreenTexture(void) +{ + GLTexture_GenerateScreenTexture(&ScreenTexture); +} + +static void MakeScreenFinalTexture(void) +{ + GLTexture_GenerateScreenTexture(&FinalScreenTexture); +} + +static void DrawScreenFinalTexture(int width, int height) +{ + float xfix, yfix; + float origaspect, newaspect; + float xoff = 1, yoff = 1; // xoffset and yoffset for the polygon to have black bars around the screen + FRGBAFloat clearColour; + INT32 texsize = 2048; + + float off[12]; + float fix[8]; + + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + origaspect = (float)GPUScreenWidth / GPUScreenHeight; + newaspect = (float)width / height; + if (origaspect < newaspect) + { + xoff = origaspect / newaspect; + yoff = 1; + } + else if (origaspect > newaspect) + { + xoff = 1; + yoff = newaspect / origaspect; + } + + // float off[12]; + off[0] = -xoff; + off[1] = -yoff; + off[2] = 1.0f; + off[3] = -xoff; + off[4] = yoff; + off[5] = 1.0f; + off[6] = xoff; + off[7] = yoff; + off[8] = 1.0f; + off[9] = xoff; + off[10] = -yoff; + off[11] = 1.0f; + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglViewport(0, 0, width, height); + + clearColour.red = clearColour.green = clearColour.blue = 0; + clearColour.alpha = 1; + ClearBuffer(true, false, &clearColour); + pglBindTexture(GL_TEXTURE_2D, FinalScreenTexture); + + GLState_SetColor(white[0], white[1], white[2], white[3]); + + pglTexCoordPointer(2, GL_FLOAT, 0, fix); + pglVertexPointer(3, GL_FLOAT, 0, off); + + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + CurrentTexture = FinalScreenTexture; +} + +static void SetShader(int type) +{ + (void)type; +} + +static boolean CompileShaders(void) +{ + return false; +} + +static void SetShaderInfo(INT32 info, INT32 value) +{ + (void)info; + (void)value; +} + +static void LoadCustomShader(int number, char *shader, size_t size, boolean fragment) +{ + (void)number; + (void)shader; + (void)size; + (void)fragment; +} + +static void UnSetShader(void) {} +static void CleanShaders(void) {} + +struct GPURenderingAPI GLInterfaceAPI = { + Init, + + SetInitialStates, + SetModelView, + SetState, + SetTransform, + SetBlend, + SetPalette, + SetDepthBuffer, + + DrawPolygon, + DrawIndexedTriangles, + Draw2DLine, + DrawModel, + DrawSkyDome, + + SetTexture, + UpdateTexture, + DeleteTexture, + + ClearTextureCache, + GetTextureUsed, + + CreateModelVBOs, + + ReadRect, + GClipRect, + ClearBuffer, + + MakeScreenTexture, + MakeScreenFinalTexture, + FlushScreenTextures, + + StartScreenWipe, + EndScreenWipe, + DoScreenWipe, + NULL, + DrawIntermissionBG, + DrawScreenFinalTexture, + + PostImgRedraw, + + CompileShaders, + CleanShaders, + SetShader, + UnSetShader, + + SetShaderInfo, + LoadCustomShader, +}; + +#endif //HWRENDER diff --git a/src/hardware/r_gles/r_gles2.c b/src/hardware/r_gles/r_gles2.c new file mode 100644 index 0000000000000000000000000000000000000000..e98a5b6f1eec43c997fe4010d3e4c6ab657991dc --- /dev/null +++ b/src/hardware/r_gles/r_gles2.c @@ -0,0 +1,1364 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1998-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_gles2.c +/// \brief OpenGL ES 2.0 API for Sonic Robo Blast 2 + +#include <stdarg.h> +#include <math.h> + +#include "r_gles.h" +#include "../r_opengl/r_vbo.h" + +#include "../shaders/gl_shaders.h" + +#include "lzml.h" + +#if defined (HWRENDER) && !defined (NOROPENGL) + +static GLRGBAFloat white = {1.0f, 1.0f, 1.0f, 1.0f}; +static GLRGBAFloat black = {0.0f, 0.0f, 0.0f, 1.0f}; + +// ========================================================================== +// CONSTANTS +// ========================================================================== + +#define Deg2Rad(x) ((x) * ((float)M_PIl / 180.0f)) + +// ========================================================================== +// GLOBALS +// ========================================================================== + +fmatrix4_t projMatrix; +fmatrix4_t viewMatrix; +fmatrix4_t modelMatrix; + +/* Drawing Functions */ +typedef void (R_GL_APIENTRY * PFNglEnableVertexAttribArray) (GLuint index); +static PFNglEnableVertexAttribArray pglEnableVertexAttribArray; +typedef void (R_GL_APIENTRY * PFNglDisableVertexAttribArray) (GLuint index); +static PFNglDisableVertexAttribArray pglDisableVertexAttribArray; +typedef void (R_GL_APIENTRY * PFNglVertexAttribPointer) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +static PFNglVertexAttribPointer pglVertexAttribPointer; + +boolean GLBackend_LoadFunctions(void) +{ + if (!GLBackend_LoadCommonFunctions()) + return false; + +#if defined(__ANDROID__) + GLBackend_LoadContextFunctions(); + GLBackend_InitShaders(); +#endif + + return true; +} + +boolean GLBackend_LoadContextFunctions(void) +{ + GETOPENGLFUNC(ActiveTexture) + + GETOPENGLFUNC(GenBuffers) + GETOPENGLFUNC(BindBuffer) + GETOPENGLFUNC(BufferData) + GETOPENGLFUNC(DeleteBuffers) + + GETOPENGLFUNC(EnableVertexAttribArray) + GETOPENGLFUNC(DisableVertexAttribArray) + GETOPENGLFUNC(VertexAttribPointer) + + GETOPENGLFUNC(BlendEquation) + + GETOPENGLFUNCALT(ClearDepthf, ClearDepth) + GETOPENGLFUNCALT(DepthRangef, DepthRange) + + if (GLTexture_InitMipmapping()) + MipmappingSupported = GL_TRUE; + + return true; +} + +static void GLPerspective(GLfloat fovy, GLfloat aspect) +{ + fmatrix4_t perspectiveMatrix; + lzml_matrix4_perspective(perspectiveMatrix, Deg2Rad((float)fovy), (float)aspect, NearClippingPlane, FAR_CLIPPING_PLANE); + lzml_matrix4_multiply(projMatrix, perspectiveMatrix); +} + +// ========================================================================== +// API +// ========================================================================== + +// -----------------+ +// Init : Initializes the OpenGL ES interface API +// -----------------+ +static boolean Init(void) +{ + return GLBackend_Init(); +} + +// -----------------+ +// SetInitialStates : Set permanent states +// -----------------+ +static void SetInitialStates(void) +{ + pglEnable(GL_BLEND); + pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + pglEnable(GL_DEPTH_TEST); // check the depth buffer + pglDepthMask(GL_TRUE); // enable writing to depth buffer + GPU->SetDepthBuffer(); + + pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + pglAlphaFunc(GL_NOTEQUAL, 0.0f); + + // this sets CurrentPolyFlags to the actual configuration + CurrentPolyFlags = 0xFFFFFFFF; + GPU->SetBlend(0); + + if (ShaderState.current) + { + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_POSITION)); + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD)); + } + + CurrentTexture = 0; + GLTexture_SetNoTexture(); +} + +// -----------------+ +// SetModelView : Resets the viewport +// -----------------+ +static void SetModelView(INT32 w, INT32 h) +{ + // The screen textures need to be flushed if the width or height change so that they be remade for the correct size + if (GPUScreenWidth != w || GPUScreenHeight != h) + GPU->FlushScreenTextures(); + + GPUScreenWidth = (GLint)w; + GPUScreenHeight = (GLint)h; + + pglViewport(0, 0, w, h); + + lzml_matrix4_identity(projMatrix); + lzml_matrix4_identity(viewMatrix); + lzml_matrix4_identity(modelMatrix); + + Shader_SetTransform(); +} + +// -----------------+ +// ReadRect : Read a rectangle region of the truecolor framebuffer +// : store pixels as RGBA8888 +// Returns : RGBA8888 pixel array stored in dst_data +// -----------------+ +static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 * dst_data) +{ + INT32 i; + GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1); + GLubyte *row = malloc(dst_stride); + if (!row) return; + pglPixelStorei(GL_PACK_ALIGNMENT, 1); + pglReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dst_data); + pglPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for(i = 0; i < height/2; i++) + { + memcpy(row, top, dst_stride); + memcpy(top, bottom, dst_stride); + memcpy(bottom, row, dst_stride); + top += dst_stride; + bottom -= dst_stride; + } + free(row); +} + +// -----------------+ +// GClipRect : Defines the 2D clipping window +// -----------------+ +static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip) +{ + pglViewport(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny); + NearClippingPlane = nearclip; + + lzml_matrix4_identity(projMatrix); + lzml_matrix4_identity(viewMatrix); + lzml_matrix4_identity(modelMatrix); + + Shader_SetTransform(); +} + +// -----------------+ +// SetBlend : Set blend mode +// -----------------+ +static void SetBlend(UINT32 PolyFlags) +{ + GLState_SetBlend(PolyFlags); +} + +// -----------------+ +// SetPalette : Changes the current texture palette +// -----------------+ +static void SetPalette(RGBA_t *palette) +{ + GLState_SetPalette(palette); +} + +// -----------------+ +// SetDepthBuffer : Set depth buffer state +// -----------------+ +static void SetDepthBuffer(void) +{ + GLState_SetDepthBuffer(); +} + +// -----------------+ +// ClearBuffer : Clear the color/alpha/depth buffer(s) +// -----------------+ +static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat * ClearColor) +{ + GLbitfield ClearMask = 0; + + if (ColorMask) + { + if (ClearColor) + pglClearColor(ClearColor->red, ClearColor->green, ClearColor->blue, ClearColor->alpha); + ClearMask |= GL_COLOR_BUFFER_BIT; + } + + if (DepthMask) + { + SetDepthBuffer(); + ClearMask |= GL_DEPTH_BUFFER_BIT; + } + + SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude); + pglClear(ClearMask); + + if (ShaderState.current) + { + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_POSITION)); + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD)); + } +} + +// -----------------+ +// Draw2DLine : Render a 2D line +// -----------------+ +static void Draw2DLine(F2DCoord *v1, F2DCoord *v2, RGBA_t Color) +{ + GLfloat p[12]; + GLfloat dx, dy; + GLfloat angle; + + GLRGBAFloat fcolor = {byte2float(Color.s.red), byte2float(Color.s.green), byte2float(Color.s.blue), byte2float(Color.s.alpha)}; + + if (ShaderState.current == NULL) + return; + + GLTexture_SetNoTexture(); + + // This is the preferred, 'modern' way of rendering lines -- creating a polygon. + if (fabsf(v2->x - v1->x) > FLT_EPSILON) + angle = (float)atan((v2->y-v1->y)/(v2->x-v1->x)); + else + angle = (float)N_PI_DEMI; + dx = (float)sin(angle) / (float)GPUScreenWidth; + dy = (float)cos(angle) / (float)GPUScreenHeight; + + p[0] = v1->x - dx; p[1] = -(v1->y + dy); p[2] = 1; + p[3] = v2->x - dx; p[4] = -(v2->y + dy); p[5] = 1; + p[6] = v2->x + dx; p[7] = -(v2->y - dy); p[8] = 1; + p[9] = v1->x + dx; p[10] = -(v1->y - dy); p[11] = 1; + + Shader_SetUniforms(NULL, &fcolor, NULL, NULL); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, p); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +// -----------------+ +// UpdateTexture : Updates the texture data. +// -----------------+ +static void UpdateTexture(HWRTexture_t *pTexInfo) +{ + GLTexture_Update(pTexInfo); +} + +// -----------------+ +// SetTexture : Uploads the texture, and sets it as the current one. +// -----------------+ +static void SetTexture(HWRTexture_t *pTexInfo) +{ + GLTexture_Set(pTexInfo); +} + +// -----------------+ +// DeleteTexture : Deletes a texture from the GPU, and frees its data. +// -----------------+ +static void DeleteTexture(HWRTexture_t *pTexInfo) +{ + if (!pTexInfo) + return; + else if (pTexInfo->downloaded) + pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); + + GLTexture_Delete(pTexInfo); + pTexInfo->downloaded = 0; +} + +// -----------------+ +// ClearTextureCache: Flush OpenGL textures from memory +// -----------------+ +static void ClearTextureCache(void) +{ + GLTexture_Flush(); +} + +// code that is common between DrawPolygon and DrawIndexedTriangles +// the corona thing is there too, i have no idea if that stuff works with DrawIndexedTriangles and batching +static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 PolyFlags) +{ + GLRGBAFloat poly = {1.0f, 1.0f, 1.0f, 1.0f}; + GLRGBAFloat tint = {1.0f, 1.0f, 1.0f, 1.0f}; + GLRGBAFloat fade = {1.0f, 1.0f, 1.0f, 1.0f}; + + GLRGBAFloat *c_poly = NULL, *c_tint = NULL, *c_fade = NULL; + + if (ShaderState.current) + { + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_POSITION)); + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD)); + } + + (void)pOutVerts; + if (PolyFlags & PF_Corona) + PolyFlags &= ~(PF_NoDepthTest|PF_Corona); + + SetBlend(PolyFlags); //TODO: inline (#pragma..) + + // If Modulated, mix the surface colour to the texture + if (pSurf && (CurrentPolyFlags & PF_Modulated)) + { + // Poly color + poly.red = byte2float(pSurf->PolyColor.s.red); + poly.green = byte2float(pSurf->PolyColor.s.green); + poly.blue = byte2float(pSurf->PolyColor.s.blue); + poly.alpha = byte2float(pSurf->PolyColor.s.alpha); + + // Tint color + tint.red = byte2float(pSurf->TintColor.s.red); + tint.green = byte2float(pSurf->TintColor.s.green); + tint.blue = byte2float(pSurf->TintColor.s.blue); + tint.alpha = byte2float(pSurf->TintColor.s.alpha); + + // Fade color + fade.red = byte2float(pSurf->FadeColor.s.red); + fade.green = byte2float(pSurf->FadeColor.s.green); + fade.blue = byte2float(pSurf->FadeColor.s.blue); + fade.alpha = byte2float(pSurf->FadeColor.s.alpha); + + c_poly = &poly; + c_tint = ∭ + c_fade = &fade; + } + else + c_poly = &white; + + Shader_SetUniforms(pSurf, c_poly, c_tint, c_fade); +} + +// -----------------+ +// DrawPolygon : Render a polygon, set the texture, set render mode +// -----------------+ +static void DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags) +{ + if (ShaderState.current == NULL) + return; + + PreparePolygon(pSurf, pOutVerts, PolyFlags); + + pglBindBuffer(GL_ARRAY_BUFFER, 0); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, sizeof(FOutVector), &pOutVerts[0].x); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, sizeof(FOutVector), &pOutVerts[0].s); + + pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts); + + if (PolyFlags & PF_RemoveYWrap) + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + if (PolyFlags & PF_ForceWrapX) + GLState_SetClamp(GL_TEXTURE_WRAP_S); + + if (PolyFlags & PF_ForceWrapY) + GLState_SetClamp(GL_TEXTURE_WRAP_T); +} + +// ---------------------+ +// DrawIndexedTriangles : Renders indexed triangles +// ---------------------+ +static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray) +{ + if (ShaderState.current == NULL) + return; + + PreparePolygon(pSurf, pOutVerts, PolyFlags); + + pglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, sizeof(FOutVector), &pOutVerts[0].x); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, sizeof(FOutVector), &pOutVerts[0].s); + + pglDrawElements(GL_TRIANGLES, iNumPts, GL_UNSIGNED_INT, IndexArray); + + // the DrawPolygon variant of this has some code about polyflags and wrapping here but havent noticed any problems from omitting it? +} + +// -----------------+ +// DrawSkyDome : Renders a sky dome +// -----------------+ +static void DrawSkyDome(FSkyDome *sky) +{ + int i, j; + + fvector3_t scale; + scale[0] = scale[2] = 1.0f; + + Shader_SetUniforms(NULL, NULL, NULL, NULL); + + // Build the sky dome! Yes! + if (sky->rebuild) + { + // delete VBO when already exists + if (sky->vbo) + pglDeleteBuffers(1, &sky->vbo); + + // generate a new VBO and get the associated ID + pglGenBuffers(1, &sky->vbo); + + // bind VBO in order to use + pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo); + + // upload data to VBO + pglBufferData(GL_ARRAY_BUFFER, sky->vertex_count * sizeof(sky->data[0]), sky->data, GL_STATIC_DRAW); + + sky->rebuild = false; + } + + if (ShaderState.current == NULL) + { + pglBindBuffer(GL_ARRAY_BUFFER, 0); + return; + } + + // bind VBO in order to use + pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo); + + // activate and specify pointers to arrays + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_COLORS)); + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, sizeof(sky->data[0]), sky_vbo_x); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, sizeof(sky->data[0]), sky_vbo_u); + pglVertexAttribPointer(Shader_AttribLoc(LOC_COLORS), 4, GL_FLOAT, GL_FALSE, sizeof(sky->data[0]), sky_vbo_r); + + // set transforms + scale[1] = (float)sky->height / 200.0f; + lzml_matrix4_scale(viewMatrix, scale); + lzml_matrix4_rotate_y(viewMatrix, Deg2Rad(270.0f)); + + Shader_SetTransform(); + + for (j = 0; j < 2; j++) + { + for (i = 0; i < sky->loopcount; i++) + { + FSkyLoopDef *loop = &sky->loops[i]; + unsigned int mode = 0; + + if (j == 0 ? loop->use_texture : !loop->use_texture) + continue; + + switch (loop->mode) + { + case GPU_SKYLOOP_FAN: + mode = GL_TRIANGLE_FAN; + break; + case GPU_SKYLOOP_STRIP: + mode = GL_TRIANGLE_STRIP; + break; + default: + continue; + } + + pglDrawArrays(mode, loop->vertexindex, loop->vertexcount); + } + } + + pglDisableVertexAttribArray(Shader_AttribLoc(LOC_COLORS)); + + // bind with 0, so, switch back to normal pointer operation + pglBindBuffer(GL_ARRAY_BUFFER, 0); +} + +static void SetState(INT32 State, INT32 Value) +{ + switch (State) + { + case GPU_STATE_MODEL_LIGHTING: + ModelLightingEnabled = Value ? GL_TRUE : GL_FALSE; + break; + + case GPU_STATE_SHADERS: + ShadersAllowed = Value ? GL_TRUE : GL_FALSE; + break; + + case GPU_STATE_TEXTUREFILTERMODE: + if (MipmappingSupported) + { + GLState_SetFilterMode(Value); + GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this + } + else + { + MipmappingEnabled = GL_FALSE; + MipmapMinFilter = GL_LINEAR; + } + break; + + case GPU_STATE_TEXTUREANISOTROPICMODE: + if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy) + { + AnisotropicFilter = min(Value, GPUMaximumAnisotropy); + GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this + } + break; + + default: + break; + } +} + +static void CreateModelVBOs(model_t *model) +{ + GLModel_GenerateVBOs(model); +} + +#define BUFFER_OFFSET(i) ((void*)(i)) + +static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +{ + static GLRGBAFloat poly = {1.0f, 1.0f, 1.0f, 1.0f}; + static GLRGBAFloat tint = {1.0f, 1.0f, 1.0f, 1.0f}; + static GLRGBAFloat fade = {1.0f, 1.0f, 1.0f, 1.0f}; + + float pol = 0.0f; + + boolean useTinyFrames; + + boolean useVBO = true; + + fvector3_t v_scale; + fvector3_t translate; + + UINT32 flags; + int i; + + if (ShaderState.current == NULL) + return; + + // Affect input model scaling + scale *= 0.5f; + v_scale[0] = v_scale[1] = v_scale[2] = scale; + + if (duration != 0 && duration != -1 && tics != -1) // don't interpolate if instantaneous or infinite in length + { + UINT32 newtime = (duration - tics); // + 1; + + pol = (newtime)/(float)duration; + + if (pol > 1.0f) + pol = 1.0f; + + if (pol < 0.0f) + pol = 0.0f; + } + + poly.red = byte2float(Surface->PolyColor.s.red); + poly.green = byte2float(Surface->PolyColor.s.green); + poly.blue = byte2float(Surface->PolyColor.s.blue); + poly.alpha = byte2float(Surface->PolyColor.s.alpha); + + tint.red = byte2float(Surface->TintColor.s.red); + tint.green = byte2float(Surface->TintColor.s.green); + tint.blue = byte2float(Surface->TintColor.s.blue); + tint.alpha = byte2float(Surface->TintColor.s.alpha); + + fade.red = byte2float(Surface->FadeColor.s.red); + fade.green = byte2float(Surface->FadeColor.s.green); + fade.blue = byte2float(Surface->FadeColor.s.blue); + fade.alpha = byte2float(Surface->FadeColor.s.alpha); + + flags = (Surface->PolyFlags | PF_Modulated); + if (Surface->PolyFlags & (PF_Additive|PF_AdditiveSource|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative)) + flags |= PF_Occlude; + else if (Surface->PolyColor.s.alpha == 0xFF) + flags |= (PF_Occlude | PF_Masked); + SetBlend(flags); + + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_NORMAL)); + + Shader_SetUniforms(Surface, &poly, &tint, &fade); + + pglEnable(GL_CULL_FACE); + +#ifdef USE_FTRANSFORM_MIRROR + // flipped is if the object is vertically flipped + // hflipped is if the object is horizontally flipped + // pos->flip is if the screen is flipped vertically + // pos->mirror is if the screen is flipped horizontally + // XOR all the flips together to figure out what culling to use! + { + boolean reversecull = (flipped ^ hflipped ^ pos->flip ^ pos->mirror); + if (reversecull) + pglCullFace(GL_FRONT); + else + pglCullFace(GL_BACK); + } +#else + // pos->flip is if the screen is flipped too + if (flipped ^ hflipped ^ pos->flip) // If one or three of these are active, but not two, invert the model's culling + { + pglCullFace(GL_FRONT); + } + else + { + pglCullFace(GL_BACK); + } +#endif + + lzml_matrix4_identity(modelMatrix); + + translate[0] = pos->x; + translate[1] = pos->z; + translate[2] = pos->y; + lzml_matrix4_translate(modelMatrix, translate); + + if (flipped) + v_scale[1] = -v_scale[1]; + if (hflipped) + v_scale[2] = -v_scale[2]; + + if (pos->roll) + { + float roll = (1.0f * pos->rollflip); + fvector3_t rotate; + + translate[0] = pos->centerx; + translate[1] = pos->centery; + translate[2] = 0.0f; + lzml_matrix4_translate(modelMatrix, translate); + + rotate[0] = rotate[1] = rotate[2] = 0.0f; + + if (pos->rotaxis == 2) // Z + rotate[2] = roll; + else if (pos->rotaxis == 1) // Y + rotate[1] = roll; + else // X + rotate[0] = roll; + + lzml_matrix4_rotate_by_vector(modelMatrix, rotate, Deg2Rad(pos->rollangle)); + + translate[0] = -translate[0]; + translate[1] = -translate[1]; + lzml_matrix4_translate(modelMatrix, translate); + } + +#ifdef USE_FTRANSFORM_ANGLEZ + lzml_matrix4_rotate_z(modelMatrix, -Deg2Rad(pos->anglez)); // rotate by slope from Kart +#endif + lzml_matrix4_rotate_y(modelMatrix, -Deg2Rad(pos->angley)); + lzml_matrix4_rotate_x(modelMatrix, Deg2Rad(pos->anglex)); + + lzml_matrix4_scale(modelMatrix, v_scale); + + useTinyFrames = (model->meshes[0].tinyframes != NULL); + if (useTinyFrames) + { + v_scale[0] = v_scale[1] = v_scale[2] = (1 / 64.0f); + lzml_matrix4_scale(modelMatrix, v_scale); + } + + // Don't use the VBO if it does not have the correct texture coordinates. + // (Can happen when model uses a sprite as a texture and the sprite changes) + // Comparing floats with the != operator here should be okay because they + // are just copies of glpatches' max_s and max_t values. + // Instead of the != operator, memcmp is used to avoid a compiler warning. + if (memcmp(&(model->vbo_max_s), &(model->max_s), sizeof(model->max_s)) != 0 || + memcmp(&(model->vbo_max_t), &(model->max_t), sizeof(model->max_t)) != 0) + useVBO = false; + + Shader_SetTransform(); + + for (i = 0; i < model->numMeshes; i++) + { + mesh_t *mesh = &model->meshes[i]; + + if (useTinyFrames) + { + tinyframe_t *frame = &mesh->tinyframes[frameIndex % mesh->numFrames]; + tinyframe_t *nextframe = NULL; + + if (nextFrameIndex != -1) + nextframe = &mesh->tinyframes[nextFrameIndex % mesh->numFrames]; + + if (!nextframe || fpclassify(pol) == FP_ZERO) + { + if (useVBO) + { + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_SHORT, GL_FALSE, sizeof(vbotiny_t), BUFFER_OFFSET(0)); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_BYTE, GL_FALSE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); + + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_SHORT, GL_FALSE, 0, frame->vertices); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, frame->normals); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_BYTE, GL_FALSE, 0, mesh->uvs); + + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } + } + else + { + short *vertPtr; + char *normPtr; + int j = 0; + + // Dangit, I soooo want to do this in a GLSL shader... + GLModel_AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3); + vertPtr = vertTinyBuffer; + normPtr = normTinyBuffer; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = (short)(frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j]))); + *normPtr++ = (char)(frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j]))); + } + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_SHORT, GL_FALSE, 0, vertTinyBuffer); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, mesh->uvs); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_BYTE, GL_FALSE, 0, normTinyBuffer); + + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + } + } + else + { + mdlframe_t *frame = &mesh->frames[frameIndex % mesh->numFrames]; + mdlframe_t *nextframe = NULL; + + if (nextFrameIndex != -1) + nextframe = &mesh->frames[nextFrameIndex % mesh->numFrames]; + + if (!nextframe || fpclassify(pol) == FP_ZERO) + { + if (useVBO) + { + // Zoom! Take advantage of just shoving the entire arrays to the GPU. + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, sizeof(vbo64_t), BUFFER_OFFSET(0)); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_FLOAT, GL_FALSE, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); + + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + + // No tinyframes, no mesh indices + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, frame->vertices); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, frame->normals); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_FLOAT, GL_FALSE, 0, mesh->uvs); + + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + } + } + else + { + float *vertPtr; + float *normPtr; + int j = 0; + + // Dangit, I soooo want to do this in a GLSL shader... + GLModel_AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3); + vertPtr = vertBuffer; + normPtr = normBuffer; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j])); + *normPtr++ = frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j])); + } + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, vertBuffer); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, mesh->uvs); + pglVertexAttribPointer(Shader_AttribLoc(LOC_NORMAL), 3, GL_FLOAT, GL_FALSE, 0, normBuffer); + + pglDrawArrays(GL_TRIANGLES, 0, mesh->numVertices); + } + } + } + + lzml_matrix4_identity(modelMatrix); + Shader_SetTransform(); + + pglDisableVertexAttribArray(Shader_AttribLoc(LOC_NORMAL)); + + pglDisable(GL_CULL_FACE); +} + +// -----------------+ +// DrawModel : Renders a model +// -----------------+ +static void DrawModel(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +{ + DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface); +} + +// -----------------+ +// SetTransform : +// -----------------+ +static void SetTransform(FTransform *stransform) +{ + static boolean special_splitscreen; + boolean shearing = false; + float used_fov; + + fvector3_t scale; + + lzml_matrix4_identity(viewMatrix); + lzml_matrix4_identity(modelMatrix); + + if (stransform) + { + used_fov = stransform->fovxangle; + +#ifdef USE_FTRANSFORM_MIRROR + // mirroring from Kart + if (stransform->mirror) + { + scale[0] = -stransform->scalex; + scale[1] = -stransform->scaley; + scale[2] = -stransform->scalez; + } + else +#endif + if (stransform->flip) + { + scale[0] = stransform->scalex; + scale[1] = -stransform->scaley; + scale[2] = -stransform->scalez; + } + else + { + scale[0] = stransform->scalex; + scale[1] = stransform->scaley; + scale[2] = -stransform->scalez; + } + + lzml_matrix4_scale(viewMatrix, scale); + + if (stransform->roll) + lzml_matrix4_rotate_z(viewMatrix, Deg2Rad(stransform->rollangle)); + lzml_matrix4_rotate_x(viewMatrix, Deg2Rad(stransform->anglex)); + lzml_matrix4_rotate_y(viewMatrix, Deg2Rad(stransform->angley + 270.0f)); + + lzml_matrix4_translate_x(viewMatrix, -stransform->x); + lzml_matrix4_translate_y(viewMatrix, -stransform->z); + lzml_matrix4_translate_z(viewMatrix, -stransform->y); + + special_splitscreen = stransform->splitscreen; + shearing = stransform->shearing; + } + else + used_fov = FIELD_OF_VIEW; + + lzml_matrix4_identity(projMatrix); + + if (stransform) + { + // jimita 14042019 + // Simulate Software's y-shearing + // https://zdoom.org/wiki/Y-shearing + if (shearing) + { + float fdy = stransform->viewaiming * 2; + if (stransform->flip) + fdy *= -1.0f; + lzml_matrix4_translate_y(projMatrix, (-fdy / BASEVIDHEIGHT)); + } + + if (special_splitscreen) + { + used_fov = atan(tan(used_fov*M_PI/360)*0.8)*360/M_PI; + GLPerspective(used_fov, 2*ASPECT_RATIO); + } + else + GLPerspective(used_fov, ASPECT_RATIO); + } + + Shader_SetTransform(); +} + +static INT32 GetTextureUsed(void) +{ + return GLTexture_GetMemoryUsage(TexCacheHead); +} + +static void PostImgRedraw(float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2]) +{ + INT32 x, y; + float float_x, float_y, float_nextx, float_nexty; + float xfix, yfix; + INT32 texsize = 2048; + + const float blackBack[16] = + { + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + if (ShaderState.current == NULL) + return; + + // Use a power of two texture, dammit + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + // X/Y stretch fix for all resolutions(!) + xfix = (float)(texsize)/((float)((GPUScreenWidth)/(float)(GPU_POSTIMGVERTS-1))); + yfix = (float)(texsize)/((float)((GPUScreenHeight)/(float)(GPU_POSTIMGVERTS-1))); + + pglDisable(GL_DEPTH_TEST); + pglDisable(GL_BLEND); + + pglDisableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD)); + + // Draw a black square behind the screen texture, + // so nothing shows through the edges + Shader_SetUniforms(NULL, &black, NULL, NULL); + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, blackBack); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD)); + Shader_SetUniforms(NULL, &white, NULL, NULL); + + for(x=0;x<GPU_POSTIMGVERTS-1;x++) + { + for(y=0;y<GPU_POSTIMGVERTS-1;y++) + { + float stCoords[8]; + float vertCoords[12]; + + // Used for texture coordinates + // Annoying magic numbers to scale the square texture to + // a non-square screen.. + float_x = (float)(x/(xfix)); + float_y = (float)(y/(yfix)); + float_nextx = (float)(x+1)/(xfix); + float_nexty = (float)(y+1)/(yfix); + + // float stCoords[8]; + stCoords[0] = float_x; + stCoords[1] = float_y; + stCoords[2] = float_x; + stCoords[3] = float_nexty; + stCoords[4] = float_nextx; + stCoords[5] = float_nexty; + stCoords[6] = float_nextx; + stCoords[7] = float_y; + + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, stCoords); + + // float vertCoords[12]; + vertCoords[0] = points[x][y][0] / 4.5f; + vertCoords[1] = points[x][y][1] / 4.5f; + vertCoords[2] = 1.0f; + vertCoords[3] = points[x][y + 1][0] / 4.5f; + vertCoords[4] = points[x][y + 1][1] / 4.5f; + vertCoords[5] = 1.0f; + vertCoords[6] = points[x + 1][y + 1][0] / 4.5f; + vertCoords[7] = points[x + 1][y + 1][1] / 4.5f; + vertCoords[8] = 1.0f; + vertCoords[9] = points[x + 1][y][0] / 4.5f; + vertCoords[10] = points[x + 1][y][1] / 4.5f; + vertCoords[11] = 1.0f; + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, vertCoords); + + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + + pglEnable(GL_DEPTH_TEST); + pglEnable(GL_BLEND); +} + +static void FlushScreenTextures(void) +{ + GLTexture_FlushScreenTextures(); +} + +// Create screen to fade from +static void StartScreenWipe(void) +{ + GLTexture_GenerateScreenTexture(&WipeStartTexture); +} + +// Create screen to fade to +static void EndScreenWipe(void) +{ + GLTexture_GenerateScreenTexture(&WipeEndTexture); +} + +// Draw the last scene under the intermission +static void DrawIntermissionBG(void) +{ + float xfix, yfix; + INT32 texsize = 2048; + + const float screenVerts[12] = + { + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + float fix[8]; + + if (ShaderState.current == NULL) + return; + + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + // const float screenVerts[12] + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + pglBindTexture(GL_TEXTURE_2D, ScreenTexture); + Shader_SetUniforms(NULL, &white, NULL, NULL); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, screenVerts); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, fix); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + CurrentTexture = ScreenTexture; +} + +// Do screen fades! +static void DoWipe(boolean tinted, boolean isfadingin, boolean istowhite) +{ + INT32 texsize = 2048; + float xfix, yfix; + + INT32 fademaskdownloaded = CurrentTexture; // the fade mask that has been set + + const float screenVerts[12] = + { + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + float fix[8]; + + const float defaultST[8] = + { + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f + }; + + if (ShaderState.current == NULL) + return; + + // Use a power of two texture, dammit + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + // const float screenVerts[12] + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest); + + Shader_Set(tinted ? SHADER_FADEMASK_ADDITIVEANDSUBTRACTIVE : SHADER_FADEMASK); + Shader_SetProgram(); + + pglDisableVertexAttribArray(Shader_AttribLoc(LOC_COLORS)); + pglEnableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD1)); + + Shader_SetSampler(uniform_startscreen, 0); + Shader_SetSampler(uniform_endscreen, 1); + Shader_SetSampler(uniform_fademask, 2); + + if (tinted) + { + Shader_SetIntegerUniform(uniform_isfadingin, isfadingin); + Shader_SetIntegerUniform(uniform_istowhite, istowhite); + } + + Shader_SetUniforms(NULL, &white, NULL, NULL); + Shader_SetTransform(); + + pglActiveTexture(GL_TEXTURE0 + 0); + pglBindTexture(GL_TEXTURE_2D, WipeStartTexture); + pglActiveTexture(GL_TEXTURE0 + 1); + pglBindTexture(GL_TEXTURE_2D, WipeEndTexture); + pglActiveTexture(GL_TEXTURE0 + 2); + pglBindTexture(GL_TEXTURE_2D, fademaskdownloaded); + + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, screenVerts); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD0), 2, GL_FLOAT, GL_FALSE, 0, fix); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD1), 2, GL_FLOAT, GL_FALSE, 0, defaultST); + + pglActiveTexture(GL_TEXTURE0); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + pglDisableVertexAttribArray(Shader_AttribLoc(LOC_TEXCOORD1)); + + Shader_UnSet(); + CurrentTexture = WipeEndTexture; +} + +static void DoScreenWipe(void) +{ + DoWipe(false, false, false); +} + +static void DoTintedWipe(boolean isfadingin, boolean istowhite) +{ + DoWipe(true, isfadingin, istowhite); +} + +// Create a texture from the screen. +static void MakeScreenTexture(void) +{ + GLTexture_GenerateScreenTexture(&ScreenTexture); +} + +static void MakeScreenFinalTexture(void) +{ + GLTexture_GenerateScreenTexture(&FinalScreenTexture); +} + +static void DrawScreenFinalTexture(int width, int height) +{ + float xfix, yfix; + float origaspect, newaspect; + float xoff = 1, yoff = 1; // xoffset and yoffset for the polygon to have black bars around the screen + FRGBAFloat clearColour; + INT32 texsize = 2048; + + float off[12]; + float fix[8]; + + if (ShaderState.current == NULL) + return; + + if(GPUScreenWidth <= 1024) + texsize = 1024; + if(GPUScreenWidth <= 512) + texsize = 512; + + xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); + yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); + + origaspect = (float)GPUScreenWidth / GPUScreenHeight; + newaspect = (float)width / height; + if (origaspect < newaspect) + { + xoff = origaspect / newaspect; + yoff = 1; + } + else if (origaspect > newaspect) + { + xoff = 1; + yoff = newaspect / origaspect; + } + + // float off[12]; + off[0] = -xoff; + off[1] = -yoff; + off[2] = 1.0f; + off[3] = -xoff; + off[4] = yoff; + off[5] = 1.0f; + off[6] = xoff; + off[7] = yoff; + off[8] = 1.0f; + off[9] = xoff; + off[10] = -yoff; + off[11] = 1.0f; + + // float fix[8]; + fix[0] = 0.0f; + fix[1] = 0.0f; + fix[2] = 0.0f; + fix[3] = yfix; + fix[4] = xfix; + fix[5] = yfix; + fix[6] = xfix; + fix[7] = 0.0f; + + pglViewport(0, 0, width, height); + + clearColour.red = clearColour.green = clearColour.blue = 0; + clearColour.alpha = 1; + ClearBuffer(true, false, &clearColour); + pglBindTexture(GL_TEXTURE_2D, FinalScreenTexture); + + Shader_SetUniforms(NULL, &white, NULL, NULL); + + pglBindBuffer(GL_ARRAY_BUFFER, 0); + pglVertexAttribPointer(Shader_AttribLoc(LOC_POSITION), 3, GL_FLOAT, GL_FALSE, 0, off); + pglVertexAttribPointer(Shader_AttribLoc(LOC_TEXCOORD), 2, GL_FLOAT, GL_FALSE, 0, fix); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + CurrentTexture = FinalScreenTexture; +} + +static void SetShader(int type) +{ + Shader_Set(GLBackend_GetShaderType(type)); +} + +static boolean CompileShaders(void) +{ + return Shader_Compile(); +} + +static void SetShaderInfo(INT32 info, INT32 value) +{ + Shader_SetInfo(info, value); +} + +static void LoadCustomShader(int number, char *shader, size_t size, boolean fragment) +{ + Shader_LoadCustom(number, shader, size, fragment); +} + +static void UnSetShader(void) +{ + Shader_UnSet(); +} + +static void CleanShaders(void) +{ + Shader_Clean(); +} + +struct GPURenderingAPI GLInterfaceAPI = { + Init, + + SetInitialStates, + SetModelView, + SetState, + SetTransform, + SetBlend, + SetPalette, + SetDepthBuffer, + + DrawPolygon, + DrawIndexedTriangles, + Draw2DLine, + DrawModel, + DrawSkyDome, + + SetTexture, + UpdateTexture, + DeleteTexture, + + ClearTextureCache, + GetTextureUsed, + + CreateModelVBOs, + + ReadRect, + GClipRect, + ClearBuffer, + + MakeScreenTexture, + MakeScreenFinalTexture, + FlushScreenTextures, + + StartScreenWipe, + EndScreenWipe, + DoScreenWipe, + DoTintedWipe, + DrawIntermissionBG, + DrawScreenFinalTexture, + + PostImgRedraw, + + CompileShaders, + CleanShaders, + SetShader, + UnSetShader, + + SetShaderInfo, + LoadCustomShader, +}; + +#endif //HWRENDER diff --git a/src/hardware/r_gles/r_gleslib.h b/src/hardware/r_gles/r_gleslib.h new file mode 100644 index 0000000000000000000000000000000000000000..d362c705503762c22821c44eb8d864378585e0ce --- /dev/null +++ b/src/hardware/r_gles/r_gleslib.h @@ -0,0 +1,40 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_gleslib.h +/// \brief OpenGL ES API libraries + +#if defined(__ANDROID__) + #if defined(HAVE_GLES2) + #include <GLES2/gl2.h> + #include <GLES2/gl2ext.h> + #elif defined(HAVE_GLES) + #include <GLES/gl.h> + #include <GLES/glext.h> + #endif +#elif defined(HAVE_SDL) + #define _MATH_DEFINES_DEFINED + + #ifdef _MSC_VER + #pragma warning(disable : 4214 4244) + #endif + + #if defined(__ANDROID__) + #if defined(HAVE_GLES2) + #include "SDL_opengles2.h" + #elif defined(HAVE_GLES) + #include "SDL_opengles.h" + #endif + #else + #include "SDL_opengl.h" + #endif + + #ifdef _MSC_VER + #pragma warning(default : 4214 4244) + #endif +#endif diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 99ffc64c5e414b38eacdb40709358b587776ae20..43b979c1768435654af6cff6d651aadf4d8b107d 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1,5 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2020 by Sonic Team Junior. // // This program is free software distributed under the @@ -38,21 +39,9 @@ static GLfloat ModelMatrix[16]; static GLfloat ProjectionMatrix[16]; static GLint SceneViewport[4]; -//* GLU functions */ -typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data); -static PFNgluBuild2DMipmaps pgluBuild2DMipmaps; - boolean GLBackend_LoadFunctions(void) { #ifndef STATIC_OPENGL -#define GETOPENGLFUNC(func) \ - p ## gl ## func = GLBackend_GetFunction("gl" #func); \ - if (!(p ## gl ## func)) \ - { \ - GL_MSG_Error("failed to get OpenGL function: %s", #func); \ - return false; \ - } \ - if (!GLBackend_LoadCommonFunctions()) return false; @@ -68,12 +57,10 @@ boolean GLBackend_LoadFunctions(void) GETOPENGLFUNC(EnableClientState) GETOPENGLFUNC(DisableClientState) - GETOPENGLFUNC(TexEnvi) - if (!GLBackend_LoadLegacyFunctions()) return false; - #endif + return true; } @@ -96,19 +83,17 @@ boolean GLBackend_LoadContextFunctions(void) if (GLMajorVersion >= 2) GETOPENGLFUNC(BlendEquation) + if (GLTexture_InitMipmapping()) + MipmappingSupported = GL_TRUE; + #ifdef GL_SHADERS if (GLExtension_shaders) Shader_LoadFunctions(); #endif - // GLU - pgluBuild2DMipmaps = GLBackend_GetFunction("gluBuild2DMipmaps"); - return true; } -#undef GETOPENGLFUNC - static void GLPerspective(GLfloat fovy, GLfloat aspect) { GLfloat m[4][4] = @@ -192,47 +177,75 @@ static boolean Init(void) } // -----------------+ -// SetNoTexture : Disable texture +// SetInitialStates : Set permanent states // -----------------+ -void SetNoTexture(void) +static void SetInitialStates(void) { - // Disable texture. - if (CurrentTexture != BlankTexture) - { - if (BlankTexture == 0) - { - // Generate a 1x1 white pixel for the blank texture - pglGenTextures(1, &BlankTexture); - pglBindTexture(GL_TEXTURE_2D, BlankTexture); - pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, white); - } - else - pglBindTexture(GL_TEXTURE_2D, BlankTexture); +#ifdef GL_LIGHT_MODEL_AMBIENT + GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; +#endif - CurrentTexture = BlankTexture; - } -} + pglEnable(GL_TEXTURE_2D); // two-dimensional texturing + pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -// -----------------+ -// DeleteTexture : Deletes a texture from the GPU and frees its data -// -----------------+ -static void DeleteTexture(HWRTexture_t *pTexInfo) -{ - if (!pTexInfo) - return; - else if (pTexInfo->downloaded) - pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); + pglEnable(GL_BLEND); // enable color blending + pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - GLTexture_Delete(pTexInfo); - pTexInfo->downloaded = 0; + pglEnable(GL_ALPHA_TEST); + pglAlphaFunc(GL_NOTEQUAL, 0.0f); + + pglEnable(GL_DEPTH_TEST); // check the depth buffer + pglDepthMask(GL_TRUE); // enable writing to depth buffer + GPU->SetDepthBuffer(); + + // Hurdler: not necessary, is it? + pglShadeModel(GL_SMOOTH); // iterate vertex colors + + // this sets CurrentPolyFlags to the actual configuration + CurrentPolyFlags = 0xFFFFFFFF; + GPU->SetBlend(0); + + CurrentTexture = 0; + GLTexture_SetNoTexture(); + + pglPolygonOffset(-1.0f, -1.0f); + + pglLoadIdentity(); + pglScalef(1.0f, 1.0f, -1.0f); + pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer) + + // Lighting for models +#ifdef GL_LIGHT_MODEL_AMBIENT + pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse); + pglEnable(GL_LIGHT0); +#endif } // -----------------+ -// ClearTextureCache: Flush OpenGL textures from memory +// SetModelView : Resets the viewport state // -----------------+ -static void ClearTextureCache(void) +static void SetModelView(INT32 w, INT32 h) { - GLTexture_Flush(); + // The screen textures need to be flushed if the width or height change so that they be remade for the correct size + if (GPUScreenWidth != w || GPUScreenHeight != h) + GPU->FlushScreenTextures(); + + GPUScreenWidth = (GLint)w; + GPUScreenHeight = (GLint)h; + + pglViewport(0, 0, w, h); + + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + + pglMatrixMode(GL_MODELVIEW); + pglLoadIdentity(); + + GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO); + + // added for new coronas' code (without depth buffer) + pglGetIntegerv(GL_VIEWPORT, SceneViewport); + pglGetFloatv(GL_PROJECTION_MATRIX, ProjectionMatrix); } // -----------------+ @@ -240,11 +253,9 @@ static void ClearTextureCache(void) // : store pixels as 16bit 565 RGB // Returns : 16bit 565 RGB pixel array stored in dst_data // -----------------+ -static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height, - INT32 dst_stride, UINT16 * dst_data) +static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 * dst_data) { INT32 i; - // GL_DBG_Printf ("ReadRect()\n"); if (dst_stride == width*3) { GLubyte*top = (GLvoid*)dst_data, *bottom = top + dst_stride * (height - 1); @@ -287,16 +298,13 @@ static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height, } // -----------------+ -// GClipRect : Defines the 2D hardware clipping window +// GClipRect : Defines the 2D clipping window // -----------------+ static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip) { - // GL_DBG_Printf ("GClipRect(%d, %d, %d, %d)\n", minx, miny, maxx, maxy); - pglViewport(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny); NearClippingPlane = nearclip; - //pglScissor(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny); pglMatrixMode(GL_PROJECTION); pglLoadIdentity(); GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO); @@ -308,31 +316,27 @@ static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float near } // -----------------+ -// SetPalette : Changes the current texture palette +// SetBlend : Set blend mode // -----------------+ -static void SetPalette(RGBA_t *palette) +static void SetBlend(UINT32 PolyFlags) { - size_t palsize = (sizeof(RGBA_t) * 256); - // on a palette change, you have to reload all of the textures - if (memcmp(&GPUTexturePalette, palette, palsize)) - { - memcpy(&GPUTexturePalette, palette, palsize); - GLTexture_Flush(); - } + GLState_SetBlend(PolyFlags); } -void SetClamp(GLenum pname) +// -----------------+ +// SetPalette : Changes the current texture palette +// -----------------+ +static void SetPalette(RGBA_t *palette) { - pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP); // fallback clamp - pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP_TO_EDGE); + GLState_SetPalette(palette); } // -----------------+ -// SetBlend : Set render mode +// SetDepthBuffer : Set depth buffer state // -----------------+ -static void SetBlend(UINT32 PolyFlags) +static void SetDepthBuffer(void) { - SetBlendingStates(PolyFlags); + GLState_SetDepthBuffer(); } // -----------------+ @@ -340,7 +344,6 @@ static void SetBlend(UINT32 PolyFlags) // -----------------+ static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor) { - // GL_DBG_Printf ("ClearBuffer(%d)\n", alpha); GLbitfield ClearMask = 0; if (ColorMask) @@ -349,11 +352,10 @@ static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearC pglClearColor(ClearColor->red, ClearColor->green, ClearColor->blue, ClearColor->alpha); ClearMask |= GL_COLOR_BUFFER_BIT; } + if (DepthMask) { - pglClearDepth(1.0f); //Hurdler: all that are permanen states - pglDepthRange(0.0f, 1.0f); - pglDepthFunc(GL_LEQUAL); + SetDepthBuffer(); ClearMask |= GL_DEPTH_BUFFER_BIT; } @@ -367,18 +369,12 @@ static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearC // -----------------+ // Draw2DLine : Render a 2D line // -----------------+ -static void Draw2DLine(F2DCoord * v1, - F2DCoord * v2, - RGBA_t Color) +static void Draw2DLine(F2DCoord *v1, F2DCoord *v2, RGBA_t Color) { - // GL_DBG_Printf ("DrawLine() (%f %f %f) %d\n", v1->x, -v1->y, -v1->z, v1->argb); GLfloat p[12]; GLfloat dx, dy; GLfloat angle; - // BP: we should reflect the new state in our variable - //SetBlend(PF_Modulated|PF_NoTexture); - pglDisable(GL_TEXTURE_2D); // This is the preferred, 'modern' way of rendering lines -- creating a polygon. @@ -395,7 +391,7 @@ static void Draw2DLine(F2DCoord * v1, p[9] = v1->x + dx; p[10] = -(v1->y - dy); p[11] = 1; pglDisableClientState(GL_TEXTURE_COORD_ARRAY); - pglColor4ubv((GLubyte*)&Color.s); + GLState_SetColorUBV((GLubyte*)&Color.s); pglVertexPointer(3, GL_FLOAT, 0, p); pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -406,215 +402,39 @@ static void Draw2DLine(F2DCoord * v1, // -----------------+ // UpdateTexture : Updates the texture data. // -----------------+ -static void UploadTexture(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer, GLenum format, boolean update) +static void UpdateTexture(HWRTexture_t *pTexInfo) { - INT32 w = pTexInfo->width; - INT32 h = pTexInfo->height; - - GLint maxLOD = 5; - if (pTexInfo->format == GPU_TEXFMT_ALPHA_INTENSITY_88 || pTexInfo->format == GPU_TEXFMT_ALPHA_8) - maxLOD = 4; - - if (MipmappingEnabled) - { - pgluBuild2DMipmaps(GL_TEXTURE_2D, format, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pTextureBuffer); - - // Control the mipmap level of detail - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); // the lower the number, the higher the detail - - if (pTexInfo->flags & TF_TRANSPARENT) - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); // No mipmaps on transparent stuff - else - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, maxLOD); - } - else - { - if (update) - pglTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pTextureBuffer); - else - pglTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTextureBuffer); - } + GLTexture_Update(pTexInfo); } -static void UpdateTexture(HWRTexture_t *pTexInfo) +// -----------------+ +// SetTexture : Uploads the texture, and sets it as the current one. +// -----------------+ +static void SetTexture(HWRTexture_t *pTexInfo) { - boolean update = true; - static RGBA_t textureBuffer[2048 * 2048]; - const GLvoid *pTextureBuffer = textureBuffer; - INT32 w = pTexInfo->width; - INT32 h = pTexInfo->height; - GLuint textureName = 0; - - if (!pTexInfo->downloaded) - { - pglGenTextures(1, &textureName); - pTexInfo->downloaded = textureName; - update = false; - } - else - textureName = pTexInfo->downloaded; - - //GL_DBG_Printf ("UpdateTexture %d %x\n",(INT32)textureName,pTexInfo->data); - - if ((pTexInfo->format == GPU_TEXFMT_P_8) || - (pTexInfo->format == GPU_TEXFMT_AP_88)) - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - if ((*pImgData == GPU_PATCHES_CHROMAKEY_COLORINDEX) && - (pTexInfo->flags & TF_CHROMAKEYED)) - { - textureBuffer[w*j+i].s.red = 0; - textureBuffer[w*j+i].s.green = 0; - textureBuffer[w*j+i].s.blue = 0; - textureBuffer[w*j+i].s.alpha = 0; - pTexInfo->flags |= TF_TRANSPARENT; // there is a hole in it - } - else - { - textureBuffer[w*j+i].s.red = GPUTexturePalette[*pImgData].s.red; - textureBuffer[w*j+i].s.green = GPUTexturePalette[*pImgData].s.green; - textureBuffer[w*j+i].s.blue = GPUTexturePalette[*pImgData].s.blue; - textureBuffer[w*j+i].s.alpha = GPUTexturePalette[*pImgData].s.alpha; - } - - pImgData++; - - if (pTexInfo->format == GPU_TEXFMT_AP_88) - { - if (!(pTexInfo->flags & TF_CHROMAKEYED)) - textureBuffer[w*j+i].s.alpha = *pImgData; - pImgData++; - } - } - } - } - else if (pTexInfo->format == GPU_TEXFMT_RGBA) - { - // corona test : passed as ARGB 8888, which is not in glide formats - // Hurdler: not used for coronas anymore, just for dynamic lighting - pTextureBuffer = pTexInfo->data; - } - else if (pTexInfo->format == GPU_TEXFMT_ALPHA_INTENSITY_88) - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - textureBuffer[w*j+i].s.red = *pImgData; - textureBuffer[w*j+i].s.green = *pImgData; - textureBuffer[w*j+i].s.blue = *pImgData; - pImgData++; - textureBuffer[w*j+i].s.alpha = *pImgData; - pImgData++; - } - } - } - else if (pTexInfo->format == GPU_TEXFMT_ALPHA_8) // Used for fade masks - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - textureBuffer[w*j+i].s.red = 255; // 255 because the fade mask is modulated with the screen texture, so alpha affects it while the colours don't - textureBuffer[w*j+i].s.green = 255; - textureBuffer[w*j+i].s.blue = 255; - textureBuffer[w*j+i].s.alpha = *pImgData; - pImgData++; - } - } - } - else - GL_MSG_Warning ("SetTexture(bad format) %ld\n", pTexInfo->format); - - // the texture number was already generated by pglGenTextures - pglBindTexture(GL_TEXTURE_2D, textureName); - CurrentTexture = textureName; - - // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues - if (pTexInfo->flags & TF_TRANSPARENT) - { - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - else - { - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MipmapMagFilter); - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MipmapMinFilter); - } - - if (pTexInfo->format == GPU_TEXFMT_ALPHA_INTENSITY_88) - UploadTexture(pTexInfo, pTextureBuffer, GL_LUMINANCE_ALPHA, update); - else if (pTexInfo->format == GPU_TEXFMT_ALPHA_8) - UploadTexture(pTexInfo, pTextureBuffer, GL_ALPHA, update); - else - UploadTexture(pTexInfo, pTextureBuffer, GPUTextureFormat, update); - - if (pTexInfo->flags & TF_WRAPX) - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - else - SetClamp(GL_TEXTURE_WRAP_S); - - if (pTexInfo->flags & TF_WRAPY) - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - else - SetClamp(GL_TEXTURE_WRAP_T); - - if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy) - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, AnisotropicFilter); + GLTexture_Set(pTexInfo); } // -----------------+ -// SetTexture : The texture becomes the current source +// DeleteTexture : Deletes a texture from the GPU, and frees its data. // -----------------+ -static void SetTexture(HWRTexture_t *pTexInfo) +static void DeleteTexture(HWRTexture_t *pTexInfo) { if (!pTexInfo) - { - SetNoTexture(); return; - } else if (pTexInfo->downloaded) - { - if (pTexInfo->downloaded != CurrentTexture) - { - pglBindTexture(GL_TEXTURE_2D, pTexInfo->downloaded); - CurrentTexture = pTexInfo->downloaded; - } - } - else - { - FTextureInfo *newTex = calloc(1, sizeof (*newTex)); - - UpdateTexture(pTexInfo); + pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); - newTex->texture = pTexInfo; - newTex->name = (UINT32)pTexInfo->downloaded; - newTex->width = (UINT32)pTexInfo->width; - newTex->height = (UINT32)pTexInfo->height; - newTex->format = (UINT32)pTexInfo->format; + GLTexture_Delete(pTexInfo); + pTexInfo->downloaded = 0; +} - // insertion at the tail - if (TexCacheTail) - { - newTex->prev = TexCacheTail; - TexCacheTail->next = newTex; - TexCacheTail = newTex; - } - else // initialization of the linked list - TexCacheTail = TexCacheHead = newTex; - } +// -----------------+ +// ClearTextureCache: Flush OpenGL textures from memory +// -----------------+ +static void ClearTextureCache(void) +{ + GLTexture_Flush(); } // code that is common between DrawPolygon and DrawIndexedTriangles @@ -639,7 +459,7 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 Po poly.blue = byte2float(pSurf->PolyColor.s.blue); poly.alpha = byte2float(pSurf->PolyColor.s.alpha); - pglColor4ubv((GLubyte*)&pSurf->PolyColor.s); + GLState_SetColorUBV((GLubyte*)&pSurf->PolyColor.s); } // Tint color @@ -659,14 +479,13 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 Po // I think I should do a separate function for drawing coronas, so it will be a little faster if (PolyFlags & PF_Corona) // check to see if we need to draw the corona { - UINT32 i; - UINT32 j; + UINT32 i, j; //rem: all 8 (or 8.0f) values are hard coded: it can be changed to a higher value - GLfloat buf[8][8]; - GLfloat cx, cy, cz; - GLfloat px = 0.0f, py = 0.0f, pz = -1.0f; - GLfloat scalef = 0.0f; + GLfloat buf[8][8]; + GLfloat cx, cy, cz; + GLfloat px = 0.0f, py = 0.0f, pz = -1.0f; + GLfloat scalef = 0.0f; GLubyte c[4]; @@ -715,7 +534,7 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 Po alpha = byte2float(pSurf->PolyColor.s.alpha); alpha *= scalef; // change the alpha value (it seems better than changing the size of the corona) c[3] = (unsigned char)(alpha * 255); - pglColor4ubv(c); + GLState_SetColorUBV(c); } Shader_SetUniforms(pSurf, &poly, &tint, &fade); @@ -736,12 +555,15 @@ static void DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumP pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); if (PolyFlags & PF_ForceWrapX) - SetClamp(GL_TEXTURE_WRAP_S); + GLState_SetClamp(GL_TEXTURE_WRAP_S); if (PolyFlags & PF_ForceWrapY) - SetClamp(GL_TEXTURE_WRAP_T); + GLState_SetClamp(GL_TEXTURE_WRAP_T); } +// ---------------------+ +// DrawIndexedTriangles : Renders indexed triangles +// ---------------------+ static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray) { PreparePolygon(pSurf, pOutVerts, PolyFlags); @@ -753,6 +575,9 @@ static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UIN // the DrawPolygon variant of this has some code about polyflags and wrapping here but havent noticed any problems from omitting it? } +// -----------------+ +// DrawSkyDome : Renders a sky dome +// -----------------+ static void DrawSkyDome(FSkyDome *sky) { int i, j; @@ -762,15 +587,12 @@ static void DrawSkyDome(FSkyDome *sky) // Build the sky dome! Yes! if (sky->rebuild) { - // delete VBO when already exists if (GLExtension_vertex_buffer_object) { + // delete VBO when already exists if (sky->vbo) pglDeleteBuffers(1, &sky->vbo); - } - if (GLExtension_vertex_buffer_object) - { // generate a new VBO and get the associated ID pglGenBuffers(1, &sky->vbo); @@ -827,7 +649,7 @@ static void DrawSkyDome(FSkyDome *sky) } pglScalef(1.0f, 1.0f, 1.0f); - pglColor4ubv(white); + GLState_SetColorUBV(white); // bind with 0, so, switch back to normal pointer operation if (GLExtension_vertex_buffer_object) @@ -850,13 +672,16 @@ static void SetState(INT32 State, INT32 Value) break; case GPU_STATE_TEXTUREFILTERMODE: - GLTexture_SetFilterMode(Value); - if (!pgluBuild2DMipmaps) + if (MipmappingSupported) + { + GLState_SetFilterMode(Value); + GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this + } + else { MipmappingEnabled = GL_FALSE; MipmapMinFilter = GL_LINEAR; } - GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this break; case GPU_STATE_TEXTUREANISOTROPICMODE: @@ -958,7 +783,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 } #endif else - pglColor4ubv((GLubyte*)&Surface->PolyColor.s); + GLState_SetColorUBV((GLubyte*)&Surface->PolyColor.s); tint.red = byte2float(Surface->TintColor.s.red); tint.green = byte2float(Surface->TintColor.s.green); @@ -1120,6 +945,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 { if (useVBO) { + // Zoom! Take advantage of just shoving the entire arrays to the GPU. pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); @@ -1127,7 +953,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); // No tinyframes, no mesh indices - //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); pglBindBuffer(GL_ARRAY_BUFFER, 0); } else @@ -1148,7 +973,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 GLModel_AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3); vertPtr = vertBuffer; normPtr = normBuffer; - //int j = 0; for (j = 0; j < mesh->numVertices * 3; j++) { @@ -1295,7 +1119,7 @@ static void PostImgRedraw(float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2]) // Draw a black square behind the screen texture, // so nothing shows through the edges - pglColor4ubv(white); + GLState_SetColorUBV(white); pglVertexPointer(3, GL_FLOAT, 0, blackBack); pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -1352,17 +1176,9 @@ static void PostImgRedraw(float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2]) pglEnable(GL_BLEND); } -// Sryder: This needs to be called whenever the screen changes resolution in order to reset the screen textures to use -// a new size static void FlushScreenTextures(void) { - pglDeleteTextures(1, &ScreenTexture); - pglDeleteTextures(1, &FinalScreenTexture); - pglDeleteTextures(1, &WipeStartTexture); - pglDeleteTextures(1, &WipeEndTexture); - - ScreenTexture = FinalScreenTexture = 0; - WipeStartTexture = WipeEndTexture = 0; + GLTexture_FlushScreenTextures(); } // Create screen to fade from @@ -1383,7 +1199,7 @@ static void DrawIntermissionBG(void) float xfix, yfix; INT32 texsize = 2048; - const float GPU_POSTIMGVERTS[12] = + const float screenVerts[12] = { -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, @@ -1416,10 +1232,10 @@ static void DrawIntermissionBG(void) pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); pglBindTexture(GL_TEXTURE_2D, ScreenTexture); - pglColor4ubv(white); + GLState_SetColorUBV(white); pglTexCoordPointer(2, GL_FLOAT, 0, fix); - pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); CurrentTexture = ScreenTexture; @@ -1433,7 +1249,7 @@ static void DoScreenWipe(void) INT32 fademaskdownloaded = CurrentTexture; // the fade mask that has been set - const float GPU_POSTIMGVERTS[12] = + const float screenVerts[12] = { -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, @@ -1463,7 +1279,7 @@ static void DoScreenWipe(void) xfix = 1/((float)(texsize)/((float)((GPUScreenWidth)))); yfix = 1/((float)(texsize)/((float)((GPUScreenHeight)))); - // const float GPU_POSTIMGVERTS[12] + // const float screenVerts[12] // float fix[8]; fix[0] = 0.0f; @@ -1482,9 +1298,9 @@ static void DoScreenWipe(void) // Draw the original screen pglBindTexture(GL_TEXTURE_2D, WipeStartTexture); - pglColor4ubv(white); + GLState_SetColorUBV(white); pglTexCoordPointer(2, GL_FLOAT, 0, fix); - pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest); @@ -1505,7 +1321,7 @@ static void DoScreenWipe(void) pglClientActiveTexture(GL_TEXTURE0); pglTexCoordPointer(2, GL_FLOAT, 0, fix); - pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS); + pglVertexPointer(3, GL_FLOAT, 0, screenVerts); pglClientActiveTexture(GL_TEXTURE1); pglEnableClientState(GL_TEXTURE_COORD_ARRAY); pglTexCoordPointer(2, GL_FLOAT, 0, defaultST); @@ -1593,7 +1409,7 @@ static void DrawScreenFinalTexture(int width, int height) ClearBuffer(true, false, &clearColour); pglBindTexture(GL_TEXTURE_2D, FinalScreenTexture); - pglColor4ubv(white); + GLState_SetColorUBV(white); pglTexCoordPointer(2, GL_FLOAT, 0, fix); pglVertexPointer(3, GL_FLOAT, 0, off); @@ -1658,13 +1474,14 @@ static void CleanShaders(void) struct GPURenderingAPI GLInterfaceAPI = { Init, - NULL, + SetInitialStates, + SetModelView, SetState, SetTransform, SetBlend, SetPalette, - ClearBuffer, + SetDepthBuffer, DrawPolygon, DrawIndexedTriangles, @@ -1683,6 +1500,7 @@ struct GPURenderingAPI GLInterfaceAPI = { ReadRect, GClipRect, + ClearBuffer, MakeScreenTexture, MakeScreenFinalTexture, @@ -1691,6 +1509,7 @@ struct GPURenderingAPI GLInterfaceAPI = { StartScreenWipe, EndScreenWipe, DoScreenWipe, + NULL, DrawIntermissionBG, DrawScreenFinalTexture, @@ -1705,82 +1524,4 @@ struct GPURenderingAPI GLInterfaceAPI = { LoadCustomShader, }; -// -----------------+ -// SetModelView : -// -----------------+ -void SetModelView(GLint w, GLint h) -{ -// GL_DBG_Printf("SetModelView(): %dx%d\n", (int)w, (int)h); - - // The screen textures need to be flushed if the width or height change so that they be remade for the correct size - if (GPUScreenWidth != w || GPUScreenHeight != h) - FlushScreenTextures(); - - GPUScreenWidth = w; - GPUScreenHeight = h; - - pglViewport(0, 0, w, h); - - pglMatrixMode(GL_PROJECTION); - pglLoadIdentity(); - - pglMatrixMode(GL_MODELVIEW); - pglLoadIdentity(); - - GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO); - - // added for new coronas' code (without depth buffer) - pglGetIntegerv(GL_VIEWPORT, SceneViewport); - pglGetFloatv(GL_PROJECTION_MATRIX, ProjectionMatrix); -} - - -// -----------------+ -// SetStates : Set permanent states -// -----------------+ -void SetStates(void) -{ -#ifdef GL_LIGHT_MODEL_AMBIENT - GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; -#endif - - pglEnable(GL_TEXTURE_2D); // two-dimensional texturing - pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - pglEnable(GL_BLEND); // enable color blending - pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - pglEnable(GL_ALPHA_TEST); - pglAlphaFunc(GL_NOTEQUAL, 0.0f); - - pglEnable(GL_DEPTH_TEST); // check the depth buffer - pglDepthMask(GL_TRUE); // enable writing to depth buffer - - pglClearDepth(1.0f); - pglDepthRange(0.0f, 1.0f); - pglDepthFunc(GL_LEQUAL); - - // Hurdler: not necessary, is it? - pglShadeModel(GL_SMOOTH); // iterate vertice colors - - // this set CurrentPolyFlags to the actual configuration - CurrentPolyFlags = 0xFFFFFFFF; - SetBlend(0); - - CurrentTexture = 0; - SetNoTexture(); - - pglPolygonOffset(-1.0f, -1.0f); - - pglLoadIdentity(); - pglScalef(1.0f, 1.0f, -1.0f); - pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer) - - // Lighting for models -#ifdef GL_LIGHT_MODEL_AMBIENT - pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse); - pglEnable(GL_LIGHT0); -#endif -} - #endif //HWRENDER diff --git a/src/hardware/r_opengl/r_opengl.h b/src/hardware/r_opengl/r_opengl.h index 6c864373019dba29ab20e0bb29d04e2445edb0a8..43588ddd79bf6b51db709f1968f50b22c027ead0 100644 --- a/src/hardware/r_opengl/r_opengl.h +++ b/src/hardware/r_opengl/r_opengl.h @@ -13,30 +13,10 @@ #ifndef _R_OPENGL_H_ #define _R_OPENGL_H_ -#ifdef HAVE_SDL -#define _MATH_DEFINES_DEFINED - -#ifdef _MSC_VER -#pragma warning(disable : 4214 4244) -#endif - -#ifndef HAVE_GLES2 -#include "SDL_opengl.h" //Alam_GBC: Simple, yes? -#endif - -#ifdef _MSC_VER -#pragma warning(default : 4214 4244) -#endif - -#else -#include <GL/gl.h> -#include <GL/glu.h> - -#ifdef STATIC_OPENGL // Because of the 1.3 functions, you'll need GLext to compile it if static +#if !defined(HAVE_SDL) && defined(STATIC_OPENGL) // Because of the 1.3 functions, you'll need GLext to compile it if static #define GL_GLEXT_PROTOTYPES #include <GL/glext.h> #endif -#endif #define _CREATE_DLL_ // necessary for Unix AND Windows #include "../../doomdef.h" diff --git a/src/hardware/shaders/gl_shaders.c b/src/hardware/shaders/gl_shaders.c index 756300cd7b41e3e7d66791720ec2fcb98a243c32..76c5517a29457ae8bd475491aa4b0ed07d4136bb 100644 --- a/src/hardware/shaders/gl_shaders.c +++ b/src/hardware/shaders/gl_shaders.c @@ -10,11 +10,26 @@ /// \brief OpenGL shaders #include "gl_shaders.h" + #include "../r_glcommon/r_glcommon.h" +#include "../hw_dll.h" + +#ifdef HAVE_GLES +#include "../r_gles/r_gles.h" +#else +#include "../r_opengl/r_opengl.h" +#endif + +#include "../../doomdef.h" +#include "../../doomtype.h" +#include "../../doomdata.h" + GLboolean ShadersEnabled = GL_FALSE; INT32 ShadersAllowed = GPU_SHADEROPTION_OFF; +#ifdef GL_SHADERS + typedef GLuint (R_GL_APIENTRY *PFNglCreateShader) (GLenum); typedef void (R_GL_APIENTRY *PFNglShaderSource) (GLuint, GLsizei, const GLchar**, GLint*); typedef void (R_GL_APIENTRY *PFNglCompileShader) (GLuint); @@ -115,6 +130,12 @@ static struct { // Sky shader {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER}, +#ifdef HAVE_GLES2 + // Fade mask shaders + {GLSL_FADEMASK_VERTEX_SHADER, GLSL_FADEMASK_FRAGMENT_SHADER}, + {GLSL_FADEMASK_VERTEX_SHADER, GLSL_FADEMASK_ADDITIVEANDSUBTRACTIVE_FRAGMENT_SHADER}, +#endif + {NULL, NULL}, }; @@ -434,7 +455,7 @@ static boolean Shader_CompileProgram(FShaderObject *shader, GLint i, const GLcha shader->attributes[attrib_texcoord] = GETATTRIB("a_texcoord"); shader->attributes[attrib_normal] = GETATTRIB("a_normal"); shader->attributes[attrib_colors] = GETATTRIB("a_colors"); - shader->attributes[attrib_fadetexcoord] = GETATTRIB("a_fadetexcoord"); + shader->attributes[attrib_fadetexcoord] = GETATTRIB("a_fademasktexcoord"); #undef GETATTRIB @@ -503,11 +524,16 @@ boolean Shader_Compile(void) return true; } -static void Shader_SetIfChanged(FShaderObject *shader) +void Shader_SetProgram(void) +{ + pglUseProgram(ShaderState.current->program); +} + +void Shader_SetProgramIfChanged(void) { if (ShaderState.changed) { - pglUseProgram(shader->program); + pglUseProgram(ShaderState.current->program); ShaderState.changed = false; } } @@ -519,7 +545,7 @@ void Shader_SetTransform(void) if (!shader) return; - Shader_SetIfChanged(shader); + Shader_SetProgramIfChanged(); if (memcmp(projMatrix, shader->projMatrix, sizeof(fmatrix4_t))) { @@ -553,7 +579,7 @@ void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *t return; } - Shader_SetIfChanged(shader); + Shader_SetProgramIfChanged(); // Color uniforms can be left NULL and will be set to white (1.0f, 1.0f, 1.0f, 1.0f) if (poly == NULL) @@ -579,7 +605,6 @@ void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *t if (uniform != -1) \ function (uniform, a, b, c, d); - // polygon UNIFORM_4(shader->uniforms[uniform_poly_color], poly->red, poly->green, poly->blue, poly->alpha, pglUniform4f); UNIFORM_4(shader->uniforms[uniform_tint_color], tint->red, tint->green, tint->blue, tint->alpha, pglUniform4f); UNIFORM_4(shader->uniforms[uniform_fade_color], fade->red, fade->green, fade->blue, fade->alpha, pglUniform4f); @@ -606,8 +631,10 @@ void Shader_SetSampler(EShaderUniform uniform, GLint value) if (!shader) return; - Shader_SetIfChanged(shader); + Shader_SetProgramIfChanged(); if (shader->uniforms[uniform] != -1) pglUniform1i(shader->uniforms[uniform], value); } + +#endif // GL_SHADERS diff --git a/src/hardware/shaders/gl_shaders.h b/src/hardware/shaders/gl_shaders.h index f925c291fa52196c9ec9093f202967526a774217..de43f0904d07af7ffe8076d38da40f3bf309c002 100644 --- a/src/hardware/shaders/gl_shaders.h +++ b/src/hardware/shaders/gl_shaders.h @@ -93,7 +93,7 @@ typedef struct FShaderObject GLint uniforms[uniform_max+1]; #ifdef HAVE_GLES2 - GLint attributes[attribute_max+1]; + GLint attributes[attrib_max+1]; #endif #ifdef HAVE_GLES2 @@ -120,6 +120,9 @@ extern FShaderState ShaderState; void Shader_Set(int type); void Shader_UnSet(void); +void Shader_SetProgram(void); +void Shader_SetProgramIfChanged(void); + #ifdef HAVE_GLES2 void Shader_SetTransform(void); #endif diff --git a/src/hardware/shaders/shaders_gles2.h b/src/hardware/shaders/shaders_gles2.h index 3b2e99ff36fd19cae75979bd69f4e1ae7a29e4f0..8481d95a5f56bab32ff407bf3bcefa51ed79d6d8 100644 --- a/src/hardware/shaders/shaders_gles2.h +++ b/src/hardware/shaders/shaders_gles2.h @@ -46,13 +46,13 @@ "attribute vec2 a_texcoord;\n" \ "attribute vec2 a_fademasktexcoord;\n" \ "varying vec2 v_texcoord;\n" \ - "varying vec3 v_fademasktexcoord;\n" \ + "varying vec2 v_fademasktexcoord;\n" \ "uniform mat4 u_projection;\n" \ "void main()\n" \ "{\n" \ "gl_Position = u_projection * vec4(a_position, 1.0f);\n" \ "v_texcoord = vec2(a_texcoord.x, a_texcoord.y);\n" \ - "v_fademasktexcoord = vec2(a_fadetex.x, a_fadetex.y);\n" \ + "v_fademasktexcoord = vec2(a_fademasktexcoord.x, a_fademasktexcoord.y);\n" \ "}\0" // replicates the way fixed function lighting is used by the model lighting option, @@ -84,6 +84,7 @@ // ================== #define GLSL_BASE_SAMPLER "uniform sampler2D t_texsampler;\n" + #define GLSL_BASE_UNIFORMS \ GLSL_BASE_SAMPLER \ "uniform vec4 poly_color;\n" \ @@ -112,7 +113,7 @@ #define GLSL_FADEMASK_BASE_IN \ "varying vec2 v_texcoord;\n" \ - "varying vec3 v_fademasktexcoord;\n" \ + "varying vec2 v_fademasktexcoord;\n" \ "uniform sampler2D t_startscreen;\n" \ "uniform sampler2D t_endscreen;\n" \ "uniform sampler2D t_fademask;\n" diff --git a/src/m_anigif.c b/src/m_anigif.c index 41f99254eff59fea3d80f7592168723d9de7a975..33afee2a4c1167274eec4aff8b90f8815de343b5 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -512,7 +512,7 @@ static void GIF_rgbconvert(UINT8 *linear, UINT8 *scr) { UINT8 r, g, b; size_t src = 0, dest = 0; - size_t size = (vid.width * vid.height * 3); + size_t size = (vid.width * vid.height * SCREENSHOT_BITS); InitColorLUT(&gif_colorlookup, (gif_localcolortable) ? gif_framepalette : gif_headerpalette, true); @@ -522,7 +522,7 @@ static void GIF_rgbconvert(UINT8 *linear, UINT8 *scr) g = (UINT8)linear[src + 1]; b = (UINT8)linear[src + 2]; scr[dest] = GetColorLUTDirect(&gif_colorlookup, r, g, b); - src += (3 * scrbuf_downscaleamt); + src += (SCREENSHOT_BITS * scrbuf_downscaleamt); dest += scrbuf_downscaleamt; } } @@ -569,7 +569,7 @@ static void GIF_framewrite(void) #ifdef HWRENDER else if (rendermode == render_opengl) { - UINT8 *linear = HWR_GetScreenshot(); + UINT8 *linear = HWR_GetScreenBuffer(); GIF_rgbconvert(linear, movie_screen); free(linear); } @@ -585,7 +585,7 @@ static void GIF_framewrite(void) // Copy the current OpenGL frame into the base screen if (rendermode == render_opengl) { - UINT8 *linear = HWR_GetScreenshot(); + UINT8 *linear = HWR_GetScreenBuffer(); GIF_rgbconvert(linear, screens[0]); free(linear); } diff --git a/src/m_misc.c b/src/m_misc.c index d97d8f94be36972a82880f79316ac8d2f7149131..fba5697e3df2e2cbd9e5eef3834cfc918e0737ed 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -49,6 +49,7 @@ #ifdef HWRENDER #include "hardware/hw_main.h" +#include "hardware/hw_gpu.h" // GPUInterface_GetAPIName #endif #ifdef HAVE_SDL @@ -768,7 +769,12 @@ static void M_PNGhdr(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png_ } else { - png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, + png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, +#ifdef SCREENSHOT_USE_RGBA + PNG_COLOR_TYPE_RGBA, +#else + PNG_COLOR_TYPE_RGB, +#endif png_interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info_before_PLTE(png_ptr, png_info_ptr); png_set_compression_strategy(png_ptr, Z_FILTERED); @@ -810,7 +816,7 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png strcpy(rendermodetxt, "Software"); break; case render_opengl: - strcpy(rendermodetxt, "OpenGL"); + strcpy(rendermodetxt, GPUInterface_GetAPIName()); break; default: // Just in case strcpy(rendermodetxt, "None"); @@ -1257,7 +1263,7 @@ void M_SaveFrame(void) } #ifdef HWRENDER else - linear = HWR_GetScreenshot(); + linear = HWR_GetScreenBuffer(); #endif M_PNGFrame(apng_ptr, apng_info_ptr, (png_bytep)linear); #ifdef HWRENDER @@ -1518,7 +1524,7 @@ void M_ScreenShot(void) * The screenshot is saved as "srb2xxxx.png" where xxxx is the lowest * four-digit number for which a file does not already exist. * - * \sa HWR_ScreenShot + * \sa HWR_TakeScreenshot */ void M_DoScreenShot(void) { @@ -1572,7 +1578,7 @@ void M_DoScreenShot(void) // save the pcx file #ifdef HWRENDER if (rendermode == render_opengl) - ret = HWR_Screenshot(va(pandf,pathname,freename)); + ret = HWR_TakeScreenshot(va(pandf,pathname,freename)); else #endif { diff --git a/src/m_misc.h b/src/m_misc.h index dbded37d0a47fdc81c0488098e5bf7ac8e677143..7daf1342b6b26464b461c61f5a124b5edcf22d10 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -39,6 +39,13 @@ void M_StartMovie(void); void M_SaveFrame(void); void M_StopMovie(void); +#ifdef HAVE_GLES +#define SCREENSHOT_USE_RGBA +#define SCREENSHOT_BITS 4 +#else +#define SCREENSHOT_BITS 3 +#endif + // the file where game vars and settings are saved #define CONFIGFILENAME "config.cfg" diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index 86957caaffc06e7acd16b8de8a12f57f80fe6b7d..1ef171dff51f93177f585330569d311579e58651 100644 --- a/src/sdl/Makefile.cfg +++ b/src/sdl/Makefile.cfg @@ -50,7 +50,7 @@ endif OPTS+=-DDIRECTFULLSCREEN -DHAVE_SDL ifndef NOHW - OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/r_glcommon.o $(OBJDIR)/gl_shaders.o $(OBJDIR)/ogl_sdl.o + OBJS+=$(OBJDIR)/ogl_sdl.o endif ifdef NOMIXER diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index 0df9fc051d603591839d852d0267c358381bf68b..56e41f3e4cf2bb7b20f6664764fa92f5ec92aef3 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -36,11 +36,19 @@ #include "../doomdef.h" #ifdef HWRENDER + +#ifdef HAVE_GLES +#include "../hardware/r_gles/r_gles.h" +#else #include "../hardware/r_opengl/r_opengl.h" +#endif + #include "../hardware/hw_main.h" + #include "ogl_sdl.h" -#include "../i_system.h" #include "hwsym_sdl.h" + +#include "../i_system.h" #include "../m_argv.h" #ifdef DEBUG_TO_FILE @@ -54,19 +62,6 @@ #include <sys/stat.h> #endif -#ifdef USE_WGL_SWAP -PFNWGLEXTSWAPCONTROLPROC wglSwapIntervalEXT = NULL; -#else -typedef int (*PFNGLXSWAPINTERVALPROC) (int); -PFNGLXSWAPINTERVALPROC glXSwapIntervalSGIEXT = NULL; -#endif - -#ifndef STATIC_OPENGL -PFNglClear pglClear; -PFNglGetIntegerv pglGetIntegerv; -PFNglGetString pglGetString; -#endif - /** \brief SDL video display surface */ void *GLUhandle = NULL; @@ -81,12 +76,19 @@ void *GLBackend_GetFunction(const char *proc) else return NULL; } + return SDL_GL_GetProcAddress(proc); } boolean GLBackend_LoadLibrary(void) { -#ifndef STATIC_OPENGL +#if defined(__ANDROID__) + if (SDL_GL_LoadLibrary(NULL) != 0) + { + CONS_Alert(CONS_ERROR, "Could not load OpenGL Library: %s\nFalling back to Software mode.\n", SDL_GetError()); + return false; + } +#elif !defined(STATIC_OPENGL) const char *OGLLibname = NULL; const char *GLULibname = NULL; @@ -138,9 +140,9 @@ boolean GLBackend_LoadLibrary(void) CONS_Alert(CONS_ERROR, "Could not load GLU Library\n"); CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n"); } +#endif return true; -#endif } /** \brief The OglSdlSurface function @@ -153,19 +155,20 @@ boolean GLBackend_LoadLibrary(void) */ boolean OglSdlSurface(INT32 w, INT32 h) { - INT32 cbpp = cv_scr_depth.value < 16 ? 16 : cv_scr_depth.value; - +#if !defined(__ANDROID__) GLBackend_InitContext(); GLBackend_LoadContextFunctions(); +#ifdef HAVE_GLES2 + GLBackend_InitShaders(); +#endif +#endif - SetSurface(w, h); + GLState_SetSurface(w, h); glanisotropicmode_cons_t[1].value = GPUMaximumAnisotropy; - SDL_GL_SetSwapInterval(cv_vidwait.value ? 1 : 0); HWR_Startup(); - GPUTextureFormat = cbpp > 16 ? GL_RGBA : GL_RGB5_A1; return true; } diff --git a/src/w_wad.c b/src/w_wad.c index 2429eaf927f9e9ffe9dd777e9a1e9589e1f18bb8..6566800c03ce0594898d4bb50026b5bc55eb3f67 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1747,6 +1747,9 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) void W_UnlockCachedPatch(void *patch) { + if (!patch) + return; + // The hardware code does its own memory management, as its patches // have different lifetimes from software's. #ifdef HWRENDER @@ -2144,7 +2147,7 @@ int W_VerifyNMUSlumps(const char *filename, boolean exit_on_error) {"LT", 2}, // Titlecard changes {"SLID", 4}, // Continue - {"CONT", 4}, + {"CONT", 4}, {"MINICAPS", 8}, // NiGHTS graphics here and below {"BLUESTAT", 8}, // Sphere status