// Project64 - A Nintendo 64 emulator // https://www.pj64-emu.com/ // Copyright(C) 2001-2021 Project64 // Copyright(C) 2003-2009 Sergey 'Gonetz' Lipski // Copyright(C) 2002 Dave2001 // GNU/GPLv2 licensed: https://gnu.org/licenses/gpl-2.0.html #include #include #include #include #ifdef _WIN32 #include #endif // _WIN32 #include "glitchmain.h" #include #define Z_MAX (65536.0f) static int xy_off = offsetof(gfxVERTEX, x); static int z_off = offsetof(gfxVERTEX, z); static int q_off = offsetof(gfxVERTEX, q); static int pargb_off = offsetof(gfxVERTEX, b); static int st0_off = offsetof(gfxVERTEX, coord[0]); static int st1_off = offsetof(gfxVERTEX, coord[2]); static int fog_ext_off = offsetof(gfxVERTEX, f); int w_buffer_mode; int inverted_culling; gfxCullMode_t culling_mode; extern int fog_enabled; inline float ZCALC(const float & z, const float & q) { float res = ((z) / Z_MAX) / (q); return res; } #define zclamp (1.0f-1.0f/zscale) static inline void zclamp_glVertex4f(float a, float b, float c, float d) { if (c < zclamp) c = zclamp; glVertex4f(a, b, c, d); } #define glVertex4f(a,b,c,d) zclamp_glVertex4f(a,b,c,d) static inline float ytex(int tmu, float y) { if (invtex[tmu]) return invtex[tmu] - y; else return y; } void init_geometry() { w_buffer_mode = 0; inverted_culling = 0; glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); grDisplayGLError("init_geometry"); } void gfxCullMode(gfxCullMode_t mode) { WriteTrace(TraceGlitch, TraceDebug, "mode: %d", mode); static int oldmode = -1, oldinv = -1; culling_mode = mode; if (inverted_culling == oldinv && oldmode == mode) return; oldmode = mode; oldinv = inverted_culling; switch (mode) { case GFX_CULL_DISABLE: glDisable(GL_CULL_FACE); break; case GFX_CULL_NEGATIVE: if (!inverted_culling) glCullFace(GL_FRONT); else glCullFace(GL_BACK); glEnable(GL_CULL_FACE); break; case GFX_CULL_POSITIVE: if (!inverted_culling) glCullFace(GL_BACK); else glCullFace(GL_FRONT); glEnable(GL_CULL_FACE); break; default: WriteTrace(TraceGlitch, TraceWarning, "Unknown cull mode : %x", mode); } grDisplayGLError("gfxCullMode"); } // Depth buffer void gfxDepthBufferMode(gfxDepthBufferMode_t mode) { WriteTrace(TraceGlitch, TraceDebug, "mode: %d", mode); switch (mode) { case GFX_DEPTHBUFFER_DISABLE: glDisable(GL_DEPTH_TEST); w_buffer_mode = 0; return; case GFX_DEPTHBUFFER_WBUFFER: case GFX_DEPTHBUFFER_WBUFFER_COMPARE_TO_BIAS: glEnable(GL_DEPTH_TEST); w_buffer_mode = 1; break; case GFX_DEPTHBUFFER_ZBUFFER: case GFX_DEPTHBUFFER_ZBUFFER_COMPARE_TO_BIAS: glEnable(GL_DEPTH_TEST); w_buffer_mode = 0; break; default: WriteTrace(TraceGlitch, TraceWarning, "Unknown depth buffer mode : %x", mode); } grDisplayGLError("gfxDepthBufferMode"); } void gfxDepthBufferFunction(gfxCmpFnc_t function) { WriteTrace(TraceGlitch, TraceDebug, "Function: %d", function); switch (function) { case GFX_CMP_GEQUAL: if (w_buffer_mode) glDepthFunc(GL_LEQUAL); else glDepthFunc(GL_GEQUAL); break; case GFX_CMP_LEQUAL: if (w_buffer_mode) glDepthFunc(GL_GEQUAL); else glDepthFunc(GL_LEQUAL); break; case GFX_CMP_LESS: if (w_buffer_mode) glDepthFunc(GL_GREATER); else glDepthFunc(GL_LESS); break; case GFX_CMP_ALWAYS: glDepthFunc(GL_ALWAYS); break; case GFX_CMP_EQUAL: glDepthFunc(GL_EQUAL); break; case GFX_CMP_GREATER: if (w_buffer_mode) glDepthFunc(GL_LESS); else glDepthFunc(GL_GREATER); break; case GFX_CMP_NEVER: glDepthFunc(GL_NEVER); break; case GFX_CMP_NOTEQUAL: glDepthFunc(GL_NOTEQUAL); break; default: WriteTrace(TraceGlitch, TraceWarning, "Unknown depth buffer function : %x", function); } grDisplayGLError("gfxDepthBufferFunction"); } void gfxDepthMask(bool mask) { WriteTrace(TraceGlitch, TraceDebug, "mask: %d", mask); glDepthMask((GLboolean)mask); grDisplayGLError("gfxDepthMask"); } float biasFactor = 0; void FindBestDepthBias() { GLfloat vertices[4][3]; float f, bestz = 0.25f; int x; if (biasFactor) return; biasFactor = 64.0f; // Default value glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); glEnable(GL_POLYGON_OFFSET_FILL); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glColor4ub(255, 255, 255, 255); glDepthMask(GL_TRUE); for (x = 0; x < 4; x++) vertices[x][2] = 0.5; for (x = 0, f = 1.0f; f <= 65536.0f; x += 4, f *= 2.0f) { float z; vertices[0][0] = float(x + 4 - widtho) / (g_width / 2); vertices[0][1] = float(0 + 0 - heighto) / (g_height / 2); vertices[1][0] = float(x + 0 - widtho) / (g_width / 2); vertices[1][1] = float(0 + 0 - heighto) / (g_height / 2); vertices[2][0] = float(x + 4 - widtho) / (g_width / 2); vertices[2][1] = float(0 + 4 - heighto) / (g_height / 2); vertices[3][0] = float(x + 0 - widtho) / (g_width / 2); vertices[3][1] = float(0 + 4 - heighto) / (g_height / 2); glPolygonOffset(0, f); glBegin(GL_TRIANGLE_STRIP); glVertex3fv(vertices[0]); glVertex3fv(vertices[1]); glVertex3fv(vertices[2]); glVertex3fv(vertices[3]); glEnd(); glReadPixels(x + 2, 2 + g_viewport_offset, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); z -= 0.75f + 8e-6f; if (z < 0.0f) z = -z; if (z > 0.01f) continue; if (z < bestz) { bestz = z; biasFactor = f; } //printf("f %g z %g\n", f, z); } //printf(" --> bias factor %g\n", biasFactor); glPopAttrib(); grDisplayGLError("FindBestDepthBias"); } void gfxDepthBiasLevel(int32_t level) { WriteTrace(TraceGlitch, TraceDebug, "level: %d", level); if (level) { if (w_buffer_mode) glPolygonOffset(1.0f, -(float)level*zscale / 255.0f); else glPolygonOffset(0, (float)level*biasFactor); glEnable(GL_POLYGON_OFFSET_FILL); } else { glPolygonOffset(0, 0); glDisable(GL_POLYGON_OFFSET_FILL); } grDisplayGLError("gfxDepthBiasLevel"); } // Draw void gfxDrawTriangle(const gfxVERTEX *a, const gfxVERTEX *b, const gfxVERTEX *c) { float *a_x = (float*)a + xy_off / sizeof(float); float *a_y = (float*)a + xy_off / sizeof(float) + 1; float *a_z = (float*)a + z_off / sizeof(float); float *a_q = (float*)a + q_off / sizeof(float); unsigned char *a_pargb = (unsigned char*)a + pargb_off; float *a_s0 = (float*)a + st0_off / sizeof(float); float *a_t0 = (float*)a + st0_off / sizeof(float) + 1; float *a_s1 = (float*)a + st1_off / sizeof(float); float *a_t1 = (float*)a + st1_off / sizeof(float) + 1; float *a_fog = (float*)a + fog_ext_off / sizeof(float); float *b_x = (float*)b + xy_off / sizeof(float); float *b_y = (float*)b + xy_off / sizeof(float) + 1; float *b_z = (float*)b + z_off / sizeof(float); float *b_q = (float*)b + q_off / sizeof(float); unsigned char *b_pargb = (unsigned char*)b + pargb_off; float *b_s0 = (float*)b + st0_off / sizeof(float); float *b_t0 = (float*)b + st0_off / sizeof(float) + 1; float *b_s1 = (float*)b + st1_off / sizeof(float); float *b_t1 = (float*)b + st1_off / sizeof(float) + 1; float *b_fog = (float*)b + fog_ext_off / sizeof(float); float *c_x = (float*)c + xy_off / sizeof(float); float *c_y = (float*)c + xy_off / sizeof(float) + 1; float *c_z = (float*)c + z_off / sizeof(float); float *c_q = (float*)c + q_off / sizeof(float); unsigned char *c_pargb = (unsigned char*)c + pargb_off; float *c_s0 = (float*)c + st0_off / sizeof(float); float *c_t0 = (float*)c + st0_off / sizeof(float) + 1; float *c_s1 = (float*)c + st1_off / sizeof(float); float *c_t1 = (float*)c + st1_off / sizeof(float) + 1; float *c_fog = (float*)c + fog_ext_off / sizeof(float); WriteTrace(TraceGlitch, TraceDebug, "-"); // This is ugly, but Nvidia drivers are losing the viewport otherwise. if (nvidia_viewport_hack && !render_to_texture) { glViewport(0, g_viewport_offset, viewport_width, viewport_height); nvidia_viewport_hack = 0; } reloadTexture(); if (need_to_compile) compile_shader(); glBegin(GL_TRIANGLES); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *a_s0 / *a_q / (float)tex1_width, ytex(0, *a_t0 / *a_q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *a_s1 / *a_q / (float)tex0_width, ytex(1, *a_t1 / *a_q / (float)tex0_height)); } else { glTexCoord2f(*a_s0 / *a_q / (float)tex0_width, ytex(0, *a_t0 / *a_q / (float)tex0_height)); } glColor4f(a_pargb[2] / 255.0f, a_pargb[1] / 255.0f, a_pargb[0] / 255.0f, a_pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *a_q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *a_fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*a_x - (float)widtho) / (float)(g_width / 2) / *a_q, -(*a_y - (float)heighto) / (float)(g_height / 2) / *a_q, ZCALC(*a_z, *a_q), 1.0f / *a_q); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *b_s0 / *b_q / (float)tex1_width, ytex(0, *b_t0 / *b_q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *b_s1 / *b_q / (float)tex0_width, ytex(1, *b_t1 / *b_q / (float)tex0_height)); } else { glTexCoord2f(*b_s0 / *b_q / (float)tex0_width, ytex(0, *b_t0 / *b_q / (float)tex0_height)); } glColor4f(b_pargb[2] / 255.0f, b_pargb[1] / 255.0f, b_pargb[0] / 255.0f, b_pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *b_q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *b_fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*b_x - (float)widtho) / (float)(g_width / 2) / *b_q, -(*b_y - (float)heighto) / (float)(g_height / 2) / *b_q, ZCALC(*b_z, *b_q), 1.0f / *b_q); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *c_s0 / *c_q / (float)tex1_width, ytex(0, *c_t0 / *c_q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *c_s1 / *c_q / (float)tex0_width, ytex(1, *c_t1 / *c_q / (float)tex0_height)); } else { glTexCoord2f(*c_s0 / *c_q / (float)tex0_width, ytex(0, *c_t0 / *c_q / (float)tex0_height)); } glColor4f(c_pargb[2] / 255.0f, c_pargb[1] / 255.0f, c_pargb[0] / 255.0f, c_pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *c_q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *c_fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*c_x - (float)widtho) / (float)(g_width / 2) / *c_q, -(*c_y - (float)heighto) / (float)(g_height / 2) / *c_q, ZCALC(*c_z, *c_q), 1.0f / *c_q); glEnd(); grDisplayGLError("gfxDrawTriangle"); } void gfxDrawLine(const gfxVERTEX *a, const gfxVERTEX *b) { float *a_x = (float*)a + xy_off / sizeof(float); float *a_y = (float*)a + xy_off / sizeof(float) + 1; float *a_z = (float*)a + z_off / sizeof(float); float *a_q = (float*)a + q_off / sizeof(float); unsigned char *a_pargb = (unsigned char*)a + pargb_off; float *a_s0 = (float*)a + st0_off / sizeof(float); float *a_t0 = (float*)a + st0_off / sizeof(float) + 1; float *a_s1 = (float*)a + st1_off / sizeof(float); float *a_t1 = (float*)a + st1_off / sizeof(float) + 1; float *a_fog = (float*)a + fog_ext_off / sizeof(float); float *b_x = (float*)b + xy_off / sizeof(float); float *b_y = (float*)b + xy_off / sizeof(float) + 1; float *b_z = (float*)b + z_off / sizeof(float); float *b_q = (float*)b + q_off / sizeof(float); unsigned char *b_pargb = (unsigned char*)b + pargb_off; float *b_s0 = (float*)b + st0_off / sizeof(float); float *b_t0 = (float*)b + st0_off / sizeof(float) + 1; float *b_s1 = (float*)b + st1_off / sizeof(float); float *b_t1 = (float*)b + st1_off / sizeof(float) + 1; float *b_fog = (float*)b + fog_ext_off / sizeof(float); WriteTrace(TraceGlitch, TraceDebug, "-"); if (nvidia_viewport_hack && !render_to_texture) { glViewport(0, g_viewport_offset, viewport_width, viewport_height); nvidia_viewport_hack = 0; } reloadTexture(); if (need_to_compile) compile_shader(); glBegin(GL_LINES); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *a_s0 / *a_q / (float)tex1_width, ytex(0, *a_t0 / *a_q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *a_s1 / *a_q / (float)tex0_width, ytex(1, *a_t1 / *a_q / (float)tex0_height)); } else { glTexCoord2f(*a_s0 / *a_q / (float)tex0_width, ytex(0, *a_t0 / *a_q / (float)tex0_height)); } glColor4f(a_pargb[2] / 255.0f, a_pargb[1] / 255.0f, a_pargb[0] / 255.0f, a_pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *a_q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *a_fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*a_x - (float)widtho) / (float)(g_width / 2) / *a_q, -(*a_y - (float)heighto) / (float)(g_height / 2) / *a_q, ZCALC(*a_z, *a_q), 1.0f / *a_q); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *b_s0 / *b_q / (float)tex1_width, ytex(0, *b_t0 / *b_q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *b_s1 / *b_q / (float)tex0_width, ytex(1, *b_t1 / *b_q / (float)tex0_height)); } else { glTexCoord2f(*b_s0 / *b_q / (float)tex0_width, ytex(0, *b_t0 / *b_q / (float)tex0_height)); } glColor4f(b_pargb[2] / 255.0f, b_pargb[1] / 255.0f, b_pargb[0] / 255.0f, b_pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *b_q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *b_fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*b_x - (float)widtho) / (float)(g_width / 2) / *b_q, -(*b_y - (float)heighto) / (float)(g_height / 2) / *b_q, ZCALC(*b_z, *b_q), 1.0f / *b_q); glEnd(); grDisplayGLError("gfxDrawLine"); } void gfxDrawVertexArray(gfxDrawMode_t mode, uint32_t Count, void *pointers2) { unsigned int i; float *x, *y, *q, *s0, *t0, *s1, *t1, *z, *fog; unsigned char *pargb; void **pointers = (void**)pointers2; WriteTrace(TraceGlitch, TraceDebug, "Mode: %d Count: %d", mode, Count); if (nvidia_viewport_hack && !render_to_texture) { glViewport(0, g_viewport_offset, viewport_width, viewport_height); nvidia_viewport_hack = 0; } reloadTexture(); if (need_to_compile) compile_shader(); switch (mode) { case GFX_TRIANGLE_FAN: glBegin(GL_TRIANGLE_FAN); break; default: WriteTrace(TraceGlitch, TraceWarning, "gfxDrawVertexArray: Unknown mode : %x", mode); } for (i = 0; i < Count; i++) { x = (float*)pointers[i] + xy_off / sizeof(float); y = (float*)pointers[i] + xy_off / sizeof(float) + 1; z = (float*)pointers[i] + z_off / sizeof(float); q = (float*)pointers[i] + q_off / sizeof(float); pargb = (unsigned char*)pointers[i] + pargb_off; s0 = (float*)pointers[i] + st0_off / sizeof(float); t0 = (float*)pointers[i] + st0_off / sizeof(float) + 1; s1 = (float*)pointers[i] + st1_off / sizeof(float); t1 = (float*)pointers[i] + st1_off / sizeof(float) + 1; fog = (float*)pointers[i] + fog_ext_off / sizeof(float); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *s0 / *q / (float)tex1_width, ytex(0, *t0 / *q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *s1 / *q / (float)tex0_width, ytex(1, *t1 / *q / (float)tex0_height)); } else { glTexCoord2f(*s0 / *q / (float)tex0_width, ytex(0, *t0 / *q / (float)tex0_height)); } glColor4f(pargb[2] / 255.0f, pargb[1] / 255.0f, pargb[0] / 255.0f, pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*x - (float)widtho) / (float)(g_width / 2) / *q, -(*y - (float)heighto) / (float)(g_height / 2) / *q, ZCALC(*z, *q), 1.0f / *q); } glEnd(); grDisplayGLError("gfxDrawVertexArray"); } void gfxDrawVertexArrayContiguous(gfxDrawMode_t mode, uint32_t Count, void *pointers, uint32_t stride) { unsigned int i; float *x, *y, *q, *s0, *t0, *s1, *t1, *z, *fog; unsigned char *pargb; WriteTrace(TraceGlitch, TraceDebug, "Mode: %d Count: %d stride: %d", mode, Count, stride); if (nvidia_viewport_hack && !render_to_texture) { glViewport(0, g_viewport_offset, viewport_width, viewport_height); nvidia_viewport_hack = 0; } reloadTexture(); if (need_to_compile) compile_shader(); switch (mode) { case GFX_TRIANGLE_STRIP: glBegin(GL_TRIANGLE_STRIP); break; case GFX_TRIANGLE_FAN: glBegin(GL_TRIANGLE_FAN); break; default: WriteTrace(TraceGlitch, TraceWarning, "gfxDrawVertexArrayContiguous: Unknown mode : %x", mode); } for (i = 0; i < Count; i++) { x = (float*)((unsigned char*)pointers + stride*i) + xy_off / sizeof(float); y = (float*)((unsigned char*)pointers + stride*i) + xy_off / sizeof(float) + 1; z = (float*)((unsigned char*)pointers + stride*i) + z_off / sizeof(float); q = (float*)((unsigned char*)pointers + stride*i) + q_off / sizeof(float); pargb = (unsigned char*)pointers + stride*i + pargb_off; s0 = (float*)((unsigned char*)pointers + stride*i) + st0_off / sizeof(float); t0 = (float*)((unsigned char*)pointers + stride*i) + st0_off / sizeof(float) + 1; s1 = (float*)((unsigned char*)pointers + stride*i) + st1_off / sizeof(float); t1 = (float*)((unsigned char*)pointers + stride*i) + st1_off / sizeof(float) + 1; fog = (float*)((unsigned char*)pointers + stride*i) + fog_ext_off / sizeof(float); if (nbTextureUnits > 2) { glMultiTexCoord2fARB(GL_TEXTURE1_ARB, *s0 / *q / (float)tex1_width, ytex(0, *t0 / *q / (float)tex1_height)); glMultiTexCoord2fARB(GL_TEXTURE0_ARB, *s1 / *q / (float)tex0_width, ytex(1, *t1 / *q / (float)tex0_height)); } else { glTexCoord2f(*s0 / *q / (float)tex0_width, ytex(0, *t0 / *q / (float)tex0_height)); } glColor4f(pargb[2] / 255.0f, pargb[1] / 255.0f, pargb[0] / 255.0f, pargb[3] / 255.0f); if (fog_enabled && fog_coord_support) { if (!g_settings->fog() || fog_enabled != 2) glSecondaryColor3f((1.0f / *q) / 255.0f, 0.0f, 0.0f); else glSecondaryColor3f((1.0f / *fog) / 255.0f, 0.0f, 0.0f); } glVertex4f((*x - (float)widtho) / (float)(g_width / 2) / *q, -(*y - (float)heighto) / (float)(g_height / 2) / *q, ZCALC(*z, *q), 1.0f / *q); } glEnd(); grDisplayGLError("gfxDrawVertexArrayContiguous"); }