commit x64 compilation from lulu cause the other branch dont seems to compile properly at home

This commit is contained in:
2026-07-17 16:08:20 +02:00
parent c0f3eeb00d
commit 0efa4ee6f7
625 changed files with 117283 additions and 4426 deletions

View File

@ -0,0 +1,148 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "metafile/nml_object.h"
#include "log/log.h"
using namespace GS::GPU;
using GS::NML::Tag;
//------------------------------------------------------------------------------
Tag *Renderer::GetPCFShaderTag() const
{
const char *pcf_tag[] = { "PCF:3x3GaussianDithered;", "PCF:3x3;", "PCF:2x2;", "PCF:1x1;" };
int pcf_q = GS::Types::Clamp(registry.GetInteger("ShadowMapping:PCF:Quality", 1), 0, 3);
return shader_dict.GetTag(pcf_tag[pcf_q]);
}
Tag *Renderer::GetPSMShaderTag() const
{ return shader_dict.GetTag("PSM;"); }
Tag *Renderer::GetPSSMShaderTag() const
{
const char *pssm_tag[] = { "PSSM:2Split;", "PSSM:3Split;", "PSSM:4Split;" };
int split_count = GS::Types::Clamp(registry.GetInteger("ShadowMapping:PSSM:Split", 3), 2, 4);
return shader_dict.GetTag(pssm_tag[split_count - 2]);
}
void Renderer::MarshallCoreShader(GS::String &source)
{
if (Tag *t = shader_dict.GetTag("UnpackGBuffer:Float;"))
source.Replace("#(UnpackNormalDepth)", t->GetString());
if (Tag *t = GetPCFShaderTag())
source.Replace("#(ComputePCF)", t->GetString());
if (Tag *t = GetPSSMShaderTag())
source.Replace("#(DispatchPSSM)", t->GetString());
}
Shader *Renderer::SetupCoreShader(const char *name)
{
Core::Shader shader;
if (!NML::LoadFromFile(shader, name))
return NULL;
shader.name = name;
// Note: Marshalling is only done on core shaders.
MarshallCoreShader(shader.vertex);
MarshallCoreShader(shader.pixel);
AutoPtr <Shader> gpu_shader((Shader *)NewShader());
if (gpu_shader.IsNull() || !gpu_shader->Create(*core_resource_factory, shader))
return NULL;
return gpu_shader.Detach();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::LoadCoreShaders(bool support_3d)
{
__LOG_FUNC__
// Load the shader dictionaries.
if (!NML::Parser::Load("@core/shaders/gpu/dict.txt", shader_dict))
__ERR__(__LOG_E__ << "Shader dictionnary missing.\n", false)
if (!NML::Parser::Load("@core/shaders/gpu/dict_material.txt", material_dict))
__ERR__(__LOG_E__ << "Material dictionnary missing.\n", false)
// Load core shaders.
single_texture_color_program = SetupCoreShader("@core/shaders/gpu/single_texture_color.nsa");
single_texture_program = SetupCoreShader("@core/shaders/gpu/single_texture.nsa");
single_color_program = SetupCoreShader("@core/shaders/gpu/single_color.nsa");
simple_program = SetupCoreShader("@core/shaders/gpu/simple.nsa");
if (support_3d)
{
single_texture_cutoff_program = SetupCoreShader("@core/shaders/gpu/single_texture_cutoff.nsa");
single_texture_fx_program = SetupCoreShader("@core/shaders/gpu/single_texture_fx.nsa");
tone_mapping_program = SetupCoreShader("@core/shaders/gpu/tone_mapping.nsa");
ambient_program = SetupCoreShader("@core/shaders/gpu/ambient.nsa");
spotlight_program = SetupCoreShader("@core/shaders/gpu/deferred/spotlight.nsa");
spotlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/spotlight_shadow.nsa");
pointlight_program = SetupCoreShader("@core/shaders/gpu/deferred/pointlight.nsa");
pointlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/pointlight_shadow.nsa");
linearlight_program = SetupCoreShader("@core/shaders/gpu/deferred/linearlight.nsa");
linearlight_shadow_program = SetupCoreShader("@core/shaders/gpu/deferred/linearlight_shadow.nsa");
ds_fog_program = SetupCoreShader("@core/shaders/gpu/deferred/ds_fog.nsa");
fx_blur_program = SetupCoreShader("@core/shaders/gpu/fx_blur.nsa");
noise_program = SetupCoreShader("@core/shaders/gpu/noise.nsa");
sharpen_program = SetupCoreShader("@core/shaders/gpu/sharpen.nsa");
hsl_program = SetupCoreShader("@core/shaders/gpu/hsl.nsa");
chromatic_dispersion_program = SetupCoreShader("@core/shaders/gpu/chromatic_dispersion.nsa");
ssaa_program = SetupCoreShader("@core/shaders/gpu/ssaa.nsa");
ssao_program = SetupCoreShader("@core/shaders/gpu/ssao.nsa");
ssao_blur_program = SetupCoreShader("@core/shaders/gpu/ssao_blur.nsa");
motion_blur_program = SetupCoreShader("@core/shaders/gpu/motion_blur.nsa");
radial_blur_program = SetupCoreShader("@core/shaders/gpu/radial_blur.nsa");
resolve_msaa_depth_program = SetupCoreShader("@core/shaders/gpu/resolve_msaa_depth.nsa");
skybox_program = SetupCoreShader("@core/shaders/gpu/skybox.nsa");
}
return true;
}
void Renderer::UnloadCoreShaders()
{
single_texture_color_program = NULL;
single_texture_program = NULL;
single_color_program = NULL;
simple_program = NULL;
single_texture_cutoff_program = NULL;
single_texture_fx_program = NULL;
tone_mapping_program = NULL;
ambient_program = NULL;
spotlight_program = NULL;
spotlight_shadow_program = NULL;
pointlight_program = NULL;
pointlight_shadow_program = NULL;
linearlight_program = NULL;
linearlight_shadow_program = NULL;
ds_fog_program = NULL;
fx_blur_program = NULL;
noise_program = NULL;
sharpen_program = NULL;
hsl_program = NULL;
chromatic_dispersion_program = NULL;
ssaa_program = NULL;
ssao_program = NULL;
ssao_blur_program = NULL;
motion_blur_program = NULL;
radial_blur_program = NULL;
resolve_msaa_depth_program = NULL;
skybox_program = NULL;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,220 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_display_list.h"
#include "gpu/gpu_renderer.h"
#include "core/triangle_list.h"
#include "log/log.h"
#define __USE_VBO__ 1
using namespace GS::GPU;
namespace GS {
namespace Core {
bool ComputeVertexArrayMinMax(const Array <Vector4> &, MinMax &, const Matrix4 * = 0);
}
}
//------------------------------------------------------------------------------
bool DisplayList::Create(GS::Core::Trilist *trilist, GS::Render::Material *list_material)
{
// Setup skin data.
bone.Clone(trilist->bone);
// Setup triangle list indices.
index_count = trilist->idx.GetCount();
#if __USE_VBO__
if (!idx)
idx = renderer.NewVBO();
if (!idx || !idx->Create(index_count * sizeof(ushort), VBO::Index, VBO::Static))
return false;
#endif
if (!idx_map.Allocate(index_count))
return false;
for (size_t n = 0; n <index_count; ++n)
idx_map[(int)n] = (ushort)trilist->idx[(int)n];
#if __USE_VBO__
idx->Update(idx_map, 0, idx_map.GetSize());
idx_map.Free();
#endif
// Setup triangle list vertex.
stride = 0;
// Vertex offset.
vertex_offset = stride;
stride += 3 * sizeof(hfloat);
// Compute normal stream size.
if (trilist->nrm)
{
normal_offset = stride;
stride += 4 * sizeof(char);
}
// Compute RGB stream size.
if (trilist->rgb)
{
rgb_offset = stride;
stride += 4 * sizeof(char);
}
// Compute UV stream size.
for (int s = 0; s < __UV_PER_GEOMETRY__; ++s)
if (trilist->uv[s])
{
uv_offset[s] = stride;
stride += 2 * sizeof(hfloat);
}
// Compute tangent stream size.
if (trilist->tangent)
{
tangent_offset = stride;
stride += 4 * 2 * sizeof(char);
}
// Compute skinning stream size.
if (trilist->skin)
{
skinning_offset = stride;
stride += 4 * 2 * sizeof(uchar);
}
#define __GPU_PADSIZE 4
// Compute vertex padding.
int padding = 0;
padding = stride % __GPU_PADSIZE ? __GPU_PADSIZE - (stride % __GPU_PADSIZE) : 0;
if (padding)
__LOG__ << "Padding GPU vertex to " << __GPU_PADSIZE << "B by " << padding << "B (from " << (uint)stride << "B)\n";
stride += padding;
// Setup attribute streams.
size_t vtx_stream_size = stride * size_t(trilist->vtx.GetCount());
#if __USE_VBO__
if (!vtx)
vtx = renderer.NewVBO();
if (!vtx || !vtx->Create(vtx_stream_size, VBO::Vertex, VBO::Static))
return false;
#endif
if (!vtx_map.Allocate(vtx_stream_size))
return false;
char *p_stream = (char *)vtx_map.c_ptr();
for (uint n = 0; n < trilist->vtx.GetCount(); ++n)
{
// Output vertex stream.
hfloat *p_vtx = (hfloat *)p_stream;
p_vtx[0] = Types::FloatToHFloat(trilist->vtx[n].x);
p_vtx[1] = Types::FloatToHFloat(trilist->vtx[n].y);
p_vtx[2] = Types::FloatToHFloat(trilist->vtx[n].z);
p_stream += 3 * sizeof(hfloat);
// Output normal stream.
if (trilist->nrm)
{
schar *p_nrm = (schar *)p_stream;
p_nrm[0] = (schar)(trilist->nrm[n].x * 127.f);
p_nrm[1] = (schar)(trilist->nrm[n].y * 127.f);
p_nrm[2] = (schar)(trilist->nrm[n].z * 127.f);
p_stream += 4 * sizeof(schar);
}
// Output RGB stream.
if (trilist->rgb)
{
uchar *p_rgb = (uchar *)p_stream;
p_rgb[0] = uchar(trilist->rgb[n].x * 255.f);
p_rgb[1] = uchar(trilist->rgb[n].y * 255.f);
p_rgb[2] = uchar(trilist->rgb[n].z * 255.f);
p_rgb[3] = uchar(trilist->rgb[n].w * 255.f);
p_stream += 4 * sizeof(uchar);
}
// Output UV streams.
for (uint s = 0; s < __UV_PER_GEOMETRY__; ++s)
if (trilist->uv[s])
{
hfloat *p_uv = (hfloat *)p_stream;
p_uv[0] = Types::FloatToHFloat(trilist->uv[s][n].x);
p_uv[1] = Types::FloatToHFloat(trilist->uv[s][n].y);
p_stream += 2 * sizeof(hfloat);
}
// Output tangent stream.
if (trilist->tangent)
{
schar *p_tng = (schar *)p_stream;
p_tng[0] = schar(trilist->tangent[n].T.x * 127.f);
p_tng[1] = schar(trilist->tangent[n].T.y * 127.f);
p_tng[2] = schar(trilist->tangent[n].T.z * 127.f);
// Mind the gap!
p_tng[4] = schar(trilist->tangent[n].B.x * 127.f);
p_tng[5] = schar(trilist->tangent[n].B.y * 127.f);
p_tng[6] = schar(trilist->tangent[n].B.z * 127.f);
p_stream += 4 * 2 * sizeof(schar);
}
// Output skinning stream.
if (trilist->skin)
{
uchar *p_skn = (uchar *)p_stream;
p_skn[0] = uchar(trilist->skin[n].bone_index[0]);
p_skn[1] = uchar(trilist->skin[n].bone_index[1]);
p_skn[2] = uchar(trilist->skin[n].bone_index[2]);
p_skn[3] = uchar(trilist->skin[n].bone_index[3]);
p_skn[4] = uchar(trilist->skin[n].w[0] * 255.f);
p_skn[5] = uchar(trilist->skin[n].w[1] * 255.f);
p_skn[6] = uchar(trilist->skin[n].w[2] * 255.f);
p_skn[7] = uchar(trilist->skin[n].w[3] * 255.f);
p_stream += 4 * 2 * sizeof(uchar);
}
p_stream += padding;
}
#if __USE_VBO__
vtx->Update(vtx_map, 0, vtx_map.GetSize());
vtx_map.Free();
#else
p_stream = (char *)vtx_map.c_ptr();
#define OffsetToAdress(_Offset) \
if (_Offset != -1) _Offset += (size_t)p_stream;
OffsetToAdress(vertex_offset)
OffsetToAdress(normal_offset)
OffsetToAdress(rgb_offset)
for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n)
OffsetToAdress(uv_offset[n])
OffsetToAdress(tangent_offset)
OffsetToAdress(skinning_offset)
#endif
Core::ComputeVertexArrayMinMax(trilist->vtx, minmax);
material = list_material;
return true;
}
//------------------------------------------------------------------------------

View File

@ -52,6 +52,7 @@ virtual void SetDepthTexture(Render::Texture *t)
virtual void Blit(FBO *out, const iRect &src, const iRect &dst, bool color = true, bool depth = true) = 0;
/// Transfer color pixels into a CPU based buffer, this buffer is expected to be big enough to hold the required region in RGBA format.
virtual void ReadColorPixels(char *out, int x, int y, int w, int h) = 0;
virtual void ReadDepthPixels(float *out) = 0;
/// Create FBO.
virtual bool Create() = 0;

View File

@ -0,0 +1,109 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_geometry.h"
#include "gpu/gpu_renderer.h"
#include "core/geometry.h"
#include "core/geometry_to_triangle_list.h"
#include "core/triangle_list.h"
#include "log/log.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
bool Geometry::AllocateVBO(uint count)
{
if (!vbo.Allocate(count))
return false;
for (uint n = 0; n < count; ++n)
vbo[n] = renderer.NewVBO();
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Geometry::SetMaterial(uint index, GS::Render::Material *m)
{
if (index >= material_table.GetCount())
return false;
// Update all display lists using this material index.
for (uint n = 0; n < display_list.GetCount(); ++n)
if (DisplayList *dlist = display_list[n])
if (dlist->material == material_table[index])
dlist->material = (GPU::Material *)m;
// Update the material table.
material_table[index] = m;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Geometry::ShouldReloadOnDependencyChange(const char *n) const
{
for (uint i = 0; i < material_table.GetCount(); ++i)
if (material_table[i] && (material_table[i]->name == n))
return true;
return name == n;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Geometry::Create(GS::Render::ResourceFactory &rf, const GS::Core::Geometry &g)
{
__LOG_H__ << "Setup geometry '" << g.name << "'.\n";
name = g.name;
using namespace Core;
// Setup materials.
if (material_table.Allocate(g.material_table.GetCount()))
for (uint n = 0; n < g.material_table.GetCount(); ++n)
material_table[n] = rf.LoadMaterial(g.material_table[n].name, !g.material_table[n].use_cache);
// Build geometry triangle list.
AutoList <Trilist *> tlist;
if (!GeometryToTriangleList::Convert(g, tlist))
return false;
// Setup geometry display lists.
if (display_list.Allocate(tlist.GetCount()))
{
uint n = 0;
ListForeachPtr(Trilist *, t, tlist)
{
display_list[n] = renderer.NewDisplayList();
display_list[n]->Create(t, material_table[t->mat]);
++n;
}
}
flag = g.flag;
// Skinning.
bone_bind_matrix.Clone(g.bone_bind_matrix);
g.ComputeBoneBoundingVolumes(bone_minmax);
// Load proxies.
if (!g.lod_proxy.IsEmpty())
lod_proxy = rf.LoadGeometry(g.lod_proxy);
lod_distance = g.lod_distance;
if (!g.shadow_proxy.IsEmpty())
shadow_proxy = rf.LoadGeometry(g.shadow_proxy);
minmax = g.ComputeMinMax();
// hotspot.Set(0, 0, 0);
hotspot = (minmax.mn + minmax.mx) * 0.5f;
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,128 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include "gpu/gpu_types.h"
#include "gpu/gpu_renderer.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
#ifdef EGL_HALF_FLOAT_SUPPORT
// -15 stored using a single precision bias of 127
static const unsigned int HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP = 0x38000000;
// max exponent value in single precision that will be converted
// to Inf or Nan when stored as a half-float
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP = 0x47800000;
// 255 is the max exponent biased value
static const unsigned int FLOAT_MAX_BIASED_EXP = (0xFF << 23);
static const unsigned int HALF_FLOAT_MAX_BIASED_EXP = (0x1F << 10);
hfloat Types::FloatToHFloat(float f)
{
unsigned int x = *(unsigned int *)&f;
unsigned int sign = (unsigned short)(x >> 31);
unsigned int mantissa;
unsigned int exp;
hfloat hf;
// get mantissa
mantissa = x & ((1 << 23) - 1);
// get exponent bits
exp = x & FLOAT_MAX_BIASED_EXP;
if (exp >= HALF_FLOAT_MAX_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// check if the original single precision float number is a NaN
if (mantissa && (exp == FLOAT_MAX_BIASED_EXP))
{
// we have a single precision NaN
mantissa = (1 << 23) - 1;
}
else
{
// 16-bit half-float representation stores number as Inf
mantissa = 0;
}
hf = (((hfloat)sign) << 15) | (hfloat)(HALF_FLOAT_MAX_BIASED_EXP) | (hfloat)(mantissa >> 13);
}
// check if exponent is <= -15
else if (exp <= HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP)
{
// store a denorm half-float value or zero.
exp = (HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP - exp) >> 23;
mantissa >>= (14 + exp);
hf = (((hfloat)sign) << 15) | (hfloat)(mantissa);
}
else
hf = (((hfloat)sign) << 15) | (hfloat)((exp - HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP) >> 13) | (hfloat)(mantissa >> 13);
return hf;
}
float Types::HFloatToFloat(hfloat hf)
{
unsigned int sign = (unsigned int)(hf >> 15);
unsigned int mantissa = (unsigned int)(hf & ((1 << 10) - 1));
unsigned int exp = (unsigned int)(hf & HALF_FLOAT_MAX_BIASED_EXP);
unsigned int f;
if (exp == HALF_FLOAT_MAX_BIASED_EXP)
{
// we have a half-float NaN or Inf
// half-float NaNs will be converted to a single precision NaN
// half-float Infs will be converted to a single precision Inf
exp = FLOAT_MAX_BIASED_EXP;
if (mantissa)
mantissa = (1 << 23) - 1; // set all bits to indicate a NaN
}
else if (exp == 0x0)
{
// convert half-float zero/denorm to single precision value
if (mantissa)
{
mantissa <<= 1;
exp = HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
// check for leading 1 in denorm mantissa
while ((mantissa & (1 << 10)) == 0)
{
// for every leading 0, decrement single precision exponent by 1
// and shift half-float mantissa value to the left
mantissa <<= 1;
exp -= (1 << 23);
}
// clamp the mantissa to 10-bits
mantissa &= ((1 << 10) - 1);
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
}
}
else
{
// shift left to generate single-precision mantissa of 23-bits
mantissa <<= 13;
// generate single precision biased exponent value
exp = (exp << 13) + HALF_FLOAT_MIN_BIASED_EXP_AS_SINGLE_FP_EXP;
}
f = (sign << 31) | exp | mantissa;
return *((float *)&f);
}
#else // No half-float support.
hfloat Types::FloatToHFloat(float f) { return f; }
float Types::HFloatToFloat(hfloat hf) { return hf; }
#endif
//------------------------------------------------------------------------------

View File

@ -0,0 +1,234 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_types.h"
#include "gpu/gpu_renderer.h"
#include "log/log.h"
using namespace GS;
using namespace GS::GPU;
/*
0 - - 3
| |
| |
1 - - 2
*/
//------------------------------------------------------------------------------
bool Renderer::BuildDirectVertexLayout(uint idx_count, const ushort *idx, const Vector4 *v, const Color *c, const Vector2 *uv, DirectVertexLayout &layout, VBO *idx_vbo, VBO *vtx_vbo)
{
if (idx_vbo)
{
size_t size = idx_count * sizeof(ushort);
if (size > idx_vbo->GetSize())
if (!idx_vbo->Create(size, VBO::Index, VBO::Dynamic))
return false;
if (void *p = idx_vbo->Map())
{
Memory::Copy(p, idx, size);
idx_vbo->Unmap();
}
}
// Find vertex count.
ushort vtx_count = 0;
for (uint n = 0; n < idx_count; ++n)
vtx_count = GS::Types::Max(vtx_count, idx[n]);
++vtx_count;
// Build layout.
layout.stride = 0;
if (v)
{
layout.vtx_offset = layout.stride;
layout.stride += 3 * sizeof(float);
}
if (c)
{
layout.color_offset = layout.stride;
layout.stride += 4 * sizeof(uchar);
}
if (uv)
{
layout.uv_offset = layout.stride;
layout.stride += 2 * sizeof(float);
}
// Build interleaved vertex data.
if (vtx_vbo)
{
size_t size = vtx_count * layout.stride;
if (size > vtx_vbo->GetSize())
if (!vtx_vbo->Create(size, VBO::Vertex, VBO::Dynamic))
return false;
if (char *p_data = (char *)vtx_vbo->Map())
{
for (uint n = 0; n < vtx_count; ++n)
{
if (v)
{
float *p_vtx = (float *)(p_data + layout.vtx_offset);
p_vtx[0] = v[n].x;
p_vtx[1] = v[n].y;
p_vtx[2] = v[n].z;
}
if (c)
{
uchar *p_color = (uchar *)(p_data + layout.color_offset);
p_color[0] = uchar(c[n].x * 255.f);
p_color[1] = uchar(c[n].y * 255.f);
p_color[2] = uchar(c[n].z * 255.f);
p_color[3] = uchar(c[n].w * 255.f);
}
if (uv)
{
float *p_uv = (float *)(p_data + layout.uv_offset);
p_uv[0] = uv[n].x;
p_uv[1] = uv[n].y;
}
p_data += layout.stride;
}
vtx_vbo->Unmap();
}
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::RenderFullscreenQuad(Shader &p, float k_x, float k_y, Render::Texture *t, Core::Light *l)
{
__NTRACE("RenderFullscreenQuad")
const size_t stride = sizeof(float) * 5;
if (gpu_config.tex_origin_is_top_left)
{
const float vtx[] = { -1, 1, 1, 0, k_y, -1, -1, 1, 0, 0, 1, -1, 1, k_x, 0, 1, 1, 1, k_x, k_y };
helper_vtx_vbo->Update(vtx, 0, stride * 4);
}
else
{
const float vtx[] = { -1, 1, 1, 0, 1.f - k_y, -1, -1, 1, 0, 1, 1, -1, 1, k_x, 1, 1, 1, 1, k_x, 1.f - k_y };
helper_vtx_vbo->Update(vtx, 0, stride * 4);
}
SetDepthFunc(DepthAlways);
EnableDepthWrite(false);
SetShaderProgram(&p);
SetIndexSource(helper_idx_vbo);
SetVertexSource(helper_vtx_vbo, sizeof(float) * 5);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
*uv_parm = p.GetInput(Core::ShaderInput::UV0),
*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);
if (!texture_parm) // get cube map (TEMP HACK)
texture_parm = p.GetInput(Core::ShaderInput::TextureCube);
if (vtx_parm)
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
if (uv_parm)
p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
if (texture_parm && t)
texture_parm->SetValue(t);
if (view_item && l)
p.SetLightInputs(*this, *view_item, *l);
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
p.SetRendererInputs(*this);
p.SetConstantInputs();
p.SetTextureInputs();
p.CommitInputs();
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
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);
EnableDepthWrite(true);
SetDepthFunc(DepthLess);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::RenderFullscreenQuad(Shader &p, const fRect &src_rect, const fRect &dst_rect, Render::Texture *t, Core::Light *l)
{
__NTRACE("RenderFullscreenQuad (Region)")
const size_t stride = sizeof(float) * 5;
if (gpu_config.tex_origin_is_top_left)
{
const float vtx[] =
{
dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.sx, src_rect.sy,
dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.ex, src_rect.sy,
dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.ex, src_rect.ey,
dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.sx, src_rect.ey
};
helper_vtx_vbo->Update(vtx, 0, stride * 4);
}
else
{
const float vtx[] =
{
dst_rect.sx * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.sx, src_rect.ey,
dst_rect.ex * 2.f - 1.f, dst_rect.sy * 2.f - 1.f, 1, src_rect.ex, src_rect.ey,
dst_rect.ex * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.ex, src_rect.sy,
dst_rect.sx * 2.f - 1.f, dst_rect.ey * 2.f - 1.f, 1, src_rect.sx, src_rect.sy
};
helper_vtx_vbo->Update(vtx, 0, stride * 4);
}
SetDepthFunc(DepthAlways);
EnableDepthWrite(false);
SetShaderProgram(&p);
SetIndexSource(helper_idx_vbo);
SetVertexSource(helper_vtx_vbo, stride);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
*uv_parm = p.GetInput(Core::ShaderInput::UV0),
*texture_parm = p.GetInput(Core::ShaderInput::Texture2D);
if (!texture_parm) // get cube map (TEMP HACK)
texture_parm = p.GetInput(Core::ShaderInput::TextureCube);
if (vtx_parm)
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
if (uv_parm)
p.Set(*uv_parm->location, 2, Types::ValueFloat, false, stride, (const void *)(sizeof(float) * 3));
if (texture_parm && t)
texture_parm->SetValue(t);
if (view_item && l)
p.SetLightInputs(*this, *view_item, *l);
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
p.SetRendererInputs(*this);
p.SetConstantInputs();
p.SetTextureInputs();
p.CommitInputs();
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
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);
EnableDepthWrite(true);
SetDepthFunc(DepthLess);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,163 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "core/camera.h"
#include "core/light.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::RenderFullscreenLight(Shader &p, Core::Light &l)
{
SetDepthFunc(DepthAlways);
const float h = 1.f, k = 100.f;
const float vtx[] = { -k, -k, h, k, -k, h, k, k, h, -k, k, h };
const size_t stride = sizeof(float) * 3;
helper_vtx_vbo->Update(vtx, 0, stride * 4);
SetShaderProgram(&p);
SetIndexSource(helper_idx_vbo);
SetVertexSource(helper_vtx_vbo, stride);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
if (vtx_parm)
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
p.SetLightInputs(*this, *view_item, l);
p.SetRendererInputs(*this);
p.CommitInputs();
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
if (vtx_parm)
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
SetDepthFunc(DepthLess);
}
void Renderer::RenderSpotLight(Core::Light &l, const DrawContext &dc)
{
if (!view_item)
return;
Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *spotlight_shadow_program : *spotlight_program;
if (!l.volume_range)
RenderFullscreenLight(p, l);
else
{
const Vector4 *vtx = l.frustum.GetVertices();
const size_t stride = sizeof(float) * 4;
helper_vtx_vbo->Update(vtx, 0, stride * 8);
SetShaderProgram(&p);
SetIndexSource(box_idx_vbo);
SetVertexSource(helper_vtx_vbo, stride);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
if (vtx_parm)
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
p.SetTransformInputs(m_projection, m_view, m_iview, &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
p.SetLightInputs(*this, *view_item, l);
p.SetRendererInputs(*this);
p.CommitInputs();
// TODO Boolean operation on light frustum and clipping planes.
Vector4 row = view_item->GetMatrix().GetRow(3);
Frustum::Visibility viscode = l.frustum.ClassifySet(1, &row, Units::Cm(50.f));
if (viscode != Frustum::Outside)
{
SetDepthFunc(DepthGreater);
SetCullFunc(CullBack);
}
DrawElements(Types::PrimitiveTriangle, 3 * 2 * 6, Types::ValueUShort);
if (vtx_parm)
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
if (viscode != Frustum::Outside)
{
SetDepthFunc(DepthLess);
SetCullFunc(CullFront);
}
}
}
void Renderer::RenderPointLight(Core::Light &l, const DrawContext &dc)
{
if (!view_item)
return;
Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *pointlight_shadow_program : *pointlight_program;
if (!l.volume_range)
RenderFullscreenLight(p, l);
else
{
float k = l.volume_range;
float pointlight_vtx[] =
{
-k, k, -k, k, k, -k, k, -k, -k, -k, -k, -k,
-k, k, k, k, k, k, k, -k, k, -k, -k, k
};
const size_t stride = sizeof(float) * 3;
helper_vtx_vbo->Update(pointlight_vtx, 0, stride * 8);
SetShaderProgram(&p);
SetIndexSource(box_idx_vbo);
SetVertexSource(helper_vtx_vbo, stride);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
if (vtx_parm)
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);
p.SetTransformInputs(m_projection, m_view, m_iview, &l.GetMatrix(), &l.GetInverseMatrix());
p.SetLightInputs(*this, *view_item, l);
p.SetRendererInputs(*this);
p.CommitInputs();
//
MinMax vminmax;
vminmax.SetFromPositionSize(l.GetMatrix().GetRow(3), Vector4(l.volume_range, l.volume_range, l.volume_range) * 2.1f);
bool inside = vminmax.IsInside(view_item->GetMatrix().GetRow(3));
if (inside)
{
SetDepthFunc(DepthGreater);
SetCullFunc(CullBack);
}
else
{
SetDepthFunc(DepthLess);
SetCullFunc(CullFront);
}
DrawElements(Types::PrimitiveTriangle, 3 * 12, Types::ValueUShort);
if (vtx_parm)
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
SetDepthFunc(DepthLess);
SetCullFunc(CullFront);
}
}
void Renderer::RenderLinearLight(Core::Light &l, const DrawContext &dc)
{
RenderFullscreenQuad((l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *linearlight_shadow_program : *linearlight_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, NULL, &l);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,172 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_material.h"
#include "gpu/gpu_renderer.h"
#include "core/material_to_shader_tree.h"
#include "core/shader_tree_convert_static_texture_block_to_dynamic.h"
#include "core/shader_tree_to_shader.h"
#include "core/shader_tree.h"
#include "log/log.h"
using namespace GS;
using namespace GPU;
//------------------------------------------------------------------------------
bool Material::ShouldReloadOnDependencyChange(const char *n) const
{ return (name == n) || (shader->GetName() == n); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Material::LoadTextureTable(Render::ResourceFactory &rf, const Core::Material &m)
{
bool r = true;
for (uint n = 0; n < Core::Material::max_texture_stage; ++n)
{
texture_table[n] = rf.LoadTexture(m.texstage[n].t);
if (texture_table[n].IsNull())
r = false;
}
return r;
}
static bool LoadCoreShader(Render::ResourceFactory &rf, const Core::Material &m, Core::Shader &core_shader, const char *name)
{
using namespace Core;
if (name == NULL)
{
// TODO convert material to shader directly (avoid creating an AST).
ShaderTree shader_tree;
if (!MaterialToShaderTree::Convert(m, shader_tree) || !ConvertShaderTreeToShader(shader_tree, core_shader))
return false;
}
else
{
using namespace NML;
// Load shader/shader tree.
File file;
if (!Parser::Load(name, file))
return false;
if (Tag *tag = file.GetTag("Shader"))
{
if (!core_shader.FromMetaTag(*tag))
return false;
core_shader.name = m.shader;
}
else if (Tag *tag = file.GetTag("ShaderMap"))
{
ShaderTree shader_tree;
if (!shader_tree.FromMetaTag(*tag))
return false;
ConvertStaticToDynamicTextureBlocks(shader_tree, m);
if (!ConvertShaderTreeToShader(shader_tree, core_shader))
return false;
}
else
__ERR__(__LOG_E__ << "No shader or shader tree definition found in '" << m.shader << "'.\n", false)
}
return true;
}
bool Material::Create(Render::ResourceFactory &rf, const Core::Material &m)
{
__LOG_H__ << "Load render material '" << m.name << "'.\n";
name = m.name;
using namespace Core;
// Transfer basic material informations to the render data.
*((BasicMaterial *)this) = ((BasicMaterial &)m);
// Load texture table.
LoadTextureTable(rf, m);
// Load core shader.
MaterialShaderStaticParm parm;
parm.no_lighting = asbool(m.renderword & Core::Material::Render_Unlit);
parm.use_skinning = asbool(m.renderword & Core::Material::Render_Skinned);
parm.use_alpha_test = asbool(m.renderword & Core::Material::Render_AlphaTest);
parm.use_depth_bias = asbool(m.depth_bias);
Core::Shader core_shader;
bool load_core_shader = LoadCoreShader(rf, m, core_shader, m.shader);
// Drop current material shader.
shader = NULL;
// Look for a compatible material shader in cache.
if (load_core_shader)
ListForeachPtr(MaterialShader *, s, renderer.material_shaders)
if ((s->GetName() == core_shader.name) && (s->GetStaticParm() == parm))
{
__LOG_V__ << "Reusing cached material shader for material '" << m.name << "'.\n";
shader = s;
return true;
}
// Build a new one otherwise.
shader = new MaterialShader(renderer);
if (shader.IsNull())
return false;
if (!load_core_shader || !shader->Create(rf, core_shader, parm))
{
if (!LoadCoreShader(rf, m, core_shader, "@core/builtin/shader/shader_error.nsa"))
return false;
if (!shader->Create(rf, core_shader, parm))
return false;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Render::Material *Material::Clone() const
{
Material *cloned = new Material(renderer);
// __LOG_W__ << "[MaterialClone] Step 1c: 'new' returned successfully!\n";
if (!cloned)
{
__LOG_E__ << "[MaterialClone] ERROR: Failed to allocate memory for cloned material!\n";
return NULL;
}
// __LOG_W__ << "[MaterialClone] Step 1: Material instance created at " << (void*)cloned << "\n";
// Copy name (with "_clone" suffix to distinguish it)
// __LOG_W__ << "[MaterialClone] Step 2: Copying name...\n";
cloned->name = name + "_clone";
// __LOG_W__ << "[MaterialClone] Step 2: Name copied: '" << cloned->name << "'\n";
// Copy basic material properties (diffuse, specular, self, ambient, glossiness, etc.)
// __LOG_W__ << "[MaterialClone] Step 3: Copying BasicMaterial properties...\n";
*((Core::BasicMaterial *)cloned) = *((Core::BasicMaterial *)this);
// __LOG_W__ << "[MaterialClone] Step 3: BasicMaterial properties copied.\n";
// Copy shader reference
// __LOG_W__ << "[MaterialClone] Step 4: Copying shader reference...\n";
cloned->shader = shader;
// __LOG_W__ << "[MaterialClone] Step 4: Shader reference copied.\n";
// Copy texture table (smart pointers, so textures are shared, not duplicated)
// __LOG_W__ << "[MaterialClone] Step 5: Copying texture table...\n";
for (uint n = 0; n < Core::Material::max_texture_stage; ++n)
{
cloned->texture_table[n] = texture_table[n];
}
// __LOG_W__ << "[MaterialClone] Step 5: Texture table copied.\n";
// __LOG_W__ << "[MaterialClone] SUCCESS: Material cloned successfully as '" << cloned->name << "'.\n";
return cloned;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,429 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_material_shader.h"
#include "gpu/gpu_material.h"
#include "gpu/gpu_renderer.h"
#include "log/log.h"
using namespace GS;
using namespace GS::GPU;
using GS::NML::Tag;
//------------------------------------------------------------------------------
bool MaterialShader::SetUserUniformValue(const char *name, const Vector4 &v)
{
uint match_count = 0;
for (uint n = 0; n < variants.GetCount(); ++n)
if (Shader *shader = variants[n].c_ptr())
if (ShaderInput *input = shader->GetInput(name))
{
input->parm_v = v;
++match_count;
}
return match_count > 0;
}
bool MaterialShader::SetUserUniformValue(const char *name, Render::Texture *t)
{
uint match_count = 0;
for (uint n = 0; n < variants.GetCount(); ++n)
if (Shader *shader = variants[n].c_ptr())
if (ShaderInput *input = shader->GetInput(name))
{
input->parm_t = t;
++match_count;
}
return match_count > 0;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void MaterialShader::CompileForwardPointLight(Core::Shader &shader, bool shadow)
{
shader.Define("_POINT_LIGHT", Core::ShaderInput::Pixel);
CompileShaderSection("OutputForwardLightModel", shader);
if (shadow)
{
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
shader.DeclareInput("psm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
for (int n = 0; n < 6; ++n)
shader.DeclareInput(String::Format("psm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_vtl", Core::ShaderInput::Matrix4, Core::ShaderInput::ViewToLightMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
if (Tag *t = renderer.GetPCFShaderTag())
shader.pixel_decl += t->GetString();
if (Tag *t = renderer.GetPSMShaderTag())
shader.pixel.Replace("%psm_pcf_evaluation%", t->GetString());
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void MaterialShader::CompileForwardSpotLight(Core::Shader &shader, bool shadow, bool projection_map)
{
shader.Define("_SPOT_LIGHT", Core::ShaderInput::Pixel);
CompileShaderSection("OutputForwardLightModel", shader);
if (shadow)
{
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
shader.DeclareInput("u_ssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMap0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_ssm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
if (Tag *t = renderer.GetPCFShaderTag())
shader.pixel_decl += t->GetString();
}
if (projection_map)
{
shader.Define("_PROJECTION_MAP", Core::ShaderInput::Pixel);
shader.DeclareInput("u_pjm", Core::ShaderInput::DataTexture2D, Core::ShaderInput::Semantic(Core::ShaderInput::LightProjectionMap), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_pjm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void MaterialShader::CompileForwardLinearLight(Core::Shader &shader, bool shadow)
{
shader.Define("_LINEAR_LIGHT", Core::ShaderInput::Pixel);
CompileShaderSection("OutputForwardLightModel", shader);
if (shadow)
{
shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel);
shader.DeclareInput("pssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
for (int n = 0; n < renderer.registry.GetInteger("ShadowMapping:PSSM:Split", 3); ++n)
{
shader.DeclareInput(String::Format("pssm_%d_slice_distance", n), Core::ShaderInput::Float, Core::ShaderInput::Semantic(Core::ShaderInput::LightPSSMSliceDistance0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput(String::Format("pssm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
}
shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
if (Tag *t = renderer.GetPCFShaderTag())
shader.pixel_decl += t->GetString();
if (Tag *t = renderer.GetPSSMShaderTag())
shader.pixel.Replace("%pssm_pcf_evaluation%", t->GetString());
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool MaterialShader::CompileShaderSection(const char *id, Core::Shader &shader)
{
Tag *shader_tag = renderer.GetMaterialDict().GetTag(id);
if (!shader_tag)
__ERR__(__LOG_E__ << "Missing shader section '" << id << "'.\n", false)
if (Tag *tag = shader_tag->GetTag("Input;"))
shader.ParseInputTag(tag);
if (Tag *tag = shader_tag->GetTag("Varying;"))
shader.ParseVaryingTag(tag);
if (Tag *tag = shader_tag->GetTag("Vertex;"))
shader.vertex += tag->GetString();
if (Tag *tag = shader_tag->GetTag("Fragment;"))
shader.pixel += tag->GetString();
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool MaterialShader::SetupMaterialProgram(Variant p, Core::Shader &shader)
{
if (parm.use_alpha_test)
{
shader.DeclareInput("u_alpha_threshold", Core::ShaderInput::Float, Core::ShaderInput::MaterialAlphaThreshold, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
shader.pixel += "if (%opacity% < u_alpha_threshold) discard;\n";
}
if (parm.use_depth_bias)
shader.DeclareInput("u_depth_bias", Core::ShaderInput::Float, Core::ShaderInput::MaterialDepthBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
if (parm.use_skinning)
{
shader.Define("_SKINNED", (Core::ShaderInput::Scope)(Core::ShaderInput::Vertex | Core::ShaderInput::Pixel));
shader.DeclareInput("bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::BoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__);
shader.DeclareInput("bone_idx", Core::ShaderInput::Vector4, Core::ShaderInput::BoneIndex, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
shader.DeclareInput("bone_w", Core::ShaderInput::Vector4, Core::ShaderInput::BoneWeight, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
shader.DeclareVarying("v_skin_mtx", "mat4");
}
CompileShaderSection("InputPosition;", shader);
// Assemble program shader.
switch (p)
{
case Depth:
CompileShaderSection("OutputDepth;", shader);
break;
case FS_Constant:
CompileShaderSection("OutputForwardConstant;", shader);
break;
case FS_PointLight:
case FS_PointLightShadowMapping:
CompileForwardPointLight(shader, p == FS_PointLightShadowMapping);
break;
case FS_SpotLight:
case FS_SpotLightShadowMapping:
case FS_SpotLightProjection:
case FS_SpotLightProjectionShadowMapping:
CompileForwardSpotLight(shader, (p == FS_SpotLightShadowMapping) || (p == FS_SpotLightProjectionShadowMapping), (p == FS_SpotLightProjection) || (p == FS_SpotLightProjectionShadowMapping));
break;
case FS_LinearLight:
case FS_LinearLightShadowMapping:
CompileForwardLinearLight(shader, p == FS_LinearLightShadowMapping);
break;
case DS_GBufferMRT4:
CompileShaderSection("OutputDeferred;", shader);
break;
case PP_NormalDepth:
CompileShaderSection("OutputNormalDepth;", shader);
break;
case PP_Velocity:
if (parm.use_skinning)
shader.DeclareInput("previous_bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::PreviousBoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__);
CompileShaderSection("OutputVelocity;", shader);
break;
case Last:
break;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static bool FindVarAssignation(const String &src, const char *var) // TODO fix with proper regex, this is proof of concept code at best...
{
if (src.FindString(String::Format("%s =", var)))
return true;
if (src.FindString(String::Format("%s\t=", var)))
return true;
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static void DeclareVertexPosition(Core::Shader &shader, bool declare_varying)
{
String decl = "vec4 %position%;\n";
if (!asbool(FindVarAssignation(shader.vertex, "%position%")))
{
Core::ShaderInput *input = shader.DeclareInput("a_position", Core::ShaderInput::Vector3, Core::ShaderInput::Position, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
// Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Position);
decl << "%position% = vec4(" << input->name << ", 1.0);\n";
}
shader.vertex = decl + shader.vertex;
}
static void DeclareVertexNormal(Core::Shader &shader)
{
String decl = "vec3 %normal%;\n";
if (!asbool(FindVarAssignation(shader.vertex, "%normal%")))
{
Core::ShaderInput *input = shader.DeclareInput("a_normal", Core::ShaderInput::Vector3, Core::ShaderInput::Normal, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex);
// Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Normal);
decl << "%normal% = " << input->name << ";\n";
}
shader.vertex = decl + shader.vertex;
}
static void InitializeShader(Core::Shader &shader)
{
// Position
{
bool pixel_consumes = asbool(shader.pixel.FindString("%in.position%"));
DeclareVertexPosition(shader, pixel_consumes);
}
// Normal
{
bool pixel_consumes = asbool(shader.pixel.FindString("%in.normal%"));
bool pixel_provides = FindVarAssignation(shader.pixel, "%normal%");
if (pixel_consumes || !pixel_provides)
DeclareVertexNormal(shader);
if (!pixel_provides)
shader.pixel << "%normal% = %in.normal%;\n";
shader.pixel = String("vec3 %normal%;\n") + shader.pixel;
}
{
struct ShaderDefault
{
const char *type, *name, *var;
Core::ShaderInput::DataType data_type;
Core::ShaderInput::Semantic semantic;
};
static ShaderDefault ps_ic[] =
{
{ "vec4", "%diffuse%", "_u_mat_diff", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialDiffuse },
{ "vec4", "%specular%", "_u_mat_spec", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSpecular },
{ "float", "%glossiness%", "_u_mat_glos", Core::ShaderInput::Float, Core::ShaderInput::MaterialGlossiness },
{ "vec4", "%constant%", "_u_mat_const", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSelf },
{ "float", "%opacity%", "_u_mat_opac", Core::ShaderInput::Float, Core::ShaderInput::MaterialOpacity },
{ NULL, NULL, NULL }
};
// Declare mandatory outputs and set default value for unprovided entries.
String decl;
for (uint n = 0; ps_ic[n].type; ++n)
{
decl << ps_ic[n].type << " " << ps_ic[n].name << ";\n";
if (!FindVarAssignation(shader.pixel, ps_ic[n].name))
{
Core::ShaderInput *input = shader.DeclareInput(ps_ic[n].var, ps_ic[n].data_type, ps_ic[n].semantic, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel);
decl << ps_ic[n].name << " = " << input->name << ";\n";
}
}
shader.pixel = decl + shader.pixel;
}
}
static void FinalizeShader(Core::Shader &shader)
{
if (asbool(shader.pixel.FindString("%in.position%")))
{
shader.DeclareVarying("_v_position", "vec4");
shader.vertex << "_v_position = %position%;\n";
}
if (asbool(shader.pixel.FindString("%in.normal%")))
{
shader.DeclareVarying("_v_normal", "vec3");
shader.vertex << "_v_normal = %normal%;\n";
}
shader.pixel.ReplaceAll("%in.position%", "_v_position", true);
shader.pixel.ReplaceAll("%in.normal%", "_v_normal", true);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static const char *GetShaderVariantName(MaterialShader::Variant v)
{
switch (v)
{
case MaterialShader::Depth: return "Depth";
case MaterialShader::DS_GBufferMRT4: return "DS_GBufferMRT4";
case MaterialShader::FS_Constant: return "FS_Constant";
case MaterialShader::FS_PointLight: return "FS_PointLight";
case MaterialShader::FS_PointLightShadowMapping: return "FS_PointLightShadowMapping";
case MaterialShader::FS_LinearLight: return "FS_LinearLight";
case MaterialShader::FS_LinearLightShadowMapping: return "FS_LinearLightShadowMapping";
case MaterialShader::FS_SpotLight: return "FS_SpotLight";
case MaterialShader::FS_SpotLightShadowMapping: return "FS_SpotLightShadowMapping";
case MaterialShader::FS_SpotLightProjection: return "FS_SpotLightProjection";
case MaterialShader::FS_SpotLightProjectionShadowMapping: return "FS_SpotLightProjectionShadowMapping";
case MaterialShader::PP_NormalDepth: return "PP_NormalDepth";
case MaterialShader::PP_Velocity: return "PP_Velocity";
case MaterialShader::Last:
break;
}
return "UnknownVariant";
}
bool MaterialShader::Create(Render::ResourceFactory &rf, const Core::Shader &shader, const MaterialShaderStaticParm &static_parm)
{
__LOG_V__ << "Creating material shader '" << shader.name << "' variants.\n";
name = shader.name;
parm = static_parm;
// Build variants.
if (!variants.Allocate(Last))
return false;
for (int n = 0; n < Last; ++n)
{
if (renderer.render_technique == Renderer::TechniqueDeferred)
if ((n >= FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping))
continue; // no forward
if (renderer.render_technique == Renderer::TechniqueForward)
if (n == DS_GBufferMRT4)
continue; // no deferred
if (!renderer.gpu_config.enable_shadow)
if (
(n == Depth) ||
(n == FS_PointLightShadowMapping) ||
(n == FS_LinearLightShadowMapping) ||
(n == FS_SpotLightShadowMapping) ||
(n == FS_SpotLightProjectionShadowMapping)
)
continue; // no shadow-mapping
if (!renderer.gpu_config.use_rtt)
if (
(n == PP_Velocity) ||
(n == PP_NormalDepth)
)
continue; // no post-processes
if (parm.no_lighting)
if ((n > FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping)) // keep constant!
continue; // no lighting pass
// Specialize shader for this program.
Core::Shader shader_variant;
if (!shader.Clone(shader_variant))
return false;
InitializeShader(shader_variant);
if (!SetupMaterialProgram((Variant)n, shader_variant))
return false;
FinalizeShader(shader_variant);
shader_variant.name << "_" << GetShaderVariantName((MaterialShader::Variant)n);
// Compile it.
if ((variants[n] = (Shader *)renderer.NewShader()) != NULL)
if (!variants[n]->Create(rf, shader_variant))
return false;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
MaterialShader::MaterialShader(Renderer &r) : renderer(r)
{ renderer.material_shaders.Add(this); }
MaterialShader::~MaterialShader()
{ renderer.material_shaders.Remove(this); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,668 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <math.h>
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_geometry.h"
#include "gpu/gpu_shader_object.h"
#include "gpu/gpu_draw_context.h"
#include "core/geometry.h"
#include "rand/rand.h"
#include "log/log.h"
using namespace GS;
using namespace GPU;
//------------------------------------------------------------------------------
static bool IsPostProcessExcluded(NML::Tag *exclusion_key, const char *tag)
{
NML::Tag *__t = exclusion_key ? exclusion_key->GetTypedTag(tag, Variant::VariantBool) : NULL;
return __t && __t->GetBool();
}
//------------------------------------------------------------------------------
//---------------------------------------------------------------
#define _SwapTarget { tmp = t[0]; t[0] = t[1]; t[1] = tmp; }
//---------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::GetPostProcessNormalDepth(const Stack <RenderPrimitive *> display_lists[2])
{
if (normal_depth_updated)
return;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
fRect old_viewport = GetViewport();
// Generate normal/depth buffer.
SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));
fx_fbo->SetColorTexture(t_fx[2]);
if (render_technique == TechniqueDeferred)
{
SetCurrentFBO(fx_fbo);
RenderFullscreenQuad(*single_texture_program, old_viewport.GetWidth() / (float)dimensions.x, old_viewport.GetHeight() / (float)dimensions.y, t_gbuffer[0]);
}
else
{
fx_fbo->SetDepthTexture(t_fx_depth);
SetCurrentFBO(fx_fbo);
Clear(0, 0, 0);
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_NormalDepth);
DrawList(display_lists[0], dc);
fx_fbo->SetDepthTexture(NULL);
}
SetViewport(old_viewport);
normal_depth_updated = true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::ApplyDirectionalBlur(Render::Texture *t[2], const Vector2 &d, float attn)
{
if (!fx_blur_program)
return;
Render::Texture *tmp;
ShaderInput *u_pass = fx_blur_program->GetInput("u_pass"),
*u_blur_d = fx_blur_program->GetInput("u_blur_d"),
*u_attenuation = fx_blur_program->GetInput("u_attenuation");
SetShaderProgram(fx_blur_program);
u_blur_d->SetValue(d.x, d.y);
u_attenuation->SetValue(attn);
fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
for (int n = 0; n < 4; ++n)
{
fx_fbo->SetColorTexture(t[1]);
SetCurrentFBO(fx_fbo);
u_pass->SetValue(float(n));
RenderFullscreenQuad(*fx_blur_program, nviewport, fRect(0, 0, 1, 1), t[0]);
_SwapTarget
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::ApplyBloomFilter(Render::Texture *t_in, float strength, float threshold, float exponent, float radius, float strength_white_screen)
{
if (!strength || !single_texture_cutoff_program || !tone_mapping_program)
return;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
Render::Texture *t[] = { t_fx[0], t_fx[1] };
ShaderInput *u_cutoff = single_texture_cutoff_program->GetInput("u_cutoff"),
*u_strength = tone_mapping_program->GetInput("u_strength"),
*u_strength_white_screen = tone_mapping_program->GetInput("u_strength_white_screen");
if (!u_cutoff || !u_strength || !u_strength_white_screen)
return;
fRect old_viewport = GetViewport();
fRect nviewport(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
fRect fx_viewport(old_viewport.sx / fx_scale, old_viewport.sy / fx_scale, old_viewport.ex / fx_scale, old_viewport.ey / fx_scale);
//--------------------------------------------------------------------------
#define CutoffFrameToBloomFX \
{ \
SetViewport(fx_viewport); \
\
fx_fbo->SetColorTexture(t[0]); \
SetCurrentFBO(fx_fbo);\
SetShaderProgram(single_texture_cutoff_program); \
u_cutoff->SetValue(threshold); \
RenderFullscreenQuad(*single_texture_cutoff_program, nviewport, fRect(0, 0, 1, 1), t_in); \
}
#define OutputBloomFXToFrame \
{ \
SetViewport(old_viewport);\
\
EnableBlending(true); \
SetBlendFunc(BlendOne, BlendOne); \
\
fx_fbo->SetColorTexture(t_in); \
SetCurrentFBO(fx_fbo);\
SetShaderProgram(tone_mapping_program); \
u_strength->SetValue(strength * 1.25f); \
u_strength_white_screen->SetValue(strength_white_screen); \
RenderFullscreenQuad(*tone_mapping_program, nviewport, fRect(0, 0, 1, 1), t[0]); \
\
EnableBlending(false); \
}
//--------------------------------------------------------------------------
if (view_registry->GetBool("PostProcess:Bloom:Streak:Enabled", false))
{
float angle = view_registry->GetReal("PostProcess:Bloom:Streak:Angle"),
attn = view_registry->GetReal("PostProcess:Bloom:Streak:Attenuation", 0.975f);
for (int n = 0; n < 2; ++n)
{
Vector2 d = Vector2(cos(angle), sin(angle)) * radius / 12.f;
CutoffFrameToBloomFX
ApplyDirectionalBlur(t, d, attn);
OutputBloomFXToFrame
if (!view_registry->GetBool("PostProcess:Bloom:Streak:CrossShaped", false))
break;
angle += Units::Deg(90.f);
}
}
else
{
CutoffFrameToBloomFX
ApplyDirectionalBlur(t, Vector2(radius / 64.f, 0));
ApplyDirectionalBlur(t, Vector2(0, radius / 64.f));
OutputBloomFXToFrame
}
SetViewport(old_viewport);
SetClippingRect(&old_viewport);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplyNoiseFilter(Render::Texture *t_in, Render::Texture *t_out, float strength, float mono, float bias)
{
if (!strength || !noise_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_strength = noise_program->GetInput("u_strength"),
*u_mono = noise_program->GetInput("u_mono"),
*u_bias = noise_program->GetInput("u_bias"),
*u_random = noise_program->GetInput("u_random");
if (!u_strength || !u_mono || !u_bias || !u_random)
return false;
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
float random[2] = { Random::FRand(), Random::FRand() };
SetShaderProgram(noise_program);
u_strength->SetValue(strength);
u_mono->SetValue(mono);
u_bias->SetValue(bias);
u_random->SetValue(random[0], random[1]);
RenderFullscreenQuad(*noise_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplySSAAFilter(Render::Texture *t_in, Render::Texture *t_out)
{
if (!ssaa_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetShaderProgram(ssaa_program);
RenderFullscreenQuad(*ssaa_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
bool Renderer::ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out)
{
if (!t_depth_msaa || !t_out || !resolve_msaa_depth_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
ShaderInput *msaa_depth = resolve_msaa_depth_program->GetInput("u_depthMSAA");
SetShaderProgram(resolve_msaa_depth_program);
msaa_depth->SetValue(t_depth_msaa);
RenderFullscreenQuad(*resolve_msaa_depth_program,viewport.GetWidth() / (float)dimensions.x,viewport.GetHeight() / (float)dimensions.y);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float s, float r, float d, float blur_r)
{
if (!s || !ssao_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_strength = ssao_program->GetInput("u_strength"),
*u_radius = ssao_program->GetInput("u_radius"),
*u_distance_scale = ssao_program->GetInput("u_distance_scale"),
*u_iproj2d = ssao_program->GetInput("u_iproj2d");
if (!u_strength || !u_radius || !u_distance_scale)
return false;
GetPostProcessNormalDepth(display_lists);
#if 0
// Normal/depth debug output.
fx_fbo->SetColorTexture(t_in);
SetCurrentFBO(fx_fbo);
RenderFullscreenQuad(single_texture_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);
return false;
#endif
fRect old_viewport = GetViewport();
SetViewport(fRect(0, 0, (float)t_fx[2]->GetWidth(), (float)t_fx[2]->GetHeight()));
// Render raw SSAO to FX texture.
SetShaderProgram(ssao_program);
u_strength->SetValue(s);
u_radius->SetValue(r * (4 / fx_scale));
u_distance_scale->SetValue(d);
float iproj[] = { 1.f / m_projection.m[0][0], 1.f / m_projection.m[1][1] };
if (!gpu_config.tex_origin_is_top_left)
iproj[1] = -iproj[1];
u_iproj2d->SetValue(iproj[0], iproj[1]);
EnableAlphaTest(false);
EnableBlending(false);
int out_index = 0;
fx_fbo->SetColorTexture(t_fx[out_index]);
SetCurrentFBO(fx_fbo);
Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.
RenderFullscreenQuad(*ssao_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[2]);
if (ssao_blur_program)
{
// SSAO blur.
if (blur_r > 0.f)
{
ShaderInput *u_blur_radius = ssao_blur_program->GetInput("u_blur_radius"),
*u_normal_depth = ssao_blur_program->GetInput("u_normal_depth"); // the normal depth texture is not always used, depending on the quality settings.
if (u_blur_radius && u_normal_depth)
{
fx_fbo->SetColorTexture(t_fx[1 - out_index]);
SetCurrentFBO(fx_fbo);
Clear(0, 0, 0, 0, 1, ClearColor); // FIXME this is USELESS, but something is killing fragments with 0 alpha in ssao_program and disabling alpha testing does not solves the issue.
SetShaderProgram(ssao_blur_program);
u_blur_radius->SetValue(blur_r);
u_normal_depth->SetValue(t_fx[2].c_ptr());
RenderFullscreenQuad(*ssao_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_fx[out_index]);
out_index = 1 - out_index;
}
}
}
// Composite back over input.
SetViewport(old_viewport);
fx_fbo->SetColorTexture(t_in);
SetCurrentFBO(fx_fbo);
EnableBlending(true);
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
RenderFullscreenQuad(*single_texture_program, 1, 1, t_fx[out_index]);
EnableBlending(false);
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplySharpenFilter(Render::Texture *t_in, Render::Texture *t_out, float strength)
{
if (!strength || !sharpen_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_strength = sharpen_program->GetInput("u_strength");
if (!u_strength)
return false;
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetShaderProgram(sharpen_program);
u_strength->SetValue(strength);
RenderFullscreenQuad(*sharpen_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplyHSL(Render::Texture *t_in, Render::Texture *t_out, float H, float S, float L)
{
if (((H == 1.f) && (S == 1.f) && (L == 1.f)) || !hsl_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_H = hsl_program->GetInput("u_H"),
*u_S = hsl_program->GetInput("u_S"),
*u_L = hsl_program->GetInput("u_L");
if (!u_H || !u_S || !u_L)
return false;
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetShaderProgram(hsl_program);
u_H->SetValue(H);
u_S->SetValue(S);
u_L->SetValue(L);
RenderFullscreenQuad(*hsl_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplyChromaticDispersion(Render::Texture *t_in, Render::Texture *t_out, float width)
{
if ((width == 0.f) || !chromatic_dispersion_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_width = chromatic_dispersion_program->GetInput("u_width");
if (!u_width)
return false;
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetShaderProgram(chromatic_dispersion_program);
u_width->SetValue(width);
RenderFullscreenQuad(*chromatic_dispersion_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y)
{
if (!strength || !radial_blur_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
ShaderInput *u_strength = radial_blur_program->GetInput("u_strength"),
*u_center = radial_blur_program->GetInput("u_center");
if (!u_strength || !u_center)
return false;
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetShaderProgram(radial_blur_program);
u_strength->SetValue(strength);
float center[] = { center_x * viewport.GetWidth() / (float)dimensions.x, center_y * viewport.GetHeight() / (float)dimensions.y };
u_center->SetValue(center[0], center[1]);
RenderFullscreenQuad(*radial_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, t_in);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> display_lists[2], float strength, int quality)
{
if (!strength || !quality || !motion_blur_program)
return false;
ScopedPerfEvent event(this, __FUNCTION__, Color::Purple);
float k_u = viewport.GetWidth() / dimensions.x, k_v = viewport.GetHeight() / dimensions.y;
// Generate a velocity buffer from the opaque surface list.
fRect old_viewport = GetViewport();
{
ScopedPerfEvent event(this, "Render Velocity Buffer", Color::Purple);
SetViewport(fRect(0, 0, (float)t_fx[0]->GetWidth(), (float)t_fx[0]->GetHeight()));
fx_fbo->SetColorTexture(t_fx[0]);
fx_fbo->SetDepthTexture(t_fx_depth);
SetCurrentFBO(fx_fbo);
Clear(0, 0, 0);
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::PP_Velocity);
DrawList(display_lists[0], dc);
}
// Vector field blur.
#if 0
float k_blur = 0.5;
Render::Texture *t_pp_0[] = { t_fx[0], t_fx[1] };
ApplyDirectionalBlur(t_pp_0, nVector2(k_blur, 0), 0.975f);
Render::Texture *t_pp_1[] = { t_fx[1], t_fx[0] };
ApplyDirectionalBlur(t_pp_1, nVector2(0, k_blur), 0.975f);
#endif
// Apply velocity field.
fx_fbo->SetDepthTexture(NULL);
// Quality: 0 - No PP, 1 - Lores blur, 2 - Hires blur.
if (quality == 1)
{
fx_fbo->SetColorTexture(t_fx[1]);
SetCurrentFBO(fx_fbo);
SetShaderProgram(single_texture_program);
RenderFullscreenQuad(*single_texture_program, 1, 1, t_in);
}
else
{
fx_fbo->SetColorTexture(t_out);
SetCurrentFBO(fx_fbo);
SetViewport(old_viewport);
}
ShaderInput *u_strength = motion_blur_program->GetInput("u_strength"),
*u_pow = motion_blur_program->GetInput("u_pow"),
*u_max = motion_blur_program->GetInput("u_max"),
*u_source = motion_blur_program->GetInput("u_source"),
*u_velocity = motion_blur_program->GetInput("u_velocity");
SetShaderProgram(motion_blur_program);
const float k = 1.f; // stats.bench_fps.GetFps() / 60.f; // Normalize to 60fps.
u_strength->SetValue(strength * 32.f * k);
u_pow->SetValue(2.f);
u_max->SetValue(1.f / 128.f); // Works in normalized space, does not need to adjust for variable resolution.
motion_blur_program->Set(*u_velocity->location, *t_fx[0], u_velocity->index);
if (quality == 1) // low-res blur path
{
fx_fbo->SetColorTexture(t_fx[2]);
SetCurrentFBO(fx_fbo);
u_source->SetValue(t_fx[1].c_ptr());
RenderFullscreenQuad(*motion_blur_program, 1, 1);
fx_fbo->SetColorTexture(t_in);
SetCurrentFBO(fx_fbo);
SetViewport(old_viewport);
EnableBlending(true);
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
SetShaderProgram(single_texture_program);
RenderFullscreenQuad(*single_texture_program, k_u, k_v, t_fx[2]);
EnableBlending(false);
return false;
}
u_source->SetValue(t_in);
RenderFullscreenQuad(*motion_blur_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Render::Texture *Renderer::ApplyPostProcessChain(const Stack <RenderPrimitive *> display_lists[2])
{
ScopedBenchmark bench(stats.bench_post_process);
ScopedPerfEvent event(this, "Apply Post-Processing Chain", Color::Green);
if (!gpu_config.use_rtt)
return t_compose[0];
normal_depth_updated = false;
NML::Tag *post_process_registry = view_registry ? view_registry->GetTag("PostProcess") : NULL;
if (!post_process_registry)
return t_compose[0];
NML::Tag *exclusion_key = registry.GetTag("PostProcess:Exclusion");
fRect old_clipping = GetClippingRect();
SetClippingRect(NULL);
uint i_compose = 0;
NML::Tag *tag;
// SSAO.
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
if (((tag = post_process_registry->GetTag("SSAO")) != NULL) && !IsPostProcessExcluded(exclusion_key, "SSAO"))
if (ApplySSAOFilter
(
t_compose[i_compose],
t_compose[1 - i_compose],
display_lists,
view_registry->GetReal("PostProcess:SSAO:Strength", 1),
view_registry->GetReal("PostProcess:SSAO:Radius", 2),
view_registry->GetReal("PostProcess:SSAO:DistanceScale", 0.5),
view_registry->GetReal("PostProcess:SSAO:BlurRadius", 8)
))
i_compose = 1 - i_compose;
#endif
// Sharpen.
if (((tag = post_process_registry->GetTag("Sharpen")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Sharpen"))
if (ApplySharpenFilter
(
t_compose[i_compose],
t_compose[1 - i_compose],
view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
))
i_compose = 1 - i_compose;
// SSAA.
if (render_technique == TechniqueDeferred)
if (registry.GetBool("Antialiasing:Enable", false))
if (ApplySSAAFilter
(
t_compose[i_compose],
t_compose[1 - i_compose]
))
i_compose = 1 - i_compose;
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
// Motion blur.
if (((tag = post_process_registry->GetTag("MotionBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "MotionBlur"))
if (ApplyMotionBlur
(
t_compose[i_compose],
t_compose[1 - i_compose],
display_lists,
view_registry->GetReal("PostProcess:MotionBlur:Strength", 0.5),
registry.GetInteger("PostProcess:MotionBlur:Quality", 2)
))
i_compose = 1 - i_compose;
#endif
// Radial blur.
if (((tag = post_process_registry->GetTag("RadialBlur")) != NULL) && !IsPostProcessExcluded(exclusion_key, "RadialBlur"))
if (ApplyRadialBlur
(
t_compose[i_compose],
t_compose[1 - i_compose],
view_registry->GetReal("PostProcess:RadialBlur:Strength", 0.5),
view_registry->GetReal("PostProcess:RadialBlur:CenterX", 0.5),
view_registry->GetReal("PostProcess:RadialBlur:CenterY", 0.5)
))
i_compose = 1 - i_compose;
// Bloom.
if (((tag = post_process_registry->GetTag("Bloom")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Bloom"))
ApplyBloomFilter
(
t_compose[i_compose],
view_registry->GetReal("PostProcess:Bloom:Strength", 1),
view_registry->GetReal("PostProcess:Bloom:Threshold", 0.95f),
view_registry->GetReal("PostProcess:Bloom:Exponent", 4),
view_registry->GetReal("PostProcess:Bloom:Radius", 12),
view_registry->GetReal("PostProcess:Sharpen:Strength", 0.5f)
);
// Noise.
#if !(__PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__)
// TODO mobile version of this.
if (((tag = post_process_registry->GetTag("Noise")) != NULL) && !IsPostProcessExcluded(exclusion_key, "Noise"))
if (ApplyNoiseFilter
(
t_compose[i_compose],
t_compose[1 - i_compose],
view_registry->GetReal("PostProcess:Noise:Strength", 0.25f),
view_registry->GetReal("PostProcess:Noise:Monochromatic", 0.f),
view_registry->GetReal("PostProcess:Noise:LumaBias", 0.5f)
) )
i_compose = 1 - i_compose;
#endif
// HSL.
if (((tag = post_process_registry->GetTag("HueSaturation")) != NULL) && !IsPostProcessExcluded(exclusion_key, "HueSaturation"))
if (ApplyHSL
(
t_compose[i_compose],
t_compose[1 - i_compose],
view_registry->GetReal("PostProcess:HueSaturation:H", 1),
view_registry->GetReal("PostProcess:HueSaturation:S", 1),
view_registry->GetReal("PostProcess:HueSaturation:L", 1)
) )
i_compose = 1 - i_compose;
// Chromatic dispersion.
if (((tag = post_process_registry->GetTag("ChromDisp")) != NULL) && !IsPostProcessExcluded(exclusion_key, "ChromDisp"))
if (ApplyChromaticDispersion
(
t_compose[i_compose],
t_compose[1 - i_compose],
view_registry->GetReal("PostProcess:ChromDisp:Width", 1)
) )
i_compose = 1 - i_compose;
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
SetClippingRect(&old_clipping);
SetIndexSource(NULL);
SetVertexSource(NULL, 0);
return t_compose[i_compose];
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,40 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "core/core_profiler.h"
#include "core/raster_font.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::DrawProfilerText(GS::Render::RasterFont *font[2], float &x, float &y)
{
Color title_color(1.f, 0.9f, 0);
WriterConfig config(false);
Write(*font[1], String::Format("%s - %s (v%s)\n\n", GetName(), GetDescription(), GetVersion()), x, y, config, 1, &title_color);
Write(*font[0], String::Format("Device: %s\n", stats.adapter.c_str()), x, y, config);
Write(*font[0], String::Format("Vendor: %s\n\n", stats.vendor.c_str()), x, y, config);
Write(*font[0], String::Format("Texture: %s Geometry: %s\n\n", Core::FormatNumber(stats.texture_memory, Core::MemorySize).c_str(), Core::FormatNumber(stats.geometry_memory, Core::MemorySize).c_str()), x, y, config);
Render::Renderer::DrawProfilerText(font, x, y);
Write(*font[1], "Batching System\n\n", x, y, config, 1, &title_color);
Write(*font[0], String::Format("Program change = %0.02f%% (%d)\n", gpu_stats.prg_change ? (gpu_stats.prg_change * 100.f) / stats.list_drawn : 0, gpu_stats.prg_change), x, y, config);
Write(*font[0], String::Format("List change = %0.02f%% (%d)\n", gpu_stats.dls_change ? (gpu_stats.dls_change * 100.f) / stats.list_drawn : 0, gpu_stats.dls_change), x, y, config);
Write(*font[0], String::Format("Material change = %0.02f%% (%d)\n", gpu_stats.mat_change ? (gpu_stats.mat_change * 100.f) / stats.list_drawn : 0, gpu_stats.mat_change), x, y, config);
Write(*font[0], String::Format("Item change = %0.02f%% (%d)\n\n", gpu_stats.item_change ? (gpu_stats.item_change * 100.f) / stats.list_drawn : 0, gpu_stats.item_change), x, y, config);
Write(*font[1], "Terrain System\n\n", x, y, config, 1, &title_color);
Write(*font[0], String::Format("Cache miss = %0.02f%% (%d query/frame)\n\n", gpu_stats.terrain_page_query_count ? (gpu_stats.terrain_page_query_miss * 100.f) / gpu_stats.terrain_page_query_count : 0, gpu_stats.terrain_page_query_count), x, y, config);
Write(*font[1], String::Format("Technique: %s\n\n", render_technique == TechniqueDeferred ? "Deferred" : "Forward"), x, y, config, 1, &title_color);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,71 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
RegistryRValue Renderer::ProcessMessage(RegistryMessage m, const Registry *registry, const void *parm)
{
switch (m)
{
case RegistryMsg_StartKeyChangeBatch:
break;
case RegistryMsg_EndKeyChangeBatch:
if (pending_shadow_map_refresh)
CreateShadowMaps();
if (pending_core_shader_refresh)
LoadCoreShaders();
if (pending_technique_refresh)
SetRenderTechnique();
if (pending_fx_refresh)
SetPostProcess();
pending_shadow_map_refresh = false;
pending_core_shader_refresh = false;
pending_technique_refresh = false;
pending_fx_refresh = false;
break;
case RegistryMsg_KeyChange:
if (RegistryKeyChange *key = (RegistryKeyChange *)parm)
{
if ((key->key == "ShadowMapping:Enable") || (key->key == "ShadowMapping:Size"))
{
gpu_config.enable_shadow = registry->GetBool("ShadowMapping:Enable", true);
pending_shadow_map_refresh = true;
}
else if (key->key == "ShadowMapping:PSSM:Split")
{
pending_shadow_map_refresh = true;
pending_core_shader_refresh = true;
}
else if (key->key == "ShadowMapping:PCF:Quality")
pending_core_shader_refresh = true;
else if (key->key == "Texture:Float:Enable")
pending_technique_refresh = true;
else if (key->key == "Technique")
pending_technique_refresh = true;
else if (key->key.StartsWith("Antialiasing"))
pending_technique_refresh = true;
else if (key->key == "PostProcess:FX:Scale")
pending_fx_refresh = true;
}
break;
default:
break;
}
return RegistryReturn_Ok;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,155 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_geometry.h"
#include "gpu/gpu_shader_object.h"
#include "gpu/gpu_draw_context.h"
#include "core/object.h"
#include "core/camera.h"
#include "core/geometry.h"
#include "log/log.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
#define ENABLE_PERFORMANCE_TOOLS
#ifdef ENABLE_PERFORMANCE_TOOLS
#define _ProgramCacheMiss ++gpu_stats.prg_change;
#define _DisplayListCacheMiss ++gpu_stats.dls_change;
#define _MaterialCacheMiss ++gpu_stats.mat_change;
#define _ItemCacheMiss ++gpu_stats.item_change;
#define _SetPerfWire if (performance_tools.show_wireframe) SetFillMode(FillWireframe);
#define _UnsetPerfWire if (performance_tools.show_wireframe) SetFillMode(FillSolid);
#else
#define _ProgramCacheMiss
#define _DisplayListCacheMiss
#define _MaterialCacheMiss
#define _ItemCacheMiss
#define _SetPerfWire
#define _UnsetPerfWire
#endif
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::ResetDisplayListCache()
{
if (dls_cache.dls)
SetDisplayList(NULL);
dls_cache.dls = NULL;
if (dls_cache.mat)
UnsetMaterial(*dls_cache.mat, dls_cache.ctx);
dls_cache.mat = NULL;
if (dls_cache.shd)
SetShaderProgram(NULL, dls_cache.shd);
dls_cache.shd = NULL;
dls_cache.item = NULL;
}
bool Renderer::SetDrawContext(DisplayList &dls, const Core::Item &item, const DrawContext &dc)
{
Material *mat = dls.material.IsValid() ? (Material *)dls.material.c_ptr() : NULL;
if (!mat || !mat->shader)
return false;
Shader *shd = mat->shader->variants[dc.ctx.variant];
if (!shd)
return false;
#define ProgramChange (1 << 0)
#define DisplayListChange (1 << 1)
#define MaterialChange (1 << 2)
#define ItemChange (1 << 3)
#define OpacityChange (1 << 4)
uint change_mask = 0;
change_mask |= (shd != dls_cache.shd) ? ProgramChange : 0;
change_mask |= (&dls != dls_cache.dls) ? DisplayListChange : 0;
change_mask |= (mat != dls_cache.mat) ? MaterialChange : 0;
change_mask |= (&item != dls_cache.item) ? ItemChange : 0;
change_mask |= (item.opacity != dls_cache.opacity) ? OpacityChange : 0;
if (change_mask & ProgramChange)
{
if (!SetShaderProgram(shd, dls_cache.shd))
return false;
_ProgramCacheMiss
}
if (change_mask & ProgramChange)
shd->SetConstantInputs();
if (change_mask & DisplayListChange)
{
SetDisplayList(&dls);
_DisplayListCacheMiss
}
if (change_mask & MaterialChange)
{
if (dls_cache.mat)
UnsetMaterial(*dls_cache.mat, dc.ctx);
SetMaterial(*mat, dc.ctx, asbool(item.opacity < 1.f));
_MaterialCacheMiss
}
// Program inputs.
if (change_mask & ProgramChange)
{
shd->SetRendererInputs(*this, mat);
if (dc.light)
shd->SetLightInputs(*this, *view_item, *dc.light);
}
if (change_mask & (ProgramChange | MaterialChange | OpacityChange))
shd->SetMaterialOpacityInputs(*mat, item.opacity);
if (change_mask & (ProgramChange | MaterialChange))
{
shd->SetMaterialInputs(*mat);
shd->SetTextureInputs();
}
if (change_mask & (ProgramChange | DisplayListChange))
shd->SetVertexStreamInputs(dls);
if (change_mask & (ProgramChange | ItemChange))
{
shd->SetTransformInputs(m_projection, m_view, m_iview, &m_world, &m_iworld);
shd->SetPreviousTransformInputs(m_projection, m_previous_iview, &m_previous_world);
_ItemCacheMiss
}
if (change_mask & (ProgramChange | DisplayListChange | ItemChange))
if (Core::Skin *skin = ((Core::Object &)item).GetSkin())
shd->SetSkinInputs(dls, *skin);
shd->CommitInputs();
// Sync cache.
dls_cache.ctx = dc.ctx;
dls_cache.shd = shd;
dls_cache.mat = mat;
dls_cache.dls = &dls;
dls_cache.item = &item;
dls_cache.opacity = item.opacity;
return true;
}
void Renderer::DrawDisplayListCached(DisplayList &dls, const Core::Item &item, const DrawContext &dc)
{
if (!SetDrawContext(dls, item, dc))
return;
// Draw.
_SetPerfWire
dls.Draw();
_UnsetPerfWire
stats.list_drawn++;
stats.triangle_drawn += dls.index_count / 3;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,298 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstddef>
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_geometry.h"
#include "core/renderer_environment_interface.h"
#include "core/terrain.h"
#include "core/geometry.h"
#include "sort/sort.h"
#include "container/container_sort.h"
#include "platform_config.h"
#include "log/log.h"
using namespace GS;
using namespace GPU;
//------------------------------------------------------------------------------
void Renderer::DrawList(const Stack <RenderPrimitive *> &dl_list, const DrawContext &dc)
{
ResetDisplayListCache();
DecayTerrainCache();
m_previous_iview = view_item->GetPreviousMatrix().InversedFast();
for (uint n = 0; n < dl_list.GetCount(); ++n)
{
RenderPrimitive *dl = dl_list[n];
if (!dl->item)
continue;
switch (dl->type)
{
case RenderPrimitive::TypeDisplayList:
if (dl->dlst)
{
m_previous_world = dl->item->GetPreviousMatrix();
SetWorldMatrix(dl->item->GetMatrix(), &dl->item->GetInverseMatrix());
DrawDisplayListCached(*dl->dlst, *dl->item, dc);
}
break;
case RenderPrimitive::TypeTerrainPatch:
if (dl->patch)
RenderTerrainPatch(*dl->patch, *dl->item, dc);
break;
case RenderPrimitive::TypeEmitter:
break;
}
}
ResetDisplayListCache();
stats.queue_pass++;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static inline uint GetRenderPrimitiveSortKey(RenderPrimitive *a)
{
// Watch nRenderMaterial and nGPUDisplayList size in bytes so that this key stays optimal.
return ((((uint)a->dlst->material.c_ptr() >> 8) & 0xffff) << 16) + (((uint)a->dlst >> 7) & 0xffff);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::SortDisplayList(AutoStack <RenderPrimitive *> &d_list) const
{
const uint count = d_list.GetCount();
if (count == 0)
return;
try {
// Byte sort list.
typedef Sort<uint, RenderPrimitive *> SortPrimitive;
Array <SortPrimitive::Entry> sort_a(count), sort_b(count);
for (uint n = 0; n < count; ++n)
{
sort_a[n].o = d_list[n];
sort_a[n].v = GetRenderPrimitiveSortKey(d_list[n]);
}
Array <SortPrimitive::Entry> *out = SortPrimitive::ByteSort(count, &sort_a, &sort_b);
// Empty the source stack, prevent primitives cleanup...
d_list.DropContentOwnership();
// ...as we now insert them back in sorted order.
for (uint n = 0; n < count; ++n)
d_list.Push((*out)[n].o);
}
catch(char *e)
{
__LOG__ << "Failed to SortDisplayList.\n";
}
}
static int GetMaterialDrawPassIndex(Render::Material *m, float opacity = 1.f)
{
if (m->renderword & Core::Material::Render_UseFramebuffer)
return 2;
if ((m->blendop != Core::Material::Blend_None) || (opacity < 1.0))
return 1;
return 0;
}
void Renderer::BuildDisplayLists(const AutoStack <Render::Primitive *> &p_list, AutoStack <RenderPrimitive *> *d_list) const
{
for (uint n = 0; n < p_list.GetCount(); ++n)
{
Render::Primitive *p = p_list[n];
switch (p->type)
{
case Render::Primitive::Type_Geometry:
if (Geometry *geo = (Geometry *)p->geometry.c_ptr())
for (uint n = 0; n < geo->display_list.GetCount(); ++n)
{
DisplayList *dlist = geo->display_list[n];
if (dlist->material.IsNull())
continue;
int draw_pass_index = GetMaterialDrawPassIndex(dlist->material, p->item->opacity);
d_list[draw_pass_index].Push(new RenderPrimitive(p->item, dlist));
}
break;
case Render::Primitive::Type_TerrainPatch:
if (p->patch->terrain->render_data->material.IsNull())
continue;
d_list[GetMaterialDrawPassIndex(p->patch->terrain->render_data->material, p->patch->terrain->opacity)].Push(new RenderPrimitive(p->item, p->patch));
break;
case Render::Primitive::Type_Emitter:
break;
}
}
for (int n = 0; n < 3; ++n)
SortDisplayList(d_list[n]);
}
void Renderer::RenderList()
{
ScopedBenchmark bench(stats.bench_render);
PerfSetMarker("RenderList", Color::Green);
// Build primitive list.
stats.renderable_processed += BuildRenderablePrimitiveList(*view_item, *view_item, frustum_rlist.primitive_list);
stats.renderable_drawn += frustum_rlist.primitive_list.GetCount();
// Build display lists.
BuildDisplayLists(frustum_rlist.primitive_list, frustum_rlist.display_lists);
// Render opaque display lists.
switch (render_technique)
{
case TechniqueDeferred:
RenderListDeferred(frustum_rlist.display_lists[0]);
break;
case TechniqueForward:
RenderListForward(frustum_rlist.display_lists[0], DrawContext::Opaque);
if (environment_interface) // draw skybox
{
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
Render::sTexture skybox_layers[2];
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
DrawSkybox(skybox_layers, user_skybox_shader);
}
break;
}
// Render transparent display lists.
RenderListForward(frustum_rlist.display_lists[1], DrawContext::Alpha);
// Render frame buffer dependent display lists.
if (gpu_config.use_rtt && ((frustum_rlist.display_lists[2].GetCount() > 0) || registry.GetBool("FrameBufferAsTexture;", false)))
{
GrabDisplay(t_fx[0]);
RenderListForward(frustum_rlist.display_lists[2], DrawContext::Opaque);
}
// Render direct user primitives hook.
if (environment_interface)
environment_interface->OnRenderUser(this);
//
switch (render_technique)
{
case TechniqueDeferred:
// Render fog.
if (environment_interface)
if (environment_interface->IsFogEnabled() && !performance_tools.disable_fog)
{
EnableBlending(true);
RenderFullscreenQuad(*ds_fog_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
EnableBlending(false);
}
break;
case TechniqueForward:
// Resolve MSSA target.
if (gpu_config.enable_aa)
render_fbo->Blit(resolve_fbo, iRect(0, 0, dimensions.x, dimensions.y), iRect(0, 0, dimensions.x, dimensions.y), true, true);
break;
}
// Post-processes.
if (gpu_config.use_rtt)
{
t_final = ApplyPostProcessChain(frustum_rlist.display_lists);
resolve_fbo->SetColorTexture(t_final);
}
// Drop render list.
frustum_rlist.Clear(false);
// Subsequent draws should render to the final texture.
if (gpu_config.use_rtt)
SetCurrentFBO(resolve_fbo);
render_fbo = resolve_fbo;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::BeginDrawList()
{
PerfSetMarker("BeginDrawList", Color::Green);
// Cache per-frame values for coherency.
frame_clock = environment_interface ? environment_interface->GetClock() : 0;
pcf_radius = registry.GetReal("ShadowMapping:PCF:Radius", 1.75f);
if (environment_interface)
if (Core::Camera *c = environment_interface->GetCurrentCamera())
{
SetCamera(c);
ApplyCamera();
}
/*
MSAA needs a special resolve step so we work on a different FBO which
will later be resolved to the resolve FBO.
*/
render_fbo = gpu_config.enable_aa ? buffer_fbo : resolve_fbo;
if (render_technique == TechniqueForward)
{
if (gpu_config.use_rtt)
SetCurrentFBO(render_fbo);
// Clear output, if a skybox is set it will be rendered after the opaque pass.
if (environment_interface)
{
Render::Shader *user_skybox_shader = environment_interface->GetSkyboxShader();
Render::sTexture skybox_layers[2];
if (environment_interface->GetSkyboxLayers(skybox_layers) || user_skybox_shader)
Clear(0, 0, 0, 1, 1, ClearDepth);
else
{
Color bg = environment_interface->GetClearColor();
Clear(bg.x, bg.y, bg.z);
}
}
}
t_final = t_compose[0];
return true;
}
void Renderer::EndDrawList()
{
PerfSetMarker("EndDrawList", Color::Green);
// when entering this function the current FBO is expected to be render_FBO.
if (gpu_config.use_rtt)
{
// Blit result to user output_fbo.
if (output_fbo)
render_fbo->Blit(output_fbo, viewport.AsInt(), output_fbo_rect, true, false);
// Blit to frame buffer.
SetCurrentFBO(NULL);
fRect src(viewport.sx / dimensions.x, viewport.sy / dimensions.y, viewport.ex / dimensions.x, viewport.ey / dimensions.y);
RenderFullscreenQuad(*single_texture_program, src, fRect(0, 0, 1, 1), t_final); // viewport is already set for destination so use the full rect output
// Restore default output.
resolve_fbo->SetColorTexture(t_final = t_compose[0]);
}
SetIndexSource(NULL);
SetVertexSource(NULL, 0);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,82 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_geometry.h"
#include "gpu/gpu_draw_context.h"
#include "core/renderer_environment_interface.h"
#include "core/camera.h"
#include "core/light.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::RenderGBufferPass(const GS::Stack <RenderPrimitive *> &display_lists)
{
SetCurrentFBO(buffer_fbo);
Clear(0, 0, 0, 0);
DrawContext dc(DrawContext::Deferred, DrawContext::Base, MaterialShader::DS_GBufferMRT4);
DrawList(display_lists, dc);
}
void Renderer::RenderDeferredLightPass(const GS::Stack <RenderPrimitive *> &)
{
SetCurrentFBO(resolve_fbo);
DrawContext dc(DrawContext::Deferred, DrawContext::Light);
// Clear to ambient.
EnableDepthWrite(false);
RenderFullscreenQuad(*ambient_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y);
EnableDepthWrite(true);
// For each light in frustum, render affected primitives with a light specific shader.
SetBlendFunc(BlendOne, BlendOne);
List <Core::Light *> light_list;
environment_interface->GetLightsInFrustum(view_item->GetMatrix().GetRow(3), frustum, light_list);
ListForeachPtr(Core::Light *, l, light_list)
{
// Render shadow map.
if (gpu_config.enable_shadow && l->shadow == Core::Light::Shadow_Map)
{
if (PrepareShadowMap(*l, true)) // TODO move to job
RenderShadowMap(*l);
SetCurrentFBO(resolve_fbo);
SetBlendFunc(BlendOne, BlendOne);
}
// Compose light.
EnableDepthWrite(false);
EnableBlending(true);
switch (l->model)
{
case Core::Light::Model_Point: RenderPointLight(*l, dc); break;
case Core::Light::Model_Linear: RenderLinearLight(*l, dc); break;
case Core::Light::Model_Spot: RenderSpotLight(*l, dc); break;
}
EnableBlending(false);
EnableDepthWrite(true);
stats.light_processed++;
}
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::RenderListDeferred(const GS::Stack <RenderPrimitive *> &display_lists)
{
RenderGBufferPass(display_lists);
if (environment_interface)
RenderDeferredLightPass(display_lists);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,318 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_shader_object.h"
#include "gpu/gpu_geometry.h"
#include "gpu/gpu_draw_context.h"
#include "core/renderer_environment_interface.h"
#include "core/geometry.h"
#include "core/terrain.h"
#include "core/camera.h"
#include "core/light.h"
#include "async/job.h"
#include "alloc/ialloc.h"
#include "platform.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::LightCullDisplayList(const Core::Light &light, const Stack <RenderPrimitive *> &in, Stack <RenderPrimitive *> &out)
{
for (uint n = 0; n < in.GetCount(); ++n)
{
RenderPrimitive *dl = in[n];
switch (light.model)
{
case Core::Light::Model_Point:
if (light.range > 0)
{
MinMax &mm = dl->dlst->minmax;
Vector4 dl_lpos = light.GetMatrix().GetRow(3) * dl->item->GetInverseMatrix();
if ( ((dl_lpos.x - light.range) < mm.mx.x) &&
((dl_lpos.x + light.range) > mm.mn.x) &&
((dl_lpos.y - light.range) < mm.mx.y) &&
((dl_lpos.y + light.range) > mm.mn.y) &&
((dl_lpos.z - light.range) < mm.mx.z) &&
((dl_lpos.z + light.range) > mm.mn.z) )
out.Push(dl);
}
else
out.Push(dl);
break;
case Core::Light::Model_Spot:
if (light.frustum.ClassifyMinMax(dl->dlst->minmax, &dl->item->GetMatrix()) != Frustum::Outside)
out.Push(dl);
break;
case Core::Light::Model_Linear:
default:
out.Push(dl);
break;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::SetupForwardContext(const DrawContext::Context &ctx)
{
switch (ctx.draw)
{
case DrawContext::Base:
switch (ctx.render)
{
case DrawContext::Opaque:
EnableDepthTest(true);
break;
case DrawContext::Alpha:
EnableDepthWrite(false);
EnableBlending(true);
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
break;
}
break;
case DrawContext::Light:
switch (ctx.render)
{
case DrawContext::Opaque:
EnableBlending(true);
SetBlendFunc(BlendSrcAlpha, BlendOne);
break;
case DrawContext::Alpha:
EnableDepthWrite(false);
EnableBlending(true);
SetBlendFunc(BlendSrcAlpha, BlendOne);
break;
}
break;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class GPU::PrepareLightJob : public ASync::Job
{
const Renderer &renderer;
const Core::Light &light;
const Stack <RenderPrimitive *> &in;
Stack <RenderPrimitive *> &out;
DrawContext::Render rc;
public:
NPLACEMENT_NEW(RendererJob)
bool use_shadow;
bool CullPrimitive(const Render::Material *m, const MinMax &mm, const Core::Item *item) const
{
if (m->renderword & Core::Material::Render_Unlit)
return false; // exclude from lighting
switch (light.model)
{
case Core::Light::Model_Point:
if (light.range > 0)
{
Vector4 dl_lpos = light.GetMatrix().GetRow(3) * item->GetInverseMatrix();
if ( ((dl_lpos.x - light.range) < mm.mx.x) &&
((dl_lpos.y - light.range) < mm.mx.y) &&
((dl_lpos.z - light.range) < mm.mx.z) &&
((dl_lpos.x + light.range) > mm.mn.x) &&
((dl_lpos.y + light.range) > mm.mn.y) &&
((dl_lpos.z + light.range) > mm.mn.z) )
return true;
}
else
return true;
break;
case Core::Light::Model_Spot:
if (light.frustum.ClassifyMinMax(mm, &item->GetMatrix()) != Frustum::Outside)
return true;
break;
default:
case Core::Light::Model_Linear:
return true;
}
return false;
}
void Execute(uint)
{
for (uint n = 0; n < in.GetCount(); ++n)
{
RenderPrimitive *dl = in[n];
bool r = false;
switch (dl->type)
{
case RenderPrimitive::TypeDisplayList:
r = CullPrimitive(dl->dlst->material, dl->dlst->minmax, dl->item);
break;
case RenderPrimitive::TypeTerrainPatch:
r = CullPrimitive(dl->patch->terrain->render_data->material, dl->patch->minmax, dl->item);
break;
default:
case RenderPrimitive::TypeEmitter:
break;
}
if (r)
out.Push(dl);
}
// Prepare shadow map if required.
if (out.GetCount() == 0)
use_shadow = false;
else
{
use_shadow = renderer.gpu_config.enable_shadow && (light.shadow == Core::Light::Shadow_Map);
if ((rc == DrawContext::Alpha) && (light.shadow_cast_all == false))
use_shadow = false;
bool use_shadow_matrix = false;
if ((light.model == Core::Light::Model_Spot) && !light.projection_texture.IsEmpty())
use_shadow_matrix = true;
if (use_shadow || use_shadow_matrix)
renderer.PrepareShadowMap(light, use_shadow);
}
}
PrepareLightJob(const Renderer &_renderer, const Core::Light &_light, const Stack <RenderPrimitive *> &_in, Stack <RenderPrimitive *> &_out, DrawContext::Render _rc) : Job("Light Draw Setup"), renderer(_renderer), light(_light), in(_in), out(_out), rc(_rc) {}
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static MaterialShader::Variant DispatchLightShaderVariant(Core::Light::Model type, bool shadow, bool projection_map)
{
static MaterialShader::Variant table[2][2][Core::Light::Model_Last] =
{
{
{ MaterialShader::Last, MaterialShader::FS_PointLight, MaterialShader::FS_LinearLight, MaterialShader::FS_SpotLight },
{ MaterialShader::Last, MaterialShader::FS_PointLightShadowMapping, MaterialShader::FS_LinearLightShadowMapping, MaterialShader::FS_SpotLightShadowMapping }
},
{
{ MaterialShader::Last, MaterialShader::FS_PointLight, MaterialShader::FS_LinearLight, MaterialShader::FS_SpotLightProjection },
{ MaterialShader::Last, MaterialShader::FS_PointLightShadowMapping, MaterialShader::FS_LinearLightShadowMapping, MaterialShader::FS_SpotLightProjectionShadowMapping }
}
};
return table[projection_map ? 1 : 0][shadow ? 1 : 0][type];
}
void Renderer::DrawForwardBaseConstant(DrawContext::Render rc, const Stack <RenderPrimitive *> &dl_list)
{
ScopedPerfEvent event(this, __FUNCTION__, Color::Blue);
DrawContext dc(rc, DrawContext::Base, MaterialShader::FS_Constant);
SetupForwardContext(dc.ctx);
DrawList(dl_list, dc);
}
void Renderer::DrawForwardLightContribution(DrawContext::Render rc, const List <Core::Light *> &light_list, const AutoStack <PrepareLightJob *> &setup_jobs, const Array <Stack <RenderPrimitive *> > &dl_list)
{
ScopedPerfEvent event(this, "Render light contribution", Color::Blue);
DrawContext dc(rc, DrawContext::Light, MaterialShader::FS_Constant);
SetupForwardContext(dc.ctx);
EnableDepthWrite(false);
SetDepthFunc(DepthLessEqual);
uint job_count = 0;
ListForeachPtr(Core::Light *, l, light_list)
{
if (l->model == Core::Light::Model_None)
continue;
// Wait for the light setup job to complete.
Platform::Get().job_manager->JoinJob(setup_jobs[job_count]);
if (dl_list[job_count].GetCount())
{
bool use_shadow = setup_jobs[job_count]->use_shadow;
if (use_shadow)
{
PerfBeginEvent("Render shadow map", Color::Yellow);
EnableDepthWrite(true);
RenderShadowMap(*l);
EnableDepthWrite(false);
SetCurrentFBO(gpu_config.use_rtt ? render_fbo : NULL);
SetupForwardContext(dc.ctx);
PerfEndEvent();
}
// Add contribution.
dc.ctx.variant = DispatchLightShaderVariant(l->model, use_shadow, asbool(l->projection_texture));
dc.light = l;
DrawList(dl_list[job_count], dc);
}
++stats.light_processed;
++job_count;
}
}
void Renderer::RenderListForward(Stack <RenderPrimitive *> &dl_list, DrawContext::Render rc)
{
ScopedPerfEvent event(this, __FUNCTION__, Color::Red);
if (dl_list.GetCount() == 0)
return;
// Setup all lights.
List <Core::Light *> frustum_light_list;
environment_interface->GetLightsInFrustum(view_item->GetMatrix().GetRow(3), frustum, frustum_light_list);
Array <Stack <RenderPrimitive *> > light_dl_list(frustum_light_list.GetCount());
AutoStack <PrepareLightJob *> light_setup_job(frustum_light_list.GetCount());
uint job_count = 0;
ListForeachPtr(Core::Light *, l, frustum_light_list)
{
if (l->model == Core::Light::Model_None)
continue;
if (PrepareLightJob *job = new PrepareLightJob(*this, *l, dl_list, light_dl_list[job_count++], rc))
{
light_setup_job.Push(job);
Platform::Get().job_manager->EnqueueJob(job);
}
}
// Draw all passes.
DrawForwardBaseConstant(rc, dl_list);
DrawForwardLightContribution(rc, frustum_light_list, light_setup_job, light_dl_list);
//
CollectJobsPerf(light_setup_job, job_count, stats.bench_prepare_light);
// Restore default state.
SetCullFunc(CullFront);
SetDepthFunc(DepthLess);
EnableDepthWrite(true);
EnableBlending(false);
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,462 @@
/* -----------------------------------------------------------------------------
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;
}
//------------------------------------------------------------------------------

View File

@ -218,6 +218,7 @@ protected:
radial_blur_program,
motion_blur_program,
resolve_msaa_depth_program,
skybox_program;
/*!
@ -325,6 +326,7 @@ public:
bool ApplySSAOFilter(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> [2], float strength, float radius, float clip_distance, float blur_radius);
bool ApplyRadialBlur(Render::Texture *t_in, Render::Texture *t_out, float strength, float center_x, float center_y);
bool ApplyMotionBlur(Render::Texture *t_in, Render::Texture *t_out, const Stack <RenderPrimitive *> [2], float strength, int quality = 1);
bool ApplyResolveMSAADepth(Render::Texture *t_depth_msaa, Render::Texture *t_out);
void GetPostProcessNormalDepth(const Stack <RenderPrimitive *> display_lists[2]);

View File

@ -0,0 +1,674 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#ifndef __PLATFORM_IOS__
#include <malloc.h>
#endif
#include "gpu/gpu_renderer.h"
#include "core/renderer_environment_interface.h"
#include "core/light.h"
#include "core/camera.h"
#include "core/object.h"
#include "core/shader.h"
#include "container/narray.h"
#include "platform_config.h"
#include "platform.h"
#include "log/file_log.h"
#include "log/log.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Shader::SetVertexStreamInputs(DisplayList &dls)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryVertexStream].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryVertexStream][n];
switch (input->semantic)
{
case Core::ShaderInput::Position:
Set(*input->location, 3, Types::ValueHalfFloat, false, dls.stride, (const void *)dls.vertex_offset);
break;
case Core::ShaderInput::Normal:
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)dls.normal_offset);
break;
case Core::ShaderInput::VertexColor:
Set(*input->location, 4, Types::ValueUByte, true, dls.stride, (const void *)dls.rgb_offset);
break;
case Core::ShaderInput::Tangent:
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)dls.tangent_offset);
break;
case Core::ShaderInput::Bitangent:
Set(*input->location, 3, Types::ValueByte, true, dls.stride, (const void *)(dls.tangent_offset + 4 * sizeof(char)));
break;
case Core::ShaderInput::BoneIndex:
Set(*input->location, 4, Types::ValueUByte, false, dls.stride, (const void *)dls.skinning_offset);
break;
case Core::ShaderInput::BoneWeight:
Set(*input->location, 4, Types::ValueUByte, true, dls.stride, (const void *)(dls.skinning_offset + 4 * sizeof(char)));
break;
case Core::ShaderInput::UV0:
case Core::ShaderInput::UV1:
case Core::ShaderInput::UV2:
{
int uv_index = (int)input->semantic - (int)Core::ShaderInput::UV0;
Set(*input->location, 2, Types::ValueHalfFloat, false, dls.stride, (const void *)dls.uv_offset[uv_index]);
}
break;
}
}
}
void Shader::SetSkinInputs(DisplayList &dls, Core::Skin &skin)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategorySkin].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategorySkin][n];
switch (input->semantic)
{
case Core::ShaderInput::BoneMatrix:
if (float *m = (float *)alloca(4 * 4 * sizeof(float) * dls.bone.GetCount()))
{
float *p_m = m;
for (uint n = 0; n < dls.bone.GetCount(); ++n)
{
Memory::Copy(p_m, skin.bones_mtx[dls.bone[n]].m, 4 * 4 * sizeof(float));
p_m += 4 * 4;
}
Set(*input->location, (Matrix4 *)m, dls.bone.GetCount());
}
break;
case Core::ShaderInput::PreviousBoneMatrix:
if (float *m = (float *)alloca(4 * 4 * sizeof(float) * dls.bone.GetCount()))
{
float *p_m = m;
for (uint n = 0; n < dls.bone.GetCount(); ++n)
{
Memory::Copy(p_m, skin.previous_bones_mtx[dls.bone[n]].m, 4 * 4 * sizeof(float));
p_m += 4 * 4;
}
Set(*input->location, (Matrix4 *)m, dls.bone.GetCount());
}
break;
}
}
}
void Shader::SetConstantInputs()
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryConstant].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryConstant][n];
switch (input->semantic)
{
case Core::ShaderInput::Constant:
switch (input->data_type)
{
default:
case Core::ShaderInput::Matrix3:
case Core::ShaderInput::Matrix4:
break;
case Core::ShaderInput::DataTexture2D:
case Core::ShaderInput::DataTexture3D:
case Core::ShaderInput::DataTextureCube:
Set(*input->location, *input->parm_t, input->index);
break;
case Core::ShaderInput::Int:
Set(*input->location, (int *)&input->parm_v.x);
break;
case Core::ShaderInput::Float:
Set(*input->location, &input->parm_v.x);
break;
case Core::ShaderInput::Vector2:
Set(*input->location, &input->parm_v.x, 2);
break;
case Core::ShaderInput::Vector3:
Set(*input->location, &input->parm_v.x, 3);
break;
case Core::ShaderInput::Vector4:
Set(*input->location, &input->parm_v.x, 4);
break;
}
break;
}
}
}
void Shader::SetTextureInputs()
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryTexture].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryTexture][n];
switch (input->semantic)
{
case Core::ShaderInput::Texture2D:
case Core::ShaderInput::Texture3D:
case Core::ShaderInput::TextureCube:
if (input->parm_t)
Set(*input->location, *input->parm_t, input->index);
break;
}
}
}
void Shader::SetRendererInputs(Renderer &r, Material *m)
{
Color fog_color;
float fog_near = 0, fog_far = 0;
bool fog_enabled = r.environment_interface ? r.environment_interface->GetFogConfiguration(fog_color, fog_near, fog_far) : false;
if (r.performance_tools.disable_fog)
fog_enabled = false;
if (m && (m->blendop == Core::Material::Blend_Add))
fog_enabled = false;
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryRenderer].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryRenderer][n];
switch (input->semantic)
{
case Core::ShaderInput::Clock:
Set(*input->location, r.frame_clock);
break;
case Core::ShaderInput::TimeOfDay:
Set(*input->location, r.environment_interface->GetTimeOfDay());
break;
case Core::ShaderInput::ViewVector:
{
Vector4 tmp = r.GetCamera()->GetMatrix().GetRow(2);
Set(*input->location, &tmp.x, 3);
}
break;
case Core::ShaderInput::ViewPosition:
{
Vector4 tmp = r.GetCamera()->GetMatrix().GetRow(3);
Set(*input->location, &tmp.x, 4);
}
break;
case Core::ShaderInput::Viewport:
{
const fRect viewport = r.GetViewport();
float v[4] = { viewport.sx, viewport.sy, viewport.GetWidth(), viewport.GetHeight() };
Set(*input->location, v, 4);
}
break;
case Core::ShaderInput::ZNear:
Set(*input->location, r.GetCamera()->GetNearClippingPlane());
break;
case Core::ShaderInput::ZFar:
Set(*input->location, r.GetCamera()->GetFarClippingPlane());
break;
case Core::ShaderInput::ZoomFactor:
Set(*input->location, r.GetCamera()->zoom_factor);
break;
case Core::ShaderInput::DisplayBufferRatio:
{
float v[] = { r.GetOutputAspectRatio(), 1.f };
Set(*input->location, v, 2);
}
break;
case Core::ShaderInput::ViewportRatio:
{
float v[] = { r.GetViewport().GetHeight() / r.GetViewport().GetWidth(), 1.f };
Set(*input->location, v, 2);
}
break;
case Core::ShaderInput::FxScale:
Set(*input->location, float(r.fx_scale));
break;
case Core::ShaderInput::InverseBufferSize:
{
tVector2 <uint> d = r.GetOutputDimensions();
float v[] = { 1.f / d.x, 1.f / d.y };
Set(*input->location, v, 2);
}
break;
case Core::ShaderInput::InverseViewportSize:
{
float v[] = { 1.f / r.GetViewport().GetWidth(), 1.f / r.GetViewport().GetHeight() };
Set(*input->location, v, 2);
}
break;
case Core::ShaderInput::ViewDepthOffset:
{
float k = 0.f;
Set(*input->location, &k);
}
break;
case Core::ShaderInput::AmbientColor:
{
Color ambient = r.environment_interface->GetAmbientColor();
Set(*input->location, &ambient.x, 3);
}
break;
case Core::ShaderInput::FogColor:
Set(*input->location, &fog_color.x, 3);
break;
case Core::ShaderInput::FogNear:
Set(*input->location, fog_near);
break;
case Core::ShaderInput::FogFar:
Set(*input->location, fog_far);
break;
case Core::ShaderInput::FogInverseRange:
{
bool use_fog = fog_enabled && (fog_far > 0.0);
if (m && (m->renderword & Core::Material::Render_NoFog))
use_fog = false;
Set(*input->location, use_fog ? 1.f / (fog_far - fog_near) : -1.f);
}
break;
case Core::ShaderInput::DepthBuffer:
if (r.render_technique == Renderer::TechniqueDeferred)
Set(*input->location, *r.t_gbuffer[0], input->index);
else Set(*input->location, *r.t_depth, input->index);
break;
case Core::ShaderInput::FrameBuffer:
if (r.t_fx[0].IsValid())
Set(*input->location, *r.t_fx[0], input->index);
break;
case Core::ShaderInput::GBuffer0:
case Core::ShaderInput::GBuffer1:
case Core::ShaderInput::GBuffer2:
case Core::ShaderInput::GBuffer3:
Set(*input->location, *r.t_gbuffer[input->semantic - Core::ShaderInput::GBuffer0], input->index);
break;
case Core::ShaderInput::NoiseMap:
if (r.t_noise.IsValid())
Set(*input->location, *r.t_noise, input->index);
break;
}
}
}
void Shader::SetTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_m, const Matrix4 &v_im, const Matrix4 *i_m, const Matrix4 *i_im, uint count)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryTransform].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryTransform][n];
switch (input->semantic)
{
case Core::ShaderInput::NormalMatrix:
if (Matrix3 *n_m = (Matrix3 *)alloca(sizeof(Matrix3) * count))
{
for (uint n = 0; n < count; ++n)
n_m[n] = Matrix3::FromMatrix4(i_m[n]).Normalized();
Set(*input->location, n_m, count);
}
break;
case Core::ShaderInput::NormalViewMatrix:
if (Matrix3 *nv_m = (Matrix3 *)alloca(sizeof(Matrix3) * count))
{
Matrix3 vn_m = Matrix3::FromMatrix4(v_m).Normalized().Transposed();
for (uint n = 0; n < count; ++n)
nv_m[n] = vn_m * Matrix3::FromMatrix4(i_m[n]).Normalized();
Set(*input->location, nv_m, count);
}
break;
case Core::ShaderInput::ModelMatrix:
Set(*input->location, i_m, count);
break;
case Core::ShaderInput::ViewMatrix:
Set(*input->location, v_im);
break;
case Core::ShaderInput::ProjectionMatrix:
Set(*input->location, v_pm);
break;
case Core::ShaderInput::ModelViewMatrix:
if (Matrix4 *mv_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
{
for (uint n = 0; n < count; ++n)
mv_m[n] = v_im * i_m[n];
Set(*input->location, mv_m, count);
}
break;
case Core::ShaderInput::ModelViewProjectionMatrix:
if (Matrix4 *mvp_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
{
for (uint n = 0; n < count; ++n)
mvp_m[n] = v_pm * (v_im * i_m[n]);
Set(*input->location, mvp_m, count);
}
break;
case Core::ShaderInput::InverseViewProjectionMatrix:
{
Matrix4 vpm = v_pm * v_im, ivpm;
vpm.Inverse(ivpm);
Set(*input->location, ivpm);
}
break;
case Core::ShaderInput::InverseViewProjectionMatrixAtOrigin:
{
Matrix4 v_im_o = v_im;
v_im_o.SetRow(3, Vector4(0, 0, 0, 1));
Matrix4 vpm = v_pm * v_im_o, ivpm;
vpm.Inverse(ivpm);
Set(*input->location, ivpm);
}
break;
}
}
}
void Shader::SetPreviousTransformInputs(const Matrix4 &v_pm, const Matrix4 &v_im, const Matrix4 *i_m, uint count)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryPreviousTransform].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryPreviousTransform][n];
switch (input->semantic)
{
case Core::ShaderInput::PreviousModelViewMatrix:
if (Matrix4 *mv_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
{
for (uint n = 0; n < count; ++n)
mv_m[n] = v_im * i_m[n];
Set(*input->location, mv_m, count);
}
break;
case Core::ShaderInput::PreviousModelViewProjectionMatrix:
if (Matrix4 *mvp_m = (Matrix4 *)alloca(sizeof(Matrix4) * count))
{
for (uint n = 0; n < count; ++n)
mvp_m[n] = v_pm * (v_im * i_m[n]);
Set(*input->location, mvp_m, count);
}
break;
}
}
}
void Shader::SetMaterialOpacityInputs(Material &m, float opacity)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryMaterialOpacity].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryMaterialOpacity][n];
switch (input->semantic)
{
case Core::ShaderInput::MaterialOpacity:
Set(*input->location, m.opacity * opacity);
break;
}
}
}
void Shader::SetMaterialInputs(Material &m)
{
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryMaterial].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryMaterial][n];
switch (input->semantic)
{
case Core::ShaderInput::MaterialDiffuse:
Set(*input->location, &m.diffuse.x, 4);
break;
case Core::ShaderInput::MaterialSpecular:
Set(*input->location, &m.specular.x, 4);
break;
case Core::ShaderInput::MaterialAmbient:
Set(*input->location, &m.ambient.x, 4);
break;
case Core::ShaderInput::MaterialSelf:
Set(*input->location, &m.self.x, 4);
break;
case Core::ShaderInput::MaterialGlossiness:
Set(*input->location, m.glossiness);
break;
case Core::ShaderInput::MaterialReflection:
Set(*input->location, m.reflection);
break;
case Core::ShaderInput::MaterialAlphaThreshold:
Set(*input->location, m.athreshold);
break;
case Core::ShaderInput::MaterialDepthBias:
Set(*input->location, m.depth_bias);
break;
case Core::ShaderInput::MaterialTexture0:
case Core::ShaderInput::MaterialTexture1:
case Core::ShaderInput::MaterialTexture2:
case Core::ShaderInput::MaterialTexture3:
case Core::ShaderInput::MaterialTexture4:
case Core::ShaderInput::MaterialTexture5:
case Core::ShaderInput::MaterialTexture6:
case Core::ShaderInput::MaterialTexture7:
if (Render::Texture *t = m.texture_table[input->semantic - Core::ShaderInput::MaterialTexture0])
Set(*input->location, *t, input->index);
break;
}
}
}
void Shader::SetLightInputs(Renderer &r, Core::Camera &view_item, Core::Light &l)
{
float k_clip_fade = 1.f;
if (l.range > 0.f) // [EJ] fade on last 10% of clip range
{
float c = l.clip_distance + l.range;
float d = Vector4::Dist(view_item.GetMatrix().GetRow(3), l.GetMatrix().GetRow(3));
k_clip_fade = 1.f - GS::Types::Clamp((d - c * 0.9f) / (c * 0.1f));
}
if (Core::Light::RenderData *light_render_data = (Core::Light::RenderData *)l.render_data.c_ptr())
for (uint n = 0; n < input_list[Core::ShaderInput::CategoryLight].GetCount(); ++n)
{
ShaderInput *input = &input_list[Core::ShaderInput::CategoryLight][n];
switch (input->semantic)
{
case Core::ShaderInput::LightRange:
Set(*input->location, l.range);
break;
case Core::ShaderInput::LightSpotEdge:
Set(*input->location, Math::Cos(l.edge_angle + l.cone_angle));
break;
case Core::ShaderInput::LightSpotCone:
Set(*input->location, Math::Cos(l.cone_angle));
break;
case Core::ShaderInput::LightShadowBias:
Set(*input->location, l.shadow_bias);
break;
case Core::ShaderInput::LightDiffuseColor:
{
Color c = l.diffuse_color * l.diffuse_intensity * k_clip_fade;
Set(*input->location, &c.x, 3);
}
break;
case Core::ShaderInput::LightSpecularColor:
{
Color c = l.specular_color * l.specular_intensity * k_clip_fade;
Set(*input->location, &c.x, 3);
}
break;
case Core::ShaderInput::LightShadowColor:
Set(*input->location, &l.shadow_color.x, 3);
break;
case Core::ShaderInput::LightViewPosition:
{
Vector4 p = l.GetMatrix().GetRow(3) * view_item.GetInverseMatrix();
Set(*input->location, &p.x, 3);
}
break;
case Core::ShaderInput::LightViewDirection:
{
Vector4 d = l.GetMatrix().GetRow(2) * Matrix3::FromMatrix4(view_item.GetMatrix()).Normalized().Transposed();
Set(*input->location, &d.x, 3);
}
break;
case Core::ShaderInput::LightShadowMatrix0:
case Core::ShaderInput::LightShadowMatrix1:
case Core::ShaderInput::LightShadowMatrix2:
case Core::ShaderInput::LightShadowMatrix3:
case Core::ShaderInput::LightShadowMatrix4:
case Core::ShaderInput::LightShadowMatrix5:
{
uint n = input->semantic - Core::ShaderInput::LightShadowMatrix0;
if (n < light_render_data->shadow_data.GetCount())
Set(*input->location, light_render_data->shadow_data[n].pmatrix * (light_render_data->shadow_data[n].imatrix * view_item.GetMatrix()));
}
break;
case Core::ShaderInput::InverseShadowMapSize:
{
float k = r.pcf_radius / r.gpu_config.shadow_size;
Set(*input->location, k);
}
break;
case Core::ShaderInput::LightShadowMap0:
case Core::ShaderInput::LightShadowMap1:
case Core::ShaderInput::LightShadowMap2:
case Core::ShaderInput::LightShadowMap3:
case Core::ShaderInput::LightShadowMap4:
case Core::ShaderInput::LightShadowMap5:
Set(*input->location, *r.shadow_map, input->index);
break;
case Core::ShaderInput::LightPSSMSliceDistance0:
case Core::ShaderInput::LightPSSMSliceDistance1:
case Core::ShaderInput::LightPSSMSliceDistance2:
case Core::ShaderInput::LightPSSMSliceDistance3:
if (light_render_data->shadow_data)
Set(*input->location, light_render_data->shadow_data[input->semantic - Core::ShaderInput::LightPSSMSliceDistance0].slice_distance);
break;
case Core::ShaderInput::ViewToLightMatrix:
Set(*input->location, l.GetInverseMatrix() * view_item.GetMatrix());
break;
case Core::ShaderInput::LightProjectionMap:
if (light_render_data->projection_texture.IsValid())
Set(*input->location, *light_render_data->projection_texture, input->index);
break;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uint Shader::GetSemanticInputList(Core::ShaderInput::Semantic semantic)
{
return Core::ShaderInput::semantic_desc[semantic].category;
}
ShaderInput *Shader::GetInput(Core::ShaderInput::Semantic semantic) const
{
uint cat = GetSemanticInputList(semantic);
for (uint n = 0; n < input_list[cat].GetCount(); ++n)
if (input_list[cat][n].semantic == semantic)
return &input_list[cat][n];
return NULL;
}
ShaderInput *Shader::GetInput(const char *n) const
{
String name(n);
for (uint l = 0; l < Core::ShaderInput::CategoryLast; ++l) // need to check all categories here
for (uint n = 0; n < input_list[l].GetCount(); ++n)
if (!input_list[l][n].name.IsEmpty() && (input_list[l][n].name == name))
return &input_list[l][n];
return NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Shader::Create(Render::ResourceFactory &rf, const Core::Shader &shader)
{
Free();
__RASSERT_MSG__(renderer.shader_compiler != NULL, String::Format("No shader compiler available for this renderer ('%s').", renderer.GetName()));
if (!renderer.shader_compiler->Compile(shader, *this))
return false;
// Solve uniforms and attributes.
Array <AutoPtr <ShaderInputLocation> > locations(shader.input_list.GetCount());
uint solved_count[Core::ShaderInput::CategoryLast], n = 0;
Memory::Set(solved_count, 0, sizeof(uint) * Core::ShaderInput::CategoryLast);
ListForeachPtr(Core::ShaderInput *, input, shader.input_list)
{
uint input_index = GetSemanticInputList(input->semantic);
locations[n] = NewGPUShaderLocation();
if (GetLocation(input->name, *locations[n], input->type))
solved_count[input_index]++;
else
locations[n] = NULL;
++n;
}
uint texture_count = 0;
for (uint l = 0; l < Core::ShaderInput::CategoryLast; ++l)
{
uint n = 0, i = 0;
if (input_list[l].Allocate(solved_count[l]))
ListForeachPtr(Core::ShaderInput *, input, shader.input_list)
{
if ((l != GetSemanticInputList(input->semantic)) || locations[i].IsNull())
{
++i;
continue;
}
ShaderInput *gpu_input = &input_list[l][n];
gpu_input->location = locations[i].Detach();
gpu_input->Set(input);
// Allocate texture unit index and load render resource.
if (input->type == Core::ShaderInput::Uniform)
if (input->ConsumesTextureUnit())
{
if (!input->parm_t.IsEmpty())
gpu_input->parm_t = rf.LoadTexture(input->parm_t);
gpu_input->index = texture_count++;
}
++i; ++n;
}
}
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,65 @@
/* -----------------------------------------------------------------------------
nEngine - GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_shader_compiler.h"
#include "gpu/gpu_renderer.h"
#include "log/file_log.h"
#include "log/log.h"
using namespace GS;
using namespace GPU;
//------------------------------------------------------------------------------
bool IShaderCompiler::Compile(const Core::Shader &shader, Shader &gpu_shader, const char *binary_cache_id)
{
if (!shader.name.IsEmpty())
__LOG_V__ << "Creating shader '" << shader.name << "'...\n";
gpu_shader.name = shader.name;
// Compile shader objects.
String vertex_source, pixel_source;
if (!renderer.TranslateShader(shader, vertex_source, pixel_source))
return false;
gpu_shader.vertex = renderer.NewGPUShaderObject();
gpu_shader.pixel = renderer.NewGPUShaderObject();
if (gpu_shader.vertex.IsNull() || gpu_shader.pixel.IsNull())
return false;
String vertex_error, pixel_error;
if (
!CompileObject(vertex_source, *gpu_shader.vertex, ShaderObject::Vertex, shader.name, &vertex_error) ||
!CompileObject(pixel_source, *gpu_shader.pixel, ShaderObject::Pixel, shader.name, &pixel_error)
)
{
__LOG_E__ << "Shader '" << shader.name << "' failed to compile.\n";
//#ifdef _DEBUG
FileLog file_log("c:/shader_error.log");
file_log << "--------------------------------------------------------------------------------\n";
file_log << "Failed to compile shader '" << shader.name << "'.\n\n";
file_log << "Vertex error:" << vertex_error << "\n\n";
file_log << "Pixel error:" << pixel_error << "\n\n";
file_log << "--------------------------------------------------------------------------------\n";
file_log << "\n";
file_log << "VERTEX:\n";
file_log << "\n";
file_log << vertex_source.NormalizedEOL(String::EOLUnix);
file_log << "\n";
file_log << "PIXEL:\n";
file_log << "\n";
file_log << pixel_source.NormalizedEOL(String::EOLUnix);
file_log << "\n";
file_log << "--------------------------------------------------------------------------------\n";
file_log << "\n";
//#endif
return false;
}
return Link(shader, gpu_shader);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,376 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "gpu/gpu_renderer.h"
#include "core/light.h"
#include "log/log.h"
using namespace GS::Core;
using namespace GS::GPU;
struct UVOffset
{ float x, y; };
//------------------------------------------------------------------------------
void Renderer::CreateShadowMaps()
{
gpu_config.enable_shadow = registry.GetBool("ShadowMapping:Enable", true);
gpu_config.shadow_size = registry.GetInteger("ShadowMapping:Size", 1024);
__LOG__ << "Creating shadow maps (" << gpu_config.shadow_size << "x" << gpu_config.shadow_size << ").\n";
if (gpu_config.enable_shadow)
{
shadow_map = NewTexture("shadow_map");
shadow_map->Create(NULL, gpu_config.shadow_size, gpu_config.shadow_size, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
shadow_map->ConfigureAsShadowMap();
shadow_map_fbo->SetDepthTexture(shadow_map);
}
}
void Renderer::FreeShadowMaps()
{
shadow_map = NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
struct ShadowRenderData : public Light::RenderData::ShadowRenderData
{
Renderer::ShadowMapSplit split;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static float ComputeSplitZBoundary(const Camera &lod_view, const Light &light, int index, int count)
{
if (index <= 0)
return lod_view.GetNearClippingPlane();
float k = float(index) / count;
float z_log = lod_view.GetNearClippingPlane() * powf(light.shadow_range / lod_view.GetNearClippingPlane(), k);
float z_lin = lod_view.GetNearClippingPlane() + (light.shadow_range - lod_view.GetNearClippingPlane()) * k;
return z_log * light.shadow_distribution + z_lin * (1.f - light.shadow_distribution);
}
static void FitViewToFrustum(Camera &view, const GS::Frustum &slice, float d)
{
using namespace GS;
/*
Compute split view item properties so that the split view frustum act as
a perfect fit for the split frustum.
*/
const Vector4 *fv = slice.GetVertices();
Vector4 vfv[8];
view.GetInverseMatrix().Apply(vfv, fv, 8);
const Matrix4 &m = view.GetMatrix();
Vector4 mm[2];
mm[0] = mm[1] = vfv[0];
for (int n = 1; n < 8; ++n)
{
mm[0] = Vector4::Minimum(mm[0], vfv[n]);
mm[1] = Vector4::Maximum(mm[1], vfv[n]);
}
Vector4 focus = ((mm[0] + mm[1]) * 0.5) * m;
// Position the shadow map light item,
view.SetPosition(focus - m.GetRow(2) * d);
// View clipping planes.
Vector4 view_front = m.GetRow(2);
view.z_far = view_front.Dot(fv[0] - view.GetPosition());
for (int n = 1; n < 8; ++n)
view.z_far = GS::Types::Max(view.z_far, view_front.Dot(fv[n] - view.GetPosition()));
// View dimensions.
Vector4 view_left = view.GetMatrix().GetRow(0),
view_top = view.GetMatrix().GetRow(1);
Vector4 dt = fv[0] - view.GetPosition();
float h_width = std::fabs(view_left.Dot(dt)),
h_height = std::fabs(view_top.Dot(dt));
for (int n = 1; n < 8; ++n)
{
dt = fv[n] - view.GetPosition();
h_width = GS::Types::Max <float> (h_width, std::fabs(view_left.Dot(dt)));
h_height = GS::Types::Max <float> (h_height, std::fabs(view_top.Dot(dt)));
}
view.ortho_w = h_width * 2.f;
view.ortho_h = h_height * 2.f;
}
bool Renderer::LightPreparePSSM(const Light &l, bool build_dlist) const
{
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
if (!light_render_data || !light_render_data->shadow_data.Allocate(4))
return false;
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
for (int n = 0; n < split_count; ++n)
{
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
if (data.render_data.IsNull())
data.render_data = new ShadowRenderData;
ShadowRenderData *pssm_data = (ShadowRenderData *)data.render_data.c_ptr();
ShadowMapSplit &split = pssm_data->split;
static UVOffset split_offset[] = { { 0.0, 0.0 }, { 0.5, 0.0 }, { 0.0, 0.5 }, { 0.5, 0.5 } };
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 2.f, gpu_config.shadow_size / 2.f);
// Compute frustum slice.
Frustum slice;
float znear = ComputeSplitZBoundary(*view_item, l, n, split_count), zfar = ComputeSplitZBoundary(*view_item, l, n + 1, split_count);
view_item->ComputeFrustum(slice, GetViewport(), znear, zfar);
// Adjust view item.
split.view.is_orthographic = true;
split.view.aspect_ratio = 1;
split.view.SnapshotTransformation(l.GetMatrix());
FitViewToFrustum(split.view, slice, l.shadow_range * 4.f);
split.view.ComputeFrustum(split.view.frustum, split.rect);
// Cull.
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store slice view to light matrix.
data.slice_distance = zfar;
data.imatrix = split.view.GetInverseMatrix();
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix);
if (!gpu_config.tex_origin_is_top_left)
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data.pmatrix;
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
// Crop on PSSM region.
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.0)) * Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 1.0)) * data.pmatrix;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderPSSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
int split_count = registry.GetInteger("ShadowMapping:PSSM:Split", 3);
for (int n = 0; n < split_count; ++n)
{
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
SetViewport(shd_data->split.rect);
SetClippingRect(&shd_data->split.rect);
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
view_item = &shd_data->split.view;
ApplyCamera();
DrawList(shd_data->split.list.display_lists[0], dc);
DrawList(shd_data->split.list.display_lists[2], dc);
shd_data->split.list.Clear(false); // limit dynamic allocations
}
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::LightPreparePSM(const Light &l, bool build_dlist) const
{
Light::RenderData *light_render_data = (Light::RenderData *)l.render_data.c_ptr();
if (!light_render_data || !light_render_data->shadow_data.Allocate(6))
return false;
for (uint n = 0; n < 6; ++n)
{
Light::RenderData::ShadowData &data = light_render_data->shadow_data[n];
if (data.render_data.IsNull())
data.render_data = new ShadowRenderData;
ShadowRenderData *shd_data = (ShadowRenderData *)data.render_data.c_ptr();
ShadowMapSplit &split = shd_data->split;
static Vector4 view_angle[6] = { Vector4(0, 0, 0), Vector4(0, Units::Deg(90.f), 0), Vector4(0, Units::Deg(180.f), 0), Vector4(0, Units::Deg(270.f), 0), Vector4(Units::Deg(90.f), 0, 0), Vector4(Units::Deg(270.f), 0, 0) };
static UVOffset split_offset[] = { { 0, 0 }, { 0.33f, 0 }, { 0.66f, 0 }, { 0, 0.5f }, { 0.33f, 0.5f }, { 0.66f, 0.5f } };
split.view.aspect_ratio = 1;
split.view.SetFov(Units::Deg(95.f)); // Add a 5<> safe area to avoid artifacts at transition.
split.view.SnapshotTransformation(l.GetMatrix() * Matrix4::FromMatrix3(Matrix3::FromEuler(view_angle[n])));
split.rect = fRect::FromWidthHeight(split_offset[n].x * gpu_config.shadow_size, split_offset[n].y * gpu_config.shadow_size, gpu_config.shadow_size / 3.f, gpu_config.shadow_size / 2.f);
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store cube view to light matrix.
data.imatrix = split.view.GetInverseMatrix();
split.view.ComputeProjectionMatrix(split.rect, data.pmatrix); // FIXME watch out, do we need the split viewport (split.rect) or the complete viewport???
if (!gpu_config.tex_origin_is_top_left)
data.pmatrix = Matrix4::ScaleMatrix(Vector4(1, -1, 1)) * data.pmatrix;
data.pmatrix = Matrix4::ScaleMatrix(Vector4(0.5f, 0.5f, 0.5f)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data.pmatrix;
// Crop on view region.
data.pmatrix = Matrix4::TranslationMatrix(Vector4(split_offset[n].x, split_offset[n].y, 0.f)) * Matrix4::ScaleMatrix(Vector4(0.33f, 0.5f, 1)) * data.pmatrix;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderPSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
for (uint n = 0; n < 6; ++n)
{
ShadowRenderData *psm_data = (ShadowRenderData *)data->shadow_data[n].render_data.c_ptr();
SetViewport(psm_data->split.rect);
SetClippingRect(&psm_data->split.rect);
view_item = &psm_data->split.view;
ApplyCamera();
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
DrawList(psm_data->split.list.display_lists[0], dc);
DrawList(psm_data->split.list.display_lists[2], dc);
psm_data->split.list.Clear(false); // limit dynamic allocations
}
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::LightPrepareSSM(const Light &l, bool build_dlist) const
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
if (!data || !data->shadow_data.Allocate(1))
return false;
if (data->shadow_data[0].render_data.IsNull())
data->shadow_data[0].render_data = new ShadowRenderData;
ShadowRenderData *shd_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
Renderer::ShadowMapSplit &split = shd_data->split;
split.view.AlignTo(l);
if (build_dlist)
{
BuildRenderablePrimitiveList(split.view, *view_item, split.list.primitive_list, Renderable::Context_Shadow);
BuildDisplayLists(split.list.primitive_list, split.list.display_lists);
}
// Store shadow matrices.
data->shadow_data[0].imatrix = split.view.GetInverseMatrix();
l.ComputeProjectionMatrix(data->shadow_data[0].pmatrix);
if (!gpu_config.tex_origin_is_top_left)
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(1.0, -1.0, 1.0)) * data->shadow_data[0].pmatrix;
data->shadow_data[0].pmatrix = Matrix4::ScaleMatrix(Vector4(0.5, 0.5, 0.5)) * Matrix4::TranslationMatrix(Vector4(1, 1, 1)) * data->shadow_data[0].pmatrix;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::LightRenderSSM(Light &l)
{
Light::RenderData *data = (Light::RenderData *)l.render_data.c_ptr();
ViewConfig view = BackupView();
SetViewport(fRect(0, 0, (float)gpu_config.shadow_size, (float)gpu_config.shadow_size));
Clear(0, 0, 0, 0, 1, ClearDepth);
ShadowRenderData *render_data = (ShadowRenderData *)data->shadow_data[0].render_data.c_ptr();
view_item = &render_data->split.view;
ApplyCamera();
DrawContext dc(DrawContext::Opaque, DrawContext::Base, MaterialShader::Depth);
DrawList(render_data->split.list.display_lists[0], dc);
DrawList(render_data->split.list.display_lists[2], dc);
render_data->split.list.Clear(false); // limit dynamic allocations
RestoreView(view);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Renderer::ViewConfig Renderer::BackupView() const
{
return ViewConfig(view_item, viewport);
}
void Renderer::RestoreViewport(const ViewConfig &config)
{
SetViewport(config.viewport);
}
void Renderer::RestoreView(const ViewConfig &config)
{
RestoreViewport(config);
view_item = config.camera;
ApplyCamera();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::PrepareShadowMap(const Light &l, bool build_dlist) const
{
switch (l.model)
{
case Light::Model_Spot: return LightPrepareSSM(l, build_dlist);
case Light::Model_Point: return LightPreparePSM(l, build_dlist);
case Light::Model_Linear: return LightPreparePSSM(l, build_dlist);
}
return false;
}
void Renderer::RenderShadowMap(Light &l)
{
PerfBeginEvent(__FUNCTION__, Color::Blue);
SetCurrentFBO(shadow_map_fbo);
SetCullFunc(CullBack);
fRect old_clipping = GetClippingRect();
SetClippingRect(NULL);
switch (l.model)
{
case Light::Model_Spot: LightRenderSSM(l); break;
case Light::Model_Point: LightRenderPSM(l); break;
case Light::Model_Linear: LightRenderPSSM(l); break;
}
SetClippingRect(&old_clipping);
SetCullFunc(CullFront);
SetCurrentFBO(NULL);
PerfEndEvent();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,48 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "core/camera.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::DrawSkybox(GS::Render::sTexture t[2], GS::Render::Shader *s)
{
static const size_t stride = sizeof(float) * 3;
SetDepthFunc(DepthEqual); // only draw on Z = 1
EnableDepthWrite(false);
Shader &p = s ? (Shader &)*s : *skybox_program;
SetShaderProgram(&p);
SetIndexSource(skybox_idx_vbo);
SetVertexSource(skybox_vtx_vbo, stride);
ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position),
*layer0_parm = p.GetInput("u_layer0"),
*layer1_parm = p.GetInput("u_layer1");
p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, (const void *)0);
if (layer0_parm && t[0].IsValid())
p.Set(*layer0_parm->location, (Render::Texture &)*t[0], layer0_parm->index);
if (layer1_parm && t[1].IsValid())
p.Set(*layer1_parm->location, (Render::Texture &)*t[1], layer1_parm->index);
p.SetRendererInputs(*this);
p.SetTransformInputs(m_projection, m_view, m_iview, &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
p.CommitInputs();
DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);
p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);
EnableDepthWrite(true);
SetDepthFunc(DepthLess);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,539 @@
#include "gpu/gpu_types.h"
#include "gpu/gpu_renderer.h"
#include "log/log.h"
#include "math/nmath.h"
#include "memory/memory.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
Render::Texture *Renderer::CreateRenderTexture(
const char *name,
uint width,
uint height,
Render::Texture::Format format,
Render::Texture::AA aa
)
{
__NTRACE("CreateRenderTexture")
// __LOG_W__ << "CreateRenderTexture: name=" << name << ", size=" << width << "x" << height << "\n";
Render::Texture *tex = NewTexture(name);
if (!tex)
{
__LOG_E__ << "CreateRenderTexture: NewTexture failed!\n";
return nullptr;
}
// __LOG_W__ << "CreateRenderTexture: NewTexture succeeded, tex ptr=" << (void*)tex << "\n";
const Render::Texture::Usage usage =
Render::Texture::Usage(
Render::Texture::IsRenderTarget |
Render::Texture::IsShaderResource
);
// __LOG_W__ << "CreateRenderTexture: Calling tex->Create with format=" << format << ", aa=" << aa << "\n";
if (tex->Create(nullptr, width, height, format, aa, usage))
{
// __LOG_W__ << "CreateRenderTexture: Success! Returning texture.\n";
return tex;
}
__LOG_E__ << "CreateRenderTexture: tex->Create failed!\n";
tex->Free();
return nullptr;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::ClearTexture(Render::Texture *target, const Color &clear_color)
{
__NTRACE("ClearTexture")
// __LOG_W__ << "ClearTexture: Clearing target texture with color (" << clear_color.x << ", " << clear_color.y << ", " << clear_color.z << ", " << clear_color.w << ")\n";
if (!target)
{
__LOG_E__ << "ClearTexture: target is null!\n";
return;
}
const uint tex_w = target->GetWidth();
const uint tex_h = target->GetHeight();
// Use DrawSplineToTexture with clear_target=true to clear the texture
// This is a workaround since Blit() doesn't work on RenderTarget textures
// __LOG_W__ << "ClearTexture: Using DrawSplineToTexture method with clear...\n";
// Create a dummy 2-point array (not used since we're clearing)
Array<Vector2> dummy_points;
dummy_points.Allocate(2);
dummy_points[0] = Vector2(0, 0);
dummy_points[1] = Vector2(1, 1);
// Call DrawSplineToTexture with clear_target=true and zero width (no drawing)
// This will fill the buffer with the clear color
const uint pitch = tex_w * 4;
const size_t buffer_size = pitch * tex_h;
unsigned char *pixels = new unsigned char[buffer_size];
// Convert color to 8-bit RGBA
const auto r = (unsigned char)(clear_color.x * 255.0f);
const auto g = (unsigned char)(clear_color.y * 255.0f);
const auto b = (unsigned char)(clear_color.z * 255.0f);
const auto a = (unsigned char)(clear_color.w * 255.0f);
// __LOG_W__ << "ClearTexture: Filling buffer with RGBA(" << (int)r << ", " << (int)g << ", " << (int)b << ", " << (int)a << ")...\n";
// Fill with clear color (ABGR format)
unsigned int pixel_value = (a << 24) | (b << 16) | (g << 8) | r;
unsigned int *pixel_buffer = (unsigned int *)pixels;
for (uint i = 0; i < tex_w * tex_h; ++i)
{
pixel_buffer[i] = pixel_value;
}
// __LOG_W__ << "ClearTexture: Uploading via Blit...\n";
target->Blit((const char *)pixels, tex_w, tex_h, 0, 0, Render::Texture::FormatRGBA8);
delete[] pixels;
// __LOG_W__ << "ClearTexture: Complete!\n";
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::BlitTextureToTexture(
Render::Texture *src,
Render::Texture *dst,
const fRect *src_rect,
const fRect *dst_rect
)
{
__NTRACE("BlitTextureToTexture")
if (!src || !dst)
return;
// Default rectangles (full texture)
const fRect src_r = src_rect ? *src_rect : fRect(0, 0, 1, 1);
const fRect dst_r = dst_rect ? *dst_rect : fRect(0, 0, 1, 1);
// Save current state
const fRect old_viewport = viewport;
// Setup FBO for destination
fx_fbo->SetColorTexture(dst);
fx_fbo->SetDepthTexture(nullptr);
SetCurrentFBO(fx_fbo);
SetViewport(fRect(0, 0, (float)dst->GetWidth(), (float)dst->GetHeight()));
// Use existing RenderFullscreenQuad infrastructure
RenderFullscreenQuad(*single_texture_program, src_r, dst_r, src);
// Restore state
SetCurrentFBO(nullptr);
SetViewport(old_viewport);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector2 Renderer::CatmullRomInterpolate(
const Vector2 &p0,
const Vector2 &p1,
const Vector2 &p2,
const Vector2 &p3,
float t
) const
{
// Catmull-Rom spline with tau = 0.5
const float t2 = t * t;
const float t3 = t2 * t;
// Manual calculation for Vector2
Vector2 result;
result.x = 0.5f * (
(2.0f * p1.x) +
(-p0.x + p2.x) * t +
(2.0f * p0.x - 5.0f * p1.x + 4.0f * p2.x - p3.x) * t2 +
(-p0.x + 3.0f * p1.x - 3.0f * p2.x + p3.x) * t3
);
result.y = 0.5f * (
(2.0f * p1.y) +
(-p0.y + p2.y) * t +
(2.0f * p0.y - 5.0f * p1.y + 4.0f * p2.y - p3.y) * t2 +
(-p0.y + 3.0f * p1.y - 3.0f * p2.y + p3.y) * t3
);
return result;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::TessellateSplineRibbon(
const Array<Vector2> &control_points,
float width_normalized,
const Color &color,
int samples_per_segment,
bool soft_edge,
SplineRibbon &out_ribbon
)
{
__NTRACE("TessellateSplineRibbon")
const int num_control = control_points.GetCount();
if (num_control < 2)
return;
// For Catmull-Rom: we have num_control-1 segments
const int num_segments = num_control - 1;
if (num_segments < 1)
return;
// Allocate arrays for sampled points
const int total_samples = num_segments * samples_per_segment + 1;
Array<Vector2> samples;
Array<Vector2> tangents;
samples.Allocate(total_samples);
tangents.Allocate(total_samples);
// Sample the spline using Catmull-Rom interpolation
int sample_idx = 0;
for (int seg = 0; seg < num_segments; seg++)
{
// Get 4 control points (with duplication at boundaries)
Vector2 p0 = (seg > 0) ? control_points[seg - 1] : control_points[seg];
Vector2 p1 = control_points[seg];
Vector2 p2 = control_points[seg + 1];
Vector2 p3 = (seg < num_segments - 1) ? control_points[seg + 2] : control_points[seg + 1];
for (int s = 0; s < samples_per_segment; s++)
{
const float t = (float)s / (float)samples_per_segment;
samples[sample_idx] = CatmullRomInterpolate(p0, p1, p2, p3, t);
// Compute tangent (derivative approximation)
const float dt = 0.01f;
const float t_next = (t + dt > 1.0f) ? 1.0f : t + dt;
Vector2 p_next = CatmullRomInterpolate(p0, p1, p2, p3, t_next);
Vector2 diff = p_next - samples[sample_idx];
tangents[sample_idx] = diff.Normalized();
sample_idx++;
}
}
// Add final point
samples[total_samples - 1] = control_points[num_control - 1];
tangents[total_samples - 1] = tangents[total_samples - 2]; // Reuse last tangent
// Build ribbon geometry
out_ribbon.positions.Allocate(total_samples * 2);
out_ribbon.colors.Allocate(total_samples * 2);
for (int i = 0; i < total_samples; i++)
{
const Vector2 pos = samples[i];
const Vector2 tangent = tangents[i];
// Perpendicular normal (2D rotation by 90 degrees)
Vector2 normal(-tangent.y, tangent.x);
// Convert from normalized [0..1] texture space to clip space [-1..1]
const float x_clip = pos.x * 2.0f - 1.0f;
const float y_clip = 1.0f - pos.y * 2.0f; // Flip Y (texture origin top-left)
// Offset in clip space
const Vector2 offset = normal * width_normalized;
// Left and right vertices
out_ribbon.positions[i * 2 + 0] = Vector4(
x_clip - offset.x,
y_clip - offset.y,
0.0f,
1.0f
);
out_ribbon.positions[i * 2 + 1] = Vector4(
x_clip + offset.x,
y_clip + offset.y,
0.0f,
1.0f
);
// Colors with soft edge via alpha gradient
if (soft_edge)
{
// Apply alpha gradient on outer edges (30% opacity) vs center (full opacity)
// But since we're using a ribbon with 2 vertices per sample, both get same alpha
// To get true soft edge, we'd need a center vertex too, but for now just use the color as-is
// TODO: Implement true soft edge with centerline vertex if needed
out_ribbon.colors[i * 2 + 0] = color;
out_ribbon.colors[i * 2 + 1] = color;
}
else
{
out_ribbon.colors[i * 2 + 0] = color;
out_ribbon.colors[i * 2 + 1] = color;
}
}
// Build indices (triangle list)
const int num_quads = total_samples - 1;
out_ribbon.indices.Allocate(num_quads * 6);
for (int i = 0; i < num_quads; i++)
{
const ushort i0 = (ushort)(i * 2);
const ushort i1 = (ushort)(i * 2 + 1);
const ushort i2 = (ushort)((i + 1) * 2);
const ushort i3 = (ushort)((i + 1) * 2 + 1);
// Triangle 1
out_ribbon.indices[i * 6 + 0] = i0;
out_ribbon.indices[i * 6 + 1] = i2;
out_ribbon.indices[i * 6 + 2] = i1;
// Triangle 2
out_ribbon.indices[i * 6 + 3] = i1;
out_ribbon.indices[i * 6 + 4] = i2;
out_ribbon.indices[i * 6 + 5] = i3;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Helper: Draw a simple line to texture pixel data (Bresenham-like)
static void DrawLineToPixels(unsigned char *pixels, uint width, uint height, uint pitch,
int x0, int y0, int x1, int y1,
unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
const int dx = abs(x1 - x0);
const int dy = abs(y1 - y0);
const int sx = (x0 < x1) ? 1 : -1;
const int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
int x = x0, y = y0;
while (true)
{
if (x >= 0 && x < (int)width && y >= 0 && y < (int)height)
{
int *pixel = (int*)(pixels + y * pitch + x * 4);
// Read destination pixel components (packed as in existing code: ABGR)
const unsigned int dst = *pixel;
const unsigned char dst_r = (unsigned char)(dst & 0xFF);
const unsigned char dst_g = (unsigned char)((dst >> 8) & 0xFF);
const unsigned char dst_b = (unsigned char)((dst >> 16) & 0xFF);
const unsigned char dst_a = (unsigned char)((dst >> 24) & 0xFF);
// Source alpha (0..1)
const float src_af = (float)a / 255.0f;
const float inv = 1.0f - src_af;
// Standard 'over' compositing: out = src*src_a + dst*(1-src_a)
const unsigned char out_r = (unsigned char)(r * src_af + dst_r * inv + 0.5f);
const unsigned char out_g = (unsigned char)(g * src_af + dst_g * inv + 0.5f);
const unsigned char out_b = (unsigned char)(b * src_af + dst_b * inv + 0.5f);
const unsigned char out_a = (unsigned char)(a + dst_a * inv + 0.5f);
*pixel = (out_a << 24) | (out_b << 16) | (out_g << 8) | out_r; // ABGR packing (kept as original)
}
if (x == x1 && y == y1) break;
const int e2 = 2 * err;
if (e2 > -dy) { err -= dy; x += sx; }
if (e2 < dx) { err += dx; y += sy; }
}
}
void Renderer::DrawSplineToTexture(
Render::Texture *target,
const Array<Vector2> &control_points,
float width_pixels,
const Color &color,
int samples_per_segment,
bool clear_target,
bool soft_edge,
const Color *border_color,
float border_width,
float margin
)
{
__NTRACE("DrawSplineToTexture")
// __LOG_W__ << "Drawing spline to texture (CPU-based method)...\n";
if (!target) {
__LOG_E__ << "DrawSplineToTexture: target texture is null!\n";
return;
}
if (control_points.GetCount() < 2) {
__LOG_E__ << "DrawSplineToTexture: not enough control points!\n";
return;
}
const uint tex_w = target->GetWidth();
const uint tex_h = target->GetHeight();
// __LOG_W__ << "Target texture size: " << tex_w << "x" << tex_h << "\n";
// Convert color to 8-bit RGBA
const auto r = (unsigned char)(color.x * 255.0f);
const auto g = (unsigned char)(color.y * 255.0f);
const auto b = (unsigned char)(color.z * 255.0f);
const auto a = (unsigned char)(color.w * 255.0f);
// __LOG_W__ << "Color: R=" << (int)r << " G=" << (int)g << " B=" << (int)b << " A=" << (int)a << "\n";
// Convert border color if provided
unsigned char border_r = 0, border_g = 0, border_b = 0, border_a = 0;
if (border_color && border_width > 0.0f) {
border_r = (unsigned char)(border_color->x * 255.0f);
border_g = (unsigned char)(border_color->y * 255.0f);
border_b = (unsigned char)(border_color->z * 255.0f);
border_a = (unsigned char)(border_color->w * 255.0f);
// __LOG_W__ << "Border Color: R=" << (int)border_r << " G=" << (int)border_g << " B=" << (int)border_b << " A=" << (int)border_a << " Width=" << border_width << "\n";
}
const int num_control = control_points.GetCount();
const int num_segments = num_control - 1;
// Adjust control points with margin to avoid clipping
Array<Vector2> adjusted_points;
adjusted_points.Allocate(num_control);
if (margin > 0.0f) {
// __LOG_W__ << "Applying margin: " << margin << "\n";
// Calculate the margin in normalized coordinates [0..1]
const float margin_x = margin / (float)tex_w;
const float margin_y = margin / (float)tex_h;
// Scale and offset control points to fit within margin bounds
float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f;
for (int i = 0; i < num_control; i++) {
if (control_points[i].x < min_x) min_x = control_points[i].x;
if (control_points[i].x > max_x) max_x = control_points[i].x;
if (control_points[i].y < min_y) min_y = control_points[i].y;
if (control_points[i].y > max_y) max_y = control_points[i].y;
}
const float range_x = max_x - min_x;
const float range_y = max_y - min_y;
const float scale_x = (range_x > 0.0f) ? (1.0f - 2.0f * margin_x) / range_x : 1.0f;
const float scale_y = (range_y > 0.0f) ? (1.0f - 2.0f * margin_y) / range_y : 1.0f;
for (int i = 0; i < num_control; i++) {
adjusted_points[i].x = margin_x + (control_points[i].x - min_x) * scale_x;
adjusted_points[i].y = margin_y + (control_points[i].y - min_y) * scale_y;
}
} else {
// No margin, use original points
for (int i = 0; i < num_control; i++) {
adjusted_points[i] = control_points[i];
}
}
Array<Vector2> samples;
samples.Allocate(num_segments * samples_per_segment + 1);
// Sample the spline using Catmull-Rom interpolation with adjusted points
int sample_idx = 0;
for (int seg = 0; seg < num_segments; seg++)
{
Vector2 p0 = (seg > 0) ? adjusted_points[seg - 1] : adjusted_points[seg];
Vector2 p1 = adjusted_points[seg];
Vector2 p2 = adjusted_points[seg + 1];
Vector2 p3 = (seg < num_segments - 1) ? adjusted_points[seg + 2] : adjusted_points[seg + 1];
for (int s = 0; s < samples_per_segment; s++)
{
const float t = (float)s / (float)samples_per_segment;
samples[sample_idx] = CatmullRomInterpolate(p0, p1, p2, p3, t);
sample_idx++;
}
}
samples[num_segments * samples_per_segment] = adjusted_points[num_control - 1];
// __LOG_W__ << "Generated " << samples.GetCount() << " sampled points\n";
// Create pixel buffer for drawing
// __LOG_W__ << "Creating pixel buffer for CPU rendering...\n";
const uint pitch = tex_w * 4; // RGBA = 4 bytes per pixel
const size_t buffer_size = pitch * tex_h;
unsigned char *pixels = new unsigned char[buffer_size];
// __LOG_W__ << "Pixel buffer created. Size=" << buffer_size << " Pitch=" << pitch << "\n";
if (clear_target)
{
// Initialize with transparent black
memset(pixels, 0, buffer_size);
}
else
{
// Read back the existing texture contents into the buffer so we can composite on top.
// __LOG_W__ << "Reading existing texture pixels into buffer (for compositing)...\n";
// Setup FBO for readback
fx_fbo->SetColorTexture(target);
fx_fbo->SetDepthTexture(nullptr);
SetCurrentFBO(fx_fbo);
fx_fbo->ReadColorPixels((char*)pixels, 0, 0, tex_w, tex_h);
// Restore
SetCurrentFBO(nullptr);
}
// __LOG_W__ << "Drawing spline lines...\n";
// Draw border first (if specified)
if (border_color && border_width > 0.0f) {
// __LOG_W__ << "Drawing border with width: " << border_width << "\n";
const float total_width = width_pixels + border_width * 2.0f;
for (uint i = 0; i + 1 < samples.GetCount(); i++)
{
const int x0 = (int)(samples[i].x * (float)tex_w);
const int y0 = (int)(samples[i].y * (float)tex_h);
const int x1 = (int)(samples[i + 1].x * (float)tex_w);
const int y1 = (int)(samples[i + 1].y * (float)tex_h);
for (int w = -(int)total_width/2; w <= (int)total_width/2; w++)
{
DrawLineToPixels(pixels, tex_w, tex_h, pitch,
x0 + w, y0, x1 + w, y1, border_r, border_g, border_b, border_a);
}
}
}
// Draw main spline on top
for (uint i = 0; i + 1 < samples.GetCount(); i++)
{
// Convert from normalized [0..1] to pixel coordinates
const int x0 = (int)(samples[i].x * (float)tex_w);
const int y0 = (int)(samples[i].y * (float)tex_h);
const int x1 = (int)(samples[i + 1].x * (float)tex_w);
const int y1 = (int)(samples[i + 1].y * (float)tex_h);
// Draw line with width by drawing multiple parallel lines
for (int w = -(int)width_pixels/2; w <= (int)width_pixels/2; w++)
{
DrawLineToPixels(pixels, tex_w, tex_h, pitch,
x0 + w, y0, x1 + w, y1, r, g, b, a);
}
}
// __LOG_W__ << "Uploading pixel buffer to texture...\n";
// Upload to texture using Blit
target->Blit((const char *)pixels, tex_w, tex_h, 0, 0, Render::Texture::FormatRGBA8);
// __LOG_W__ << "Freeing pixel buffer...\n";
delete[] pixels;
// __LOG_W__ << "Buffer freed!\n";
// __LOG_W__ << "DrawSplineToTexture complete!\n";
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,264 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "core/terrain.h"
#include "log/log.h"
using namespace GS::GPU;
// Terrain patch size.
#define _PatchSize 64
// Terrain page count.
#if __PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__
#define _PageCount 4
#else
#define _PageCount 32
#endif
//-----------------------------------------------------------------------------
bool Renderer::CreateTerrainPatch()
{
// Allocate terrain cache.
if (!terrain_patch_cache.Allocate(_PageCount))
__ERR__(__LOG_E__ << "Failed to allocate terrain cache.\n", false)
// Compute page stride.
size_t stride = 0;
size_t vertex_offset = stride;
stride += 3 * 4; // Vertex buffer (3x float).
size_t normal_offset = stride;
stride += (3 + 1) * 1; // Normal buffer (3x byte + 1 padding).
size_t uv_offset = stride;
stride += 2 * 4; // Large UV (2x float).
// Setup default page layout.
size_t byte_size = stride * (_PatchSize + 1) * (_PatchSize + 1);
__LOG_H__ << "Setup terrain cache: " << terrain_patch_cache.GetCount() << " pages (" << uint(byte_size * terrain_patch_cache.GetCount()) << " bytes).\n";
terrain_patch_vtx.Allocate((uint)byte_size);
if (char *p = terrain_patch_vtx)
{
Vector4 wp(0, 0, 0);
for (int v = 0; v < (_PatchSize + 1); ++v)
{
for (int u = 0; u < (_PatchSize + 1); ++u)
{
float *pv = (float *)p;
pv[0] = wp.x; pv[1] = 0; pv[2] = wp.z;
p += stride;
wp.x += 1.f;
}
wp.x = 0.f;
wp.z += 1.f;
}
}
// Allocate pages.
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
{
DisplayList *patch = terrain_patch_cache[n].patch = NewDisplayList();
// Create the patch index buffer.
patch->idx = NewVBO();
if (patch->idx->Create(6 * _PatchSize * _PatchSize * sizeof(ushort), VBO::Index, VBO::Static))
{
SetIndexSource(patch->idx);
if (ushort *p_idx = (ushort *)patch->idx->Map())
{
for (int v = 0; v < _PatchSize; ++v)
for (int u = 0; u < _PatchSize; ++u)
{
*p_idx++ = (ushort)(u + v * (_PatchSize + 1));
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1) + 1);
*p_idx++ = (ushort)(u + v * (_PatchSize + 1) + 1);
*p_idx++ = (ushort)(u + v * (_PatchSize + 1));
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1));
*p_idx++ = (ushort)(u + (v + 1) * (_PatchSize + 1) + 1);
}
patch->idx->Unmap();
patch->index_count = 6 * _PatchSize * _PatchSize;
}
SetIndexSource(NULL);
}
// Create the patch vertex buffer.
patch->vertex_offset = vertex_offset;
patch->normal_offset = normal_offset;
patch->uv_offset[0] = uv_offset;
patch->stride = stride;
patch->vtx = NewVBO();
if (patch->vtx->Create(byte_size, VBO::Vertex, VBO::Dynamic))
{
SetVertexSource(patch->vtx, patch->stride);
patch->vtx->Update(terrain_patch_vtx);
SetVertexSource(NULL, 0);
}
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Renderer::InvalidateTerrainCache(int, int, int, int)
{
// Very naive invalidation that ignores the input region.
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
terrain_patch_cache[n].terrain = NULL;
}
void Renderer::DecayTerrainCache()
{
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
if (terrain_patch_cache[n].score > 0)
--terrain_patch_cache[n].score;
}
void Renderer::UploadTerrainPatchToPage(CachedTerrainPatch &page, const GS::Core::Item &item, const GS::Core::Patch &patch, const DrawContext &dc)
{
if (!patch.terrain || !page.patch)
return;
Core::Terrain &terrain = *patch.terrain;
DisplayList &terrain_patch = *page.patch;
float pixel_size = terrain.GetUnit();
Vector4 patch_scale(pixel_size * patch.decimation, 1, pixel_size * patch.decimation);
#if 1
// Update patch CPU-side data.
Core::Terrain::Attrib *psa = terrain.GetAttributesMap() + patch.v * terrain.GetHeightmapPitch() + patch.u;
float *psh = terrain.GetHeightmap() + patch.v * terrain.GetHeightmapPitch() + patch.u;
char *p = terrain_patch_vtx;
Vector4 uv(0, 0, patch.v * pixel_size);
for (int v = 0; v < (_PatchSize + 1); ++v)
{
uv.x = patch.u * pixel_size;
Core::Terrain::Attrib *pa = psa;
float *ph = psh;
for (int u = 0; u < (_PatchSize + 1); ++u)
{
// Vertex.
float *pv = (float *)(p + terrain_patch.vertex_offset);
pv[1] = ph[0];
// Normal.
char *pn = (char *)(p + terrain_patch.normal_offset);
pn[0] = pa[0].nx;
pn[1] = pa[0].ny;
pn[2] = pa[0].nz;
// UV.
float *pu = (float *)(p + terrain_patch.uv_offset[0]);
pu[0] = uv.x / terrain.GetWidth();
uv.x += patch_scale.x;
pu[1] = uv.z / terrain.GetDepth();
p += terrain_patch.stride;
pa += patch.decimation;
ph += patch.decimation;
}
uv.z += patch_scale.z;
psa += terrain.GetHeightmapPitch() * patch.decimation;
psh += terrain.GetHeightmapPitch() * patch.decimation;
}
// Update VBO.
SetVertexSource(terrain_patch.vtx, terrain_patch.stride);
terrain_patch.vtx->Update(terrain_patch_vtx);
// [EJ 9 Mar] the display list cache MUST be reset here otherwise an invalid
// VBO reference will be used by the next draw calls for this patch.
dls_cache.dls = NULL;
#endif
// Synchronize cache.
page.terrain = patch.terrain;
page.u = patch.u; page.v = patch.v;
page.w = patch.w; page.h = patch.h;
page.decimation = patch.decimation;
}
void Renderer::RenderTerrainPatch(const GS::Core::Patch &patch, const GS::Core::Item &item, const DrawContext &dc)
{
// Cache query.
++gpu_stats.terrain_page_query_count;
DisplayList *dlist = NULL;
CachedTerrainPatch *page = NULL;
uint lowest_score = (uint)~0;
for (uint n = 0; n < terrain_patch_cache.GetCount(); ++n)
if (CachedTerrainPatch *c_page = &terrain_patch_cache[n])
{
if (
(c_page->terrain == patch.terrain) &&
(c_page->u == patch.u) && (c_page->v == patch.v) &&
(c_page->w == patch.w) && (c_page->h == patch.h) &&
(c_page->decimation == patch.decimation)
)
{
dlist = c_page->patch;
c_page->score++; // Increase score.
break;
}
// Track the lowest scoring page.
if (c_page->score <= lowest_score)
{
lowest_score = c_page->score;
page = c_page;
}
}
// Cache miss.
if (!dlist)
{
++gpu_stats.terrain_page_query_miss;
if (!page || !page->patch)
__ERRRAW__(__LOG_E__ << "No page to upload terrain patch.\n") // Unexpected...
// Create new page.
UploadTerrainPatchToPage(*page, item, patch, dc);
dlist = page->patch;
page->score = 1; // Reset score (+1 for being used).
}
// Draw patch scaled and translated on the GPU.
float pixel_size = patch.terrain->GetUnit();
Vector4 patch_scale(pixel_size * patch.decimation, 1, pixel_size * patch.decimation);
Matrix4 patch_matrix =
Matrix4::TranslationMatrix(Vector4(patch.u * pixel_size - patch.terrain->GetWidth() * 0.5f, 0, patch.v * pixel_size - patch.terrain->GetDepth() * 0.5f)) *
Matrix4::ScaleMatrix(patch_scale);
SetWorldMatrix(item.GetMatrix() * patch_matrix);
m_previous_world = item.GetPreviousMatrix() * patch_matrix;
dls_cache.item = NULL; // Force matrices upload to program.
// Draw.
if (patch.terrain->render_data)
{
dlist->material = patch.terrain->render_data->material;
DrawDisplayListCached(*dlist, item, dc);
}
}
//-----------------------------------------------------------------------------

View File

@ -0,0 +1,68 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_triangle_batch.h"
#include "gpu/gpu_renderer.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
void TriangleBatch::Flush()
{
if (batch_triangle_count > 0)
renderer.DrawTriangle(batch_triangle_count, batch_vtx, batch_idx, batch_col, batch_uv, batch_t, batch_blend_op, batch_render_word);
batch_triangle_count = 0;
batch_attrib_offset = 0;
}
void TriangleBatch::DrawTriangle(uint triangle_count, uint attrib_count, const Vector4 *vtx, const ushort *idx, const Color *col, const Vector2 *uv, const Render::Texture *t, Core::Material::BlendOperator blend_op, Core::Material::RenderWord render_word)
{
bool batch_broken = (batch_triangle_count == 0) || (batch_t != t) || (batch_blend_op != blend_op) || (batch_render_word != render_word);
if (batch_triangle_count + triangle_count > batch_max_triangle_count)
batch_broken = true;
if (batch_broken)
Flush();
// Transfer and fix-up indices.
for (uint n = 0; n < triangle_count; ++n)
{
batch_idx[batch_triangle_count * 3 + 0] = (ushort)(idx[n * 3 + 0] + batch_attrib_offset);
batch_idx[batch_triangle_count * 3 + 1] = (ushort)(idx[n * 3 + 1] + batch_attrib_offset);
batch_idx[batch_triangle_count * 3 + 2] = (ushort)(idx[n * 3 + 2] + batch_attrib_offset);
++batch_triangle_count;
}
// Transfer attributes.
Memory::Copy(&batch_vtx[batch_attrib_offset], vtx, sizeof(Vector4) * attrib_count);
Memory::Copy(&batch_col[batch_attrib_offset], col, sizeof(Color) * attrib_count);
Memory::Copy(&batch_uv[batch_attrib_offset], uv, sizeof(Vector2) * attrib_count);
batch_attrib_offset += attrib_count;
batch_render_word = render_word;
batch_blend_op = blend_op;
batch_t = t;
}
//------------------------------------------------------------------------------
TriangleBatch::TriangleBatch(Renderer &r, uint max) : renderer(r)
{
batch_t = NULL;
batch_render_word = Core::Material::Render_None;
batch_blend_op = Core::Material::Blend_None;
batch_max_triangle_count = max;
batch_triangle_count = 0;
batch_attrib_offset = 0;
batch_idx.Allocate(3 * batch_max_triangle_count);
batch_vtx.Allocate(3 * batch_max_triangle_count);
batch_col.Allocate(3 * batch_max_triangle_count);
batch_uv.Allocate(3 * batch_max_triangle_count);
}

View File

@ -0,0 +1,328 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "gpu/gpu_renderer.h"
#include "log/log.h"
using namespace GS::GPU;
//------------------------------------------------------------------------------
Renderer::RenderTechnique Renderer::GetDefaultRenderTechnique() const
{
String technique = registry.GetString("Technique", "Forward");
if (technique == "Deferred")
return TechniqueDeferred;
return TechniqueForward;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::FreeRenderTechnique()
{
for (uint n = 0; n < 4; ++n)
t_gbuffer[n] = NULL;
t_depth = NULL;
t_color_aa = NULL;
t_depth_aa = NULL;
}
void Renderer::SetRenderTechnique(RenderTechnique p)
{
if (p == TechniqueDefault)
p = GetDefaultRenderTechnique();
// Filter out illegal configurations.
if (!gpu_config.use_rtt && (p == TechniqueDeferred))
p = TechniqueForward;
FreeRenderTechnique();
render_technique = p;
// Select AA method.
Render::Texture::AA aa = Render::Texture::NoAA;
if ((p == TechniqueForward) && gpu_config.can_resolve_msaa)
if (registry.GetBool("Antialiasing:Enable", false))
{
float sample = registry.GetReal("Antialiasing:Sample", 4.0);
if (sample > 8.f) aa = Render::Texture::MSAA16x;
else if (sample > 4.f) aa = Render::Texture::MSAA8x;
else if (sample > 2.f) aa = Render::Texture::MSAA4x;
else if (sample > 1.f) aa = Render::Texture::MSAA2x;
}
gpu_config.enable_aa = asbool(aa != Render::Texture::NoAA);
// Setup technique objects.
__LOG__ << "SetRenderTechnique(): " << dimensions.x << "x" << dimensions.y << "\n";
bool use_float = registry.GetBool("Texture:Float:Enable", false);
Render::Texture::Format t_format = use_float ? Render::Texture::FormatRGBAF : Render::Texture::FormatRGBA8;
for (uint n = 0; n < 2; ++n)
{
t_compose[n] = NewTexture(String("t_compose") << n);
t_compose[n]->Create(NULL, dimensions.x, dimensions.y, t_format, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
t_compose[n]->SetFiltering(Render::TextureParm::FilterTrilinear);
t_compose[n]->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp);
}
switch (p)
{
case TechniqueForward:
{
if (gpu_config.can_resolve_msaa)
{
t_color_aa = NewTexture("t_color_aa");
t_color_aa->Create(NULL, dimensions.x, dimensions.y, t_format, aa, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
t_depth_aa = NewTexture("t_depth_aa");
t_depth_aa->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, aa, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
buffer_fbo->SetColorTexture(t_color_aa);
buffer_fbo->SetDepthTexture(t_depth_aa);
}
if (gpu_config.use_rtt)
{
t_depth = NewTexture("t_depth");
t_depth->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
resolve_fbo->SetColorTexture(t_compose[0]);
resolve_fbo->SetDepthTexture(t_depth);
}
}
break;
case TechniqueDeferred:
{
for (uint n = 0; n < 4; ++n)
{
t_gbuffer[n] = NewTexture(String("t_gbuffer") << n);
t_gbuffer[n]->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatRGBAF, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
t_gbuffer[n]->SetFiltering(Render::TextureParm::FilterNearest);
}
t_depth = NewTexture("t_depth");
t_depth->Create(NULL, dimensions.x, dimensions.y, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
Render::Texture *rt[4] = { t_gbuffer[0], t_gbuffer[1], t_gbuffer[2], t_gbuffer[3] };
buffer_fbo->SetColorTexture(rt, 4);
buffer_fbo->SetDepthTexture(t_depth);
resolve_fbo->SetColorTexture(t_compose[0]);
resolve_fbo->SetDepthTexture(t_depth);
}
break;
}
SetPostProcess();
}
void Renderer::SetPostProcess(uint k)
{
if (!gpu_config.use_rtt)
return;
if (k == 0)
k = registry.GetInteger("PostProcess:FX:Scale", 4);
for (uint n = 0; n < 3; ++n)
{
t_fx[n] = NewTexture(String("t_fx") << n);
t_fx[n]->Create(NULL, dimensions.x / k, dimensions.y / k, Render::Texture::FormatRGBAF, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
t_fx[n]->SetFiltering(Render::TextureParm::FilterBilinear);
t_fx[n]->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp);
}
t_fx_depth = NewTexture("t_fx_depth");
t_fx_depth->Create(NULL, dimensions.x / k, dimensions.y / k, Render::Texture::FormatDepth, Render::Texture::NoAA, Render::Texture::Usage(Render::Texture::IsRenderTarget | Render::Texture::IsShaderResource));
t_fx_depth->SetFiltering(Render::TextureParm::FilterNearest);
t_fx_depth->SetWrapping(Render::TextureParm::WrapClamp, Render::TextureParm::WrapClamp);
fx_scale = k;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::SetupCoreResources(bool support_3d)
{
// Core shaders & objects.
if (!LoadCoreShaders(support_3d))
return false;
resolve_fbo = NewFBO();
resolve_fbo->Create();
if (support_3d)
{
buffer_fbo = NewFBO();
buffer_fbo->Create();
fx_fbo = NewFBO();
fx_fbo->Create();
// Shadow mapping objects.
if (gpu_config.enable_shadow)
{
shadow_map_fbo = NewFBO();
shadow_map_fbo->Create();
CreateShadowMaps();
}
// Default technique.
SetRenderTechnique();
if (!CreateTerrainPatch())
return false;
t_noise = core_resource_factory->LoadTexture("@core/noise.tga");
}
{
static const ushort idx[] = { 2, 1, 0, 3, 2, 0 };
static const float vtx[] = { -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1 };
skybox_idx_vbo = NewVBO();
skybox_idx_vbo->Create(idx, sizeof(ushort) * 6, VBO::Index);
skybox_vtx_vbo = NewVBO();
skybox_vtx_vbo->Create(vtx, sizeof(float) * 3 * 4, VBO::Vertex);
helper_idx_vbo = NewVBO();
helper_idx_vbo->Create(idx, sizeof(ushort) * 6, VBO::Index);
helper_vtx_vbo = NewVBO();
helper_vtx_vbo->Create(sizeof(float) * 256, VBO::Vertex, VBO::Dynamic); // 1Kb WARNING watch out for possible overflow when updating this buffer!
ushort box_idx[] =
{
0, 1, 2, 0, 2, 3, 1, 5, 6, 1, 6, 2,
5, 4, 7, 5, 7, 6, 4, 0, 3, 4, 3, 7,
4, 5, 1, 4, 1, 0, 3, 2, 6, 3, 6, 7
};
box_idx_vbo = NewVBO();
box_idx_vbo->Create(box_idx, sizeof(ushort) * 3 * 4 * 3, VBO::Index);
direct_idx_vbo = NewVBO();
direct_vtx_vbo = NewVBO();
}
return true;
}
void Renderer::SetDefaultStates()
{
// Set defaults states.
EnableDepthTest(true);
SetDepthFunc(DepthLess);
EnableCulling(true);
SetCullFunc(CullFront);
EnableBlending(false);
SetBlendFunc(BlendSrcAlpha, BlendOneMinusSrcAlpha);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Renderer::DiscoverGPUConfiguration()
{
Variant v;
if (QueryCaps(CanBlitRBO, v))
gpu_config.can_resolve_msaa = v.b_value;
if (QueryCaps(MaxAnisotropy, v))
gpu_config.max_anisotropy = v.i_value;
if (QueryCaps(TextureTopLeftOrigin, v))
gpu_config.tex_origin_is_top_left = v.b_value;
#if __PLATFORM_ANDROID_NDK__ || __PLATFORM_IOS__ || __PLATFORM_EMSCRIPTEN__
// Drop all advanced features... for now.
gpu_config.enable_shadow = false;
gpu_config.can_resolve_msaa = false;
gpu_config.use_rtt = false;
gpu_config.npot = RendererConfig::NPOT_Limited;
#endif
if (!gpu_config.can_resolve_msaa)
__LOG_W__ << "Insufficient RBO support, falling back to RTT: No hardware MSAA.\n";
if (!gpu_config.enable_shadow)
__LOG_W__ << "Shadow-mapping support disabled.\n";
if (gpu_config.npot == RendererConfig::NPOT_Limited)
__LOG_V__ << "NPOT limited support.\n";
if (gpu_config.npot == RendererConfig::NPOT_Full)
__LOG_V__ << "NPOT full support.\n";
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Renderer::ResizeVideo(uint w, uint h)
{
__LOG_V__ << "GPU Resize video to " << w << "x" << h << ".\n";
dimensions.Set(w, h);
SetRenderTechnique(render_technique);
return true;
}
bool Renderer::Open(uint w, uint h, char bpp, GS::Render::VideoMode mode, const void *sys_handle)
{
if (!OpenPlatformVideo(w, h, bpp, mode, sys_handle))
return false;
if (!InitializePlatform())
return false;
__LOG__ << "\n";
__LOG_H__ << "GPU-based (" << GetName() << ") on adapter " << stats.adapter << " (vendor: " << stats.vendor << ").\n";
__LOG__ << "\n";
DiscoverGPUConfiguration();
dimensions.Set(w, h);
SetViewport(fRect(0, 0, (float)w, (float)h));
return true;
}
void Renderer::Free()
{
__LOG_FUNC__
terrain_patch_cache.Free();
FreeRenderTechnique();
helper_idx_vbo = NULL;
helper_vtx_vbo = NULL;
skybox_idx_vbo = NULL;
skybox_vtx_vbo = NULL;
direct_idx_vbo = NULL;
direct_vtx_vbo = NULL;
box_idx_vbo = NULL;
buffer_fbo = NULL;
resolve_fbo = NULL;
fx_fbo = NULL;
for (uint n = 0; n < 2; ++n)
t_compose[n] = NULL;
for (uint n = 0; n < 3; ++n)
t_fx[n] = NULL;
t_fx_depth = NULL;
fx_scale = 0;
t_noise = NULL;
FreeShadowMaps();
shadow_map_fbo = NULL;
UnloadCoreShaders();
}
void Renderer::Close()
{
Free();
ClosePlatformVideo();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,154 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <math.h>
#include "gpu/gpu_renderer.h"
#include "gpu/gpu_triangle_batch.h"
#include "core/raster_font.h"
#include "log/log.h"
using namespace GS;
using namespace GS::GPU;
//------------------------------------------------------------------------------
void Renderer::Write(const Render::RasterFont &f, const char *t, float &x, float &y, const WriterConfig &config, float s, const Color *c, WriterAlignment a, bool mirrored)
{
Render::Texture *page = f.GetPage(0);
if (!page)
__ERRRAW__(__LOG_E__ << "No glyph page in raster font '" << f.name << "'.\n")
float scale_width = s;
if(mirrored)
scale_width = -s;
// Aspect ratio, texel/pixel mapping.
float ar = config.correct_ar ? viewport.GetHeight() / viewport.GetWidth() : 1.f; // w / h
Vector2 glyph_mapping((float)page->GetWidth() / viewport.GetWidth(), (float)page->GetHeight() / viewport.GetHeight()),
pixel_mapping(1.f / viewport.GetWidth(), 1.f / viewport.GetHeight());
if (!config.normalized)
{
x *= pixel_mapping.x;
y *= pixel_mapping.y;
y = floor(y * viewport.GetHeight()) / viewport.GetHeight(); // Stay on a pixel boundary.
}
float in_x = x;
Vector4 vtx[4];
Vector2 uv[4];
Color col[4];
for (uint n = 0; n < 4; ++n)
col[n] = c ? *c : Color(1, 1, 1, 1);
// Draw glyphs.
Render::Texture *t_page = NULL;
bool aligned = false;
TriangleBatch batch(*this);
for ( ; t[0]; ++t)
{
// Catch line feed.
if (t[0] == '\n')
{
x = in_x;
if (config.normalized)
y += f.GetHeight() * s;
else
{
y += f.GetHeight() * glyph_mapping.y * s;
y = floor(y * viewport.GetHeight()) / viewport.GetHeight(); // Avoid float drift, stay on pixel boundary.
}
aligned = false;
continue;
}
// Compute alignment.
if (!aligned)
{
switch (a)
{
case AlignMiddle:
if (config.normalized)
x -= f.ComputeLineRect(t).x * scale_width * 0.5f * ar;
else x -= f.ComputeLineRect(t).x * scale_width * 0.5f * glyph_mapping.x;
break;
case AlignRight:
if (config.normalized)
x -= f.ComputeLineRect(t).x * scale_width * ar;
else x -= f.ComputeLineRect(t).x * scale_width * glyph_mapping.x;
break;
default:
break;
}
// Make sure we stay as close as possible to a pixel boundary for non-normalized modes.
if (!config.normalized)
x = floor(x * viewport.GetWidth()) / viewport.GetWidth();
aligned = true;
}
// Output glyph.
if (const Render::RasterFont::Glyph *glyph = f.GetGlyphInfos(t[0]))
{
if ((t_page = f.GetPage(glyph->page)) != NULL)
{
float _x, _y, _w, _h;
if (config.normalized)
{
_x = x * 2.f - 1.f + glyph->offx * 1.f * scale_width;
_y = (1.f - y) * 2.f - 1.f - glyph->offy * 2.f * s;
_w = glyph->w * 2.f * scale_width * ar;
_h = glyph->h * 2.f * s;
_x *= GetGlobalAspectRatio();
_w *= GetGlobalAspectRatio();
}
else
{
_x = x * 2.f - 1.f + glyph->offx * 2.f * glyph_mapping.x * scale_width;
_y = (1.f - y) * 2.f - 1.f - glyph->offy * 2.f * glyph_mapping.y * s;
_w = glyph->w * 2.f * glyph_mapping.x * scale_width;
_h = glyph->h * 2.f * glyph_mapping.y * s;
}
vtx[0].Set(_x, _y, 0.5);
vtx[1].Set(_x + _w, _y, 0.5);
vtx[2].Set(_x + _w, _y - _h, 0.5);
vtx[3].Set(_x, _y - _h, 0.5);
uv[0].Set(glyph->u, glyph->v);
uv[1].Set(glyph->u + glyph->w, glyph->v);
uv[2].Set(glyph->u + glyph->w, glyph->v + glyph->h);
uv[3].Set(glyph->u, glyph->v + glyph->h);
const ushort indice[] = { 0, 1, 2, 0, 2, 3 }; // order is reversed because the quad is drawn from the baseline (bottom to top)
batch.DrawTriangle(2, 4, vtx, indice, col, uv, t_page, Core::Material::Blend_Alpha, (Core::Material::RenderWord)(Core::Material::Render_NoZTest | Core::Material::Render_NoZWrite | Core::Material::Render_DoubleSided));
}
if (config.normalized)
x += glyph->step * scale_width * ar;
else x += glyph->step * glyph_mapping.x * scale_width;
}
}
batch.Flush();
if (!config.normalized)
{
x /= pixel_mapping.x;
y /= pixel_mapping.y;
}
}
//------------------------------------------------------------------------------