openspades/Sources/Draw/GLSparseShadowMapRenderer.cpp

826 lines
24 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades 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 3 of the License, or
(at your option) any later version.
OpenSpades 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 OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GLSparseShadowMapRenderer.h"
#include "GLModel.h"
#include "GLModelRenderer.h"
#include "GLProfiler.h"
#include "GLRenderer.h"
#include "IGLDevice.h"
#include <Core/Debug.h>
#include <Core/Exception.h>
#include <Core/Settings.h>
namespace spades {
namespace draw {
GLSparseShadowMapRenderer::GLSparseShadowMapRenderer(GLRenderer &r)
: IGLShadowMapRenderer(r), device(r.GetGLDevice()), settings(r.GetSettings()) {
SPADES_MARK_FUNCTION();
textureSize = settings.r_shadowMapSize;
if ((int)textureSize > 4096) {
SPLog("r_shadowMapSize is too large; changed to 4096");
settings.r_shadowMapSize = textureSize = 4096;
}
for (minLod = 0; (Tiles << minLod) < textureSize; minLod++)
;
// minLod = std::max(0, minLod - 2);
minLod = 0;
maxLod = 15;
colorTexture = device.GenTexture();
device.BindTexture(IGLDevice::Texture2D, colorTexture);
device.TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGB, textureSize, textureSize, 0,
IGLDevice::RGB, IGLDevice::UnsignedByte, NULL);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
IGLDevice::Linear);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
IGLDevice::Linear);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
IGLDevice::ClampToEdge);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
IGLDevice::ClampToEdge);
texture = device.GenTexture();
device.BindTexture(IGLDevice::Texture2D, texture);
device.TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::DepthComponent24, textureSize,
textureSize, 0, IGLDevice::DepthComponent, IGLDevice::UnsignedInt,
NULL);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
IGLDevice::Linear);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
IGLDevice::Linear);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
IGLDevice::ClampToEdge);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
IGLDevice::ClampToEdge);
pagetableTexture = device.GenTexture();
device.BindTexture(IGLDevice::Texture2D, pagetableTexture);
device.TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGBA8, Tiles, Tiles, 0,
IGLDevice::BGRA, IGLDevice::UnsignedByte, NULL);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
IGLDevice::Nearest);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
IGLDevice::Nearest);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
IGLDevice::ClampToEdge);
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
IGLDevice::ClampToEdge);
framebuffer = device.GenFramebuffer();
device.BindFramebuffer(IGLDevice::Framebuffer, framebuffer);
device.FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0,
IGLDevice::Texture2D, colorTexture, 0);
device.FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::DepthAttachment,
IGLDevice::Texture2D, texture, 0);
device.BindFramebuffer(IGLDevice::Framebuffer, 0);
}
GLSparseShadowMapRenderer::~GLSparseShadowMapRenderer() {
SPADES_MARK_FUNCTION();
device.DeleteTexture(texture);
device.DeleteTexture(pagetableTexture);
device.DeleteFramebuffer(framebuffer);
device.DeleteTexture(colorTexture);
}
#define Segment GLSSMRSegment
struct Segment {
float low, high;
bool empty;
Segment() : empty(true) {}
Segment(float l, float h) : low(std::min(l, h)), high(std::max(l, h)), empty(false) {}
void operator+=(const Segment &seg) {
if (seg.empty)
return;
if (empty) {
low = seg.low;
high = seg.high;
empty = false;
} else {
low = std::min(low, seg.low);
high = std::max(high, seg.high);
}
}
void operator+=(float v) {
if (empty) {
low = high = v;
} else {
if (v < low)
low = v;
if (v > high)
high = v;
}
}
};
static Vector3 FrustrumCoord(const client::SceneDefinition &def, float x, float y,
float z) {
x *= z;
y *= z;
return def.viewOrigin + def.viewAxis[0] * x + def.viewAxis[1] * y + def.viewAxis[2] * z;
}
static Segment ZRange(Vector3 base, Vector3 dir, Plane3 plane1, Plane3 plane2) {
return Segment(plane1.GetDistanceTo(base) / Vector3::Dot(dir, plane1.n),
plane2.GetDistanceTo(base) / Vector3::Dot(dir, plane2.n));
}
void GLSparseShadowMapRenderer::BuildMatrix(float near, float far) {
// TODO: variable light direction?
Vector3 lightDir = MakeVector3(0, -1, -1).Normalize();
// set better up dir?
Vector3 up = MakeVector3(0, 0, 1);
Vector3 side = Vector3::Cross(up, lightDir).Normalize();
up = Vector3::Cross(lightDir, side).Normalize();
// build frustrum
client::SceneDefinition def = GetRenderer().GetSceneDef();
Vector3 frustrum[8];
float tanX = tanf(def.fovX * .5f);
float tanY = tanf(def.fovY * .5f);
frustrum[0] = FrustrumCoord(def, tanX, tanY, near);
frustrum[1] = FrustrumCoord(def, tanX, -tanY, near);
frustrum[2] = FrustrumCoord(def, -tanX, tanY, near);
frustrum[3] = FrustrumCoord(def, -tanX, -tanY, near);
frustrum[4] = FrustrumCoord(def, tanX, tanY, far);
frustrum[5] = FrustrumCoord(def, tanX, -tanY, far);
frustrum[6] = FrustrumCoord(def, -tanX, tanY, far);
frustrum[7] = FrustrumCoord(def, -tanX, -tanY, far);
// compute frustrum's x,y boundary
float minX, maxX, minY, maxY;
minX = maxX = Vector3::Dot(frustrum[0], side);
minY = maxY = Vector3::Dot(frustrum[0], up);
for (int i = 1; i < 8; i++) {
float x = Vector3::Dot(frustrum[i], side);
float y = Vector3::Dot(frustrum[i], up);
if (x < minX)
minX = x;
if (x > maxX)
maxX = x;
if (y < minY)
minY = y;
if (y > maxY)
maxY = y;
}
// compute frustrum's z boundary
Segment seg;
Plane3 plane1(0, 0, 1, -4.f);
Plane3 plane2(0, 0, 1, 64.f);
seg += ZRange(side * minX + up * minY, lightDir, plane1, plane2);
seg += ZRange(side * minX + up * maxY, lightDir, plane1, plane2);
seg += ZRange(side * maxX + up * minY, lightDir, plane1, plane2);
seg += ZRange(side * maxX + up * maxY, lightDir, plane1, plane2);
for (int i = 1; i < 8; i++) {
seg += Vector3::Dot(frustrum[i], lightDir);
}
// build frustrum obb
Vector3 origin = side * minX + up * minY + lightDir * seg.low;
Vector3 axis1 = side * (maxX - minX);
Vector3 axis2 = up * (maxY - minY);
Vector3 axis3 = lightDir * (seg.high - seg.low);
obb = OBB3(Matrix4::FromAxis(axis1, axis2, axis3, origin));
vpWidth = 2.f / axis1.GetLength();
vpHeight = 2.f / axis2.GetLength();
// convert to projectionview matrix
matrix = obb.m.InversedFast();
matrix = Matrix4::Scale(2.f) * matrix;
matrix = Matrix4::Translate(-1, -1, -1) * matrix;
// scale a little big for padding
matrix = Matrix4::Scale(.98f) * matrix;
//
matrix = Matrix4::Scale(1, 1, -1) * matrix;
// make sure frustrums in range
#ifndef NDEBUG
for (int i = 0; i < 8; i++) {
Vector4 v = matrix * frustrum[i];
SPAssert(v.x >= -1.f);
SPAssert(v.y >= -1.f);
// SPAssert(v.z >= -1.f);
SPAssert(v.x < 1.f);
SPAssert(v.y < 1.f);
// SPAssert(v.z < 1.f);
}
#endif
}
void GLSparseShadowMapRenderer::Render() {
SPADES_MARK_FUNCTION();
IGLDevice::Integer lastFb = device.GetInteger(IGLDevice::FramebufferBinding);
// client::SceneDefinition def = GetRenderer().GetSceneDef();
float nearDist = 0.f;
float farDist = 150.f;
BuildMatrix(nearDist, farDist);
device.BindFramebuffer(IGLDevice::Framebuffer, framebuffer);
device.Viewport(0, 0, textureSize, textureSize);
device.ClearDepth(1.f);
device.Clear(IGLDevice::DepthBufferBit);
RenderShadowMapPass();
device.BindFramebuffer(IGLDevice::Framebuffer, lastFb);
device.Viewport(0, 0, device.ScreenWidth(), device.ScreenHeight());
}
#pragma mark - Sparse Processor
static const size_t NoGroup = (size_t)-1;
static const size_t NoInstance = (size_t)-1;
static const size_t NoNode = (size_t)-1;
static const size_t GroupNodeFlag = NoNode ^ (NoNode >> 1);
struct GLSparseShadowMapRenderer::Internal {
GLSparseShadowMapRenderer &renderer;
Vector3 cameraShadowCoord;
typedef int LodUnit;
struct Instance {
GLModel *model;
const client::ModelRenderParam *param;
IntVector3 tile1, tile2; // int aabb2
size_t prev, next; // instance in the same group
};
/** All Instances. Sorted by model somehow. */
std::vector<Instance> allInstances;
struct Group {
IntVector3 tile1, tile2; // int aabb2
size_t firstInstance, lastInstance;
LodUnit lod;
bool valid;
// shadow map texture coordinate
int rawX, rawY, rawW, rawH;
};
std::vector<Group> groups;
// packing node
struct Node {
size_t child1, child2;
int pos;
bool horizontal; // true: x=pos, false: y=pos
};
std::vector<Node> nodes;
int lodBias;
int mapSize;
size_t groupMap[Tiles][Tiles];
static IntVector3 ShadowMapToTileCoord(Vector3 vec) {
vec.x = (vec.x * (float)(Tiles / 2)) + (float)(Tiles / 2);
vec.y = (vec.y * (float)(Tiles / 2)) + (float)(Tiles / 2);
IntVector3 v;
v.x = (int)floorf(vec.x);
v.y = (int)floorf(vec.y);
;
return v;
}
static Vector3 TileToShadowMapCoord(IntVector3 tile) {
Vector3 v;
v.x = (float)tile.x * (2.f / (float)Tiles) - 1.f;
v.y = (float)tile.y * (2.f / (float)Tiles) - 1.f;
return v;
}
LodUnit ComputeLod(Vector3 shadowCoord) {
float dx = shadowCoord.x - cameraShadowCoord.x;
float dy = shadowCoord.y - cameraShadowCoord.y;
float sq = dx * dx + dy * dy;
float lod = 1.f / (sqrtf(sq) + .01f);
int iv = (int)(lod * 200.f);
if (iv < 1)
iv = 1;
if (iv > 65536)
iv = 65535;
int ld = 0;
while (iv > 0) {
ld++;
iv >>= 1;
}
return ld;
}
void FillGroupMap(IntVector3 tile1, IntVector3 tile2, size_t val) {
for (int x = tile1.x; x < tile2.x; x++)
for (int y = tile1.y; y < tile2.y; y++)
groupMap[x][y] = val;
}
/** Combines `dest` and `src`. Group `src` will be
* no longer valid. */
void CombineGroup(size_t dest, size_t src) {
Group &g1 = groups[dest];
Group &g2 = groups[src];
FillGroupMap(g2.tile1, g2.tile2, dest);
// extend the area
g1.tile1.x = std::min(g1.tile1.x, g2.tile1.x);
g1.tile1.y = std::min(g1.tile1.y, g2.tile1.y);
g1.tile2.x = std::max(g1.tile2.x, g2.tile2.x);
g1.tile2.y = std::max(g1.tile2.y, g2.tile2.y);
g1.lod = std::max(g1.lod, g2.lod);
// combine the instance list
// [g1] + [g2] -> [g1, g2]
Instance &destLast = allInstances[g1.lastInstance];
Instance &srcFirst = allInstances[g2.firstInstance];
SPAssert(destLast.next == NoInstance);
SPAssert(srcFirst.prev == NoInstance);
destLast.next = g2.firstInstance;
srcFirst.prev = g1.lastInstance;
g1.lastInstance = g2.lastInstance;
// make sure the area is filled with the group `dest`
IntVector3 tile1 = g1.tile1, tile2 = g1.tile2;
for (int x = tile1.x; x < tile2.x; x++)
for (int y = tile1.y; y < tile2.y; y++) {
size_t g = groupMap[x][y];
SPAssert(g != src);
if (g != NoGroup && g != dest) {
CombineGroup(dest, g);
SPAssert(groupMap[x][y] == dest);
} else {
groupMap[x][y] = dest;
}
}
g2.valid = false;
}
/** Finds an group in the specified range (tile1, tile2).
* If one was not found, creates a new group.
* If more than one was found, combines all groups. */
size_t RegisterGroup(IntVector3 tile1, IntVector3 tile2) {
size_t foundGroup = NoGroup;
for (int x = tile1.x; x < tile2.x; x++)
for (int y = tile1.y; y < tile2.y; y++) {
size_t g = groupMap[x][y];
if (g == NoGroup)
continue;
if (g == foundGroup)
continue;
if (foundGroup == NoGroup) {
foundGroup = g;
break;
}
// another group found.
// this should be combined with `foundGroup`.
CombineGroup(foundGroup, g);
SPAssert(groupMap[x][y] == foundGroup);
}
if (foundGroup == NoGroup) {
// new group here.
Group g;
g.tile1 = tile1;
g.tile2 = tile2;
g.firstInstance = NoInstance;
g.lastInstance = NoInstance;
g.lod = -100; // lod is an int
g.valid = true;
groups.push_back(g);
size_t id = groups.size() - 1;
FillGroupMap(tile1, tile2, id);
return id;
} else {
// instance is added to an existing group.
bool extended = false;
Group &g = groups[foundGroup];
if (tile1.x < g.tile1.x) {
extended = true;
g.tile1.x = tile1.x;
}
if (tile1.y < g.tile1.y) {
extended = true;
g.tile1.y = tile1.y;
}
if (tile2.x > g.tile2.x) {
extended = true;
g.tile2.x = tile2.x;
}
if (tile2.y > g.tile2.y) {
extended = true;
g.tile2.y = tile2.y;
}
if (extended) {
SPAssert(foundGroup != NoGroup);
for (int x = g.tile1.x; x < g.tile2.x; x++)
for (int y = g.tile1.y; y < g.tile2.y; y++) {
size_t g = groupMap[x][y];
if (g == NoGroup) {
groupMap[x][y] = foundGroup;
continue;
}
if (g == foundGroup)
continue;
// another group found.
// this should be combined with `foundGroup`.
CombineGroup(foundGroup, g);
SPAssert(groupMap[x][y] == foundGroup);
}
}
// don't add instance here
return foundGroup;
}
}
Internal(GLSparseShadowMapRenderer &r) : renderer(r) {
GLProfiler::Context profiler(r.GetRenderer().GetGLProfiler(),
"Sparse Page Table Generation");
cameraShadowCoord = r.GetRenderer().GetSceneDef().viewOrigin;
cameraShadowCoord = (r.matrix * cameraShadowCoord).GetXYZ();
// clear group maps
for (size_t x = 0; x < Tiles; x++)
for (size_t y = 0; y < Tiles; y++)
groupMap[x][y] = NoGroup;
const std::vector<GLModelRenderer::RenderModel> &rmodels =
renderer.GetRenderer().GetModelRenderer()->models;
allInstances.reserve(256);
groups.reserve(64);
nodes.reserve(256);
for (std::vector<GLModelRenderer::RenderModel>::const_iterator it = rmodels.begin();
it != rmodels.end(); it++) {
const GLModelRenderer::RenderModel &rmodel = *it;
Instance inst;
inst.model = rmodel.model;
OBB3 modelBounds = inst.model->GetBoundingBox();
for (size_t i = 0; i < rmodel.params.size(); i++) {
inst.param = &(rmodel.params[i]);
if (inst.param->depthHack)
continue;
OBB3 instWorldBoundsOBB = inst.param->matrix * modelBounds;
// w should be 1, so this should wor
OBB3 instBoundsOBB = r.matrix * instWorldBoundsOBB;
AABB3 instBounds = instBoundsOBB.GetBoundingAABB();
// frustrum(?) cull
if (instBounds.max.x < -1.f || instBounds.max.y < -1.f ||
instBounds.min.x > 1.f || instBounds.min.y > 1.f)
continue;
inst.tile1 = ShadowMapToTileCoord(instBounds.min);
inst.tile2 = ShadowMapToTileCoord(instBounds.max);
inst.tile2.x++;
inst.tile2.y++;
if (inst.tile1.x < 0)
inst.tile1.x = 0;
if (inst.tile1.y < 0)
inst.tile1.y = 0;
if (inst.tile2.x > Tiles)
inst.tile2.x = Tiles;
if (inst.tile2.y > Tiles)
inst.tile2.y = Tiles;
if (inst.tile1.x >= inst.tile2.x)
continue;
if (inst.tile1.y >= inst.tile2.y)
continue;
size_t instId = allInstances.size();
size_t grp = RegisterGroup(inst.tile1, inst.tile2);
Group &g = groups[grp];
SPAssert(g.valid);
if (g.lastInstance == NoInstance) {
// this is new group.
g.firstInstance = instId;
g.lastInstance = instId;
inst.prev = NoInstance;
inst.next = NoInstance;
} else {
// adding this instance to the group
Instance &oldLast = allInstances[g.lastInstance];
SPAssert(oldLast.next == NoInstance);
oldLast.next = instId;
inst.prev = g.lastInstance;
inst.next = NoInstance;
g.lastInstance = instId;
}
g.lod = std::max(g.lod, ComputeLod(instBoundsOBB.m.GetOrigin()));
allInstances.push_back(inst);
}
}
mapSize = r.settings.r_shadowMapSize;
}
bool AddGroupToNode(size_t &nodeRef, int nx, int ny, int nw, int nh, size_t gId) {
Group &g = groups[gId];
if (nodeRef == NoNode) {
// empty node, try putting here
int w = g.tile2.x - g.tile1.x;
int h = g.tile2.y - g.tile1.y;
int lod = g.lod;
int minLod = renderer.minLod;
int maxLod = renderer.maxLod;
lod += lodBias;
if (lod < minLod)
lod = minLod;
if (lod > maxLod)
lod = maxLod;
w <<= lod;
h <<= lod;
w += 2;
h += 2; // safety margin
if (w > nw || h > nh) {
return false;
}
g.rawX = nx + 1;
g.rawY = ny + 1;
g.rawW = w - 1;
g.rawH = h - 1;
// how fits
if (w == nw && h == nh) {
// completely fits
nodeRef = gId | GroupNodeFlag;
} else if (w == nw) {
Node node;
node.child1 = gId | GroupNodeFlag;
node.child2 = NoNode;
node.horizontal = false;
node.pos = ny + h;
nodeRef = nodes.size();
nodes.push_back(node);
} else if (h == nh) {
Node node;
node.child1 = gId | GroupNodeFlag;
node.child2 = NoNode;
node.horizontal = true;
node.pos = nx + w;
nodeRef = nodes.size();
nodes.push_back(node);
} else {
Node node;
node.child1 = gId | GroupNodeFlag;
node.child2 = NoNode;
node.horizontal = true;
node.pos = nx + w;
size_t subnode = nodes.size();
nodes.push_back(node);
node.child1 = subnode;
node.child2 = NoNode;
node.horizontal = false;
node.pos = ny + h;
nodeRef = nodes.size();
nodes.push_back(node);
}
return true;
} else {
if (nodeRef & GroupNodeFlag)
return false;
Node &node = nodes[nodeRef];
if (node.horizontal) {
if (AddGroupToNode(node.child1, nx, ny, node.pos - nx, nh, gId))
return true;
if (AddGroupToNode(node.child2, node.pos, ny, nx + nw - node.pos, nh, gId))
return true;
} else {
if (AddGroupToNode(node.child1, nx, ny, nw, node.pos - ny, gId))
return true;
if (AddGroupToNode(node.child2, nx, node.pos, nw, ny + nh - node.pos, gId))
return true;
}
return false;
}
}
bool TryPack() {
size_t rootNode = NoNode;
nodes.clear();
for (size_t i = 0; i < groups.size(); i++) {
if (!AddGroupToNode(rootNode, 0, 0, mapSize, mapSize, i))
return false;
}
return true;
}
void Pack() {
if (groups.empty())
return;
GLProfiler::Context profiler(renderer.GetRenderer().GetGLProfiler(),
"Pack [%d group(s)]", (int)groups.size());
lodBias = 100;
if (TryPack()) {
// try to make it more detail
do {
lodBias++;
} while (TryPack() && lodBias < 140);
lodBias--;
TryPack();
} else {
// try to succeed packing by making it more rough
do {
lodBias--;
} while (!TryPack());
}
}
};
struct GLSparseShadowMapRenderer::ModelRenderer {
std::vector<client::ModelRenderParam> params;
GLModel *lastModel;
ModelRenderer() {
params.resize(64);
lastModel = NULL;
}
void Flush() {
if (lastModel) {
lastModel->RenderShadowMapPass(params);
params.clear();
lastModel = NULL;
}
}
void RenderModel(GLModel *m, const client::ModelRenderParam &param) {
if (m != lastModel)
Flush();
lastModel = m;
params.push_back(param);
}
};
void GLSparseShadowMapRenderer::RenderShadowMapPass() {
Internal itnl(*this);
itnl.Pack();
{
GLProfiler::Context profiler(GetRenderer().GetGLProfiler(),
"Page Table Generation");
for (int x = 0; x < Tiles; x++) {
for (int y = 0; y < Tiles; y++) {
size_t val = itnl.groupMap[x][y];
uint32_t out;
if (val == NoGroup) {
out = 0xffffffff;
} else {
Internal::Group &g = itnl.groups[val];
int lod = g.lod;
lod += itnl.lodBias;
if (lod < minLod)
lod = minLod;
if (lod > maxLod)
lod = maxLod;
int ix = x - g.tile1.x;
int iy = y - g.tile1.y;
ix <<= lod;
iy <<= lod;
ix += g.rawX;
iy += g.rawY;
unsigned int rr, gg, bb, aa;
rr = (ix & 255);
gg = (iy & 255);
bb = (ix >> 8) | ((iy >> 8) << 4);
aa = lod; // 1 << (lod - minLod);
out = bb | (gg << 8) | (rr << 16) | (aa << 24);
}
pagetable[y][x] = out;
}
}
}
{
GLProfiler::Context profiler(GetRenderer().GetGLProfiler(), "Page Table Upload");
device.BindTexture(IGLDevice::Texture2D, pagetableTexture);
device.TexSubImage2D(IGLDevice::Texture2D, 0, 0, 0, Tiles, Tiles, IGLDevice::BGRA,
IGLDevice::UnsignedByte, pagetable);
}
Matrix4 baseMatrix = matrix;
{
GLProfiler::Context profiler(GetRenderer().GetGLProfiler(),
"Shadow Maps [%d group(s)]", (int)itnl.groups.size());
ModelRenderer mrend;
for (size_t i = 0; i < itnl.groups.size(); i++) {
Internal::Group &g = itnl.groups[i];
device.Viewport(g.rawX, g.rawY, g.rawW, g.rawH);
Vector3 dest1 = Internal::TileToShadowMapCoord(g.tile1);
Vector3 dest2 = Internal::TileToShadowMapCoord(g.tile2);
Matrix4 mat;
mat = Matrix4::Translate((dest1.x + dest2.x) * -.5f, (dest1.y + dest2.y) * -.5f,
0.f);
mat =
Matrix4::Scale(2.f / (dest2.x - dest1.x), 2.f / (dest2.y - dest1.y), 1.f) *
mat;
mat = mat * baseMatrix;
matrix = mat;
for (size_t mId = g.firstInstance; mId != NoInstance;
mId = itnl.allInstances[mId].next) {
Internal::Instance &inst = itnl.allInstances[mId];
mrend.RenderModel(inst.model, *inst.param);
Vector3 v =
inst.model->GetBoundingBox().min + inst.model->GetBoundingBox().max;
v *= .5f;
v = (inst.param->matrix * v).GetXYZ();
{
v = (baseMatrix * v).GetXYZ();
SPAssert(v.x >= -1.2f);
SPAssert(v.y >= -1.2f);
SPAssert(v.x <= 1.2f);
SPAssert(v.y <= 1.2f);
}
}
mrend.Flush();
}
}
matrix = baseMatrix;
}
bool GLSparseShadowMapRenderer::Cull(const spades::AABB3 &) {
// TODO: who uses this?
SPNotImplemented();
return true;
}
bool GLSparseShadowMapRenderer::SphereCull(const spades::Vector3 &center, float rad) {
return true; // for models, this is already done
/*
Vector4 vw = matrix * center;
float xx = fabsf(vw.x);
float yy = fabsf(vw.y);
float rx = rad * vpWidth;
float ry = rad * vpHeight;
return xx < (1.f + rx) && yy < (1.f + ry);*/
}
} // namespace draw
} // namespace spades