Index: ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 23940)
+++ ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 23941)
@@ -1,272 +1,273 @@
{
"Title": "Programming",
"Content": [
{
"Title": "Programming managers",
"List": [
{ "nick": "Acumen", "name": "Stuart Walpole" },
{ "nick": "Dak Lozar", "name": "Dave Loeser" },
{ "nick": "h20", "name": "Daniel Wilhelm" },
{ "nick": "Janwas", "name": "Jan Wassenberg" },
{ "nick": "Raj", "name": "Raj Sharma" }
]
},
{
"Subtitle": "Special thanks to",
"List": [
{ "nick": "leper", "name": "Georg Kilzer" },
{ "nick": "Ykkrosh", "name": "Philip Taylor" }
]
},
{
"List": [
{ "nick": "01d55" },
{ "nick": "aBothe", "name": "Alexander Bothe" },
{ "nick": "Acumen", "name": "Stuart Walpole" },
{ "nick": "adrian", "name": "Adrian Boguszewszki" },
{ "name": "Adrian Fatol" },
{ "nick": "AI-Amsterdam" },
{ "nick": "Alan", "name": "Alan Kemp" },
{ "nick": "Alex", "name": "Alexander Yakobovich" },
{ "nick": "alpha123", "name": "Peter P. Cannici" },
{ "nick": "Ampaex", "name": "Antonio Vazquez" },
{ "name": "André Puel" },
{ "nick": "andy5995", "name": "Andy Alt" },
{ "nick": "Angen" },
{ "nick": "ArnH", "name": "Arno Hemelhof" },
{ "nick": "Aurium", "name": "Aurélio Heckert" },
{ "nick": "badmadblacksad", "name": "Martin F" },
{ "nick": "badosu", "name": "Amadeus Folego" },
{ "nick": "bb", "name": "Bouke Jansen" },
{ "nick": "Ben", "name": "Ben Vinegar" },
{ "nick": "Bird" },
{ "nick": "Blue", "name": "Richard Welsh" },
{ "nick": "bmwiedemann" },
{ "nick": "boeseRaupe", "name": "Michael Kluge" },
{ "nick": "bog_dan_ro", "name": "BogDan Vatra" },
{ "nick": "Bonk", "name": "Christopher Ebbert" },
{ "nick": "Boudica" },
{ "nick": "Caius", "name": "Lars Kemmann" },
{ "nick": "Calefaction", "name": "Matt Holmes" },
{ "nick": "Calvinh", "name": "Carl-Johan Höiby" },
{ "nick": "causative", "name": "Bart Parkis" },
{ "name": "Cédric Houbart" },
{ "nick": "Chakakhan", "name": "Kenny Long" },
{ "nick": "Clockwork-Muse", "name": "Stephen A. Imhoff" },
{ "nick": "Cracker78", "name": "Chad Heim" },
{ "nick": "Crynux", "name": "Stephen J. Fewer" },
{ "nick": "cwprogger" },
{ "nick": "cygal", "name": "Quentin Pradet" },
{ "nick": "Dak Lozar", "name": "Dave Loeser" },
{ "nick": "dalerank", "name": "Sergey Kushnirenko" },
{ "nick": "dan", "name": "Dan Strandberg" },
{ "nick": "DanCar", "name": "Daniel Cardenas" },
{ "nick": "danger89", "name": "Melroy van den Berg" },
{ "name": "Daniel Trevitz" },
{ "nick": "Dariost", "name": "Dario Ostuni" },
{ "nick": "Dave", "name": "David Protasowski" },
{ "nick": "dax", "name": "Dacian Fiordean" },
{ "nick": "deebee", "name": "Deepak Anthony" },
{ "nick": "Deiz" },
{ "nick": "Dietger", "name": "Dietger van Antwerpen" },
{ "nick": "DigitalSeraphim", "name": "Nick Owens" },
{ "nick": "dp304" },
{ "nick": "dpiquet", "name": "Damien Piquet" },
{ "nick": "dumbo" },
{ "nick": "Dunedan", "name": "Daniel Roschka" },
{ "nick": "dvangennip", "name": "Doménique" },
{ "nick": "Echelon9", "name": "Rhys Kidd" },
{ "nick": "echotangoecho" },
{ "nick": "eihrul", "name": "Lee Salzman" },
{ "nick": "elexis", "name": "Alexander Heinsius" },
{ "nick": "EmjeR", "name": "Matthijs de Rijk" },
{ "nick": "EMontana" },
{ "nick": "ericb" },
{ "nick": "evanssthomas", "name": "Evans Thomas" },
{ "nick": "Evulant", "name": "Alexander S." },
{ "nick": "fabio", "name": "Fabio Pedretti" },
{ "nick": "falsevision", "name": "Mahdi Khodadadifard" },
{ "nick": "fatherbushido", "name": "Nicolas Tisserand" },
{ "nick": "fcxSanya", "name": "Alexander Olkhovskiy" },
{ "nick": "FeXoR", "name": "Florian Finke" },
{ "nick": "Fire Giant", "name": "Malte Schwarzkopf" },
{ "name": "Fork AD" },
{ "nick": "fpre", "name": "Frederick Stallmeyer" },
{ "nick": "Freagarach" },
{ "nick": "freenity", "name": "Anton Galitch" },
{ "nick": "Gallaecio", "name": "Adrián Chaves" },
{ "nick": "gbish (aka Iny)", "name": "Grant Bishop" },
{ "nick": "Gee", "name": "Gustav Larsson" },
{ "nick": "Gentz", "name": "Hal Gentz" },
{ "nick": "gerbilOFdoom" },
{ "nick": "godlikeldh" },
{ "nick": "greybeard", "name": "Joe Cocovich" },
{ "nick": "grillaz" },
{ "nick": "Grugnas", "name": "Giuseppe Tranchese" },
{ "nick": "gudo" },
{ "nick": "Guuts", "name": "Matthew Guttag" },
{ "nick": "h20", "name": "Daniel Wilhelm" },
{ "nick": "Hannibal_Barca", "name": "Clive Juhász S." },
{ "nick": "Haommin" },
{ "nick": "happyconcepts", "name": "Ben Bird" },
{ "nick": "historic_bruno", "name": "Ben Brian" },
{ "nick": "idanwin" },
{ "nick": "Imarok", "name": "J. S." },
{ "nick": "Inari" },
{ "nick": "infyquest", "name": "Vijay Kiran Kamuju" },
{ "nick": "irishninja", "name": "Brian Broll" },
{ "nick": "IronNerd", "name": "Matthew McMullan" },
{ "nick": "Itms", "name": "Nicolas Auvray" },
{ "nick": "Jaison", "name": "Marco tom Suden" },
{ "nick": "jammus", "name": "James Scott" },
{ "nick": "javiergodas", "name": "Javier Godas Vieitez" },
{ "nick": "Jgwman" },
{ "nick": "JonBaer", "name": "Jon Baer" },
{ "nick": "Josh", "name": "Joshua J. Bakita" },
{ "nick": "joskar", "name": "Johnny Oskarsson" },
{ "nick": "jP_wanN", "name": "Jonas Platte" },
{ "nick": "Jubalbarca", "name": "James Baillie" },
{ "nick": "JubJub", "name": "Sebastian Vetter" },
{ "nick": "jurgemaister" },
{ "nick": "kabzerek", "name": "Grzegorz Kabza" },
{ "nick": "Kai", "name": "Kai Chen" },
{ "name": "Kareem Ergawy" },
{ "nick": "kevmo", "name": "Kevin Caffrey" },
{ "nick": "kezz", "name": "Graeme Kerry" },
{ "nick": "kingadami", "name": "Adam Winsor" },
{ "nick": "kingbasil", "name": "Giannis Fafalios" },
{ "nick": "Krinkle", "name": "Timo Tijhof" },
{ "nick": "lafferjm", "name": "Justin Lafferty" },
{ "nick": "LeanderH", "name": "Leander Hemelhof" },
{ "nick": "leper", "name": "Georg Kilzer" },
{ "nick": "Link Mauve", "name": "Emmanuel Gil Peyrot" },
{ "nick": "LittleDev" },
{ "nick": "livingaftermidnight", "name": "Will Dull" },
{ "nick": "Louhike" },
{ "nick": "lsdh" },
{ "nick": "Ludovic", "name": "Ludovic Rousseau" },
{ "nick": "luiko", "name": "Luis Carlos Garcia Barajas" },
{ "nick": "m0l0t0ph", "name": "Christoph Gielisch" },
{ "nick": "madmax", "name": "Abhijit Nandy" },
{ "nick": "madpilot", "name": "Guido Falsi" },
{ "nick": "markcho" },
{ "nick": "MarkT", "name": "Mark Thompson" },
{ "nick": "Markus" },
{ "nick": "Mate-86", "name": "Mate Kovacs" },
{ "nick": "Matei", "name": "Matei Zaharia" },
{ "nick": "MattDoerksen", "name": "Matt Doerksen" },
{ "nick": "mattlott", "name": "Matt Lott" },
{ "nick": "maveric", "name": "Anton Protko" },
{ "nick": "Micnasty", "name": "Travis Gorkin" },
{ "name": "Mikołaj \"Bajter\" Korcz" },
{ "nick": "mimo" },
{ "nick": "mk12", "name": "Mitchell Kember" },
{ "nick": "mmayfield45", "name": "Michael Mayfield" },
{ "nick": "mmoanis", "name": "Mohamed Moanis" },
{ "nick": "Molotov", "name": "Dario Alvarez" },
{ "nick": "mpmoreti", "name": "Marcos Paulo Moreti" },
{ "nick": "mreiland", "name": "Michael Reiland" },
{ "nick": "myconid" },
{ "nick": "nani", "name": "S. N." },
{ "nick": "nd3c3nt", "name": "Gavin Fowler" },
{ "nick": "nephele" },
{ "nick": "Nescio" },
{ "nick": "niektb", "name": "Niek ten Brinke" },
{ "nick": "njm" },
{ "nick": "NoMonkey", "name": "John Mena" },
{ "nick": "norsnor" },
{ "nick": "notpete", "name": "Rich Cross" },
{ "nick": "nwtour" },
{ "nick": "odoaker", "name": "Ágoston Sipos" },
{ "nick": "Offensive ePeen", "name": "Jared Ryan Bills" },
{ "nick": "Ols", "name": "Oliver Whiteman" },
{ "nick": "olsner", "name": "Simon Brenner" },
{ "nick": "OptimusShepard", "name": "Pirmin Stanglmeier" },
{ "nick": "otero" },
{ "nick": "Palaxin", "name": "David A. Freitag" },
{ "name": "Paul Withers" },
{ "nick": "paulobezerr", "name": "Paulo George Gomes Bezerra" },
{ "nick": "pcpa", "name": "Paulo Andrade" },
{ "nick": "Pendingchaos" },
{ "nick": "PeteVasi", "name": "Pete Vasiliauskas" },
{ "nick": "pilino1234" },
{ "nick": "PingvinBetyar", "name": "Schronk Tamás" },
{ "nick": "plugwash", "name": "Peter Michael Green" },
{ "nick": "Polakrity" },
{ "nick": "Poya", "name": "Poya Manouchehri" },
{ "nick": "prefect", "name": "Nicolai Hähnle" },
{ "nick": "Prodigal Son" },
{ "nick": "pstumpf", "name": "Pascal Stumpf" },
{ "nick": "pyrolink", "name": "Andrew Decker" },
{ "nick": "quantumstate", "name": "Jonathan Waller" },
{ "nick": "QuickShot", "name": "Walter Krawec" },
{ "nick": "quonter" },
{ "nick": "qwertz" },
{ "nick": "Radagast" },
{ "nick": "Raj", "name": "Raj Sharma" },
{ "nick": "rapidelectron", "name": "Christian Weihsbach" },
{ "nick": "RedFox", "name": "Jorma Rebane" },
{ "nick": "RefinedCode" },
{ "nick": "Riemer" },
{ "name": "Rolf Sievers" },
{ "nick": "s0600204", "name": "Matthew Norwood" },
{ "nick": "sacha_vrand", "name": "Sacha Vrand" },
{ "nick": "SafaAlfulaij" },
{ "name": "Samuel Guarnieri" },
{ "nick": "Sandarac" },
{ "nick": "sanderd17", "name": "Sander Deryckere" },
{ "nick": "sathyam", "name": "Sathyam Vellal" },
{ "nick": "sbirmi", "name": "Sharad Birmiwal" },
{ "nick": "sbte", "name": "Sven Baars" },
{ "nick": "scroogie", "name": "André Gemünd" },
{ "nick": "scythetwirler", "name": "Casey X." },
{ "nick": "serveurix" },
{ "nick": "Shane", "name": "Shane Grant" },
{ "nick": "shh" },
{ "nick": "Silk", "name": "Josh Godsiff" },
{ "nick": "silure" },
{ "nick": "Simikolon", "name": "Yannick & Simon" },
{ "nick": "smiley", "name": "M. L." },
{ "nick": "Spahbod", "name": "Omid Davoodi" },
{ "nick": "Stan", "name": "Stanislas Dolcini" },
{ "nick": "Stefan" },
+ { "nick": "StefanBruens", "name": "Stefan Brüns" },
{ "nick": "stilz", "name": "Sławomir Zborowski" },
{ "nick": "stwf", "name": "Steven Fuchs" },
{ "nick": "svott", "name": "Sven Ott" },
{ "nick": "t4nk004" },
{ "nick": "tau" },
{ "nick": "tbm", "name": "Martin Michlmayr" },
{ "nick": "Teiresias" },
{ "nick": "temple" },
{ "nick": "texane" },
{ "nick": "thamlett", "name": "Timothy Hamlett" },
{ "nick": "thedrunkyak", "name": "Dan Fuhr" },
{ "nick": "Tobbi" },
{ "nick": "TrinityDeath", "name": "Jethro Lu" },
{ "nick": "triumvir", "name": "Corin Schedler" },
{ "nick": "trompetin17", "name": "Juan Guillermo" },
{ "nick": "user1", "name": "A. C." },
{ "nick": "usey11" },
{ "nick": "vincent_c", "name": "Vincent Cheng" },
{ "nick": "vinhig", "name": "Vincent Higginson" },
{ "nick": "vladislavbelov", "name": "Vladislav Belov" },
{ "nick": "voroskoi" },
{ "nick": "vts", "name": "Jeroen DR" },
{ "nick": "wacko", "name": "Andrew Spiering" },
{ "nick": "WhiteTreePaladin", "name": "Brian Ashley" },
{ "nick": "wraitii", "name": "Lancelot de Ferrière le Vayer" },
{ "nick": "Xentelian", "name": "Mark Strawson" },
{ "nick": "Xienen", "name": "Dayle Flowers" },
{ "nick": "xtizer", "name": "Matt Green" },
{ "nick": "yashi", "name": "Yasushi Shoji" },
{ "nick": "Ykkrosh", "name": "Philip Taylor" },
{ "nick": "Yves" },
{ "nick": "Zeusthor", "name": "Jeffrey Tavares" },
{ "nick": "zoot" },
{ "nick": "zsol", "name": "Zsolt Dollenstein" },
{ "nick": "ztamas", "name": "Tamas Zolnai" },
{ "nick": "Zyi", "name": "Charles De Meulenaer" }
]
}
]
}
Index: ps/trunk/source/renderer/HWLightingModelRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/HWLightingModelRenderer.cpp (revision 23940)
+++ ps/trunk/source/renderer/HWLightingModelRenderer.cpp (revision 23941)
@@ -1,314 +1,314 @@
/* Copyright (C) 2020 Wildfire Games.
* 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 .
*/
#include "precompiled.h"
#include "lib/bits.h"
#include "lib/ogl.h"
#include "lib/sysdep/rtl.h"
#include "maths/Vector3D.h"
#include "graphics/Color.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/ShaderProgram.h"
#include "renderer/HWLightingModelRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/RenderModifiers.h"
#include "renderer/VertexArray.h"
struct ShaderModelDef : public CModelDefRPrivate
{
/// Indices are the same for all models, so share them
VertexIndexArray m_IndexArray;
/// Static per-CModelDef vertex array
VertexArray m_Array;
/// The number of UVs is determined by the model
std::vector m_UVs;
ShaderModelDef(const CModelDefPtr& mdef);
};
ShaderModelDef::ShaderModelDef(const CModelDefPtr& mdef)
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
size_t numVertices = mdef->GetNumVertices();
m_UVs.resize(mdef->GetNumUVsPerVertex());
for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); ++i)
{
m_UVs[i].type = GL_FLOAT;
m_UVs[i].elems = 2;
m_Array.AddAttribute(&m_UVs[i]);
}
m_Array.SetNumVertices(numVertices);
m_Array.Layout();
for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); ++i)
{
VertexArrayIterator UVit = m_UVs[i].GetIterator();
ModelRenderer::BuildUV(mdef, UVit, i);
}
m_Array.Upload();
m_Array.FreeBackingStore();
m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
m_IndexArray.Layout();
ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
struct ShaderModel : public CModelRData
{
/// Dynamic per-CModel vertex array
VertexArray m_Array;
/// Position and normals/lighting are recalculated on CPU every frame
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Normal; // valid iff cpuLighting == false
VertexArray::Attribute m_Color; // valid iff cpuLighting == true
ShaderModel(const void* key) : CModelRData(key), m_Array(GL_DYNAMIC_DRAW) { }
};
-struct ShaderModelRendererInternals
+struct ShaderModelVertexRenderer::ShaderModelRendererInternals
{
bool cpuLighting;
/**
* Scratch space for normal vector calculation.
* Only used if cpuLighting == true.
* Space is reserved so we don't have to do frequent reallocations.
* Allocated with rtl_AllocateAligned(normalsNumVertices*16, 16) for SSE writes.
*/
char* normals;
size_t normalsNumVertices;
/// Previously prepared modeldef
ShaderModelDef* shadermodeldef;
};
// Construction and Destruction
ShaderModelVertexRenderer::ShaderModelVertexRenderer(bool cpuLighting)
{
m = new ShaderModelRendererInternals;
m->cpuLighting = cpuLighting;
m->normals = NULL;
m->normalsNumVertices = 0;
m->shadermodeldef = NULL;
}
ShaderModelVertexRenderer::~ShaderModelVertexRenderer()
{
rtl_FreeAligned(m->normals);
delete m;
}
// Build model data (and modeldef data if necessary)
CModelRData* ShaderModelVertexRenderer::CreateModelData(const void* key, CModel* model)
{
CModelDefPtr mdef = model->GetModelDef();
ShaderModelDef* shadermodeldef = (ShaderModelDef*)mdef->GetRenderData(m);
if (!shadermodeldef)
{
shadermodeldef = new ShaderModelDef(mdef);
mdef->SetRenderData(m, shadermodeldef);
}
// Build the per-model data
ShaderModel* shadermodel = new ShaderModel(key);
if (m->cpuLighting)
{
// Positions must be 16-byte aligned for SSE writes.
// We can pack the color after the position; it will be corrupted by
// BuildPositionAndNormals, but that's okay since we'll recompute the
// colors afterwards.
shadermodel->m_Color.type = GL_UNSIGNED_BYTE;
shadermodel->m_Color.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Color);
shadermodel->m_Position.type = GL_FLOAT;
shadermodel->m_Position.elems = 3;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Position);
}
else
{
// Positions and normals must be 16-byte aligned for SSE writes.
shadermodel->m_Position.type = GL_FLOAT;
shadermodel->m_Position.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Position);
shadermodel->m_Normal.type = GL_FLOAT;
shadermodel->m_Normal.elems = 4;
shadermodel->m_Array.AddAttribute(&shadermodel->m_Normal);
}
shadermodel->m_Array.SetNumVertices(mdef->GetNumVertices());
shadermodel->m_Array.Layout();
// Verify alignment
ENSURE(shadermodel->m_Position.offset % 16 == 0);
if (!m->cpuLighting)
ENSURE(shadermodel->m_Normal.offset % 16 == 0);
ENSURE(shadermodel->m_Array.GetStride() % 16 == 0);
return shadermodel;
}
// Fill in and upload dynamic vertex array
void ShaderModelVertexRenderer::UpdateModelData(CModel* model, CModelRData* data, int updateflags)
{
ShaderModel* shadermodel = static_cast(data);
if (!m->cpuLighting && (updateflags & RENDERDATA_UPDATE_VERTICES))
{
// build vertices
VertexArrayIterator Position = shadermodel->m_Position.GetIterator();
VertexArrayIterator Normal = shadermodel->m_Normal.GetIterator();
ModelRenderer::BuildPositionAndNormals(model, Position, Normal);
// upload everything to vertex buffer
shadermodel->m_Array.Upload();
}
if (m->cpuLighting && (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR)))
{
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
// allocate working space for computing normals
if (numVertices > m->normalsNumVertices)
{
rtl_FreeAligned(m->normals);
size_t newSize = round_up_to_pow2(numVertices);
m->normals = (char*)rtl_AllocateAligned(newSize*16, 16);
m->normalsNumVertices = newSize;
}
VertexArrayIterator Position = shadermodel->m_Position.GetIterator();
VertexArrayIterator Normal = VertexArrayIterator(m->normals, 16);
ModelRenderer::BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator Color = shadermodel->m_Color.GetIterator();
ModelRenderer::BuildColor4ub(model, Normal, Color);
// upload everything to vertex buffer
shadermodel->m_Array.Upload();
}
shadermodel->m_Array.PrepareForRendering();
}
// Setup one rendering pass
void ShaderModelVertexRenderer::BeginPass(int streamflags)
{
if (m->cpuLighting)
ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_COLOR)));
else
ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_NORMAL)));
}
// Cleanup one rendering pass
void ShaderModelVertexRenderer::EndPass(int UNUSED(streamflags))
{
CVertexBuffer::Unbind();
}
// Prepare UV coordinates for this modeldef
void ShaderModelVertexRenderer::PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def)
{
m->shadermodeldef = (ShaderModelDef*)def.GetRenderData(m);
ENSURE(m->shadermodeldef);
u8* base = m->shadermodeldef->m_Array.Bind();
GLsizei stride = (GLsizei)m->shadermodeldef->m_Array.GetStride();
if (streamflags & STREAM_UV0)
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->shadermodeldef->m_UVs[0].offset);
if ((streamflags & STREAM_UV1) && def.GetNumUVsPerVertex() >= 2)
shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m->shadermodeldef->m_UVs[1].offset);
}
// Render one model
void ShaderModelVertexRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data)
{
const CModelDefPtr& mdldef = model->GetModelDef();
ShaderModel* shadermodel = static_cast(data);
u8* base = shadermodel->m_Array.Bind();
GLsizei stride = (GLsizei)shadermodel->m_Array.GetStride();
u8* indexBase = m->shadermodeldef->m_IndexArray.Bind();
if (streamflags & STREAM_POS)
shader->VertexPointer(3, GL_FLOAT, stride, base + shadermodel->m_Position.offset);
if (streamflags & STREAM_NORMAL)
shader->NormalPointer(GL_FLOAT, stride, base + shadermodel->m_Normal.offset);
if (streamflags & STREAM_COLOR)
shader->ColorPointer(3, GL_UNSIGNED_BYTE, stride, base + shadermodel->m_Color.offset);
shader->AssertPointersBound();
// render the lot
size_t numFaces = mdldef->GetNumFaces();
if (!g_Renderer.m_SkipSubmit)
{
// Draw with DrawRangeElements where available, since it might be more efficient
#if CONFIG2_GLES
glDrawElements(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase);
#else
pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1,
(GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase);
#endif
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_ModelTris += numFaces;
}
Index: ps/trunk/source/renderer/HWLightingModelRenderer.h
===================================================================
--- ps/trunk/source/renderer/HWLightingModelRenderer.h (revision 23940)
+++ ps/trunk/source/renderer/HWLightingModelRenderer.h (revision 23941)
@@ -1,55 +1,54 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* 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 .
*/
/*
* ModelVertexRenderer that transforms models on the CPU but performs
* lighting in a vertex shader.
*/
#ifndef INCLUDED_HWLIGHTINGMODELRENDERER
#define INCLUDED_HWLIGHTINGMODELRENDERER
#include "renderer/ModelVertexRenderer.h"
-struct ShaderModelRendererInternals;
-
/**
* Render animated models using a ShaderRenderModifier.
* This computes and binds per-vertex data; the modifier is responsible
* for setting any shader uniforms etc.
*/
class ShaderModelVertexRenderer : public ModelVertexRenderer
{
public:
ShaderModelVertexRenderer(bool cpuLighting);
~ShaderModelVertexRenderer();
// Implementations
CModelRData* CreateModelData(const void* key, CModel* model);
void UpdateModelData(CModel* model, CModelRData* data, int updateflags);
void BeginPass(int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def);
void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data);
protected:
+ struct ShaderModelRendererInternals;
ShaderModelRendererInternals* m;
};
#endif // INCLUDED_HWLIGHTINGMODELRENDERER
Index: ps/trunk/source/renderer/ModelRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/ModelRenderer.cpp (revision 23940)
+++ ps/trunk/source/renderer/ModelRenderer.cpp (revision 23941)
@@ -1,773 +1,773 @@
/* Copyright (C) 2020 Wildfire Games.
* 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 .
*/
#include "precompiled.h"
#include "graphics/Color.h"
#include "graphics/LightEnv.h"
#include "graphics/Material.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextureManager.h"
#include "lib/allocators/allocator_adapters.h"
#include "lib/allocators/arena.h"
#include "lib/hash.h"
#include "lib/ogl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "renderer/MikktspaceWrap.h"
#include "renderer/ModelRenderer.h"
#include "renderer/ModelVertexRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/RenderModifiers.h"
#include "renderer/SkyManager.h"
#include "renderer/TimeManager.h"
#include "renderer/WaterManager.h"
#if ARCH_X86_X64
# include "lib/sysdep/arch/x86_x64/x86_x64.h"
#endif
///////////////////////////////////////////////////////////////////////////////////////////////
// ModelRenderer implementation
#if ARCH_X86_X64
static bool g_EnableSSE = false;
#endif
void ModelRenderer::Init()
{
#if ARCH_X86_X64
if (x86_x64::Cap(x86_x64::CAP_SSE))
g_EnableSSE = true;
#endif
}
// Helper function to copy object-space position and normal vectors into arrays.
void ModelRenderer::CopyPositionAndNormals(
const CModelDefPtr& mdef,
const VertexArrayIterator& Position,
const VertexArrayIterator& Normal)
{
size_t numVertices = mdef->GetNumVertices();
SModelVertex* vertices = mdef->GetVertices();
for (size_t j = 0; j < numVertices; ++j)
{
Position[j] = vertices[j].m_Coords;
Normal[j] = vertices[j].m_Norm;
}
}
// Helper function to transform position and normal vectors into world-space.
void ModelRenderer::BuildPositionAndNormals(
CModel* model,
const VertexArrayIterator& Position,
const VertexArrayIterator& Normal)
{
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
SModelVertex* vertices = mdef->GetVertices();
if (model->IsSkinned())
{
// boned model - calculate skinned vertex positions/normals
// Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
// some broken situations
if (numVertices && vertices[0].m_Blend.m_Bone[0] == 0xff)
{
LOGERROR("Model %s is boned with unboned animation", mdef->GetName().string8());
return;
}
#if HAVE_SSE
if (g_EnableSSE)
{
CModelDef::SkinPointsAndNormals_SSE(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
}
else
#endif
{
CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
}
}
else
{
PROFILE("software transform");
// just copy regular positions, transform normals to world space
const CMatrix3D& transform = model->GetTransform();
const CMatrix3D& invtransform = model->GetInvTransform();
for (size_t j = 0; j < numVertices; ++j)
{
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,
const VertexArrayIterator& Normal,
const VertexArrayIterator& Color)
{
PROFILE("lighting vertices");
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CColor shadingColor = model->GetShadingColor();
for (size_t j = 0; j < numVertices; ++j)
{
RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]);
tempcolor.X *= shadingColor.r;
tempcolor.Y *= shadingColor.g;
tempcolor.Z *= shadingColor.b;
Color[j] = ConvertRGBColorTo4ub(tempcolor);
}
}
void ModelRenderer::GenTangents(const CModelDefPtr& mdef, std::vector& newVertices, bool gpuSkinning)
{
MikkTSpace ms(mdef, newVertices, gpuSkinning);
ms.Generate();
}
// Copy UV coordinates
void ModelRenderer::BuildUV(
const CModelDefPtr& mdef,
const VertexArrayIterator& UV,
int UVset)
{
size_t numVertices = mdef->GetNumVertices();
SModelVertex* vertices = mdef->GetVertices();
for (size_t j = 0; j < numVertices; ++j)
{
UV[j][0] = vertices[j].m_UVs[UVset * 2];
UV[j][1] = 1.0 - vertices[j].m_UVs[UVset * 2 + 1];
}
}
// Build default indices array.
void ModelRenderer::BuildIndices(
const CModelDefPtr& mdef,
const VertexArrayIterator& Indices)
{
size_t idxidx = 0;
SModelFace* faces = mdef->GetFaces();
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];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// ShaderModelRenderer implementation
/**
* Internal data of the ShaderModelRenderer.
*
* Separated into the source file to increase implementation hiding (and to
* avoid some causes of recompiles).
*/
-struct ShaderModelRendererInternals
+struct ShaderModelRenderer::ShaderModelRendererInternals
{
ShaderModelRendererInternals(ShaderModelRenderer* r) : m_Renderer(r) { }
/// Back-link to "our" renderer
ShaderModelRenderer* m_Renderer;
/// ModelVertexRenderer used for vertex transformations
ModelVertexRendererPtr vertexRenderer;
/// List of submitted models for rendering in this frame
std::vector submissions[CRenderer::CULL_MAX];
};
// Construction/Destruction
ShaderModelRenderer::ShaderModelRenderer(ModelVertexRendererPtr vertexrenderer)
{
m = new ShaderModelRendererInternals(this);
m->vertexRenderer = vertexrenderer;
}
ShaderModelRenderer::~ShaderModelRenderer()
{
delete m;
}
// Submit one model.
void ShaderModelRenderer::Submit(int cullGroup, CModel* model)
{
CModelRData* rdata = (CModelRData*)model->GetRenderData();
// 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);
}
m->submissions[cullGroup].push_back(model);
}
// Call update for all submitted models and enter the rendering phase
void ShaderModelRenderer::PrepareModels()
{
for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup)
{
for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
{
CModel* model = m->submissions[cullGroup][i];
model->ValidatePosition();
CModelRData* rdata = static_cast(model->GetRenderData());
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
m->vertexRenderer->UpdateModelData(model, rdata, rdata->m_UpdateFlags);
rdata->m_UpdateFlags = 0;
}
}
}
// Clear the submissions list
void ShaderModelRenderer::EndFrame()
{
for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup)
m->submissions[cullGroup].clear();
}
// Helper structs for ShaderModelRenderer::Render():
struct SMRSortByDistItem
{
size_t techIdx;
CModel* model;
float dist;
};
struct SMRBatchModel
{
bool operator()(CModel* a, CModel* b)
{
if (a->GetModelDef() < b->GetModelDef())
return true;
if (b->GetModelDef() < a->GetModelDef())
return false;
if (a->GetMaterial().GetDiffuseTexture() < b->GetMaterial().GetDiffuseTexture())
return true;
if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
return false;
return a->GetMaterial().GetStaticUniforms() < b->GetMaterial().GetStaticUniforms();
}
};
struct SMRCompareSortByDistItem
{
bool operator()(const SMRSortByDistItem& a, const SMRSortByDistItem& b)
{
// 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)
}
};
struct SMRMaterialBucketKey
{
SMRMaterialBucketKey(CStrIntern effect, const CShaderDefines& defines)
: effect(effect), defines(defines) { }
CStrIntern effect;
CShaderDefines defines;
bool operator==(const SMRMaterialBucketKey& b) const
{
return (effect == b.effect && defines == b.defines);
}
private:
SMRMaterialBucketKey& operator=(const SMRMaterialBucketKey&);
};
struct SMRMaterialBucketKeyHash
{
size_t operator()(const SMRMaterialBucketKey& key) const
{
size_t hash = 0;
hash_combine(hash, key.effect.GetHash());
hash_combine(hash, key.defines.GetHash());
return hash;
}
};
struct SMRTechBucket
{
CShaderTechniquePtr tech;
CModel** models;
size_t numModels;
// Model list is stored as pointers, not as a std::vector,
// so that sorting lists of this struct is fast
};
struct SMRCompareTechBucket
{
bool operator()(const SMRTechBucket& a, const SMRTechBucket& b)
{
return a.tech < b.tech;
}
};
void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags)
{
if (m->submissions[cullGroup].empty())
return;
CMatrix3D worldToCam;
g_Renderer.GetViewCamera().GetOrientation().GetInverse(worldToCam);
/*
* Rendering approach:
*
* m->submissions contains the list of CModels to render.
*
* The data we need to render a model is:
* - CShaderTechnique
* - CTexture
* - CShaderUniforms
* - CModelDef (mesh data)
* - CModel (model instance data)
*
* For efficient rendering, we need to batch the draw calls to minimise state changes.
* (Uniform and texture changes are assumed to be cheaper than binding new mesh data,
* 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.
* Within those sub-sub-groups, sub-sub-sub-group by CShaderUniforms.
*
* 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.
* Otherwise, the bucket's list of models is sorted by modeldef+texture+uniforms,
* 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.
*/
Allocators::DynamicArena arena(256 * KiB);
using ModelListAllocator = ProxyAllocator;
using ModelList_t = std::vector;
using MaterialBuckets_t = std::unordered_map<
SMRMaterialBucketKey,
ModelList_t,
SMRMaterialBucketKeyHash,
std::equal_to,
ProxyAllocator<
std::pair,
Allocators::DynamicArena> >;
MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena)));
{
PROFILE3("bucketing by material");
for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
{
CModel* model = m->submissions[cullGroup][i];
uint32_t condFlags = 0;
const CShaderConditionalDefines& condefs = model->GetMaterial().GetConditionalDefines();
for (size_t j = 0; j < condefs.GetSize(); ++j)
{
const CShaderConditionalDefines::CondDefine& item = condefs.GetItem(j);
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))
condFlags |= (1 << j);
break;
}
}
}
CShaderDefines defs = model->GetMaterial().GetShaderDefines(condFlags);
SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defs);
MaterialBuckets_t::iterator it = materialBuckets.find(key);
if (it == materialBuckets.end())
{
std::pair 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);
}
}
}
typedef ProxyAllocator SortByDistItemsAllocator;
std::vector sortByDistItems((SortByDistItemsAllocator(arena)));
typedef ProxyAllocator SortByTechItemsAllocator;
std::vector sortByDistTechs((SortByTechItemsAllocator(arena)));
// 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)
typedef ProxyAllocator TechBucketsAllocator;
std::vector techBuckets((TechBucketsAllocator(arena)));
{
PROFILE3("processing material buckets");
for (MaterialBuckets_t::iterator it = materialBuckets.begin(); it != materialBuckets.end(); ++it)
{
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(it->first.effect, context, it->first.defines);
// Skip invalid techniques (e.g. from data file errors)
if (!tech)
continue;
if (tech->GetSortByDistance())
{
// 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
// 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.
std::sort(it->second.begin(), it->second.end(), SMRBatchModel());
// Add a tech bucket pointing at this model list
SMRTechBucket techBucket = { tech, &it->second[0], it->second.size() };
techBuckets.push_back(techBucket);
}
}
}
{
PROFILE3("sorting tech buckets");
// Sort by technique, for better batching
std::sort(techBuckets.begin(), techBuckets.end(), SMRCompareTechBucket());
}
// 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)
std::vector sortByDistModels((ModelListAllocator(arena)));
if (!sortByDistItems.empty())
{
{
PROFILE3("sorting items by dist");
std::sort(sortByDistItems.begin(), sortByDistItems.end(), SMRCompareSortByDistItem());
}
{
PROFILE3("batching dist-sorted items");
sortByDistModels.reserve(sortByDistItems.size());
// Find runs of distance-sorted models that share a technique,
// and create a new tech bucket for each run
size_t start = 0; // start of current run
size_t currentTechIdx = sortByDistItems[start].techIdx;
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;
}
}
// Add the tech bucket for the final run
SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], sortByDistItems.size() - start };
techBuckets.push_back(techBucket);
}
}
{
PROFILE3("rendering bucketed submissions");
size_t idxTechStart = 0;
// 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.
typedef ProxyAllocator TextureListAllocator;
std::vector currentTexs((TextureListAllocator(arena)));
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.
typedef ProxyAllocator BindingListAllocator;
std::vector texBindings((BindingListAllocator(arena)));
texBindings.reserve(64);
typedef ProxyAllocator BindingNamesListAllocator;
std::vector texBindingNames((BindingNamesListAllocator(arena)));
texBindingNames.reserve(64);
while (idxTechStart < techBuckets.size())
{
CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
// 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;
}
// 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);
const CShaderProgramPtr& shader = currentTech->GetShader(pass);
int streamflags = shader->GetStreamFlags();
modifier->BeginPass(shader);
m->vertexRenderer->BeginPass(streamflags);
// 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();
CModelDef* currentModeldef = NULL;
CShaderUniforms currentStaticUniforms;
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;
const CMaterial::SamplersVector& samplers = model->GetMaterial().GetSamplers();
size_t samplersNum = samplers.size();
// make sure the vectors are the right virtual sizes, and also
// 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());
// 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());
}
// bind the samplers to the shader
for (size_t s = 0; s < samplersNum; ++s)
{
const CMaterial::TextureSampler& samp = samplers[s];
// check that the handles are current
// and reevaluate them if necessary
if (texBindingNames[s] != samp.Name || !texBindings[s].Active())
{
texBindings[s] = shader->GetTextureBinding(samp.Name);
texBindingNames[s] = samp.Name;
}
// same with the actual sampler bindings
CTexture* newTex = samp.Sampler.get();
if (texBindings[s].Active() && newTex != currentTexs[s])
{
shader->BindTexture(texBindings[s], newTex->GetHandle());
currentTexs[s] = newTex;
}
}
// Bind modeldef when it changes
CModelDef* newModeldef = model->GetModelDef().get();
if (newModeldef != currentModeldef)
{
currentModeldef = newModeldef;
m->vertexRenderer->PrepareModelDef(shader, streamflags, *currentModeldef);
}
// Bind all uniforms when any change
CShaderUniforms newStaticUniforms = model->GetMaterial().GetStaticUniforms();
if (newStaticUniforms != currentStaticUniforms)
{
currentStaticUniforms = newStaticUniforms;
currentStaticUniforms.BindUniforms(shader);
}
const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries();
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.0f, 0.0f, 0.0f);
}
}
else if (rq.first == RQUERY_WATER_TEX)
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = static_cast(time * 60.0 / period) % 60;
if (WaterMgr->m_RenderWater && WaterMgr->WillRenderFancyWater())
shader->BindTexture(str_waterTex, WaterMgr->m_NormalMap[curTex]);
else
shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture());
}
else if (rq.first == RQUERY_SKY_CUBE)
{
shader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
}
}
modifier->PrepareModel(shader, model);
CModelRData* rdata = static_cast(model->GetRenderData());
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
m->vertexRenderer->RenderModel(shader, streamflags, model, rdata);
}
}
m->vertexRenderer->EndPass(streamflags);
currentTech->EndPass(pass);
}
idxTechStart = idxTechEnd;
}
}
}
Index: ps/trunk/source/renderer/ModelRenderer.h
===================================================================
--- ps/trunk/source/renderer/ModelRenderer.h (revision 23940)
+++ ps/trunk/source/renderer/ModelRenderer.h (revision 23941)
@@ -1,276 +1,274 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* 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 .
*/
/*
* Home to the ModelRenderer class, an abstract base class that manages
* a per-frame list of submitted models, as well as simple helper
* classes.
*/
#ifndef INCLUDED_MODELRENDERER
#define INCLUDED_MODELRENDERER
#include
#include "graphics/MeshManager.h"
#include "graphics/RenderableObject.h"
#include "graphics/SColor.h"
#include "renderer/VertexArray.h"
class RenderModifier;
typedef shared_ptr RenderModifierPtr;
class LitRenderModifier;
typedef shared_ptr LitRenderModifierPtr;
class ModelVertexRenderer;
typedef shared_ptr ModelVertexRendererPtr;
class ModelRenderer;
typedef shared_ptr ModelRendererPtr;
class CModel;
class CShaderDefines;
/**
* Class CModelRData: Render data that is maintained per CModel.
* ModelRenderer implementations may derive from this class to store
* per-CModel data.
*
* The main purpose of this class over CRenderData is to track which
* ModelRenderer the render data belongs to (via the key that is passed
* to the constructor). When a model changes the renderer it uses
* (e.g. via run-time modification of the renderpath configuration),
* the old ModelRenderer's render data is supposed to be replaced by
* the new data.
*/
class CModelRData : public CRenderData
{
public:
CModelRData(const void* key) : m_Key(key) { }
/**
* GetKey: Retrieve the key that can be used to identify the
* ModelRenderer that created this data.
*
* @return The opaque key that was passed to the constructor.
*/
const void* GetKey() const { return m_Key; }
private:
/// The key for model renderer identification
const void* m_Key;
};
/**
* Class ModelRenderer: Abstract base class for all model renders.
*
* A ModelRenderer manages a per-frame list of models.
*
* It is supposed to be derived in order to create new ways in which
* the per-frame list of models can be managed (for batching, for
* transparent rendering, etc.) or potentially for rarely used special
* effects.
*
* A typical ModelRenderer will delegate vertex transformation/setup
* to a ModelVertexRenderer.
* It will delegate fragment stage setup to a RenderModifier.
*
* For most purposes, you should use a BatchModelRenderer with
* specialized ModelVertexRenderer and RenderModifier implementations.
*
* It is suggested that a derived class implement the provided generic
* Render function, however in some cases it may be necessary to supply
* a Render function with a different prototype.
*
* ModelRenderer also contains a number of static helper functions
* for building vertex arrays.
*/
class ModelRenderer
{
public:
ModelRenderer() { }
virtual ~ModelRenderer() { }
/**
* Initialise global settings.
* Should be called before using the class.
*/
static void Init();
/**
* Submit: Submit a model for rendering this frame.
*
* preconditions : The model must not have been submitted to any
* ModelRenderer in this frame. Submit may only be called
* after EndFrame and before PrepareModels.
*
* @param model The model that will be added to the list of models
* submitted this frame.
*/
virtual void Submit(int cullGroup, CModel* model) = 0;
/**
* PrepareModels: Calculate renderer data for all previously
* submitted models.
*
* Must be called before any rendering calls and after all models
* for this frame have been submitted.
*/
virtual void PrepareModels() = 0;
/**
* EndFrame: Remove all models from the list of submitted
* models.
*/
virtual void EndFrame() = 0;
/**
* Render: Render submitted models, using the given RenderModifier to setup
* the fragment stage.
*
* @note It is suggested that derived model renderers implement and use
* this Render functions. However, a highly specialized model renderer
* may need to "disable" this function and provide its own Render function
* with a different prototype.
*
* preconditions : PrepareModels must be called after all models have been
* submitted and before calling Render.
*
* @param modifier The RenderModifier that specifies the fragment stage.
* @param flags If flags is 0, all submitted models are rendered.
* If flags is non-zero, only models that contain flags in their
* CModel::GetFlags() are rendered.
*/
virtual void Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags) = 0;
/**
* CopyPositionAndNormals: Copy unanimated object-space vertices and
* normals into the given vertex array.
*
* @param mdef The underlying CModelDef that contains mesh data.
* @param Position Points to the array that will receive
* position vectors. The array behind the iterator
* must be large enough to hold model->GetModelDef()->GetNumVertices()
* vertices.
* @param Normal Points to the array that will receive normal vectors.
* The array behind the iterator must be as large as the Position array.
*/
static void CopyPositionAndNormals(
const CModelDefPtr& mdef,
const VertexArrayIterator& Position,
const VertexArrayIterator& Normal);
/**
* BuildPositionAndNormals: Build animated vertices and normals,
* transformed into world space.
*
* @param model The model that is to be transformed.
* @param Position Points to the array that will receive
* transformed position vectors. The array behind the iterator
* must be large enough to hold model->GetModelDef()->GetNumVertices()
* vertices. It must allow 16 bytes to be written to each element
* (i.e. provide 4 bytes of padding after each CVector3D).
* @param Normal Points to the array that will receive transformed
* normal vectors. The array behind the iterator must be as large as
* the Position array.
*/
static void BuildPositionAndNormals(
CModel* model,
const VertexArrayIterator& Position,
const VertexArrayIterator& Normal);
/**
* BuildColor4ub: Build lighting colors for the given model,
* based on previously calculated world space normals.
*
* @param model The model that is to be lit.
* @param Normal Array of the model's normal vectors, animated and
* transformed into world space.
* @param Color Points to the array that will receive the lit vertex color.
* The array behind the iterator must large enough to hold
* model->GetModelDef()->GetNumVertices() vertices.
*/
static void BuildColor4ub(
CModel* model,
const VertexArrayIterator& Normal,
const VertexArrayIterator& Color);
/**
* BuildUV: Copy UV coordinates into the given vertex array.
*
* @param mdef The model def.
* @param UV Points to the array that will receive UV coordinates.
* The array behind the iterator must large enough to hold
* mdef->GetNumVertices() vertices.
*/
static void BuildUV(
const CModelDefPtr& mdef,
const VertexArrayIterator& UV,
int UVset);
/**
* BuildIndices: Create the indices array for the given CModelDef.
*
* @param mdef The model definition object.
* @param Indices The index array, must be able to hold
* mdef->GetNumFaces()*3 elements.
*/
static void BuildIndices(
const CModelDefPtr& mdef,
const VertexArrayIterator& Indices);
/**
* GenTangents: Generate tangents for the given CModelDef.
*
* @param mdef The model definition object.
* @param newVertices An out vector of the unindexed vertices with tangents added.
* The new vertices cannot be used with existing face index and must be welded/reindexed.
*/
static void GenTangents(const CModelDefPtr& mdef, std::vector& newVertices, bool gpuSkinning);
};
-
-struct ShaderModelRendererInternals;
-
/**
* Implementation of ModelRenderer that loads the appropriate shaders for
* rendering each model, and that batches by shader (and by mesh and texture).
*
* Note that the term "Shader" is somewhat misleading, as this handled
* fixed-function rendering using the same API as real GLSL/ARB shaders.
*/
class ShaderModelRenderer : public ModelRenderer
{
friend struct ShaderModelRendererInternals;
public:
ShaderModelRenderer(ModelVertexRendererPtr vertexrender);
virtual ~ShaderModelRenderer();
// Batching implementations
virtual void Submit(int cullGroup, CModel* model);
virtual void PrepareModels();
virtual void EndFrame();
virtual void Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags);
private:
+ struct ShaderModelRendererInternals;
ShaderModelRendererInternals* m;
};
#endif // INCLUDED_MODELRENDERER