commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
148
include/engine/gpu/gpu_core_shader.cpp
Normal file
148
include/engine/gpu/gpu_core_shader.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
220
include/engine/gpu/gpu_display_list.cpp
Normal file
220
include/engine/gpu/gpu_display_list.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -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;
|
||||
|
||||
109
include/engine/gpu/gpu_geometry.cpp
Normal file
109
include/engine/gpu/gpu_geometry.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
128
include/engine/gpu/gpu_half_float.cpp
Normal file
128
include/engine/gpu/gpu_half_float.cpp
Normal 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
|
||||
//------------------------------------------------------------------------------
|
||||
234
include/engine/gpu/gpu_helper.cpp
Normal file
234
include/engine/gpu/gpu_helper.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
163
include/engine/gpu/gpu_light_volume.cpp
Normal file
163
include/engine/gpu/gpu_light_volume.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
172
include/engine/gpu/gpu_material.cpp
Normal file
172
include/engine/gpu/gpu_material.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
429
include/engine/gpu/gpu_material_shader.cpp
Normal file
429
include/engine/gpu/gpu_material_shader.cpp
Normal 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); }
|
||||
//------------------------------------------------------------------------------
|
||||
668
include/engine/gpu/gpu_post_process_chain.cpp
Normal file
668
include/engine/gpu/gpu_post_process_chain.cpp
Normal 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];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
40
include/engine/gpu/gpu_profiler.cpp
Normal file
40
include/engine/gpu/gpu_profiler.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
71
include/engine/gpu/gpu_registry.cpp
Normal file
71
include/engine/gpu/gpu_registry.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
155
include/engine/gpu/gpu_render_display_list.cpp
Normal file
155
include/engine/gpu/gpu_render_display_list.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
298
include/engine/gpu/gpu_render_queue.cpp
Normal file
298
include/engine/gpu/gpu_render_queue.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
82
include/engine/gpu/gpu_render_queue_deferred.cpp
Normal file
82
include/engine/gpu/gpu_render_queue_deferred.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
318
include/engine/gpu/gpu_render_queue_forward.cpp
Normal file
318
include/engine/gpu/gpu_render_queue_forward.cpp
Normal 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 *> ∈
|
||||
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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
462
include/engine/gpu/gpu_renderer.cpp
Normal file
462
include/engine/gpu/gpu_renderer.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@ -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]);
|
||||
|
||||
|
||||
674
include/engine/gpu/gpu_shader.cpp
Normal file
674
include/engine/gpu/gpu_shader.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
65
include/engine/gpu/gpu_shader_compiler.cpp
Normal file
65
include/engine/gpu/gpu_shader_compiler.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
376
include/engine/gpu/gpu_shadow_map.cpp
Normal file
376
include/engine/gpu/gpu_shadow_map.cpp
Normal 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();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
48
include/engine/gpu/gpu_skybox.cpp
Normal file
48
include/engine/gpu/gpu_skybox.cpp
Normal 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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
539
include/engine/gpu/gpu_spline.cpp
Normal file
539
include/engine/gpu/gpu_spline.cpp
Normal 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";
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
264
include/engine/gpu/gpu_terrain.cpp
Normal file
264
include/engine/gpu/gpu_terrain.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
68
include/engine/gpu/gpu_triangle_batch.cpp
Normal file
68
include/engine/gpu/gpu_triangle_batch.cpp
Normal 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);
|
||||
}
|
||||
328
include/engine/gpu/gpu_video.cpp
Normal file
328
include/engine/gpu/gpu_video.cpp
Normal 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();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
154
include/engine/gpu/gpu_writer.cpp
Normal file
154
include/engine/gpu/gpu_writer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user