From 00aa96af3ed93eb7bb593952b3a6c2c0e5dc5a0b Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Tue, 4 Aug 2015 21:34:02 +0200 Subject: [PATCH 1/5] Lighting base and directional lights --- hw/xbox/nv2a.c | 240 +++++++++++++++++++++++++++++++++++++++++ hw/xbox/nv2a_shaders.c | 137 +++++++++++++++++++---- hw/xbox/nv2a_shaders.h | 4 + hw/xbox/nv2a_vsh.h | 7 ++ 4 files changed, 369 insertions(+), 19 deletions(-) diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index 348f0dcd87..b1b2636d1e 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -260,6 +260,12 @@ # define NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN (1 << 0) # define NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT (1 << 1) #define NV_PGRAPH_CSV0_D 0x00000FB4 +# define NV_PGRAPH_CSV0_D_LIGHTS 0x0000FFFF +# define NV_PGRAPH_CSV0_D_LIGHT0 0x00000003 +# define NV_PGRAPH_CSV0_D_LIGHT0_OFF 0 +# define NV_PGRAPH_CSV0_D_LIGHT0_INFINITE 1 +# define NV_PGRAPH_CSV0_D_LIGHT0_LOCAL 2 +# define NV_PGRAPH_CSV0_D_LIGHT0_SPOT 3 # define NV_PGRAPH_CSV0_D_RANGE_MODE (1 << 18) # define NV_PGRAPH_CSV0_D_FOGENABLE (1 << 19) # define NV_PGRAPH_CSV0_D_TEXGEN_REF (1 << 20) @@ -286,6 +292,7 @@ #define NV_PGRAPH_CSV0_C 0x00000FB8 # define NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START 0x0000FF00 # define NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE (1 << 27) +# define NV_PGRAPH_CSV0_C_LIGHTING (1 << 31) #define NV_PGRAPH_CSV1_B 0x00000FBC #define NV_PGRAPH_CSV1_A 0x00000FC0 # define NV_PGRAPH_CSV1_A_T0_ENABLE (1 << 0) @@ -765,6 +772,7 @@ # define NV097_SET_BLEND_ENABLE 0x00970304 # define NV097_SET_CULL_FACE_ENABLE 0x00970308 # define NV097_SET_DEPTH_TEST_ENABLE 0x0097030C +# define NV097_SET_LIGHTING_ENABLE 0x00970314 # define NV097_SET_SKIN_MODE 0x00970328 # define NV097_SET_SKIN_MODE_OFF 0 # define NV097_SET_SKIN_MODE_2G 1 @@ -854,6 +862,11 @@ # define NV097_SET_FRONT_FACE_V_CW 0x900 # define NV097_SET_FRONT_FACE_V_CCW 0x901 # define NV097_SET_NORMALIZATION_ENABLE 0x009703A4 +# define NV097_SET_LIGHT_ENABLE_MASK 0x009703BC +# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_OFF 0 +# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_INFINITE 1 +# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_LOCAL 2 +# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_SPOT 3 # define NV097_SET_TEXGEN_S 0x009703C0 # define NV097_SET_TEXGEN_S_DISABLE 0x0000 # define NV097_SET_TEXGEN_S_EYE_LINEAR 0x2400 @@ -879,6 +892,7 @@ # define NV097_SET_TEXGEN_VIEW_MODEL_LOCAL_VIEWER 0 # define NV097_SET_TEXGEN_VIEW_MODEL_INFINITE_VIEWER 1 # define NV097_SET_FOG_PLANE 0x009709D0 +# define NV097_SET_SCENE_AMBIENT_COLOR 0x00970A10 # define NV097_SET_VIEWPORT_OFFSET 0x00970A20 # define NV097_SET_COMBINER_FACTOR0 0x00970A60 # define NV097_SET_COMBINER_FACTOR1 0x00970A80 @@ -888,6 +902,19 @@ # define NV097_SET_TRANSFORM_PROGRAM 0x00970B00 # define NV097_SET_TRANSFORM_CONSTANT 0x00970B80 # define NV097_SET_VERTEX3F 0x00971500 +# define NV097_SET_BACK_LIGHT_AMBIENT_COLOR 0x00970C00 +# define NV097_SET_BACK_LIGHT_DIFFUSE_COLOR 0x00970C0C +# define NV097_SET_BACK_LIGHT_SPECULAR_COLOR 0x00970C18 +# define NV097_SET_LIGHT_AMBIENT_COLOR 0x00971000 +# define NV097_SET_LIGHT_DIFFUSE_COLOR 0x0097100C +# define NV097_SET_LIGHT_SPECULAR_COLOR 0x00971018 +# define NV097_SET_LIGHT_LOCAL_RANGE 0x00971024 +# define NV097_SET_LIGHT_INFINITE_HALF_VECTOR 0x00971028 +# define NV097_SET_LIGHT_INFINITE_DIRECTION 0x00971034 +# define NV097_SET_LIGHT_SPOT_FALLOFF 0x00971040 +# define NV097_SET_LIGHT_SPOT_DIRECTION 0x0097104C +# define NV097_SET_LIGHT_LOCAL_POSITION 0x0097105C +# define NV097_SET_LIGHT_LOCAL_ATTENUATION 0x00971068 # define NV097_SET_VERTEX4F 0x00971518 # define NV097_SET_VERTEX_DATA_ARRAY_OFFSET 0x00971720 # define NV097_SET_VERTEX_DATA_ARRAY_FORMAT 0x00971760 @@ -1541,6 +1568,22 @@ typedef struct PGRAPHState { /* FIXME: Also in vshader consts? */ float fog_plane[4]; + /* FIXME: These are probably stored in the vshader consts */ + float scene_ambient_color[3]; + float back_light_ambient_color[NV2A_MAX_LIGHTS][3]; + float back_light_diffuse_color[NV2A_MAX_LIGHTS][3]; + float back_light_specular_color[NV2A_MAX_LIGHTS][3]; + float light_ambient_color[NV2A_MAX_LIGHTS][3]; + float light_diffuse_color[NV2A_MAX_LIGHTS][3]; + float light_specular_color[NV2A_MAX_LIGHTS][3]; + float light_local_range[NV2A_MAX_LIGHTS]; + float light_infinite_half_vector[NV2A_MAX_LIGHTS][3]; + float light_infinite_direction[NV2A_MAX_LIGHTS][3]; + float light_spot_falloff[NV2A_MAX_LIGHTS][3]; + float light_spot_direction[NV2A_MAX_LIGHTS][4]; + float light_local_position[NV2A_MAX_LIGHTS][3]; + float light_local_attenuation[NV2A_MAX_LIGHTS][3]; + /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ float bump_env_matrix[NV2A_MAX_TEXTURES-1][4]; /* 3 allowed stages with 2x2 matrix each */ @@ -2836,6 +2879,9 @@ static void pgraph_bind_shaders(PGRAPHState *pg) .skinning = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN), + .lighting = GET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_LIGHTING), + .normalization = pg->regs[NV_PGRAPH_CSV0_C] & NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, @@ -2901,6 +2947,14 @@ static void pgraph_bind_shaders(PGRAPHState *pg) state.texture_matrix_enable[i] = pg->texture_matrix_enable[i]; } + /* Lighting */ + if (state.lighting) { + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + state.light[i] = GET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2)); + } + } + for (i = 0; i < 8; i++) { state.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4]; state.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4]; @@ -3090,6 +3144,84 @@ static void pgraph_bind_shaders(PGRAPHState *pg) glUniformMatrix4fv(projLoc, 1, GL_FALSE, pg->projection_matrix); } + /* FIXME: Only do this if lighting is allowed? I'd believe lighting works + * with both FFP and VPs. + */ + GLint ambLoc = glGetUniformLocation(pg->shader_binding->gl_program, + "sceneAmbientColor"); + if (ambLoc != -1) { + glUniform3fv(ambLoc, 1, pg->scene_ambient_color); + } + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + GLint loc; + char tmp[64]; + snprintf(tmp, sizeof(tmp), "backLightAmbientColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->back_light_ambient_color[i]); + } + snprintf(tmp, sizeof(tmp), "backLightDiffuseColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->back_light_diffuse_color[i]); + } + snprintf(tmp, sizeof(tmp), "backLightSpecularColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->back_light_specular_color[i]); + } + snprintf(tmp, sizeof(tmp), "lightAmbientColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_ambient_color[i]); + } + snprintf(tmp, sizeof(tmp), "lightDiffuseColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_diffuse_color[i]); + } + snprintf(tmp, sizeof(tmp), "lightSpecularColor%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_specular_color[i]); + } + snprintf(tmp, sizeof(tmp), "lightLocalRange%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform1f(loc, pg->light_local_range[i]); + } + snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]); + } + snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_direction[i]); + } + snprintf(tmp, sizeof(tmp), "lightSpotFalloff%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_spot_falloff[i]); + } + snprintf(tmp, sizeof(tmp), "lightSpotDirection%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform4fv(loc, 1, pg->light_spot_direction[i]); + } + snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_position[i]); + } + snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i); + loc = glGetUniformLocation(pg->shader_binding->gl_program, tmp); + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_attenuation[i]); + } + } + float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX]; float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN]; @@ -4139,6 +4271,10 @@ static void pgraph_method(NV2AState *d, SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE, parameter); break; + case NV097_SET_LIGHTING_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING, + parameter); + break; case NV097_SET_SKIN_MODE: SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN, parameter); @@ -4385,6 +4521,12 @@ static void pgraph_method(NV2AState *d, parameter); break; + case NV097_SET_LIGHT_ENABLE_MASK: + SET_MASK(d->pgraph.regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_LIGHTS, + parameter); + break; + CASE_4(NV097_SET_TEXGEN_S, 16): { slot = (class_method - NV097_SET_TEXGEN_S) / 16; unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A @@ -4486,6 +4628,12 @@ static void pgraph_method(NV2AState *d, pg->fog_plane[slot] = *(float*)¶meter; break; + case NV097_SET_SCENE_AMBIENT_COLOR ... + NV097_SET_SCENE_AMBIENT_COLOR + 8: + slot = (class_method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; + pg->scene_ambient_color[slot] = *(float*)¶meter; + break; + case NV097_SET_VIEWPORT_OFFSET ... NV097_SET_VIEWPORT_OFFSET + 12: @@ -4583,6 +4731,98 @@ static void pgraph_method(NV2AState *d, break; } + /* Handles NV097_SET_BACK_LIGHT_* */ + case NV097_SET_BACK_LIGHT_AMBIENT_COLOR ... + NV097_SET_BACK_LIGHT_SPECULAR_COLOR + 0x1C8: { + slot = (class_method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16; + slot /= 16; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + case NV097_SET_BACK_LIGHT_AMBIENT_COLOR ... + NV097_SET_BACK_LIGHT_AMBIENT_COLOR + 8: + part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4; + pg->back_light_ambient_color[slot][part] = *(float*)¶meter; + break; + case NV097_SET_BACK_LIGHT_DIFFUSE_COLOR ... + NV097_SET_BACK_LIGHT_DIFFUSE_COLOR + 8: + part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4; + pg->back_light_diffuse_color[slot][part] = *(float*)¶meter; + break; + case NV097_SET_BACK_LIGHT_SPECULAR_COLOR ... + NV097_SET_BACK_LIGHT_SPECULAR_COLOR + 8: + part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4; + pg->back_light_specular_color[slot][part] = *(float*)¶meter; + break; + default: + assert(false); + break; + } + break; + } + /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */ + case NV097_SET_LIGHT_AMBIENT_COLOR ... + NV097_SET_LIGHT_LOCAL_ATTENUATION + 0x38C: { + slot = (class_method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32; + slot /= 32; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + case NV097_SET_LIGHT_AMBIENT_COLOR ... + NV097_SET_LIGHT_AMBIENT_COLOR + 8: + part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4; + pg->light_ambient_color[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_DIFFUSE_COLOR ... + NV097_SET_LIGHT_DIFFUSE_COLOR + 8: + part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4; + pg->light_diffuse_color[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_SPECULAR_COLOR ... + NV097_SET_LIGHT_SPECULAR_COLOR + 8: + part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4; + pg->light_specular_color[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_LOCAL_RANGE: + pg->light_local_range[slot] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_INFINITE_HALF_VECTOR ... + NV097_SET_LIGHT_INFINITE_HALF_VECTOR + 8: + part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4; + pg->light_infinite_half_vector[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_INFINITE_DIRECTION ... + NV097_SET_LIGHT_INFINITE_DIRECTION + 8: + part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4; + pg->light_infinite_direction[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_SPOT_FALLOFF ... + NV097_SET_LIGHT_SPOT_FALLOFF + 8: + part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4; + pg->light_spot_falloff[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_SPOT_DIRECTION ... + NV097_SET_LIGHT_SPOT_DIRECTION + 12: + part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4; + pg->light_spot_direction[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_LOCAL_POSITION ... + NV097_SET_LIGHT_LOCAL_POSITION + 8: + part -= NV097_SET_LIGHT_LOCAL_POSITION / 4; + pg->light_local_position[slot][part] = *(float*)¶meter; + break; + case NV097_SET_LIGHT_LOCAL_ATTENUATION ... + NV097_SET_LIGHT_LOCAL_ATTENUATION + 8: + part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4; + pg->light_local_attenuation[slot][part] = *(float*)¶meter; + break; + default: + assert(false); + break; + } + break; + } + case NV097_SET_VERTEX4F ... NV097_SET_VERTEX4F + 12: { slot = (class_method - NV097_SET_VERTEX4F) / 4; diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index e6cdc02321..6a4d6abc93 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -116,8 +116,9 @@ static QString* generate_fixed_function(const ShaderState state, int i, j; /* generate vertex shader mimicking fixed function */ + QString* h = qstring_new(); QString* s = qstring_new(); - qstring_append(s, + qstring_append(h, "#version 330\n" "\n" "#define position v0\n" @@ -139,16 +140,16 @@ static QString* generate_fixed_function(const ShaderState state, "\n"); for(i = 0; i < 16; i++) { - qstring_append_fmt(s, "in vec4 v%d;\n", i); + qstring_append_fmt(h, "in vec4 v%d;\n", i); } - qstring_append(s, "\n" + qstring_append(h, "\n" STRUCT_VERTEX_DATA); - qstring_append_fmt(s, "noperspective out VertexData %c_vtx;\n", out_prefix); - qstring_append_fmt(s, "#define vtx %c_vtx", out_prefix); + qstring_append_fmt(h, "noperspective out VertexData %c_vtx;\n", out_prefix); + qstring_append_fmt(h, "#define vtx %c_vtx", out_prefix); - qstring_append(s, + qstring_append(h, "\n" /* FIXME: Add these uniforms using code when they are used */ "uniform vec4 fogColor;\n" @@ -185,8 +186,7 @@ static QString* generate_fixed_function(const ShaderState state, "uniform mat4 projectionMat; /* FIXME: when is this used? */\n" "uniform mat4 compositeMat;\n" "uniform mat4 invViewport;\n" -"\n" -"void main() {\n"); +"\n"); /* Skinning */ unsigned int count; @@ -303,6 +303,101 @@ static QString* generate_fixed_function(const ShaderState state, } } + /* Lighting */ + if (state.lighting) { + + //FIXME: Do 2 passes if we want 2 sided-lighting? + qstring_append_fmt(h, "uniform vec3 sceneAmbientColor;\n"); + qstring_append(s, "vec4 tD0 = vec4(sceneAmbientColor, diffuse.a);\n"); + qstring_append(s, "vec4 tD1 = vec4(0.0, 0.0, 0.0, specular.a);\n"); + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + if (state.light[i] == LIGHT_OFF) { + continue; + } + + qstring_append_fmt(h, + "uniform vec3 lightAmbientColor%d;\n" + "uniform vec3 lightDiffuseColor%d;\n" + "uniform vec3 lightSpecularColor%d;\n", + i, i, i); + + /* FIXME: It seems that we only have to handle the surface colors if + * they are not part of the material [= vertex colors]. + * If they are material the cpu will premultiply light + * colors + */ + + qstring_append_fmt(s, "/* Light %d */ {\n", i); + + qstring_append_fmt(h, + "uniform float lightLocalRange%d;\n", i); + + if (state.light[i] == LIGHT_LOCAL + || state.light[i] == LIGHT_SPOT) { + + qstring_append_fmt(h, + "uniform vec3 lightLocalPosition%d;\n" + "uniform vec3 lightAttenuation%d;\n", + i, i); + qstring_append_fmt(s, + " float distance = distance(lightLocalPosition%d, tPosition.xyz);\n" + " float attenuation = 1.0 / (lightAttenuation%d.x\n" + " + lightAttenuation%d.y * distance\n" + " + lightAttenuation%d.z * distance * distance);\n", + i, i, i, i); + + } + + switch(state.light[i]) { + case LIGHT_INFINITE: + + /* lightLocalRange will be 1e+30 here */ + + qstring_append_fmt(h, + "uniform vec3 lightInfiniteHalfVector%d;\n" + "uniform vec3 lightInfiniteDirection%d;\n", + i, i); + qstring_append_fmt(s, + " float L = dot(tNormal, lightInfiniteDirection%d);\n" + " float attenuation = max(L, 0.0);\n", + i); + + /* FIXME: Do specular */ + + /* FIXME: tBackDiffuse */ + + break; + case LIGHT_LOCAL: + /* Everything done already */ + break; + case LIGHT_SPOT: + qstring_append_fmt(h, + "uniform vec3 lightSpotFalloff%d;\n" + "uniform vec4 lightSpotDirection%d;\n", + i, i); + assert(false); + /*FIXME: calculate falloff */ + break; + default: + assert(false); + break; + } + + qstring_append_fmt(s, + " float highlight = 0.0;\n" + " vec3 lightDiffuse = attenuation * lightDiffuseColor%d;\n" + " vec3 lightSpecular = highlight * lightSpecularColor%d;\n" + " tD0.xyz += lightAmbientColor%d;\n" /* FIXME: Clamp? */ + " tD0.xyz += diffuse.xyz * lightDiffuse;\n" + " tD1.xyz += specular.xyz * lightSpecular;\n" + "}\n", + i, i, i); + } + } else { + qstring_append(s, "vec4 tD0 = diffuse;\n"); + qstring_append(s, "vec4 tD1 = specular;\n"); + } + /* Fog */ if (state.fog_enable) { @@ -414,8 +509,8 @@ static QString* generate_fixed_function(const ShaderState state, " gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); qstring_append(s, "vtx.inv_w = 1.0/gl_Position.w;\n"); - qstring_append(s, "vtx.D0 = diffuse * vtx.inv_w;\n"); - qstring_append(s, "vtx.D1 = specular * vtx.inv_w;\n"); + qstring_append(s, "vtx.D0 = tD0 * vtx.inv_w;\n"); + qstring_append(s, "vtx.D1 = tD1 * vtx.inv_w;\n"); qstring_append(s, "vtx.B0 = backDiffuse * vtx.inv_w;\n"); qstring_append(s, "vtx.B1 = backSpecular * vtx.inv_w;\n"); qstring_append(s, "vtx.Fog = tFog * vtx.inv_w;\n"); @@ -424,9 +519,13 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append(s, "vtx.T2 = tTexture2 * vtx.inv_w;\n"); qstring_append(s, "vtx.T3 = tTexture3 * vtx.inv_w;\n"); - qstring_append(s, "}\n"); + qstring_append(h,"void main() {\n"); + qstring_append(h, qstring_get_str(s)); + qstring_append(h, "}\n"); - return s; + QDECREF(s); + + return h; } static GLuint create_gl_shader(GLenum gl_shader_type, @@ -475,12 +574,12 @@ ShaderBinding* generate_shaders(const ShaderState state) /* create the vertex shader */ - QString *vertex_shader_code = NULL; + QString *s = NULL; if (state.fixed_function) { - vertex_shader_code = generate_fixed_function(state, vtx_prefix); + s = generate_fixed_function(state, vtx_prefix); } else if (state.vertex_program) { - vertex_shader_code = vsh_translate(VSH_VERSION_XVS, + s = vsh_translate(VSH_VERSION_XVS, (uint32_t*)state.program_data, state.program_length, vtx_prefix); @@ -488,15 +587,15 @@ ShaderBinding* generate_shaders(const ShaderState state) assert(false); } - if (vertex_shader_code) { - const char* vertex_shader_code_str = qstring_get_str(vertex_shader_code); + if (s) { + const char* s_str = qstring_get_str(s); GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER, - vertex_shader_code_str, + s_str, "vertex shader"); glAttachShader(program, vertex_shader); - QDECREF(vertex_shader_code); + QDECREF(s); } diff --git a/hw/xbox/nv2a_shaders.h b/hw/xbox/nv2a_shaders.h index 3271cb5415..9077556c0a 100644 --- a/hw/xbox/nv2a_shaders.h +++ b/hw/xbox/nv2a_shaders.h @@ -29,6 +29,7 @@ #define NV2A_MAX_TRANSFORM_PROGRAM_LENGTH 136 #define NV2A_VERTEXSHADER_CONSTANTS 192 +#define NV2A_MAX_LIGHTS 8 enum ShaderPrimitiveMode { PRIM_TYPE_NONE, @@ -73,6 +74,9 @@ typedef struct ShaderState { bool normalization; + bool lighting; + enum VshLight light[NV2A_MAX_LIGHTS]; + bool fixed_function; /* vertex program */ diff --git a/hw/xbox/nv2a_vsh.h b/hw/xbox/nv2a_vsh.h index 8cb49e9ccf..a981c56950 100644 --- a/hw/xbox/nv2a_vsh.h +++ b/hw/xbox/nv2a_vsh.h @@ -24,6 +24,13 @@ #include "qapi/qmp/qstring.h" +enum VshLight { + LIGHT_OFF, + LIGHT_INFINITE, + LIGHT_LOCAL, + LIGHT_SPOT +}; + enum VshTexgen { TEXGEN_DISABLE, TEXGEN_EYE_LINEAR, From b7936a463de8f933332ae6baa581bcdbae2d4ce2 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Thu, 6 Aug 2015 03:29:17 +0200 Subject: [PATCH 2/5] Point lights --- hw/xbox/nv2a_shaders.c | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 6a4d6abc93..edcdd9cc02 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -337,13 +337,19 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append_fmt(h, "uniform vec3 lightLocalPosition%d;\n" - "uniform vec3 lightAttenuation%d;\n", + "uniform vec3 lightLocalAttenuation%d;\n", i, i); + qstring_append_fmt(s,"vec3 eye = vec3(0.0);\n"); /*FIXME: Uniform?! */ qstring_append_fmt(s, - " float distance = distance(lightLocalPosition%d, tPosition.xyz);\n" - " float attenuation = 1.0 / (lightAttenuation%d.x\n" - " + lightAttenuation%d.y * distance\n" - " + lightAttenuation%d.z * distance * distance);\n", + " vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n" + " float d = length(VP);\n" + " VP = normalize(VP);\n" + " float attenuation = 1.0 / (lightLocalAttenuation%d.x\n" + " + lightLocalAttenuation%d.y * d\n" + " + lightLocalAttenuation%d.z * d * d);\n" + " vec3 halfVector = normalize(VP + eye);\n" + " float nDotVP = max(0.0, dot(tNormal, VP));\n" + " float nDotHV = max(0.0, dot(tNormal, halfVector));\n", i, i, i, i); } @@ -358,9 +364,10 @@ static QString* generate_fixed_function(const ShaderState state, "uniform vec3 lightInfiniteDirection%d;\n", i, i); qstring_append_fmt(s, - " float L = dot(tNormal, lightInfiniteDirection%d);\n" - " float attenuation = max(L, 0.0);\n", - i); + " float attenuation = 1.0;\n" + " float nDotVP = max(0.0, dot(tNormal, normalize(vec3(lightInfiniteDirection%d))));\n" + " float nDotHV = max(0.0, dot(tNormal, vec3(lightInfiniteHalfVector%d)));\n", + i, i); /* FIXME: Do specular */ @@ -384,19 +391,33 @@ static QString* generate_fixed_function(const ShaderState state, } qstring_append_fmt(s, - " float highlight = 0.0;\n" - " vec3 lightDiffuse = attenuation * lightDiffuseColor%d;\n" - " vec3 lightSpecular = highlight * lightSpecularColor%d;\n" - " tD0.xyz += lightAmbientColor%d;\n" /* FIXME: Clamp? */ - " tD0.xyz += diffuse.xyz * lightDiffuse;\n" - " tD1.xyz += specular.xyz * lightSpecular;\n" - "}\n", + " float pf;\n" + " if (nDotVP == 0.0) {\n" + " pf = 0.0;\n" + " } else {\n" + " pf = pow(nDotHV, /* specular(l, m, n, l1, m1, n1) */ 0.001);\n" + " }\n" + " vec3 lightAmbient = lightAmbientColor%d * attenuation;\n" + " vec3 lightDiffuse = lightDiffuseColor%d * attenuation * nDotVP;\n" + " vec3 lightSpecular = lightSpecularColor%d * pf;\n", i, i, i); + + qstring_append(s, + " tD0.xyz += lightAmbient;\n"); + + qstring_append(s, + " tD0.xyz += diffuse.xyz * lightDiffuse;\n"); + + qstring_append(s, + " tD1.xyz += specular.xyz * lightSpecular;\n" + "}\n"); } } else { qstring_append(s, "vec4 tD0 = diffuse;\n"); qstring_append(s, "vec4 tD1 = specular;\n"); } + qstring_append(s, "vec4 tB0 = backDiffuse;\n"); + qstring_append(s, "vec4 tB1 = backSpecular;\n"); /* Fog */ if (state.fog_enable) { @@ -509,10 +530,10 @@ static QString* generate_fixed_function(const ShaderState state, " gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); qstring_append(s, "vtx.inv_w = 1.0/gl_Position.w;\n"); - qstring_append(s, "vtx.D0 = tD0 * vtx.inv_w;\n"); - qstring_append(s, "vtx.D1 = tD1 * vtx.inv_w;\n"); - qstring_append(s, "vtx.B0 = backDiffuse * vtx.inv_w;\n"); - qstring_append(s, "vtx.B1 = backSpecular * vtx.inv_w;\n"); + qstring_append(s, "vtx.D0 = clamp(tD0, 0.0, 1.0) * vtx.inv_w;\n"); + qstring_append(s, "vtx.D1 = clamp(tD1, 0.0, 1.0) * vtx.inv_w;\n"); + qstring_append(s, "vtx.B0 = clamp(tB0, 0.0, 1.0) * vtx.inv_w;\n"); + qstring_append(s, "vtx.B1 = clamp(tB1, 0.0, 1.0) * vtx.inv_w;\n"); qstring_append(s, "vtx.Fog = tFog * vtx.inv_w;\n"); qstring_append(s, "vtx.T0 = tTexture0 * vtx.inv_w;\n"); qstring_append(s, "vtx.T1 = tTexture1 * vtx.inv_w;\n"); From f6169030dcbc9fd1069eda5bafcb3be44841f3ef Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Thu, 6 Aug 2015 15:14:32 +0200 Subject: [PATCH 3/5] Comment about range being ignored --- hw/xbox/nv2a_shaders.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index edcdd9cc02..82370fd42c 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -343,6 +343,7 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append_fmt(s, " vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n" " float d = length(VP);\n" +//FIXME: if (d > lightLocalRange) { .. don't process this light .. } /* inclusive?! */ - what about directional lights? " VP = normalize(VP);\n" " float attenuation = 1.0 / (lightLocalAttenuation%d.x\n" " + lightLocalAttenuation%d.y * d\n" From 9edbfd08bcfd3f05cc4079057d201736d4862b4a Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Thu, 6 Aug 2015 16:22:03 +0200 Subject: [PATCH 4/5] Eye registers and smaller changes --- hw/xbox/nv2a.c | 50 ++++++++++++++++++++++++++++++++++++++++-- hw/xbox/nv2a_shaders.c | 6 +++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index b1b2636d1e..7809088c51 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -539,8 +539,11 @@ #define NV_PGRAPH_TEXPALETTE2 0x00001A3C #define NV_PGRAPH_TEXPALETTE3 0x00001A40 #define NV_PGRAPH_ZSTENCILCLEARVALUE 0x00001A88 -#define NV_PGRAPH_ZCLIPMAX 0x00001ABC #define NV_PGRAPH_ZCLIPMIN 0x00001A90 +#define NV_PGRAPH_EYEVEC0 0x00001AAC +#define NV_PGRAPH_EYEVEC1 0x00001AB0 +#define NV_PGRAPH_EYEVEC2 0x00001AB4 +#define NV_PGRAPH_ZCLIPMAX 0x00001ABC #define NV_PCRTC_INTR_0 0x00000100 @@ -894,6 +897,7 @@ # define NV097_SET_FOG_PLANE 0x009709D0 # define NV097_SET_SCENE_AMBIENT_COLOR 0x00970A10 # define NV097_SET_VIEWPORT_OFFSET 0x00970A20 +# define NV097_SET_EYE_POSITION 0x00970A50 # define NV097_SET_COMBINER_FACTOR0 0x00970A60 # define NV097_SET_COMBINER_FACTOR1 0x00970A80 # define NV097_SET_COMBINER_ALPHA_OCW 0x00970AA0 @@ -937,6 +941,7 @@ # define NV097_GET_REPORT_OFFSET 0x00FFFFFF # define NV097_GET_REPORT_TYPE 0xFF000000 # define NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT 1 +# define NV097_SET_EYE_DIRECTION 0x009717E0 # define NV097_SET_SHADER_CLIP_PLANE_MODE 0x009717F8 # define NV097_SET_BEGIN_END 0x009717FC # define NV097_SET_BEGIN_END_OP_END 0x00 @@ -956,6 +961,7 @@ # define NV097_DRAW_ARRAYS_COUNT 0xFF000000 # define NV097_DRAW_ARRAYS_START_INDEX 0x00FFFFFF # define NV097_INLINE_ARRAY 0x00971818 +# define NV097_SET_EYE_VECTOR 0x0097181C # define NV097_SET_VERTEX_DATA2F_M 0x00971880 # define NV097_SET_VERTEX_DATA4F_M 0x00971A00 # define NV097_SET_VERTEX_DATA2S 0x00971900 @@ -1584,6 +1590,10 @@ typedef struct PGRAPHState { float light_local_position[NV2A_MAX_LIGHTS][3]; float light_local_attenuation[NV2A_MAX_LIGHTS][3]; + /* FIXME: These are probably stored in the vshader consts */ + float eye_position[4]; + float eye_direction[3]; + /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ float bump_env_matrix[NV2A_MAX_TEXTURES-1][4]; /* 3 allowed stages with 2x2 matrix each */ @@ -3144,11 +3154,30 @@ static void pgraph_bind_shaders(PGRAPHState *pg) glUniformMatrix4fv(projLoc, 1, GL_FALSE, pg->projection_matrix); } + GLint eyeVecLoc = glGetUniformLocation(pg->shader_binding->gl_program, + "eyeVector"); + if (eyeVecLoc != -1) { + glUniform3f(eyeVecLoc, *(float*)&pg->regs[NV_PGRAPH_EYEVEC0], + *(float*)&pg->regs[NV_PGRAPH_EYEVEC1], + *(float*)&pg->regs[NV_PGRAPH_EYEVEC2]); + } + GLint eyePosLoc = glGetUniformLocation(pg->shader_binding->gl_program, + "eyePosition"); + if (eyePosLoc != -1) { + glUniform4fv(eyePosLoc, 1, pg->eye_position); + } + GLint eyeDirLoc = glGetUniformLocation(pg->shader_binding->gl_program, + "eyeDirection"); + if (eyeDirLoc != -1) { + glUniform3fv(eyeDirLoc, 1, pg->eye_direction); + } + /* FIXME: Only do this if lighting is allowed? I'd believe lighting works * with both FFP and VPs. */ + NV2A_GL_DGROUP_BEGIN("Lighting uniforms"); GLint ambLoc = glGetUniformLocation(pg->shader_binding->gl_program, - "sceneAmbientColor"); + "sceneAmbientColor"); if (ambLoc != -1) { glUniform3fv(ambLoc, 1, pg->scene_ambient_color); } @@ -3221,6 +3250,7 @@ static void pgraph_bind_shaders(PGRAPHState *pg) glUniform3fv(loc, 1, pg->light_local_attenuation[i]); } } + NV2A_GL_DGROUP_END(); float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX]; float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN]; @@ -4644,6 +4674,11 @@ static void pgraph_method(NV2AState *d, pg->constants[59].dirty = true; break; + case NV097_SET_EYE_POSITION ... + NV097_SET_EYE_POSITION + 12: + slot = (class_method - NV097_SET_EYE_POSITION) / 4; + pg->eye_position[slot] = *(float*)¶meter; + break; case NV097_SET_COMBINER_FACTOR0 ... NV097_SET_COMBINER_FACTOR0 + 28: slot = (class_method - NV097_SET_COMBINER_FACTOR0) / 4; @@ -5001,6 +5036,12 @@ static void pgraph_method(NV2AState *d, break; } + case NV097_SET_EYE_DIRECTION ... + NV097_SET_EYE_DIRECTION + 8: + slot = (class_method - NV097_SET_EYE_DIRECTION) / 4; + pg->eye_direction[slot] = *(float*)¶meter; + break; + case NV097_SET_BEGIN_END: { bool depth_test = pg->regs[NV_PGRAPH_CONTROL_0] & NV_PGRAPH_CONTROL_0_ZENABLE; @@ -5406,6 +5447,11 @@ static void pgraph_method(NV2AState *d, pg->inline_array[ pg->inline_array_length++] = parameter; break; + case NV097_SET_EYE_VECTOR ... + NV097_SET_EYE_VECTOR + 8: + slot = (class_method - NV097_SET_EYE_VECTOR) / 4; + pg->regs[NV_PGRAPH_EYEVEC0 + slot * 4] = parameter; + break; case NV097_SET_VERTEX_DATA2F_M ... NV097_SET_VERTEX_DATA2F_M + 0x7c: { diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 82370fd42c..254678a6af 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -335,11 +335,13 @@ static QString* generate_fixed_function(const ShaderState state, if (state.light[i] == LIGHT_LOCAL || state.light[i] == LIGHT_SPOT) { + qstring_append(h, + "uniform vec4 eyePosition;\n"); + qstring_append_fmt(h, "uniform vec3 lightLocalPosition%d;\n" "uniform vec3 lightLocalAttenuation%d;\n", i, i); - qstring_append_fmt(s,"vec3 eye = vec3(0.0);\n"); /*FIXME: Uniform?! */ qstring_append_fmt(s, " vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n" " float d = length(VP);\n" @@ -348,7 +350,7 @@ static QString* generate_fixed_function(const ShaderState state, " float attenuation = 1.0 / (lightLocalAttenuation%d.x\n" " + lightLocalAttenuation%d.y * d\n" " + lightLocalAttenuation%d.z * d * d);\n" - " vec3 halfVector = normalize(VP + eye);\n" + " vec3 halfVector = normalize(VP + eyePosition.xyz / eyePosition.w);\n" /* FIXME: Not sure if eyePosition is correct */ " float nDotVP = max(0.0, dot(tNormal, VP));\n" " float nDotHV = max(0.0, dot(tNormal, halfVector));\n", i, i, i, i); From 90c2eca5a8cc3c26f06f92dc0056882c3701c44f Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Tue, 25 Aug 2015 08:23:58 +0200 Subject: [PATCH 5/5] Fix bug with duplicate uniform --- hw/xbox/nv2a_shaders.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 254678a6af..012611b8f6 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -310,6 +310,11 @@ static QString* generate_fixed_function(const ShaderState state, qstring_append_fmt(h, "uniform vec3 sceneAmbientColor;\n"); qstring_append(s, "vec4 tD0 = vec4(sceneAmbientColor, diffuse.a);\n"); qstring_append(s, "vec4 tD1 = vec4(0.0, 0.0, 0.0, specular.a);\n"); + + /* FIXME: Only add if necessary */ + qstring_append(h, + "uniform vec4 eyePosition;\n"); + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { if (state.light[i] == LIGHT_OFF) { continue; @@ -335,9 +340,6 @@ static QString* generate_fixed_function(const ShaderState state, if (state.light[i] == LIGHT_LOCAL || state.light[i] == LIGHT_SPOT) { - qstring_append(h, - "uniform vec4 eyePosition;\n"); - qstring_append_fmt(h, "uniform vec3 lightLocalPosition%d;\n" "uniform vec3 lightLocalAttenuation%d;\n",