From 6655d711ebd39ed76954f6e0054d8001d12b8334 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Mon, 27 Jul 2015 18:41:15 +0200 Subject: [PATCH 1/3] Make texgen reflections work WIP --- hw/xbox/nv2a_shaders.c | 69 +++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index f94a286340..5d616206b0 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -115,6 +115,22 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append(s, "\n" /* FIXME: Add these uniforms using code when they are used */ +"uniform vec4 texPlaneS0;\n" +"uniform vec4 texPlaneT0;\n" +"uniform vec4 texPlaneQ0;\n" +"uniform vec4 texPlaneR0;\n" +"uniform vec4 texPlaneS1;\n" +"uniform vec4 texPlaneT1;\n" +"uniform vec4 texPlaneQ1;\n" +"uniform vec4 texPlaneR1;\n" +"uniform vec4 texPlaneS2;\n" +"uniform vec4 texPlaneT2;\n" +"uniform vec4 texPlaneQ2;\n" +"uniform vec4 texPlaneR2;\n" +"uniform vec4 texPlaneS3;\n" +"uniform vec4 texPlaneT3;\n" +"uniform vec4 texPlaneQ3;\n" +"uniform vec4 texPlaneR3;\n" "uniform mat4 texMat0;\n" "uniform mat4 texMat1;\n" "uniform mat4 texMat2;\n" @@ -206,37 +222,62 @@ static QString* generate_fixed_function(const ShaderState state, /* FIXME: could be nicer if some channels share the same texgen */ for (j = 0; j < 4; j++) { char c = "xyzw"[j]; + char cSuffix = "STRQ"[i]; switch (state.texgen[i][j]) { case TEXGEN_DISABLE: - qstring_append_fmt(s, - "tTexture%d.%c = texture%d.%c;\n", + qstring_append_fmt(s, "tTexture%d.%c = texture%d.%c;\n", i, c, i, c); break; case TEXGEN_EYE_LINEAR: - qstring_append_fmt(s, - "tTexture%d.%c = tPosition.%c;\n", - i, c, c); + +/* FIXME: This might not have to be done? { + * FIXME: calculate tTexPlane[STRQ][0-3] - I guess this happens skinned? + * tTexPlane[STRQ][0-3] = texPlane[STRQ][0-3] * invModelViewMat[%d or 0?!] probably + * } + */ + + qstring_append_fmt(s, "vec4 tTexPlane%c%d = texPlane%c%d * invModelViewMat0;\n", + cSuffix, i, cSuffix, i); + + qstring_append_fmt(s, "tTexture%d.%c = dot(tTexPlane%c%d, tPosition);\n", + i, c, cSuffix, i); break; case TEXGEN_OBJECT_LINEAR: - qstring_append_fmt(s, - "tTexture%d.%c = position.%c;\n", - i, c, c); + qstring_append_fmt(s, "tTexture%d.%c = dot(texPlane%c%d, position);\n", + i, c, cSuffix, i); break; case TEXGEN_SPHERE_MAP: assert(i < 2); /* Channels S,T only! */ - assert(false); + qstring_append(s, "{\n"); + /* FIXME: r and m only have to be calculated once */ + qstring_append(s, " vec3 u = normalize(tPosition.xyz);\n"); + //FIXME: tNormal before or after normalization? Always normalize? + qstring_append(s, " vec3 r = reflect(u, tNormal);\n"); + +/* FIXME: This would consume 1 division fewer and *might* be faster than length: + * // [z=1/(2*x) => z=1/x*0.5] + * vec3 ro = r + vec3(0.0, 0.0, 1.0); + * float m = inversesqrt(dot(ro,ro))*0.5; + */ + + qstring_append(s, " float invM = 1.0 / (2.0 * length(r + vec3(0.0, 0.0, 1.0)));\n"); + qstring_append_fmt(s, " tTexture%d.%c = r.%c * invM + 0.5;\n", + i, c, c); + qstring_append(s, "}\n"); break; case TEXGEN_REFLECTION_MAP: assert(i < 3); /* Channels S,T,R only! */ - qstring_append_fmt(s, - "tTexture%d.%c = reflect(???, tNormal).%c;\n", + qstring_append(s, "{\n"); + /* FIXME: u and r only have to be calculated once, can share the one from SPHERE_MAP */ + qstring_append(s, " vec3 u = normalize(tPosition.xyz);\n"); + qstring_append(s, " vec3 r = reflect(u, tNormal);\n"); + qstring_append_fmt(s, " tTexture%d.%c = r.%c;\n", i, c, c); - assert(false); /* FIXME: Code not complete yet! */ + qstring_append(s, "}\n"); break; case TEXGEN_NORMAL_MAP: assert(i < 3); /* Channels S,T,R only! */ - qstring_append_fmt(s, - "tTexture%d.%c = tNormal.%c;\n", + qstring_append_fmt(s, "tTexture%d.%c = tNormal.%c;\n", i, c, c); break; default: From d2fa51bb823e3ee2f53fe5ab486404572ada3e9b Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Mon, 27 Jul 2015 20:18:04 +0200 Subject: [PATCH 2/3] Support for texgen planes --- hw/xbox/nv2a.c | 32 ++++++++++-- hw/xbox/nv2a_shaders.c | 116 ++++++++++++++++++++++++----------------- 2 files changed, 95 insertions(+), 53 deletions(-) diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index 5f95d45608..3f3e9bae46 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -825,6 +825,10 @@ # define NV097_SET_INVERSE_MODEL_VIEW_MATRIX 0x00970580 # define NV097_SET_COMPOSITE_MATRIX 0x00970680 # define NV097_SET_TEXTURE_MATRIX 0x009706C0 +# define NV097_SET_TEXGEN_PLANE_S 0x00970840 +# define NV097_SET_TEXGEN_PLANE_T 0x00970850 +# define NV097_SET_TEXGEN_PLANE_R 0x00970860 +# define NV097_SET_TEXGEN_PLANE_Q 0x00970870 # define NV097_SET_TEXGEN_VIEW_MODEL 0x009709CC # define NV097_SET_TEXGEN_VIEW_MODEL_LOCAL_VIEWER 0 # define NV097_SET_TEXGEN_VIEW_MODEL_INFINITE_VIEWER 1 @@ -1446,14 +1450,15 @@ typedef struct PGRAPHState { float composite_matrix[16]; /* FIXME: These are probably stored in the vshader consts */ - bool texture_matrix_enable[4]; - float texture_matrix[4][16]; /* 4 stages with 4x4 matrix each */ + bool texture_matrix_enable[NV2A_MAX_TEXTURES]; + float texture_matrix[NV2A_MAX_TEXTURES][16]; /* 4 stages with 4x4 matrix each */ + float texture_plane[NV2A_MAX_TEXTURES][4][4]; /* 4 stages, 4 components + plane for each */ float projection_matrix[16]; float inverse_model_view_matrix[4][16]; /* 4 weights with 4x4 matrix each */ float model_view_matrix[4][16]; /* 4 weights with 4x4 matrix each */ /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ - float bump_env_matrix[3][4]; /* 4 stages with 2x2 matrix each */ + float bump_env_matrix[NV2A_MAX_TEXTURES-1][4]; /* 3 allowed stages with 2x2 matrix each */ GloContext *gl_context; GLuint gl_framebuffer; @@ -2874,7 +2879,7 @@ static void pgraph_bind_shaders(PGRAPHState *pg) } /* For each texture stage */ - for (i = 0; i < 4; i++) { + for (i = 0; i < NV2A_MAX_TEXTURES; i++) { char name[32]; GLint loc; @@ -2910,6 +2915,16 @@ static void pgraph_bind_shaders(PGRAPHState *pg) glUniformMatrix4fv(loc, 1, GL_FALSE, pg->texture_matrix[i]); } + /* TexGen planes */ + for(j = 0; j < 4; j++) { + char cSuffix = "STRQ"[j]; + snprintf(name, sizeof(name), "texPlane%c%d", cSuffix, i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, name); + if (loc != -1) { + glUniform4fv(loc, 1, pg->texture_plane[i][j]); + } + } + } /* For each vertex weight */ @@ -4240,6 +4255,15 @@ static void pgraph_method(NV2AState *d, pg->texture_matrix[slot / 16][slot % 16] = *(float*)¶meter; break; + /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ + case NV097_SET_TEXGEN_PLANE_S ... + NV097_SET_TEXGEN_PLANE_S + 0xfc: { + slot = (class_method - NV097_SET_TEXGEN_PLANE_S) / 4; + unsigned int part = slot % 16; + pg->texture_plane[slot / 16][part / 4][part % 4] = *(float*)¶meter; + break; + } + case NV097_SET_TEXGEN_VIEW_MODEL: SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter); diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 5d616206b0..473373ca98 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -72,7 +72,43 @@ static QString* generate_geometry_shader(enum ShaderPrimitiveMode primitive_mode return s; } +static void pgraph_append_skinning_code(QString* str, bool mix, + unsigned int count, const char* type, + const char* output, const char* input, + const char* matrix, const char* swizzle) +{ + if (count == 0) { + qstring_append_fmt(str, "%s %s = (%s * %s0).%s;\n", + type, output, input, matrix, swizzle); + } else { + qstring_append_fmt(str, "%s %s = %s(0.0);\n", type, output, type); + if (mix) { + /* Tweening */ + if (count == 2) { + qstring_append_fmt(str, + "%s += mix((%s * %s1).%s,\n" + " (%s * %s0).%s, weight.x);\n", + output, + input, matrix, swizzle, + input, matrix, swizzle); + } else { + /* FIXME: Not sure how blend weights are calculated */ + assert(false); + } + } else { + /* Individual matrices */ + int i; + for (i = 0; i < count; i++) { + char c = "xyzw"[i]; + qstring_append_fmt(str, "%s += (%s * %s%d * weight.%c).%s;\n", + output, input, matrix, i, c, + swizzle); + } + assert(false); /* FIXME: Untested */ + } + } +} static QString* generate_fixed_function(const ShaderState state, char out_prefix) @@ -154,7 +190,7 @@ static QString* generate_fixed_function(const ShaderState state, bool mix; switch (state.skinning) { case SKINNING_OFF: - count = 0; break; + mix = false; count = 0; break; case SKINNING_1WEIGHTS: mix = true; count = 2; break; case SKINNING_2WEIGHTS: @@ -173,37 +209,27 @@ static QString* generate_fixed_function(const ShaderState state, } qstring_append_fmt(s, "/* Skinning mode %d */\n", state.skinning); - if (count == 0) { - qstring_append(s, "vec4 tPosition = position * modelViewMat0;\n"); - /* FIXME: Is the normal still transformed? */ - qstring_append(s, "vec3 tNormal = (vec4(normal, 0.0) * invModelViewMat0).xyz;\n"); - } else { - qstring_append(s, "vec4 tPosition = vec4(0.0);\n"); - qstring_append(s, "vec3 tNormal = vec3(0.0);\n"); - if (mix) { - /* Tweening */ - if (count == 2) { - qstring_append(s, - "tPosition += mix(position * modelViewMat1,\n" - " position * modelViewMat0, weight.x);\n" - "tNormal += mix((vec4(normal, 0.0) * invModelViewMat1).xyz,\n" - " (vec4(normal, 0.0) * invModelViewMat0).xyz, weight.x);\n"); - } else { - /* FIXME: Not sure how blend weights are calculated */ - assert(false); - } - } else { - /* Individual matrices */ - for (i = 0; i < count; i++) { - char c = "xyzw"[i]; - qstring_append_fmt(s, - "tPosition += position * modelViewMat%d * weight.%c;\n", - i, c); - qstring_append_fmt(s, - "tNormal += (vec4(normal, 0.0) * invModelViewMat%d * weight.%c).xyz;\n", - i, c); - } - assert(false); /* FIXME: Untested */ + + pgraph_append_skinning_code(s, mix, count, "vec4", + "tPosition", "position", + "modelViewMat", "xyzw"); + pgraph_append_skinning_code(s, mix, count, "vec3", + "tNormal", "vec4(normal, 0.0)", + "invModelViewMat", "xyz"); + + for(i = 0; i < 4 /* FIXME: NV2A_MAX_TEXTURES*/; i++) { + for(j = 0; j < 4; j++) { + + /* FIXME: Only do these if necessary */ + + char output[16]; + char input[16]; + char cSuffix = "STRQ"[j]; + snprintf(output, sizeof(output), "tTexPlane%c%d", cSuffix, i); + snprintf(input, sizeof(input), "texPlane%c%d", cSuffix, i); + pgraph_append_skinning_code(s, mix, count, + "vec4", output, input, + "invModelViewMat", "xyzw"); } } @@ -213,7 +239,7 @@ static QString* generate_fixed_function(const ShaderState state, } /* Texgen */ - for (i = 0; i < 4; i++) { + for (i = 0; i < 4 /* NV2A_MAX_TEXTURES */; i++) { qstring_append_fmt(s, "/* Texgen for stage %d */\n", i); qstring_append_fmt(s, "vec4 tTexture%d;\n", @@ -221,24 +247,15 @@ static QString* generate_fixed_function(const ShaderState state, /* Set each component individually */ /* FIXME: could be nicer if some channels share the same texgen */ for (j = 0; j < 4; j++) { + /* TODO: TexGen View Model missing! */ char c = "xyzw"[j]; - char cSuffix = "STRQ"[i]; + char cSuffix = "STRQ"[j]; switch (state.texgen[i][j]) { case TEXGEN_DISABLE: qstring_append_fmt(s, "tTexture%d.%c = texture%d.%c;\n", i, c, i, c); break; case TEXGEN_EYE_LINEAR: - -/* FIXME: This might not have to be done? { - * FIXME: calculate tTexPlane[STRQ][0-3] - I guess this happens skinned? - * tTexPlane[STRQ][0-3] = texPlane[STRQ][0-3] * invModelViewMat[%d or 0?!] probably - * } - */ - - qstring_append_fmt(s, "vec4 tTexPlane%c%d = texPlane%c%d * invModelViewMat0;\n", - cSuffix, i, cSuffix, i); - qstring_append_fmt(s, "tTexture%d.%c = dot(tTexPlane%c%d, tPosition);\n", i, c, cSuffix, i); break; @@ -254,11 +271,12 @@ static QString* generate_fixed_function(const ShaderState state, //FIXME: tNormal before or after normalization? Always normalize? qstring_append(s, " vec3 r = reflect(u, tNormal);\n"); -/* FIXME: This would consume 1 division fewer and *might* be faster than length: - * // [z=1/(2*x) => z=1/x*0.5] - * vec3 ro = r + vec3(0.0, 0.0, 1.0); - * float m = inversesqrt(dot(ro,ro))*0.5; - */ + /* FIXME: This would consume 1 division fewer and *might* be + * faster than `length`: + * // [z=1/(2*x) => z=1/x*0.5] + * vec3 ro = r + vec3(0.0, 0.0, 1.0); + * float m = inversesqrt(dot(ro,ro))*0.5; + */ qstring_append(s, " float invM = 1.0 / (2.0 * length(r + vec3(0.0, 0.0, 1.0)));\n"); qstring_append_fmt(s, " tTexture%d.%c = r.%c * invM + 0.5;\n", From 92d79c8a7d0679d41a471deea76fdc640d96f9cd Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Tue, 28 Jul 2015 00:50:55 +0200 Subject: [PATCH 3/3] WIP asserts --- hw/xbox/nv2a_shaders.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 473373ca98..eceb849666 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -239,7 +239,7 @@ static QString* generate_fixed_function(const ShaderState state, } /* Texgen */ - for (i = 0; i < 4 /* NV2A_MAX_TEXTURES */; i++) { + for (i = 0; i < 4 /* FIXME: NV2A_MAX_TEXTURES */; i++) { qstring_append_fmt(s, "/* Texgen for stage %d */\n", i); qstring_append_fmt(s, "vec4 tTexture%d;\n", @@ -258,21 +258,23 @@ static QString* generate_fixed_function(const ShaderState state, case TEXGEN_EYE_LINEAR: qstring_append_fmt(s, "tTexture%d.%c = dot(tTexPlane%c%d, tPosition);\n", i, c, cSuffix, i); +assert(false); /* Untested */ break; case TEXGEN_OBJECT_LINEAR: qstring_append_fmt(s, "tTexture%d.%c = dot(texPlane%c%d, position);\n", i, c, cSuffix, i); +assert(false); /* Untested */ break; case TEXGEN_SPHERE_MAP: assert(i < 2); /* Channels S,T only! */ qstring_append(s, "{\n"); - /* FIXME: r and m only have to be calculated once */ + /* FIXME: u, r and m only have to be calculated once */ qstring_append(s, " vec3 u = normalize(tPosition.xyz);\n"); //FIXME: tNormal before or after normalization? Always normalize? qstring_append(s, " vec3 r = reflect(u, tNormal);\n"); /* FIXME: This would consume 1 division fewer and *might* be - * faster than `length`: + * faster than length: * // [z=1/(2*x) => z=1/x*0.5] * vec3 ro = r + vec3(0.0, 0.0, 1.0); * float m = inversesqrt(dot(ro,ro))*0.5; @@ -282,6 +284,7 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append_fmt(s, " tTexture%d.%c = r.%c * invM + 0.5;\n", i, c, c); qstring_append(s, "}\n"); +assert(false); /* Untested */ break; case TEXGEN_REFLECTION_MAP: assert(i < 3); /* Channels S,T,R only! */