463 lines
13 KiB
C++
463 lines
13 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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 <ushort> 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 <Vector4> sprite_p;
|
|
Array <Color> sprite_c;
|
|
Array <Vector2> sprite_uv;
|
|
Array <ushort> 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;
|
|
}
|
|
//------------------------------------------------------------------------------
|