/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "gpu/gpu_renderer.h" #include "core/renderer_environment_interface.h" #include "core/renderer_resource_factory.h" #include "core/camera.h" using namespace GS; using namespace GS::GPU; using Render::TextureParm; //------------------------------------------------------------------------------ void Renderer::SetViewMatrix(const Matrix4 &m, const Matrix4 *inverse) { m_view = m; m_iview = inverse ? *inverse : m.InversedFast(); } void Renderer::SetProjectionMatrix(const Matrix4 &m) { m_projection = m; } void Renderer::SetWorldMatrix(const Matrix4 &m, const Matrix4 *inverse) { m_world = m; m_iworld = inverse ? *inverse : m.InversedFast(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ int Renderer::GetAnisotropySampleCount(TextureParm::Anisotropy aniso) const { uint sample = 1; switch (aniso) { default: sample = registry.GetInteger("Texture:Filtering:Sample", 4); break; case TextureParm::AnisotropyNone: sample = 1; break; case TextureParm::Anisotropy2x: sample = 2; break; case TextureParm::Anisotropy4x: sample = 4; break; case TextureParm::Anisotropy8x: sample = 8; break; case TextureParm::Anisotropy16x: sample = 16; break; } if (sample > gpu_config.max_anisotropy) sample = gpu_config.max_anisotropy; return sample; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::SetDisplayList(DisplayList *dlist) { SetIndexSource(dlist ? dlist->idx.c_ptr() : NULL); SetVertexSource(dlist ? dlist->vtx.c_ptr() : NULL, dlist ? dlist->stride : 0); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::SetMaterial(const GPU::Material &m, const DrawContext::Context &ctx, bool force_alpha) { bool fog_enabled = (environment_interface != NULL) && environment_interface->IsFogEnabled(); if (m.renderword & Core::Material::Render_DoubleSided) EnableCulling(false); if (m.renderword & Core::Material::Render_AlphaTest) if (gpu_config.enable_aa) EnableAlphaToCoverage(true); uint blendop = m.blendop; if ((blendop == Core::Material::Blend_None) && force_alpha) blendop = Core::Material::Blend_Alpha; switch (ctx.render) { case DrawContext::Deferred: case DrawContext::Opaque: break; case DrawContext::Alpha: switch (ctx.draw) { case DrawContext::Base: switch (blendop) { case Core::Material::Blend_Add: if (fog_enabled) SetBlendFunc(BlendSrcAlpha, BlendOne); else SetBlendFunc(BlendOne, BlendOne); break; case Core::Material::Blend_Alpha: SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); break; } break; case DrawContext::Light: switch (blendop) { case Core::Material::Blend_Add: if (fog_enabled) SetBlendFunc(BlendSrcAlpha, BlendOne); else SetBlendFunc(BlendOne, BlendOne); break; case Core::Material::Blend_Alpha: SetBlendFunc(BlendSrcAlpha, BlendOne); break; } break; } } } void Renderer::UnsetMaterial(const GPU::Material &m, const DrawContext::Context &ctx) { if (m.renderword & Core::Material::Render_DoubleSided) EnableCulling(true); if (m.renderword & Core::Material::Render_AlphaTest) if (gpu_config.enable_aa) EnableAlphaToCoverage(false); switch (ctx.render) { case DrawContext::Deferred: case DrawContext::Opaque: break; case DrawContext::Alpha: switch (ctx.draw) { case DrawContext::Base: SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); break; case DrawContext::Light: SetBlendFunc(BlendSrcAlpha, BlendOne); break; } break; } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Renderer::DrawSetState(Core::Material::BlendOperator bo, Core::Material::RenderWord f) { switch (bo) { default: case Core::Material::Blend_None: break; case Core::Material::Blend_Alpha: EnableBlending(true); SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); break; case Core::Material::Blend_Add: EnableBlending(true); SetBlendFunc(BlendSrcAlpha, BlendOne); break; } EnableDepthWrite(!asbool(f & Core::Material::Render_NoZWrite)); EnableDepthTest(!asbool(f & Core::Material::Render_NoZTest)); EnableCulling(!asbool(f & Core::Material::Render_DoubleSided)); } void Renderer::DrawRestoreState(Core::Material::BlendOperator bo, Core::Material::RenderWord f) { EnableCulling(true); EnableDepthTest(true); EnableDepthWrite(true); if (bo != Core::Material::Blend_None) { EnableBlending(false); SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha); } } void Renderer::DrawLine(uint count, const Vector4 *v, const Color *c, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p) { DrawSetState(bo, f); // Dispatch inputs to the correct program. Shader *p = (Shader *)r_p; if (p == NULL) { p = c ? single_color_program : simple_program; if (!p) return; } SetShaderProgram(p); ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position), *color_parm = p->GetInput(Core::ShaderInput::VertexColor); SetIndexSource(NULL); // FIXME broken on the DirectX back-end SetVertexSource(NULL, 0); // FIXME broken on the DirectX back-end if (vtx_parm) p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)v); if (color_parm && c) p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)c); p->SetRendererInputs(*this); p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld); DrawElements(Types::PrimitiveLine, 2 * count); if (vtx_parm) p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL); if (color_parm) p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL); DrawRestoreState(bo, f); } void Renderer::DrawTriangle(uint count, const Vector4 *v, const ushort *idx, const Color *c, const Vector2 *uv, const Render::Texture *t, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p) { DrawSetState(bo, f); // Dispatch inputs to the correct program. Shader *p = (Shader *)r_p; if (p == NULL) { if (c) p = t ? single_texture_color_program : single_color_program; else if (t) p = single_texture_program; if (!p) return; } SetShaderProgram(p); // If no indice were provided, assume a linear attribute array. Array indice; if (!idx) if (indice.Allocate(count * 3)) { idx = indice; for (uint n = 0; n < count * 3; ++n) indice[n] = ushort(n); } // Set program inputs. ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position), *uv_parm = p->GetInput(Core::ShaderInput::UV0), *color_parm = p->GetInput(Core::ShaderInput::VertexColor), *texture_parm = p->GetInput(Core::ShaderInput::Texture2D); if (gpu_config.can_stream_vertex_from_memory) { SetIndexSource(NULL); SetVertexSource(NULL, 0); if (vtx_parm) p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)v); if (uv_parm && uv) p->Set(*uv_parm->location, 2, Types::ValueFloat, false, sizeof(Vector2), (const void *)uv); if (color_parm && c) p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)c); } else { DirectVertexLayout layout; BuildDirectVertexLayout(count * 3, idx, v, c, uv, layout, direct_idx_vbo, direct_vtx_vbo); SetIndexSource(direct_idx_vbo); SetVertexSource(direct_vtx_vbo, layout.stride); if (vtx_parm) p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, layout.stride, (const void *)layout.vtx_offset); if (uv_parm && uv) p->Set(*uv_parm->location, 2, Types::ValueFloat, false, layout.stride, (const void *)layout.uv_offset); if (color_parm && c) p->Set(*color_parm->location, 4, Types::ValueUByte, true, layout.stride, (const void *)layout.color_offset); } if (texture_parm && t) p->Set(*texture_parm->location, *t, texture_parm->index); p->SetRendererInputs(*this); p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld); p->CommitInputs(); DrawElements(Types::PrimitiveTriangle, count * 3, Types::ValueUShort, gpu_config.can_stream_vertex_from_memory ? idx : 0); if (vtx_parm) p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL); if (uv_parm) p->Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL); if (color_parm) p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL); DrawRestoreState(bo, f); stats.triangle_drawn += count; } void Renderer::DrawSprite(uint count, const Vector4 *v, const Color *c, const float *s, const Render::Texture *t, float g_size, Core::Material::BlendOperator bo, Core::Material::RenderWord f, Render::Shader *r_p) { Array sprite_p; Array sprite_c; Array sprite_uv; Array sprite_idx; Shader *p = (Shader *)r_p; if (p == NULL) p = simple_program; // Setup display list. if (!sprite_p.Allocate(count * 4) || !sprite_idx.Allocate(count * 3 * 2)) return; Vector4 *p_v = sprite_p.c_ptr(); ushort *idx = sprite_idx.c_ptr(); Vector4 left = m_view.GetRow(0), up = m_view.GetRow(1); if (s) for (uint n = 0; n < count; ++n) { register float k = g_size * s[n]; Vector4 uml = (up - left) * k; Vector4 lpu = (left + up) * k; Vector4 lmu = (left - up) * k; *p_v++ = v[n] + uml; *p_v++ = v[n] + lpu; *p_v++ = v[n] + lmu; *p_v++ = v[n] - lpu; } else for (uint n = 0; n < count; ++n) { Vector4 uml = (up - left) * g_size; Vector4 lpu = (left + up) * g_size; Vector4 lmu = (left - up) * g_size; *p_v++ = v[n] + uml; *p_v++ = v[n] + lpu; *p_v++ = v[n] + lmu; *p_v++ = v[n] - lpu; } for (ushort n = 0; n < count; ++n) { ushort s = (ushort)(n << 2); *idx++ = s; *idx++ = s + 1; *idx++ = s + 2; *idx++ = s; *idx++ = s + 2; *idx++ = s + 3; } if (c) // Color. { if (!sprite_c.Allocate(count * 4)) return; Color *p_c = sprite_c.c_ptr(); for (uint n = 0; n < count; ++n) { const Color &cl = c[n]; for (uint i = 0; i < 4; ++i) *p_c++ = cl; } p = single_color_program; } if (t) // Texture. { if (!sprite_uv.Allocate(count * 4)) return; Vector2 *p_uv = sprite_uv.c_ptr(); for (uint n = 0; n < count; ++n) { p_uv[0].Set(0, 0); p_uv[1].Set(1, 0); p_uv[2].Set(1, 1); p_uv[3].Set(0, 1); p_uv += 4; } p = c ? single_texture_color_program : single_texture_program; } // Compute aspect ratio. Matrix4 m_ar; if (view_item) m_ar = Matrix4::ScaleMatrix(view_item->ComputeAspectRatioCorrection(viewport)); else m_ar = Matrix4::ScaleMatrix(Vector4(viewport.GetHeight() / viewport.GetWidth(), 1, 0, 1)); // Set program inputs. DrawSetState(bo, f); SetShaderProgram(p); ShaderInput *vtx_parm = p->GetInput(Core::ShaderInput::Position), *uv_parm = p->GetInput(Core::ShaderInput::UV0), *color_parm = p->GetInput(Core::ShaderInput::VertexColor), *texture_parm = p->GetInput(Core::ShaderInput::Texture2D); if (vtx_parm) p->Set(*vtx_parm->location, 3, Types::ValueFloat, false, sizeof(Vector4), (const void *)sprite_p.c_ptr()); if (uv_parm && t) p->Set(*uv_parm->location, 2, Types::ValueFloat, false, sizeof(Vector2), (const void *)sprite_uv.c_ptr()); if (color_parm && c) p->Set(*color_parm->location, 4, Types::ValueFloat, false, sizeof(Color), (const void *)sprite_c.c_ptr()); if (texture_parm && t) p->Set(*texture_parm->location, *t, texture_parm->index); p->SetRendererInputs(*this); p->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld); p->CommitInputs(); DrawElements(Types::PrimitiveTriangle, sprite_idx.GetCount(), Types::ValueUShort, sprite_idx.c_ptr()); if (vtx_parm) p->Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL); if (uv_parm) p->Set(*uv_parm->location, 0, Types::ValueLast, false, 0, NULL); if (color_parm) p->Set(*color_parm->location, 0, Types::ValueLast, false, 0, NULL); DrawRestoreState(bo, f); stats.triangle_drawn += count * 2; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Renderer::Renderer() : terrain_patch_vtx(Alloc::RendererTerrain), terrain_patch_cache(Alloc::RendererTerrain) { core_resource_factory = new Render::RendererResourceFactory(*this); pending_shadow_map_refresh = false; pending_core_shader_refresh = false; render_technique = TechniqueForward; m_view = Matrix4::IdentityMatrix(); m_iview = Matrix4::IdentityMatrix(); m_world = Matrix4::IdentityMatrix(); m_iworld = Matrix4::IdentityMatrix(); m_projection = Matrix4::IdentityMatrix(); pcf_radius = 1.75f; frame_clock = 0.f; clipping.Set(-1, -1, -1, -1); output_fbo = NULL; } //------------------------------------------------------------------------------