mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	renderer_opengl: Implement diffuse component of HW fragment lighting.
This commit is contained in:
		
							parent
							
								
									b003075570
								
							
						
					
					
						commit
						afbef52516
					
				
					 6 changed files with 270 additions and 15 deletions
				
			
		|  | @ -662,17 +662,18 @@ struct Regs { | |||
|         LN = 3, // Cosine of the angle between the light and the normal vectors
 | ||||
|     }; | ||||
| 
 | ||||
|     union LightColor { | ||||
|         BitField< 0, 10, u32> b; | ||||
|         BitField<10, 10, u32> g; | ||||
|         BitField<20, 10, u32> r; | ||||
| 
 | ||||
|         Math::Vec3f ToVec3f() const { | ||||
|             // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color component
 | ||||
|             return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct { | ||||
|         union LightColor { | ||||
|             BitField< 0, 10, u32> b; | ||||
|             BitField<10, 10, u32> g; | ||||
|             BitField<20, 10, u32> r; | ||||
| 
 | ||||
|             Math::Vec3f ToVec3f() const { | ||||
|                 return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         struct LightSrc { | ||||
|             LightColor specular_0;  // material.specular_0 * light.specular_0
 | ||||
|             LightColor specular_1;  // material.specular_1 * light.specular_1
 | ||||
|  |  | |||
|  | @ -75,6 +75,12 @@ void RasterizerOpenGL::InitObjects() { | |||
|     glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); | ||||
|     glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); | ||||
| 
 | ||||
|     glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat)); | ||||
|     glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT); | ||||
| 
 | ||||
|     glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, view)); | ||||
|     glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW); | ||||
| 
 | ||||
|     SetShader(); | ||||
| 
 | ||||
|     // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
 | ||||
|  | @ -283,6 +289,98 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
|     case PICA_REG_INDEX(tev_combiner_buffer_color): | ||||
|         SyncCombinerColor(); | ||||
|         break; | ||||
| 
 | ||||
|     // Fragment lighting diffuse color
 | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10): | ||||
|         SyncLightDiffuse(0); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[1].diffuse, 0x142 + 1 * 0x10): | ||||
|         SyncLightDiffuse(1); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[2].diffuse, 0x142 + 2 * 0x10): | ||||
|         SyncLightDiffuse(2); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[3].diffuse, 0x142 + 3 * 0x10): | ||||
|         SyncLightDiffuse(3); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[4].diffuse, 0x142 + 4 * 0x10): | ||||
|         SyncLightDiffuse(4); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[5].diffuse, 0x142 + 5 * 0x10): | ||||
|         SyncLightDiffuse(5); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[6].diffuse, 0x142 + 6 * 0x10): | ||||
|         SyncLightDiffuse(6); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[7].diffuse, 0x142 + 7 * 0x10): | ||||
|         SyncLightDiffuse(7); | ||||
|         break; | ||||
| 
 | ||||
|     // Fragment lighting ambient color
 | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].ambient, 0x143 + 0 * 0x10): | ||||
|         SyncLightAmbient(0); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[1].ambient, 0x143 + 1 * 0x10): | ||||
|         SyncLightAmbient(1); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[2].ambient, 0x143 + 2 * 0x10): | ||||
|         SyncLightAmbient(2); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[3].ambient, 0x143 + 3 * 0x10): | ||||
|         SyncLightAmbient(3); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[4].ambient, 0x143 + 4 * 0x10): | ||||
|         SyncLightAmbient(4); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[5].ambient, 0x143 + 5 * 0x10): | ||||
|         SyncLightAmbient(5); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[6].ambient, 0x143 + 6 * 0x10): | ||||
|         SyncLightAmbient(6); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[7].ambient, 0x143 + 7 * 0x10): | ||||
|         SyncLightAmbient(7); | ||||
|         break; | ||||
| 
 | ||||
|      // Fragment lighting position
 | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].x, 0x144 + 0 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].z, 0x145 + 0 * 0x10): | ||||
|         SyncLightPosition(0); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[1].x, 0x144 + 1 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[1].z, 0x145 + 1 * 0x10): | ||||
|         SyncLightPosition(1); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[2].x, 0x144 + 2 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[2].z, 0x145 + 2 * 0x10): | ||||
|         SyncLightPosition(2); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[3].x, 0x144 + 3 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[3].z, 0x145 + 3 * 0x10): | ||||
|         SyncLightPosition(3); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[4].x, 0x144 + 4 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[4].z, 0x145 + 4 * 0x10): | ||||
|         SyncLightPosition(4); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[5].x, 0x144 + 5 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[5].z, 0x145 + 5 * 0x10): | ||||
|         SyncLightPosition(5); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[6].x, 0x144 + 6 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[6].z, 0x145 + 6 * 0x10): | ||||
|         SyncLightPosition(6); | ||||
|         break; | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[7].x, 0x144 + 7 * 0x10): | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[7].z, 0x145 + 7 * 0x10): | ||||
|         SyncLightPosition(7); | ||||
|         break; | ||||
| 
 | ||||
|     // Fragment lighting global ambient color (emission + ambient * ambient)
 | ||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0): | ||||
|         SyncGlobalAmbient(); | ||||
|         break; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -503,6 +601,13 @@ void RasterizerOpenGL::SetShader() { | |||
|     auto& tev_stages = Pica::g_state.regs.GetTevStages(); | ||||
|     for (int index = 0; index < tev_stages.size(); ++index) | ||||
|         SyncTevConstColor(index, tev_stages[index]); | ||||
| 
 | ||||
|     SyncGlobalAmbient(); | ||||
|     for (int light_index = 0; light_index < 8; light_index++) { | ||||
|         SyncLightDiffuse(light_index); | ||||
|         SyncLightAmbient(light_index); | ||||
|         SyncLightPosition(light_index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncFramebuffer() { | ||||
|  | @ -683,6 +788,42 @@ void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevS | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncGlobalAmbient() { | ||||
|     auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.global_ambient); | ||||
|     if (color != uniform_block_data.data.lighting_global_ambient) { | ||||
|         uniform_block_data.data.lighting_global_ambient = color; | ||||
|         uniform_block_data.dirty = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncLightDiffuse(int light_index) { | ||||
|     auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse); | ||||
|     if (color != uniform_block_data.data.light_src[light_index].diffuse) { | ||||
|         uniform_block_data.data.light_src[light_index].diffuse = color; | ||||
|         uniform_block_data.dirty = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncLightAmbient(int light_index) { | ||||
|     auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].ambient); | ||||
|     if (color != uniform_block_data.data.light_src[light_index].ambient) { | ||||
|         uniform_block_data.data.light_src[light_index].ambient = color; | ||||
|         uniform_block_data.dirty = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncLightPosition(int light_index) { | ||||
|     std::array<GLfloat, 3> position = { | ||||
|         Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(), | ||||
|         Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(), | ||||
|         Pica::float16::FromRawFloat16(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() }; | ||||
| 
 | ||||
|     if (position != uniform_block_data.data.light_src[light_index].position) { | ||||
|         uniform_block_data.data.light_src[light_index].position = position; | ||||
|         uniform_block_data.dirty = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RasterizerOpenGL::SyncDrawState() { | ||||
|     const auto& regs = Pica::g_state.regs; | ||||
| 
 | ||||
|  |  | |||
|  | @ -71,6 +71,18 @@ struct PicaShaderConfig { | |||
|             regs.tev_combiner_buffer_input.update_mask_rgb.Value() | | ||||
|             regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; | ||||
| 
 | ||||
|         // Fragment lighting
 | ||||
| 
 | ||||
|         res.lighting_enabled = !regs.lighting.disable; | ||||
|         res.num_lights = regs.lighting.src_num + 1; | ||||
| 
 | ||||
|         for (unsigned light_index = 0; light_index < res.num_lights; ++light_index) { | ||||
|             unsigned num = regs.lighting.light_enable.GetNum(light_index); | ||||
|             res.light_src[light_index].num = num; | ||||
|             res.light_src[light_index].directional = regs.lighting.light[num].w; | ||||
|             res.light_src[light_index].two_sided_diffuse = regs.lighting.light[num].two_sided_diffuse; | ||||
|         } | ||||
| 
 | ||||
|         return res; | ||||
|     } | ||||
| 
 | ||||
|  | @ -89,6 +101,16 @@ struct PicaShaderConfig { | |||
|     Pica::Regs::CompareFunc alpha_test_func; | ||||
|     std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {}; | ||||
|     u8 combiner_buffer_input; | ||||
| 
 | ||||
|     struct { | ||||
|         unsigned num; | ||||
|         bool directional; | ||||
|         bool two_sided_diffuse; | ||||
|         bool dist_atten_enabled; | ||||
|     } light_src[8]; | ||||
| 
 | ||||
|     bool lighting_enabled; | ||||
|     unsigned num_lights; | ||||
| }; | ||||
| 
 | ||||
| namespace std { | ||||
|  | @ -182,6 +204,13 @@ private: | |||
|             tex_coord1[1] = v.tc1.y.ToFloat32(); | ||||
|             tex_coord2[0] = v.tc2.x.ToFloat32(); | ||||
|             tex_coord2[1] = v.tc2.y.ToFloat32(); | ||||
|             normquat[0] = v.quat.x.ToFloat32(); | ||||
|             normquat[1] = v.quat.y.ToFloat32(); | ||||
|             normquat[2] = v.quat.z.ToFloat32(); | ||||
|             normquat[3] = v.quat.w.ToFloat32(); | ||||
|             view[0] = v.view.x.ToFloat32(); | ||||
|             view[1] = v.view.y.ToFloat32(); | ||||
|             view[2] = v.view.z.ToFloat32(); | ||||
|         } | ||||
| 
 | ||||
|         GLfloat position[4]; | ||||
|  | @ -189,6 +218,17 @@ private: | |||
|         GLfloat tex_coord0[2]; | ||||
|         GLfloat tex_coord1[2]; | ||||
|         GLfloat tex_coord2[2]; | ||||
|         GLfloat normquat[4]; | ||||
|         GLfloat view[3]; | ||||
|     }; | ||||
| 
 | ||||
|     struct LightSrc { | ||||
|         std::array<GLfloat, 3> diffuse; | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|         std::array<GLfloat, 3> ambient; | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|         std::array<GLfloat, 3> position; | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|     }; | ||||
| 
 | ||||
|     /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
 | ||||
|  | @ -198,11 +238,14 @@ private: | |||
|         std::array<GLfloat, 4> tev_combiner_buffer_color; | ||||
|         GLint alphatest_ref; | ||||
|         GLfloat depth_offset; | ||||
|         INSERT_PADDING_BYTES(8); | ||||
|         INSERT_PADDING_WORDS(2); | ||||
|         std::array<GLfloat, 3> lighting_global_ambient; | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|         LightSrc light_src[8]; | ||||
|     }; | ||||
| 
 | ||||
|     static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader"); | ||||
|     static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec"); | ||||
|     static_assert(sizeof(UniformData) == 0x210, "The size of the UniformData structure has changed, update the structure in the shader"); | ||||
|     static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); | ||||
| 
 | ||||
|     /// Reconfigure the OpenGL color texture to use the given format and dimensions
 | ||||
|     void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height); | ||||
|  | @ -249,6 +292,18 @@ private: | |||
|     /// Syncs the TEV combiner color buffer to match the PICA register
 | ||||
|     void SyncCombinerColor(); | ||||
| 
 | ||||
|     /// Syncs the lighting global ambient color to match the PICA register
 | ||||
|     void SyncGlobalAmbient(); | ||||
| 
 | ||||
|     /// Syncs the specified light's diffuse color to match the PICA register
 | ||||
|     void SyncLightDiffuse(int light_index); | ||||
| 
 | ||||
|     /// Syncs the specified light's ambient color to match the PICA register
 | ||||
|     void SyncLightAmbient(int light_index); | ||||
| 
 | ||||
|     /// Syncs the specified light's position to match the PICA register
 | ||||
|     void SyncLightPosition(int light_index); | ||||
| 
 | ||||
|     /// Syncs the remaining OpenGL drawing state to match the current PICA state
 | ||||
|     void SyncDrawState(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,8 +32,7 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, | |||
|         out += "primary_color"; | ||||
|         break; | ||||
|     case Source::PrimaryFragmentColor: | ||||
|         // HACK: Until we implement fragment lighting, use primary_color
 | ||||
|         out += "primary_color"; | ||||
|         out += "primary_fragment_color"; | ||||
|         break; | ||||
|     case Source::SecondaryFragmentColor: | ||||
|         // HACK: Until we implement fragment lighting, use zero
 | ||||
|  | @ -324,24 +323,67 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { | |||
|     std::string out = R"( | ||||
| #version 330 core | ||||
| #define NUM_TEV_STAGES 6 | ||||
| #define NUM_LIGHTS 8 | ||||
| 
 | ||||
| in vec4 primary_color; | ||||
| in vec2 texcoord[3]; | ||||
| in vec4 normquat; | ||||
| in vec3 view; | ||||
| 
 | ||||
| out vec4 color; | ||||
| 
 | ||||
| struct LightSrc { | ||||
|     vec3 diffuse; | ||||
|     vec3 ambient; | ||||
|     vec3 position; | ||||
| }; | ||||
| 
 | ||||
| layout (std140) uniform shader_data { | ||||
|     vec4 const_color[NUM_TEV_STAGES]; | ||||
|     vec4 tev_combiner_buffer_color; | ||||
|     int alphatest_ref; | ||||
|     float depth_offset; | ||||
|     vec3 lighting_global_ambient; | ||||
|     LightSrc light_src[NUM_LIGHTS]; | ||||
| }; | ||||
| 
 | ||||
| uniform sampler2D tex[3]; | ||||
| 
 | ||||
| void main() { | ||||
| vec4 primary_fragment_color = vec4(0.0); | ||||
| )"; | ||||
| 
 | ||||
|     if (config.lighting_enabled) { | ||||
|         out += "vec3 normal = normalize(vec3(\n"; | ||||
|         out += "          2.f*(normquat.x*normquat.z + normquat.y*normquat.w),\n"; | ||||
|         out += "          2.f*(normquat.y*normquat.z + normquat.x*normquat.w),\n"; | ||||
|         out += "    1.f - 2.f*(normquat.x*normquat.x + normquat.y*normquat.y)));\n"; | ||||
|         out += "vec4 secondary_color = vec4(0.0);\n"; | ||||
|         out += "vec3 diffuse_sum = vec3(0.0);\n"; | ||||
|         out += "vec3 fragment_position = -view;\n"; | ||||
| 
 | ||||
|         for (unsigned light_index = 0; light_index < config.num_lights; ++light_index) { | ||||
|             unsigned num = config.light_src[light_index].num; | ||||
| 
 | ||||
|             std::string light_vector; | ||||
|             if (config.light_src[light_index].directional) | ||||
|                 light_vector = "normalize(-light_src[" + std::to_string(num) + "].position)"; | ||||
|             else | ||||
|                 light_vector = "normalize(light_src[" + std::to_string(num) + "].position - fragment_position)"; | ||||
| 
 | ||||
|             std::string dot_product; | ||||
|             if (config.light_src[light_index].two_sided_diffuse) | ||||
|                 dot_product = "abs(dot(" + light_vector + ", normal))"; | ||||
|             else | ||||
|                 dot_product = "max(dot(" + light_vector + ", normal), 0.0)"; | ||||
| 
 | ||||
|             out += "diffuse_sum += ((light_src[" + std::to_string(num) + "].diffuse * " + dot_product + ") + light_src[" + std::to_string(num) + "].ambient) * 1.0;\n"; | ||||
|         } | ||||
| 
 | ||||
|         out += "diffuse_sum += lighting_global_ambient;\n"; | ||||
|         out += "primary_fragment_color = vec4(clamp(diffuse_sum, vec3(0.0), vec3(1.0)), 1.0);\n"; | ||||
|     } | ||||
| 
 | ||||
|     // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | ||||
|     if (config.alpha_test_func == Regs::CompareFunc::Never) { | ||||
|         out += "discard; }"; | ||||
|  | @ -369,21 +411,28 @@ void main() { | |||
| 
 | ||||
| std::string GenerateVertexShader() { | ||||
|     std::string out = "#version 330 core\n"; | ||||
| 
 | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION)  + ") in vec4 vert_position;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR)     + ") in vec4 vert_color;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT)  + ") in vec4 vert_normquat;\n"; | ||||
|     out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW)      + ") in vec3 vert_view;\n"; | ||||
| 
 | ||||
|     out += R"( | ||||
| out vec4 primary_color; | ||||
| out vec2 texcoord[3]; | ||||
| out vec4 normquat; | ||||
| out vec3 view; | ||||
| 
 | ||||
| void main() { | ||||
|     primary_color = vert_color; | ||||
|     texcoord[0] = vert_texcoord0; | ||||
|     texcoord[1] = vert_texcoord1; | ||||
|     texcoord[2] = vert_texcoord2; | ||||
|     normquat = vert_normquat; | ||||
|     view = vert_view; | ||||
|     gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w); | ||||
| } | ||||
| )"; | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ enum Attributes { | |||
|     ATTRIBUTE_TEXCOORD0, | ||||
|     ATTRIBUTE_TEXCOORD1, | ||||
|     ATTRIBUTE_TEXCOORD2, | ||||
|     ATTRIBUTE_NORMQUAT, | ||||
|     ATTRIBUTE_VIEW, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -183,4 +183,11 @@ inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) { | |||
|            } }; | ||||
| } | ||||
| 
 | ||||
| inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) { | ||||
|     return { { color.r / 255.0f, | ||||
|                color.g / 255.0f, | ||||
|                color.b / 255.0f | ||||
|            } }; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue