573 lines
16 KiB
C++
573 lines
16 KiB
C++
// Copyright (C) 2021-2022, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
|
|
#include "verus.h"
|
|
|
|
using namespace verus;
|
|
using namespace verus::Scene;
|
|
|
|
LightMapBaker::LightMapBaker()
|
|
{
|
|
VERUS_ZERO_MEM(_queuedCount);
|
|
}
|
|
|
|
LightMapBaker::~LightMapBaker()
|
|
{
|
|
Done();
|
|
}
|
|
|
|
void LightMapBaker::Init(RcDesc desc)
|
|
{
|
|
VERUS_INIT();
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_TIMER;
|
|
|
|
_desc = desc;
|
|
_pathname = desc._pathname;
|
|
_desc._pathname = nullptr;
|
|
|
|
_stats = Stats();
|
|
_stats._startTime = timer.GetTime();
|
|
|
|
_currentI = 0;
|
|
_currentJ = 0;
|
|
|
|
_vMap.resize(_desc._texWidth * _desc._texHeight);
|
|
_invMapSize = 1.f / _vMap.size();
|
|
|
|
switch (GetMode())
|
|
{
|
|
case Mode::faces:
|
|
{
|
|
_repsPerUpdate = Math::Max<int>(1, Utils::Cast32(_vMap.size()) / 100);
|
|
std::fill(_vMap.begin(), _vMap.end(), VERUS_COLOR_RGBA(0, 0, 0, 255));
|
|
}
|
|
break;
|
|
case Mode::ambientOcclusion:
|
|
{
|
|
std::fill(_vMap.begin(), _vMap.end(), VERUS_COLOR_RGBA(255, 255, 255, 0));
|
|
}
|
|
break;
|
|
}
|
|
|
|
Vector<Vertex> vVerts;
|
|
vVerts.reserve(_desc._pMesh->GetVertCount());
|
|
_desc._pMesh->ForEachVertex([&vVerts](int index, RcPoint3 pos, RcVector3 nrm, RcPoint3 tc)
|
|
{
|
|
Vertex v;
|
|
v._pos = pos.GLM();
|
|
v._nrm = nrm.GLM();
|
|
v._tc = tc.GLM2();
|
|
vVerts.push_back(v);
|
|
return Continue::yes;
|
|
});
|
|
_vFaces.resize(_desc._pMesh->GetFaceCount());
|
|
VERUS_FOR(i, _vFaces.size())
|
|
{
|
|
const int offset = i * 3;
|
|
const int indices[3] =
|
|
{
|
|
_desc._pMesh->GetIndices()[offset + 0],
|
|
_desc._pMesh->GetIndices()[offset + 1],
|
|
_desc._pMesh->GetIndices()[offset + 2]
|
|
};
|
|
_vFaces[i]._v[0] = vVerts[indices[0]];
|
|
_vFaces[i]._v[1] = vVerts[indices[1]];
|
|
_vFaces[i]._v[2] = vVerts[indices[2]];
|
|
}
|
|
|
|
const float limit = Math::Min(0.5f, 2 / sqrt(static_cast<float>(_vFaces.size()))); // 2 shows best result.
|
|
_quadtree.Init(Math::Bounds(Point3(0, 0), Point3(1, 1)), Vector3(limit, limit));
|
|
_quadtree.SetDelegate(this);
|
|
VERUS_FOR(i, _vFaces.size())
|
|
{
|
|
Math::Quadtree::Element element;
|
|
VERUS_FOR(j, 3)
|
|
element._bounds.Include(_vFaces[i]._v[j]._tc);
|
|
element._pToken = reinterpret_cast<void*>(static_cast<INT64>(i));
|
|
_quadtree.BindElement(element);
|
|
}
|
|
|
|
if (Mode::faces == GetMode())
|
|
return;
|
|
|
|
_rph = renderer->CreateRenderPass(
|
|
{
|
|
CGI::RP::Attachment("Color", CGI::Format::floatR32).LoadOpClear().Layout(CGI::ImageLayout::fsReadOnly),
|
|
CGI::RP::Attachment("Depth", CGI::Format::unormD24uintS8).LoadOpClear().Layout(CGI::ImageLayout::depthStencilAttachment),
|
|
},
|
|
{
|
|
CGI::RP::Subpass("Sp0").Color(
|
|
{
|
|
CGI::RP::Ref("Color", CGI::ImageLayout::colorAttachment)
|
|
}).DepthStencil(CGI::RP::Ref("Depth", CGI::ImageLayout::depthStencilAttachment)),
|
|
},
|
|
{});
|
|
|
|
{
|
|
CGI::PipelineDesc pipeDesc(renderer.GetGeoQuad(), renderer.GetShaderQuad(), "#HemicubeMask", _rph);
|
|
pipeDesc._colorAttachBlendEqs[0] = VERUS_COLOR_BLEND_MUL;
|
|
pipeDesc._topology = CGI::PrimitiveTopology::triangleStrip;
|
|
pipeDesc.DisableDepthTest();
|
|
_pipe[PIPE_HEMICUBE_MASK].Init(pipeDesc);
|
|
}
|
|
{
|
|
CGI::PipelineDesc pipeDesc(renderer.GetGeoQuad(), renderer.GetShaderQuad(), "#", renderer.GetRenderPassHandle_AutoWithDepth());
|
|
pipeDesc._topology = CGI::PrimitiveTopology::triangleStrip;
|
|
pipeDesc.DisableDepthTest();
|
|
_pipe[PIPE_QUAD].Init(pipeDesc);
|
|
}
|
|
|
|
CGI::TextureDesc texDesc;
|
|
texDesc._name = "LightMapBaker.DepthTex";
|
|
texDesc._clearValue = Vector4(1);
|
|
texDesc._format = CGI::Format::unormD24uintS8;
|
|
texDesc._width = _desc._texLumelSide;
|
|
texDesc._height = _desc._texLumelSide;
|
|
_tex[TEX_DEPTH].Init(texDesc);
|
|
texDesc.Reset();
|
|
texDesc._name = "LightMapBaker.Dummy";
|
|
texDesc._format = CGI::Format::unormR8G8B8A8;
|
|
texDesc._width = 16;
|
|
texDesc._height = 16;
|
|
_tex[TEX_DUMMY].Init(texDesc);
|
|
|
|
VERUS_FOR(ringBufferIndex, CGI::BaseRenderer::s_ringBufferSize)
|
|
{
|
|
VERUS_FOR(batchIndex, s_batchSize)
|
|
{
|
|
CGI::TextureDesc texDesc;
|
|
texDesc._name = "LightMapBaker.ColorTex";
|
|
texDesc._clearValue = Vector4::Replicate(1);
|
|
texDesc._format = CGI::Format::floatR32;
|
|
texDesc._width = _desc._texLumelSide;
|
|
texDesc._height = _desc._texLumelSide;
|
|
texDesc._mipLevels = 0;
|
|
texDesc._flags = CGI::TextureDesc::Flags::colorAttachment | CGI::TextureDesc::Flags::generateMips;
|
|
texDesc._readbackMip = -1;
|
|
_texColor[ringBufferIndex][batchIndex].Init(texDesc);
|
|
}
|
|
_cshQuad[ringBufferIndex] = renderer.GetShaderQuad()->BindDescriptorSetTextures(1, { _texColor[ringBufferIndex][0] });
|
|
}
|
|
_cshHemicubeMask = renderer.GetShaderQuad()->BindDescriptorSetTextures(1, { _tex[TEX_DUMMY] });
|
|
|
|
VERUS_FOR(ringBufferIndex, CGI::BaseRenderer::s_ringBufferSize)
|
|
{
|
|
VERUS_FOR(batchIndex, s_batchSize)
|
|
{
|
|
CGI::TexturePtr tex = _texColor[ringBufferIndex][batchIndex];
|
|
_fbh[ringBufferIndex][batchIndex] = renderer->CreateFramebuffer(_rph,
|
|
{
|
|
tex,
|
|
_tex[TEX_DEPTH]
|
|
},
|
|
_desc._texLumelSide,
|
|
_desc._texLumelSide);
|
|
|
|
// Define texture:
|
|
renderer.GetCommandBuffer()->BeginRenderPass(_rph, _fbh[ringBufferIndex][batchIndex],
|
|
{
|
|
tex->GetClearValue(),
|
|
_tex[TEX_DEPTH]->GetClearValue()
|
|
});
|
|
renderer.GetCommandBuffer()->EndRenderPass();
|
|
tex->GenerateMips();
|
|
}
|
|
_vQueued[ringBufferIndex].resize(s_batchSize);
|
|
}
|
|
|
|
_drawEmptyState = CGI::BaseRenderer::s_ringBufferSize + 1;
|
|
}
|
|
|
|
void LightMapBaker::Done()
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
if (renderer.GetShaderQuad())
|
|
{
|
|
VERUS_FOR(ringBufferIndex, CGI::BaseRenderer::s_ringBufferSize)
|
|
renderer.GetShaderQuad()->FreeDescriptorSet(_cshQuad[ringBufferIndex]);
|
|
renderer.GetShaderQuad()->FreeDescriptorSet(_cshHemicubeMask);
|
|
}
|
|
VERUS_FOR(ringBufferIndex, CGI::BaseRenderer::s_ringBufferSize)
|
|
{
|
|
VERUS_FOR(batchIndex, s_batchSize)
|
|
renderer->DeleteFramebuffer(_fbh[ringBufferIndex][batchIndex]);
|
|
}
|
|
renderer->DeleteRenderPass(_rph);
|
|
VERUS_DONE(LightMapBaker);
|
|
}
|
|
|
|
void LightMapBaker::Update()
|
|
{
|
|
if (!IsInitialized() || !IsBaking())
|
|
return;
|
|
|
|
if (_drawEmptyState)
|
|
{
|
|
if (CGI::BaseRenderer::s_ringBufferSize + 1 == _drawEmptyState)
|
|
DrawEmpty();
|
|
_drawEmptyState--;
|
|
if (!_drawEmptyState)
|
|
{
|
|
float value = 0;
|
|
_texColor[0][0]->ReadbackSubresource(&value, false);
|
|
_normalizationFactor = 1 / value;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
VERUS_QREF_RENDERER;
|
|
|
|
const int ringBufferIndex = renderer->GetRingBufferIndex();
|
|
|
|
auto NextLumel = [this]()
|
|
{
|
|
_currentJ++;
|
|
if (_currentJ == _desc._texWidth)
|
|
{
|
|
_currentJ = 0;
|
|
_currentI++;
|
|
}
|
|
};
|
|
|
|
int& queuedCount = _queuedCount[ringBufferIndex];
|
|
if (queuedCount > 0) // Was there anything queued?
|
|
{
|
|
VERUS_FOR(i, queuedCount)
|
|
{
|
|
float value = 0;
|
|
_texColor[ringBufferIndex][i]->ReadbackSubresource(&value, false);
|
|
const BYTE value8 = Convert::UnormToUint8(Math::Clamp<float>(value * _normalizationFactor, 0, 1));
|
|
|
|
BYTE* pDst = reinterpret_cast<BYTE*>(_vQueued[ringBufferIndex][i]._pDst);
|
|
if (pDst[3]) // Already has some data?
|
|
{
|
|
VERUS_FOR(j, 3)
|
|
pDst[j] = Math::Max(pDst[j], value8);
|
|
}
|
|
else // Empty?
|
|
{
|
|
VERUS_FOR(j, 3)
|
|
pDst[j] = value8;
|
|
pDst[3] = 0xFF;
|
|
}
|
|
}
|
|
}
|
|
// Clear queued:
|
|
if (!_vQueued[ringBufferIndex].empty())
|
|
{
|
|
memset(_vQueued[ringBufferIndex].data(), 0, sizeof(Queued) * s_batchSize);
|
|
queuedCount = 0;
|
|
}
|
|
|
|
if (_currentI >= _desc._texHeight)
|
|
{
|
|
bool finish = true;
|
|
VERUS_FOR(ringBufferIndex, CGI::BaseRenderer::s_ringBufferSize)
|
|
{
|
|
if (_queuedCount[ringBufferIndex])
|
|
{
|
|
finish = false;
|
|
break;
|
|
}
|
|
}
|
|
if (finish)
|
|
{
|
|
if (Mode::faces != GetMode())
|
|
ComputeEdgePadding();
|
|
Save();
|
|
_desc._mode = Mode::idle;
|
|
}
|
|
return;
|
|
}
|
|
|
|
VERUS_FOR(rep, _repsPerUpdate)
|
|
{
|
|
// Fill queue:
|
|
int& queuedCount = _queuedCount[ringBufferIndex];
|
|
VERUS_RT_ASSERT(!queuedCount);
|
|
while (queuedCount + s_maxLayers <= s_batchSize) // Enough space for this lumel?
|
|
{
|
|
_currentUV = glm::vec2(
|
|
(_currentJ + 0.5f) / _desc._texWidth,
|
|
(_currentI + 0.5f) / _desc._texHeight);
|
|
|
|
_currentLayer = 0;
|
|
_quadtree.TraverseVisible(_currentUV);
|
|
_stats._maxLayer = Math::Max<int>(_stats._maxLayer, _currentLayer);
|
|
|
|
NextLumel();
|
|
if (_currentI >= _desc._texHeight)
|
|
break; // No more lumels.
|
|
}
|
|
|
|
// Draw queue:
|
|
VERUS_FOR(i, queuedCount)
|
|
{
|
|
RQueued queued = _vQueued[ringBufferIndex][i];
|
|
DrawLumel(queued._pos, queued._nrm, i);
|
|
}
|
|
}
|
|
|
|
const int progressPrev = static_cast<int>(_stats._progress * 100);
|
|
_stats._progress = Math::Clamp<float>((_currentI * _desc._texWidth + _currentJ) * _invMapSize, 0, 1);
|
|
const int progressNext = static_cast<int>(_stats._progress * 100);
|
|
if (progressPrev != progressNext)
|
|
{
|
|
VERUS_QREF_TIMER;
|
|
const float elapsedTime = timer.GetTime() - _stats._startTime;
|
|
char buffer[80];
|
|
sprintf_s(buffer, "Elapsed time: %.1fs, max layer: %d, progress: %.1f%%", elapsedTime, _stats._maxLayer, _stats._progress * 100);
|
|
_stats._info = buffer;
|
|
}
|
|
}
|
|
|
|
void LightMapBaker::Draw()
|
|
{
|
|
if (!IsInitialized() || !IsBaking() || !_debugDraw || Mode::faces == GetMode())
|
|
return;
|
|
|
|
VERUS_QREF_RENDERER;
|
|
|
|
const int ringBufferIndex = renderer->GetRingBufferIndex();
|
|
auto cb = renderer.GetCommandBuffer();
|
|
auto shader = renderer.GetShaderQuad();
|
|
|
|
renderer.GetUbQuadVS()._matW = Math::QuadMatrix().UniformBufferFormat();
|
|
renderer.GetUbQuadVS()._matV = Math::ToUVMatrix().UniformBufferFormat();
|
|
renderer.ResetQuadMultiplexer();
|
|
|
|
cb->BindPipeline(_pipe[PIPE_QUAD]);
|
|
shader->BeginBindDescriptors();
|
|
cb->BindDescriptors(shader, 0);
|
|
cb->BindDescriptors(shader, 1, _cshQuad[ringBufferIndex]);
|
|
shader->EndBindDescriptors();
|
|
renderer.DrawQuad(cb.Get());
|
|
}
|
|
|
|
void LightMapBaker::DrawEmpty()
|
|
{
|
|
// Let's compute normalization factor by drawing an empty scene.
|
|
VERUS_QREF_RENDERER;
|
|
|
|
auto cb = renderer.GetCommandBuffer();
|
|
CGI::TexturePtr tex = _texColor[0][0];
|
|
|
|
cb->BeginRenderPass(_rph, _fbh[0][0],
|
|
{
|
|
tex->GetClearValue(),
|
|
_tex[TEX_DEPTH]->GetClearValue()
|
|
});
|
|
|
|
DrawHemicubeMask();
|
|
|
|
cb->EndRenderPass();
|
|
|
|
tex->GenerateMips();
|
|
tex->ReadbackSubresource(nullptr);
|
|
}
|
|
|
|
void LightMapBaker::DrawHemicubeMask()
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
|
|
auto cb = renderer.GetCommandBuffer();
|
|
auto shader = renderer.GetShaderQuad();
|
|
|
|
const float side = static_cast<float>(_desc._texLumelSide);
|
|
|
|
renderer.GetUbQuadVS()._matW = Math::QuadMatrix().UniformBufferFormat();
|
|
renderer.GetUbQuadVS()._matV = Math::ToUVMatrix().UniformBufferFormat();
|
|
renderer.ResetQuadMultiplexer();
|
|
|
|
cb->BindPipeline(_pipe[PIPE_HEMICUBE_MASK]);
|
|
cb->SetViewport({ Vector4(0, 0, side, side) });
|
|
shader->BeginBindDescriptors();
|
|
cb->BindDescriptors(shader, 0);
|
|
cb->BindDescriptors(shader, 1, _cshHemicubeMask);
|
|
shader->EndBindDescriptors();
|
|
renderer.DrawQuad(cb.Get());
|
|
}
|
|
|
|
void LightMapBaker::DrawLumel(RcPoint3 pos, RcVector3 nrm, int batchIndex)
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_SM;
|
|
|
|
const int ringBufferIndex = renderer->GetRingBufferIndex();
|
|
auto cb = renderer.GetCommandBuffer();
|
|
auto shader = renderer.GetShaderQuad();
|
|
CGI::TexturePtr tex = _texColor[ringBufferIndex][batchIndex];
|
|
|
|
cb->BeginRenderPass(_rph, _fbh[ringBufferIndex][batchIndex],
|
|
{
|
|
tex->GetClearValue(),
|
|
_tex[TEX_DEPTH]->GetClearValue()
|
|
});
|
|
if (_pDelegate)
|
|
{
|
|
const glm::vec2 randVec = glm::circularRand(1.f);
|
|
const Matrix3 m3 = Matrix3::MakeTrackTo(Vector3(0, 0, 1), nrm);
|
|
const Vector3 perpVecA = m3 * Vector3(randVec.x, randVec.y, 0); // Up.
|
|
const Vector3 perpVecB = VMath::cross(perpVecA, nrm); // Side.
|
|
|
|
DrawLumelDesc drawLumelDesc;
|
|
drawLumelDesc._eyePos = pos;
|
|
drawLumelDesc._frontDir = nrm;
|
|
_pDelegate->LightMapBaker_Draw(CGI::CubeMapFace::none, drawLumelDesc);
|
|
VERUS_FOR(i, 5)
|
|
{
|
|
const CGI::CubeMapFace cubeMapFace = static_cast<CGI::CubeMapFace>(i);
|
|
|
|
Camera cam;
|
|
cam.MoveEyeTo(drawLumelDesc._eyePos);
|
|
switch (cubeMapFace)
|
|
{
|
|
case CGI::CubeMapFace::posX:
|
|
cam.MoveAtTo(drawLumelDesc._eyePos + perpVecB);
|
|
cam.SetUpDirection(perpVecA);
|
|
break;
|
|
case CGI::CubeMapFace::negX:
|
|
cam.MoveAtTo(drawLumelDesc._eyePos - perpVecB);
|
|
cam.SetUpDirection(perpVecA);
|
|
break;
|
|
case CGI::CubeMapFace::posY:
|
|
cam.MoveAtTo(drawLumelDesc._eyePos + perpVecA);
|
|
cam.SetUpDirection(-nrm);
|
|
break;
|
|
case CGI::CubeMapFace::negY:
|
|
cam.MoveAtTo(drawLumelDesc._eyePos - perpVecA);
|
|
cam.SetUpDirection(nrm);
|
|
break;
|
|
case CGI::CubeMapFace::posZ:
|
|
cam.MoveAtTo(drawLumelDesc._eyePos + nrm);
|
|
cam.SetUpDirection(perpVecA);
|
|
break;
|
|
}
|
|
cam.SetYFov(VERUS_PI * 0.5f);
|
|
cam.SetAspectRatio(1);
|
|
cam.SetZNear(0.00001f);
|
|
cam.SetZFar(_desc._distance);
|
|
cam.Update();
|
|
PCamera pPrevCamera = sm.SetCamera(&cam);
|
|
|
|
drawLumelDesc._frontDir = cam.GetFrontDirection();
|
|
_pDelegate->LightMapBaker_Draw(cubeMapFace, drawLumelDesc);
|
|
|
|
sm.SetCamera(pPrevCamera);
|
|
}
|
|
_pDelegate->LightMapBaker_Draw(CGI::CubeMapFace::negZ, drawLumelDesc);
|
|
|
|
DrawHemicubeMask();
|
|
}
|
|
cb->EndRenderPass();
|
|
|
|
tex->GenerateMips();
|
|
tex->ReadbackSubresource(nullptr);
|
|
}
|
|
|
|
Continue LightMapBaker::Quadtree_ProcessNode(void* pToken, void* pUser)
|
|
{
|
|
RcFace face = _vFaces[reinterpret_cast<INT64>(pToken)];
|
|
const int index = _currentI * _desc._texWidth + _currentJ;
|
|
if (Math::IsPointInsideTriangle(face._v[0]._tc, face._v[1]._tc, face._v[2]._tc, _currentUV))
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
switch (GetMode())
|
|
{
|
|
case Mode::faces:
|
|
{
|
|
_vMap[index] = VERUS_COLOR_WHITE;
|
|
}
|
|
break;
|
|
case Mode::ambientOcclusion:
|
|
{
|
|
VERUS_RT_ASSERT(_currentLayer < s_maxLayers);
|
|
|
|
const Vector3 bc = Math::Barycentric(face._v[0]._tc, face._v[1]._tc, face._v[2]._tc, _currentUV);
|
|
const glm::vec3 pos = Math::BarycentricInterpolation(face._v[0]._pos, face._v[1]._pos, face._v[2]._pos, bc);
|
|
const glm::vec3 nrm = Math::BarycentricInterpolation(face._v[0]._nrm, face._v[1]._nrm, face._v[2]._nrm, bc);
|
|
|
|
const int ringBufferIndex = renderer->GetRingBufferIndex();
|
|
int& queuedCount = _queuedCount[ringBufferIndex];
|
|
RQueued queued = _vQueued[ringBufferIndex][queuedCount];
|
|
queued._pos = pos;
|
|
queued._nrm = glm::normalize(nrm);
|
|
queued._pos += Vector3(nrm) * _desc._bias;
|
|
queued._pDst = &_vMap[index];
|
|
|
|
_currentLayer++;
|
|
queuedCount++;
|
|
|
|
return (_currentLayer >= s_maxLayers) ? Continue::no : Continue::yes;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return Continue::yes;
|
|
}
|
|
|
|
void LightMapBaker::ComputeEdgePadding()
|
|
{
|
|
Utils::ComputeEdgePadding(
|
|
reinterpret_cast<BYTE*>(_vMap.data()) + 0, sizeof(UINT32),
|
|
reinterpret_cast<BYTE*>(_vMap.data()) + 3, sizeof(UINT32),
|
|
_desc._texWidth, _desc._texHeight);
|
|
}
|
|
|
|
void LightMapBaker::Save()
|
|
{
|
|
IO::FileSystem::SaveImage(_C(_pathname), _vMap.data(), _desc._texWidth, _desc._texHeight, IO::ImageFormat::extension);
|
|
}
|
|
|
|
void LightMapBaker::BindPipeline(RcMesh mesh, CGI::CommandBufferPtr cb)
|
|
{
|
|
static const PIPE pipes[] =
|
|
{
|
|
PIPE_MESH_SIMPLE_BAKE_AO,
|
|
PIPE_MESH_SIMPLE_BAKE_AO_INSTANCED,
|
|
PIPE_MESH_SIMPLE_BAKE_AO_ROBOTIC,
|
|
PIPE_MESH_SIMPLE_BAKE_AO_SKINNED
|
|
};
|
|
const PIPE pipe = pipes[mesh.GetPipelineIndex()];
|
|
if (!_pipe[pipe])
|
|
{
|
|
static CSZ branches[] =
|
|
{
|
|
"#BakeAO",
|
|
"#BakeAOInstanced",
|
|
"#BakeAORobotic",
|
|
"#BakeAOSkinned"
|
|
};
|
|
CGI::PipelineDesc pipeDesc(mesh.GetGeometry(), Mesh::GetSimpleShader(), branches[pipe], _rph);
|
|
pipeDesc._vertexInputBindingsFilter = mesh.GetBindingsMask();
|
|
_pipe[pipe].Init(pipeDesc);
|
|
}
|
|
cb->BindPipeline(_pipe[pipe]);
|
|
}
|
|
|
|
void LightMapBaker::SetViewportFor(CGI::CubeMapFace cubeMapFace, CGI::CommandBufferPtr cb)
|
|
{
|
|
const float sideDiv2 = static_cast<float>(_desc._texLumelSide >> 1);
|
|
const float sideDiv4 = static_cast<float>(_desc._texLumelSide >> 2);
|
|
switch (cubeMapFace)
|
|
{
|
|
case CGI::CubeMapFace::posX:
|
|
cb->SetViewport({ Vector4(-sideDiv4, sideDiv4, sideDiv2, sideDiv2) });
|
|
break;
|
|
case CGI::CubeMapFace::negX:
|
|
cb->SetViewport({ Vector4(sideDiv2 + sideDiv4, sideDiv4, sideDiv2, sideDiv2) });
|
|
break;
|
|
case CGI::CubeMapFace::posY:
|
|
cb->SetViewport({ Vector4(sideDiv4, -sideDiv4, sideDiv2, sideDiv2) });
|
|
break;
|
|
case CGI::CubeMapFace::negY:
|
|
cb->SetViewport({ Vector4(sideDiv4, sideDiv2 + sideDiv4, sideDiv2, sideDiv2) });
|
|
break;
|
|
case CGI::CubeMapFace::posZ:
|
|
cb->SetViewport({ Vector4(sideDiv4, sideDiv4, sideDiv2, sideDiv2) });
|
|
break;
|
|
}
|
|
}
|