2013-09-30 03:22:44 +02:00
|
|
|
/* Copyright (C) 2013 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
#include "lib/allocators/allocator_adapters.h"
|
|
|
|
#include "lib/allocators/arena.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "lib/ogl.h"
|
|
|
|
#include "maths/Vector3D.h"
|
|
|
|
#include "maths/Vector4D.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
|
|
|
|
#include "ps/CLogger.h"
|
|
|
|
#include "ps/Profile.h"
|
|
|
|
|
|
|
|
#include "graphics/Color.h"
|
2006-02-13 15:18:20 +01:00
|
|
|
#include "graphics/LightEnv.h"
|
2012-07-24 00:49:46 +02:00
|
|
|
#include "graphics/Material.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "graphics/Model.h"
|
|
|
|
#include "graphics/ModelDef.h"
|
2012-04-03 20:44:46 +02:00
|
|
|
#include "graphics/ShaderManager.h"
|
2010-09-10 23:02:10 +02:00
|
|
|
#include "graphics/TextureManager.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-08-06 21:10:47 +02:00
|
|
|
#include "renderer/MikktspaceWrap.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/ModelRenderer.h"
|
2005-11-06 00:15:23 +01:00
|
|
|
#include "renderer/ModelVertexRenderer.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
#include "renderer/Renderer.h"
|
2005-11-06 00:15:23 +01:00
|
|
|
#include "renderer/RenderModifiers.h"
|
2012-09-08 20:56:13 +02:00
|
|
|
#include "renderer/SkyManager.h"
|
|
|
|
#include "renderer/WaterManager.h"
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2006-12-20 04:22:24 +01:00
|
|
|
#include <boost/weak_ptr.hpp>
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2011-11-10 00:11:28 +01:00
|
|
|
#if ARCH_X86_X64
|
|
|
|
# include "lib/sysdep/arch/x86_x64/x86_x64.h"
|
|
|
|
#endif
|
2005-10-25 03:43:07 +02:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ModelRenderer implementation
|
|
|
|
|
2012-02-14 00:53:09 +01:00
|
|
|
#if ARCH_X86_X64
|
2011-11-10 00:11:28 +01:00
|
|
|
static bool g_EnableSSE = false;
|
2012-02-14 00:53:09 +01:00
|
|
|
#endif
|
2011-11-10 00:11:28 +01:00
|
|
|
|
|
|
|
void ModelRenderer::Init()
|
|
|
|
{
|
|
|
|
#if ARCH_X86_X64
|
2011-12-27 15:12:31 +01:00
|
|
|
if (x86_x64::Cap(x86_x64::CAP_SSE))
|
2011-11-10 00:11:28 +01:00
|
|
|
g_EnableSSE = true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-10-30 02:22:22 +02:00
|
|
|
// Helper function to copy object-space position and normal vectors into arrays.
|
|
|
|
void ModelRenderer::CopyPositionAndNormals(
|
2008-07-17 16:23:51 +02:00
|
|
|
const CModelDefPtr& mdef,
|
|
|
|
const VertexArrayIterator<CVector3D>& Position,
|
|
|
|
const VertexArrayIterator<CVector3D>& Normal)
|
2005-10-30 02:22:22 +02:00
|
|
|
{
|
|
|
|
size_t numVertices = mdef->GetNumVertices();
|
|
|
|
SModelVertex* vertices = mdef->GetVertices();
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-30 02:22:22 +02:00
|
|
|
for(size_t j = 0; j < numVertices; ++j)
|
|
|
|
{
|
|
|
|
Position[j] = vertices[j].m_Coords;
|
|
|
|
Normal[j] = vertices[j].m_Norm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// Helper function to transform position and normal vectors into world-space.
|
|
|
|
void ModelRenderer::BuildPositionAndNormals(
|
|
|
|
CModel* model,
|
2008-07-17 16:23:51 +02:00
|
|
|
const VertexArrayIterator<CVector3D>& Position,
|
|
|
|
const VertexArrayIterator<CVector3D>& Normal)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
CModelDefPtr mdef = model->GetModelDef();
|
|
|
|
size_t numVertices = mdef->GetNumVertices();
|
|
|
|
SModelVertex* vertices=mdef->GetVertices();
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2006-12-15 17:09:30 +01:00
|
|
|
if (model->IsSkinned())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
// boned model - calculate skinned vertex positions/normals
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2006-04-14 06:35:36 +02:00
|
|
|
// Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
|
|
|
|
// some broken situations
|
|
|
|
if (numVertices && vertices[0].m_Blend.m_Bone[0] == 0xff)
|
|
|
|
{
|
2011-03-23 14:36:20 +01:00
|
|
|
LOGERROR(L"Model %ls is boned with unboned animation", mdef->GetName().string().c_str());
|
2006-04-14 06:35:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-03 20:22:10 +02:00
|
|
|
#if HAVE_SSE
|
2011-11-10 00:11:28 +01:00
|
|
|
if (g_EnableSSE)
|
2012-02-14 00:53:09 +01:00
|
|
|
{
|
2011-11-10 00:11:28 +01:00
|
|
|
CModelDef::SkinPointsAndNormals_SSE(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
|
2012-02-14 00:53:09 +01:00
|
|
|
}
|
2011-11-10 00:11:28 +01:00
|
|
|
else
|
2012-02-14 00:53:09 +01:00
|
|
|
#endif
|
|
|
|
{
|
2011-11-10 00:11:28 +01:00
|
|
|
CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
|
2012-02-14 00:53:09 +01:00
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PROFILE( "software transform" );
|
|
|
|
// just copy regular positions, transform normals to world space
|
|
|
|
const CMatrix3D& transform = model->GetTransform();
|
|
|
|
const CMatrix3D& invtransform = model->GetInvTransform();
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (size_t j=0; j<numVertices; j++)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
transform.Transform(vertices[j].m_Coords,Position[j]);
|
|
|
|
invtransform.RotateTransposed(vertices[j].m_Norm,Normal[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function for lighting
|
|
|
|
void ModelRenderer::BuildColor4ub(
|
|
|
|
CModel* model,
|
2008-07-17 16:23:51 +02:00
|
|
|
const VertexArrayIterator<CVector3D>& Normal,
|
2011-04-07 00:07:13 +02:00
|
|
|
const VertexArrayIterator<SColor4ub>& Color)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
PROFILE( "lighting vertices" );
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
CModelDefPtr mdef = model->GetModelDef();
|
|
|
|
size_t numVertices = mdef->GetNumVertices();
|
2006-02-13 15:18:20 +01:00
|
|
|
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
|
|
|
|
CColor shadingColor = model->GetShadingColor();
|
|
|
|
|
2011-04-07 00:07:13 +02:00
|
|
|
for (size_t j=0; j<numVertices; j++)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]);
|
2011-04-07 00:07:13 +02:00
|
|
|
tempcolor.X *= shadingColor.r;
|
|
|
|
tempcolor.Y *= shadingColor.g;
|
|
|
|
tempcolor.Z *= shadingColor.b;
|
|
|
|
Color[j] = ConvertRGBColorTo4ub(tempcolor);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-29 14:20:21 +01:00
|
|
|
void ModelRenderer::GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices, bool gpuSkinning)
|
2012-08-06 21:10:47 +02:00
|
|
|
{
|
2012-10-29 14:20:21 +01:00
|
|
|
MikkTSpace ms(mdef, newVertices, gpuSkinning);
|
2012-08-06 21:10:47 +02:00
|
|
|
|
|
|
|
ms.generate();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
// Copy UV coordinates
|
|
|
|
void ModelRenderer::BuildUV(
|
2008-07-17 16:23:51 +02:00
|
|
|
const CModelDefPtr& mdef,
|
2012-07-24 00:49:46 +02:00
|
|
|
const VertexArrayIterator<float[2]>& UV,
|
|
|
|
int UVset)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
size_t numVertices = mdef->GetNumVertices();
|
|
|
|
SModelVertex* vertices = mdef->GetVertices();
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2008-07-17 16:23:51 +02:00
|
|
|
for (size_t j=0; j < numVertices; ++j)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-07-24 00:49:46 +02:00
|
|
|
UV[j][0] = vertices[j].m_UVs[UVset * 2];
|
|
|
|
UV[j][1] = 1.0-vertices[j].m_UVs[UVset * 2 + 1];
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Build default indices array.
|
|
|
|
void ModelRenderer::BuildIndices(
|
2008-07-17 16:23:51 +02:00
|
|
|
const CModelDefPtr& mdef,
|
2011-03-13 19:58:09 +01:00
|
|
|
const VertexArrayIterator<u16>& Indices)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
size_t idxidx = 0;
|
2005-10-25 03:43:07 +02:00
|
|
|
SModelFace* faces = mdef->GetFaces();
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
for (size_t j = 0; j < mdef->GetNumFaces(); ++j) {
|
|
|
|
SModelFace& face=faces[j];
|
|
|
|
Indices[idxidx++]=face.m_Verts[0];
|
|
|
|
Indices[idxidx++]=face.m_Verts[1];
|
|
|
|
Indices[idxidx++]=face.m_Verts[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
2012-04-03 20:44:46 +02:00
|
|
|
// ShaderModelRenderer implementation
|
2005-10-25 03:43:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2012-04-03 20:44:46 +02:00
|
|
|
* Internal data of the ShaderModelRenderer.
|
|
|
|
*
|
|
|
|
* Separated into the source file to increase implementation hiding (and to
|
|
|
|
* avoid some causes of recompiles).
|
2005-10-25 03:43:07 +02:00
|
|
|
*/
|
2012-04-03 20:44:46 +02:00
|
|
|
struct ShaderModelRendererInternals
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
ShaderModelRendererInternals(ShaderModelRenderer* r) : m_Renderer(r) { }
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
/// Back-link to "our" renderer
|
|
|
|
ShaderModelRenderer* m_Renderer;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
/// ModelVertexRenderer used for vertex transformations
|
|
|
|
ModelVertexRendererPtr vertexRenderer;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
/// List of submitted models for rendering in this frame
|
|
|
|
std::vector<CModel*> submissions;
|
2005-10-25 03:43:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Construction/Destruction
|
|
|
|
ShaderModelRenderer::ShaderModelRenderer(ModelVertexRendererPtr vertexrenderer)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
m = new ShaderModelRendererInternals(this);
|
|
|
|
m->vertexRenderer = vertexrenderer;
|
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
ShaderModelRenderer::~ShaderModelRenderer()
|
|
|
|
{
|
|
|
|
delete m;
|
|
|
|
}
|
2005-10-30 22:31:10 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Submit one model.
|
|
|
|
void ShaderModelRenderer::Submit(CModel* model)
|
|
|
|
{
|
|
|
|
CModelDefPtr mdef = model->GetModelDef();
|
|
|
|
CModelRData* rdata = (CModelRData*)model->GetRenderData();
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Ensure model data is valid
|
|
|
|
const void* key = m->vertexRenderer.get();
|
|
|
|
if (!rdata || rdata->GetKey() != key)
|
|
|
|
{
|
|
|
|
rdata = m->vertexRenderer->CreateModelData(key, model);
|
|
|
|
model->SetRenderData(rdata);
|
|
|
|
model->SetDirty(~0u);
|
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->submissions.push_back(model);
|
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Call update for all submitted models and enter the rendering phase
|
|
|
|
void ShaderModelRenderer::PrepareModels()
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
for (size_t i = 0; i < m->submissions.size(); ++i)
|
|
|
|
{
|
|
|
|
CModel* model = m->submissions[i];
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
|
|
|
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->vertexRenderer->UpdateModelData(model, rdata, rdata->m_UpdateFlags);
|
|
|
|
rdata->m_UpdateFlags = 0;
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Clear the submissions list
|
|
|
|
void ShaderModelRenderer::EndFrame()
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
m->submissions.clear();
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Helper structs for ShaderModelRenderer::Render():
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
struct SMRSortByDistItem
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
size_t techIdx;
|
|
|
|
CModel* model;
|
|
|
|
float dist;
|
|
|
|
};
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
struct SMRBatchModel
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
bool operator()(CModel* a, CModel* b)
|
|
|
|
{
|
|
|
|
if (a->GetModelDef() < b->GetModelDef())
|
|
|
|
return true;
|
|
|
|
if (b->GetModelDef() < a->GetModelDef())
|
|
|
|
return false;
|
2007-02-10 23:07:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (a->GetMaterial().GetDiffuseTexture() < b->GetMaterial().GetDiffuseTexture())
|
|
|
|
return true;
|
|
|
|
if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
|
|
|
|
return false;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-08 17:55:06 +02:00
|
|
|
return a->GetMaterial().GetStaticUniforms() < b->GetMaterial().GetStaticUniforms();
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
};
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
struct SMRCompareSortByDistItem
|
|
|
|
{
|
|
|
|
bool operator()(const SMRSortByDistItem& a, const SMRSortByDistItem& b)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
// Prefer items with greater distance, so we draw back-to-front
|
|
|
|
return (a.dist > b.dist);
|
|
|
|
|
|
|
|
// (Distances will almost always be distinct, so we don't need to bother
|
|
|
|
// tie-breaking on modeldef/texture/etc)
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SMRMaterialBucketKey
|
|
|
|
{
|
|
|
|
SMRMaterialBucketKey(CStrIntern effect, const CShaderDefines& defines)
|
|
|
|
: effect(effect), defines(defines) { }
|
|
|
|
|
|
|
|
CStrIntern effect;
|
2012-08-06 21:10:47 +02:00
|
|
|
CShaderDefines defines;
|
2012-04-03 20:44:46 +02:00
|
|
|
|
|
|
|
bool operator==(const SMRMaterialBucketKey& b) const
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
return (effect == b.effect && defines == b.defines);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
private:
|
|
|
|
SMRMaterialBucketKey& operator=(const SMRMaterialBucketKey&);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SMRMaterialBucketKeyHash
|
|
|
|
{
|
|
|
|
size_t operator()(const SMRMaterialBucketKey& key) const
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
size_t hash = 0;
|
|
|
|
boost::hash_combine(hash, key.effect.GetHash());
|
|
|
|
boost::hash_combine(hash, key.defines.GetHash());
|
|
|
|
return hash;
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
};
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
struct SMRTechBucket
|
|
|
|
{
|
|
|
|
CShaderTechniquePtr tech;
|
|
|
|
CModel** models;
|
|
|
|
size_t numModels;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Model list is stored as pointers, not as a std::vector,
|
|
|
|
// so that sorting lists of this struct is fast
|
|
|
|
};
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
struct SMRCompareTechBucket
|
|
|
|
{
|
|
|
|
bool operator()(const SMRTechBucket& a, const SMRTechBucket& b)
|
|
|
|
{
|
|
|
|
return a.tech < b.tech;
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int flags)
|
|
|
|
{
|
|
|
|
if (m->submissions.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
CMatrix3D worldToCam;
|
|
|
|
g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rendering approach:
|
|
|
|
*
|
|
|
|
* m->submissions contains the list of CModels to render.
|
|
|
|
*
|
|
|
|
* The data we need to render a model is:
|
|
|
|
* - CShaderTechnique
|
|
|
|
* - CTexture
|
2012-04-08 17:55:06 +02:00
|
|
|
* - CShaderUniforms
|
2012-04-03 20:44:46 +02:00
|
|
|
* - CModelDef (mesh data)
|
|
|
|
* - CModel (model instance data)
|
|
|
|
*
|
|
|
|
* For efficient rendering, we need to batch the draw calls to minimise state changes.
|
2012-04-08 17:55:06 +02:00
|
|
|
* (Uniform and texture changes are assumed to be cheaper than binding new mesh data,
|
2012-04-03 20:44:46 +02:00
|
|
|
* and shader changes are assumed to be most expensive.)
|
|
|
|
* First, group all models that share a technique to render them together.
|
|
|
|
* Within those groups, sub-group by CModelDef.
|
|
|
|
* Within those sub-groups, sub-sub-group by CTexture.
|
2012-04-08 17:55:06 +02:00
|
|
|
* Within those sub-sub-groups, sub-sub-sub-group by CShaderUniforms.
|
2012-04-03 20:44:46 +02:00
|
|
|
*
|
|
|
|
* Alpha-blended models have to be sorted by distance from camera,
|
|
|
|
* then we can batch as long as the order is preserved.
|
|
|
|
* Non-alpha-blended models can be arbitrarily reordered to maximise batching.
|
|
|
|
*
|
|
|
|
* For each model, the CShaderTechnique is derived from:
|
|
|
|
* - The current global 'context' defines
|
|
|
|
* - The CModel's material's defines
|
|
|
|
* - The CModel's material's shader effect name
|
|
|
|
*
|
|
|
|
* There are a smallish number of materials, and a smaller number of techniques.
|
|
|
|
*
|
|
|
|
* To minimise technique lookups, we first group models by material,
|
|
|
|
* in 'materialBuckets' (a hash table).
|
|
|
|
*
|
|
|
|
* For each material bucket we then look up the appropriate shader technique.
|
|
|
|
* If the technique requires sort-by-distance, the model is added to the
|
|
|
|
* 'sortByDistItems' list with its computed distance.
|
2012-04-08 17:55:06 +02:00
|
|
|
* Otherwise, the bucket's list of models is sorted by modeldef+texture+uniforms,
|
2012-04-03 20:44:46 +02:00
|
|
|
* then the technique and model list is added to 'techBuckets'.
|
|
|
|
*
|
|
|
|
* 'techBuckets' is then sorted by technique, to improve batching when multiple
|
|
|
|
* materials map onto the same technique.
|
|
|
|
*
|
|
|
|
* (Note that this isn't perfect batching: we don't sort across models in
|
|
|
|
* multiple buckets that share a technique. In practice that shouldn't reduce
|
|
|
|
* batching much (we rarely have one mesh used with multiple materials),
|
|
|
|
* and it saves on copying and lets us sort smaller lists.)
|
|
|
|
*
|
|
|
|
* Extra tech buckets are added for the sorted-by-distance models without reordering.
|
|
|
|
* Finally we render by looping over each tech bucket, then looping over the model
|
|
|
|
* list in each, rebinding the GL state whenever it changes.
|
|
|
|
*/
|
|
|
|
|
2013-09-30 03:22:44 +02:00
|
|
|
Allocators::DynamicArena arena(256 * KiB);
|
|
|
|
typedef ProxyAllocator<void*, Allocators::DynamicArena > ArenaProxyAllocator;
|
2013-09-29 16:48:11 +02:00
|
|
|
typedef std::vector<CModel*, ArenaProxyAllocator> ModelList_t;
|
|
|
|
typedef boost::unordered_map<SMRMaterialBucketKey, ModelList_t, SMRMaterialBucketKeyHash,
|
2013-09-30 03:22:44 +02:00
|
|
|
std::equal_to<SMRMaterialBucketKey>, ProxyAllocator<void*, Allocators::DynamicArena >
|
2013-09-29 16:48:11 +02:00
|
|
|
> MaterialBuckets_t;
|
|
|
|
MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena)));
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
PROFILE3("bucketing by material");
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m->submissions.size(); ++i)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
CModel* model = m->submissions[i];
|
2013-09-29 02:30:58 +02:00
|
|
|
|
|
|
|
uint32_t condFlags = 0;
|
|
|
|
|
2013-09-29 04:05:02 +02:00
|
|
|
const CShaderConditionalDefines& condefs = model->GetMaterial().GetConditionalDefines();
|
2012-08-06 21:10:47 +02:00
|
|
|
for (size_t j = 0; j < condefs.GetSize(); ++j)
|
|
|
|
{
|
2013-09-29 16:48:11 +02:00
|
|
|
const CShaderConditionalDefines::CondDefine& item = condefs.GetItem(j);
|
2012-08-06 21:10:47 +02:00
|
|
|
int type = item.m_CondType;
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case DCOND_DISTANCE:
|
|
|
|
{
|
|
|
|
CVector3D modelpos = model->GetTransform().GetTranslation();
|
|
|
|
float dist = worldToCam.Transform(modelpos).Z;
|
|
|
|
|
|
|
|
float dmin = item.m_CondArgs[0];
|
|
|
|
float dmax = item.m_CondArgs[1];
|
|
|
|
|
|
|
|
if ((dmin < 0 || dist >= dmin) && (dmax < 0 || dist < dmax))
|
2013-09-29 02:30:58 +02:00
|
|
|
condFlags |= (1 << j);
|
2012-08-06 21:10:47 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-09-04 18:59:03 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
|
2013-09-29 02:30:58 +02:00
|
|
|
CShaderDefines defs = model->GetMaterial().GetShaderDefines(condFlags);
|
2012-08-06 21:10:47 +02:00
|
|
|
SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defs);
|
2013-09-29 16:48:11 +02:00
|
|
|
|
|
|
|
MaterialBuckets_t::iterator it = materialBuckets.find(key);
|
|
|
|
if (it == materialBuckets.end())
|
|
|
|
{
|
|
|
|
std::pair<MaterialBuckets_t::iterator, bool> inserted = materialBuckets.insert(
|
|
|
|
std::make_pair(key, ModelList_t(ModelList_t::allocator_type(arena))));
|
|
|
|
inserted.first->second.reserve(32);
|
|
|
|
inserted.first->second.push_back(model);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
it->second.push_back(model);
|
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<SMRSortByDistItem, ArenaProxyAllocator> sortByDistItems((ArenaProxyAllocator(arena)));
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<CShaderTechniquePtr, ArenaProxyAllocator> sortByDistTechs((ArenaProxyAllocator(arena)));
|
2012-04-03 20:44:46 +02:00
|
|
|
// indexed by sortByDistItems[i].techIdx
|
|
|
|
// (which stores indexes instead of CShaderTechniquePtr directly
|
|
|
|
// to avoid the shared_ptr copy cost when sorting; maybe it'd be better
|
|
|
|
// if we just stored raw CShaderTechnique* and assumed the shader manager
|
|
|
|
// will keep it alive long enough)
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<SMRTechBucket, ArenaProxyAllocator> techBuckets((ArenaProxyAllocator(arena)));
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
PROFILE3("processing material buckets");
|
|
|
|
for (MaterialBuckets_t::iterator it = materialBuckets.begin(); it != materialBuckets.end(); ++it)
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(it->first.effect, context, it->first.defines);
|
2012-04-08 17:55:06 +02:00
|
|
|
|
|
|
|
// Skip invalid techniques (e.g. from data file errors)
|
|
|
|
if (!tech)
|
|
|
|
continue;
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (tech->GetSortByDistance())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
// Add the tech into a vector so we can index it
|
|
|
|
// (There might be duplicates in this list, but that doesn't really matter)
|
|
|
|
if (sortByDistTechs.empty() || sortByDistTechs.back() != tech)
|
|
|
|
sortByDistTechs.push_back(tech);
|
|
|
|
size_t techIdx = sortByDistTechs.size()-1;
|
|
|
|
|
|
|
|
// Add each model into sortByDistItems
|
|
|
|
for (size_t i = 0; i < it->second.size(); ++i)
|
|
|
|
{
|
|
|
|
SMRSortByDistItem itemWithDist;
|
|
|
|
itemWithDist.techIdx = techIdx;
|
|
|
|
|
|
|
|
CModel* model = it->second[i];
|
|
|
|
itemWithDist.model = model;
|
|
|
|
|
|
|
|
CVector3D modelpos = model->GetTransform().GetTranslation();
|
|
|
|
itemWithDist.dist = worldToCam.Transform(modelpos).Z;
|
|
|
|
|
|
|
|
sortByDistItems.push_back(itemWithDist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Sort model list by modeldef+texture, for batching
|
2012-07-24 00:49:46 +02:00
|
|
|
// TODO: This only sorts by base texture. While this is an OK approximation
|
|
|
|
// for most cases (as related samplers are usually used together), it would be better
|
|
|
|
// to take all the samplers into account when sorting here.
|
2012-04-03 20:44:46 +02:00
|
|
|
std::sort(it->second.begin(), it->second.end(), SMRBatchModel());
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Add a tech bucket pointing at this model list
|
|
|
|
SMRTechBucket techBucket = { tech, &it->second[0], it->second.size() };
|
|
|
|
techBuckets.push_back(techBucket);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
{
|
|
|
|
PROFILE3("sorting tech buckets");
|
|
|
|
// Sort by technique, for better batching
|
|
|
|
std::sort(techBuckets.begin(), techBuckets.end(), SMRCompareTechBucket());
|
|
|
|
}
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// List of models corresponding to sortByDistItems[i].model
|
|
|
|
// (This exists primarily because techBuckets wants a CModel**;
|
|
|
|
// we could avoid the cost of copying into this list by adding
|
|
|
|
// a stride length into techBuckets and not requiring contiguous CModel*s)
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<CModel*, ArenaProxyAllocator> sortByDistModels((ArenaProxyAllocator(arena)));
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (!sortByDistItems.empty())
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
PROFILE3("sorting items by dist");
|
|
|
|
std::sort(sortByDistItems.begin(), sortByDistItems.end(), SMRCompareSortByDistItem());
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
{
|
|
|
|
PROFILE3("batching dist-sorted items");
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
sortByDistModels.reserve(sortByDistItems.size());
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Find runs of distance-sorted models that share a technique,
|
|
|
|
// and create a new tech bucket for each run
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
size_t start = 0; // start of current run
|
|
|
|
size_t currentTechIdx = sortByDistItems[start].techIdx;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
for (size_t end = 0; end < sortByDistItems.size(); ++end)
|
|
|
|
{
|
|
|
|
sortByDistModels.push_back(sortByDistItems[end].model);
|
|
|
|
|
|
|
|
size_t techIdx = sortByDistItems[end].techIdx;
|
|
|
|
if (techIdx != currentTechIdx)
|
|
|
|
{
|
|
|
|
// Start of a new run - push the old run into a new tech bucket
|
|
|
|
SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], end-start };
|
|
|
|
techBuckets.push_back(techBucket);
|
|
|
|
start = end;
|
|
|
|
currentTechIdx = techIdx;
|
|
|
|
}
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Add the tech bucket for the final run
|
|
|
|
SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], sortByDistItems.size()-start };
|
|
|
|
techBuckets.push_back(techBucket);
|
|
|
|
}
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2005-10-25 03:43:07 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
PROFILE3("rendering bucketed submissions");
|
2006-03-26 01:54:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
size_t idxTechStart = 0;
|
2012-07-24 00:49:46 +02:00
|
|
|
|
|
|
|
// This vector keeps track of texture changes during rendering. It is kept outside the
|
|
|
|
// loops to avoid excessive reallocations. The token allocation of 64 elements
|
|
|
|
// should be plenty, though it is reallocated below (at a cost) if necessary.
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<CTexture*, ArenaProxyAllocator> currentTexs((ArenaProxyAllocator(arena)));
|
2012-07-24 00:49:46 +02:00
|
|
|
currentTexs.reserve(64);
|
|
|
|
|
|
|
|
// texBindings holds the identifier bindings in the shader, which can no longer be defined
|
|
|
|
// statically in the ShaderRenderModifier class. texBindingNames uses interned strings to
|
|
|
|
// keep track of when bindings need to be reevaluated.
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<CShaderProgram::Binding, ArenaProxyAllocator> texBindings((ArenaProxyAllocator(arena)));
|
2012-07-24 00:49:46 +02:00
|
|
|
texBindings.reserve(64);
|
2013-09-29 16:48:11 +02:00
|
|
|
std::vector<CStrIntern, ArenaProxyAllocator> texBindingNames((ArenaProxyAllocator(arena)));
|
2012-07-24 00:49:46 +02:00
|
|
|
texBindingNames.reserve(64);
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
while (idxTechStart < techBuckets.size())
|
|
|
|
{
|
|
|
|
CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
|
2005-11-06 00:15:23 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Find runs [idxTechStart, idxTechEnd) in techBuckets of the same technique
|
|
|
|
size_t idxTechEnd;
|
|
|
|
for (idxTechEnd = idxTechStart + 1; idxTechEnd < techBuckets.size(); ++idxTechEnd)
|
|
|
|
{
|
|
|
|
if (techBuckets[idxTechEnd].tech != currentTech)
|
|
|
|
break;
|
|
|
|
}
|
2005-11-06 00:15:23 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// For each of the technique's passes, render all the models in this run
|
|
|
|
for (int pass = 0; pass < currentTech->GetNumPasses(); ++pass)
|
|
|
|
{
|
|
|
|
currentTech->BeginPass(pass);
|
2005-11-06 00:15:23 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
const CShaderProgramPtr& shader = currentTech->GetShader(pass);
|
|
|
|
int streamflags = shader->GetStreamFlags();
|
2012-02-09 18:55:25 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
modifier->BeginPass(shader);
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->vertexRenderer->BeginPass(streamflags);
|
2012-07-24 00:49:46 +02:00
|
|
|
|
|
|
|
// When the shader technique changes, textures need to be
|
|
|
|
// rebound, so ensure there are no remnants from the last pass.
|
|
|
|
// (the vector size is set to 0, but memory is not freed)
|
|
|
|
currentTexs.clear();
|
|
|
|
texBindings.clear();
|
|
|
|
texBindingNames.clear();
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CModelDef* currentModeldef = NULL;
|
2012-04-08 17:55:06 +02:00
|
|
|
CShaderUniforms currentStaticUniforms;
|
2005-10-25 03:43:07 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
for (size_t idx = idxTechStart; idx < idxTechEnd; ++idx)
|
|
|
|
{
|
|
|
|
CModel** models = techBuckets[idx].models;
|
|
|
|
size_t numModels = techBuckets[idx].numModels;
|
|
|
|
for (size_t i = 0; i < numModels; ++i)
|
|
|
|
{
|
|
|
|
CModel* model = models[i];
|
|
|
|
|
|
|
|
if (flags && !(model->GetFlags() & flags))
|
|
|
|
continue;
|
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
const CMaterial::SamplersVector& samplers = model->GetMaterial().GetSamplers();
|
2012-07-24 00:49:46 +02:00
|
|
|
size_t samplersNum = samplers.size();
|
|
|
|
|
2012-09-04 18:59:03 +02:00
|
|
|
// make sure the vectors are the right virtual sizes, and also
|
2012-07-24 00:49:46 +02:00
|
|
|
// reallocate if there are more samplers than expected.
|
|
|
|
if (currentTexs.size() != samplersNum)
|
|
|
|
{
|
|
|
|
currentTexs.resize(samplersNum, NULL);
|
|
|
|
texBindings.resize(samplersNum, CShaderProgram::Binding());
|
|
|
|
texBindingNames.resize(samplersNum, CStrIntern());
|
2012-08-25 23:41:31 +02:00
|
|
|
|
|
|
|
// ensure they are definitely empty
|
|
|
|
std::fill(texBindings.begin(), texBindings.end(), CShaderProgram::Binding());
|
|
|
|
std::fill(currentTexs.begin(), currentTexs.end(), (CTexture*)NULL);
|
|
|
|
std::fill(texBindingNames.begin(), texBindingNames.end(), CStrIntern());
|
2012-07-24 00:49:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// bind the samplers to the shader
|
|
|
|
for (size_t s = 0; s < samplersNum; ++s)
|
2012-04-03 20:44:46 +02:00
|
|
|
{
|
2013-09-29 16:48:11 +02:00
|
|
|
const CMaterial::TextureSampler& samp = samplers[s];
|
2012-07-24 00:49:46 +02:00
|
|
|
|
|
|
|
CShaderProgram::Binding bind = texBindings[s];
|
|
|
|
// check that the handles are current
|
|
|
|
// and reevaluate them if necessary
|
|
|
|
if (texBindingNames[s] == samp.Name && bind.Active())
|
|
|
|
{
|
|
|
|
bind = texBindings[s];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-09-29 15:19:52 +02:00
|
|
|
bind = shader->GetTextureBinding(samp.Name);
|
2012-07-24 00:49:46 +02:00
|
|
|
texBindings[s] = bind;
|
|
|
|
texBindingNames[s] = samp.Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
// same with the actual sampler bindings
|
|
|
|
CTexture* newTex = samp.Sampler.get();
|
|
|
|
if (bind.Active() && newTex != currentTexs[s])
|
|
|
|
{
|
|
|
|
shader->BindTexture(bind, samp.Sampler->GetHandle());
|
|
|
|
currentTexs[s] = newTex;
|
|
|
|
}
|
2012-09-04 18:59:03 +02:00
|
|
|
}
|
2012-09-04 13:09:36 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
// Bind modeldef when it changes
|
|
|
|
CModelDef* newModeldef = model->GetModelDef().get();
|
2012-09-04 18:59:03 +02:00
|
|
|
if (newModeldef != currentModeldef)
|
2012-04-03 20:44:46 +02:00
|
|
|
{
|
|
|
|
currentModeldef = newModeldef;
|
|
|
|
m->vertexRenderer->PrepareModelDef(shader, streamflags, *currentModeldef);
|
|
|
|
}
|
|
|
|
|
2012-04-08 17:55:06 +02:00
|
|
|
// Bind all uniforms when any change
|
|
|
|
CShaderUniforms newStaticUniforms = model->GetMaterial().GetStaticUniforms();
|
2012-09-04 18:59:03 +02:00
|
|
|
if (newStaticUniforms != currentStaticUniforms)
|
2012-04-08 17:55:06 +02:00
|
|
|
{
|
|
|
|
currentStaticUniforms = newStaticUniforms;
|
|
|
|
currentStaticUniforms.BindUniforms(shader);
|
|
|
|
}
|
2012-08-06 21:10:47 +02:00
|
|
|
|
2013-09-29 16:48:11 +02:00
|
|
|
const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries();
|
2012-08-06 21:10:47 +02:00
|
|
|
|
|
|
|
for (size_t q = 0; q < renderQueries.GetSize(); q++)
|
|
|
|
{
|
|
|
|
CShaderRenderQueries::RenderQuery rq = renderQueries.GetItem(q);
|
|
|
|
if (rq.first == RQUERY_TIME)
|
|
|
|
{
|
|
|
|
CShaderProgram::Binding binding = shader->GetUniformBinding(rq.second);
|
|
|
|
if (binding.Active())
|
|
|
|
{
|
|
|
|
double time = g_Renderer.GetTimeManager().GetGlobalTime();
|
|
|
|
shader->Uniform(binding, time, 0,0,0);
|
|
|
|
}
|
|
|
|
}
|
2012-09-08 20:56:13 +02:00
|
|
|
else if (rq.first == RQUERY_WATER_TEX)
|
|
|
|
{
|
|
|
|
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
|
|
|
|
double time = WaterMgr->m_WaterTexTimer;
|
|
|
|
double period = 1.6;
|
|
|
|
int curTex = (int)(time*60/period) % 60;
|
|
|
|
|
2012-09-16 13:39:04 +02:00
|
|
|
if (WaterMgr->m_RenderWater && WaterMgr->WillRenderFancyWater())
|
2013-09-29 15:19:52 +02:00
|
|
|
shader->BindTexture(str_waterTex, WaterMgr->m_NormalMap[curTex]);
|
2012-09-16 13:39:04 +02:00
|
|
|
else
|
2013-09-29 15:19:52 +02:00
|
|
|
shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture());
|
2012-09-08 20:56:13 +02:00
|
|
|
}
|
|
|
|
else if (rq.first == RQUERY_SKY_CUBE)
|
|
|
|
{
|
2013-09-29 15:19:52 +02:00
|
|
|
shader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
|
2012-09-08 20:56:13 +02:00
|
|
|
}
|
2012-08-06 21:10:47 +02:00
|
|
|
}
|
2012-04-08 17:55:06 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
modifier->PrepareModel(shader, model);
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
|
|
|
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->vertexRenderer->RenderModel(shader, streamflags, model, rdata);
|
|
|
|
}
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
m->vertexRenderer->EndPass(streamflags);
|
|
|
|
|
|
|
|
currentTech->EndPass(pass);
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
2012-04-03 20:44:46 +02:00
|
|
|
|
|
|
|
idxTechStart = idxTechEnd;
|
2005-10-25 03:43:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
void ShaderModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
|
2011-07-13 01:48:05 +02:00
|
|
|
{
|
2012-04-03 20:44:46 +02:00
|
|
|
for (size_t i = 0; i < m->submissions.size(); ++i)
|
|
|
|
{
|
|
|
|
CModel* model = m->submissions[i];
|
2011-07-13 01:48:05 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (flags && !(model->GetFlags() & flags))
|
|
|
|
continue;
|
2011-07-13 01:48:05 +02:00
|
|
|
|
2012-04-03 20:44:46 +02:00
|
|
|
if (filter.Filter(model))
|
|
|
|
model->SetFlags(model->GetFlags() | passed);
|
|
|
|
else
|
|
|
|
model->SetFlags(model->GetFlags() & ~passed);
|
2011-07-13 01:48:05 +02:00
|
|
|
}
|
|
|
|
}
|