18#include <SDL2/SDL_vulkan.h>
37#include "common/config.h"
41#include "zeroerr/assert.h"
46#include <assimp/mesh.h>
47#include <assimp/matrix3x3.h>
48#include <assimp/matrix4x4.h>
49#include <assimp/vector3.h>
50#include <glm/gtc/matrix_transform.hpp>
52#include "graphics/shaders/mesh3d_vert_spv.inc"
53#include "graphics/shaders/mesh3d_frag_spv.inc"
54#include "graphics/shaders/voxel_rect_vert_spv.inc"
55#include "graphics/shaders/voxel_rect_frag_spv.inc"
65 if (!initialized)
throw Exception(
"begin3DFrame: graphics not initialized");
67 createMesh3DPipeline();
68 if (swapchainPassOpen) {
78 if (!beginPresentCommandBuffer())
80 currentFrame2DBuffers().primitive3DDrawIndex = 0;
81 decalLayerFresh =
false;
82 recordDeferredFrameGraph();
84 vgAnyThisFrame_ =
false;
86 ensureGpuDrivenCullResources(gbufferWidth > 0 ? gbufferWidth : int(swapchain.extent.width),
87 gbufferHeight > 0 ? gbufferHeight : int(swapchain.extent.height));
91 createSceneColorResources(
int(swapchain.extent.width),
int(swapchain.extent.height));
96 gpuDrivenScenePassPending_ = gpuDrivenEnabled_ && gpuDrivenCullReady_;
97 if (!gpuDrivenScenePassPending_) {
98 if (beginSceneColorRenderPass()) {
103 ensureScenePassPipelines(activeScenePass(), activeSceneSamples());
106 ensureScenePassPipelines(renderpass, vk::SampleCountFlagBits::e1);
107 beginSwapchainColorPass();
111 auto &cb = currentPresentCb();
112 setViewportAndScissor(cb, swapchain.extent.width, swapchain.extent.height);
115 auto &fslots = currentMesh3dFrameSlots();
116 fslots.lastDrawCount = fslots.drawIndex;
117 fslots.drawIndex = 0;
118 fslots.activePalette = 0;
119 ensureMesh3dRing(fslots);
120 auto &cslots = currentMesh3dClusteredFrameSlots();
121 cslots.lastDrawCount = cslots.drawIndex;
122 cslots.drawIndex = 0;
123 ensureMesh3dClusteredRing(cslots);
125 lastMesh3dPipeline =
nullptr;
126 lastMesh3dClusteredPipeline =
nullptr;
127 currentVoxelInstanceFrame().drawIndex = 0;
128 swapchainPassOpen = !gpuDrivenScenePassPending_;
130 hasPendingClear =
false;
134void Graphics::ensureOffscreen3DResources() {
136 auto createResources = [&](vk::Format colorFormat, vkb::BuiltRenderPass &renderPass, vk::Pipeline &meshPipeline,
137 std::array<vk::Pipeline, kMesh3DPipelineVariants> &surfacePipelines,
138 std::array<vk::Pipeline, kPrimitive3DPipelineVariants> &primitivePipelines) {
139 if (renderPass)
return;
141 dev.createRenderPass()
142 .addSampledColorAttachment(colorFormat)
143 .addDepthAttachment(depthFormat, vk::AttachmentLoadOp::eClear,
144 vk::AttachmentStoreOp::eDontCare)
145 .addSubpass(vkb::SubpassBuilder()
146 .addAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal)
147 .setDepthStencilAttachment(
148 1, vk::ImageLayout::eDepthStencilAttachmentOptimal))
149 .addExternalShaderReadDependencies()
151 meshPipeline = createMesh3DStylePipeline(
152 embeddedSpirv(mesh3d_vert_spv), embeddedSpirv(mesh3d_frag_spv), mesh3dPipelineLayout,
153 renderPass, vk::SampleCountFlagBits::e1);
154 for (
int blendValue = 0; blendValue < 5; ++blendValue) {
156 for (
int depthValue = 0; depthValue < 2; ++depthValue) {
157 for (
int doubleValue = 0; doubleValue < 2; ++doubleValue) {
159 mesh3dPipelineIndex(
blend, depthValue != 0, doubleValue != 0);
160 surfacePipelines[
index] = createMesh3DStylePipeline(
161 embeddedSpirv(mesh3d_vert_spv), embeddedSpirv(mesh3d_frag_spv),
162 mesh3dPipelineLayout, renderPass, vk::SampleCountFlagBits::e1,
163 blend, depthValue != 0, doubleValue != 0);
167 buildPrimitive3DPipelines(renderPass, vk::SampleCountFlagBits::e1, primitivePipelines);
169 createResources(vk::Format::eR8G8B8A8Unorm, offscreen3DRenderPass, offscreen3DMeshPipeline,
170 offscreen3DSurfacePipelines, offscreenPrimitive3DPipelines);
171 createResources(vk::Format::eR16G16B16A16Sfloat, hdrOffscreen3DRenderPass, hdrOffscreen3DMeshPipeline,
172 hdrOffscreen3DSurfacePipelines, hdrOffscreenPrimitive3DPipelines);
173 if (!offscreen3DPool) {
174 vk::CommandPoolCreateInfo poolInfo{};
175 poolInfo.flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
176 offscreen3DPool = dev->createCommandPool(poolInfo);
177 vk::CommandBufferAllocateInfo allocInfo{};
178 allocInfo.commandPool = offscreen3DPool;
179 allocInfo.level = vk::CommandBufferLevel::ePrimary;
180 allocInfo.commandBufferCount = 1;
181 auto bufs = dev->allocateCommandBuffers(allocInfo);
182 offscreen3DCB = bufs[0];
183 vk::FenceCreateInfo fenceInfo{};
184 fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
185 offscreen3DFence = dev->createFence(fenceInfo);
186 const auto &
limits = dev.physical_device.properties.limits;
187 if (
limits.timestampComputeAndGraphics &&
limits.timestampPeriod > 0.f) {
188 vk::QueryPoolCreateInfo queryInfo{};
189 queryInfo.queryType = vk::QueryType::eTimestamp;
190 queryInfo.queryCount = 2;
191 offscreen3DTimestampQueryPool = dev->createQueryPool(queryInfo);
192 offscreen3DTimestampPeriodNs =
limits.timestampPeriod;
197void Graphics::destroyOffscreen3DResources() {
199 if (offscreen3DFence) {
200 dev->destroyFence(offscreen3DFence);
201 offscreen3DFence =
nullptr;
203 if (offscreen3DTimestampQueryPool) {
204 dev->destroyQueryPool(offscreen3DTimestampQueryPool);
205 offscreen3DTimestampQueryPool =
nullptr;
207 offscreen3DTimestampPeriodNs = 0.f;
208 lastOffscreen3DGpuDurationMs = 0.f;
209 if (offscreen3DPool) {
210 dev->destroyCommandPool(offscreen3DPool);
211 offscreen3DPool =
nullptr;
213 offscreen3DCB =
nullptr;
214 if (offscreen3DMeshPipeline) {
215 device->destroyPipeline(offscreen3DMeshPipeline);
216 offscreen3DMeshPipeline =
nullptr;
218 if (hdrOffscreen3DMeshPipeline) {
219 device->destroyPipeline(hdrOffscreen3DMeshPipeline);
220 hdrOffscreen3DMeshPipeline =
nullptr;
222 for (
auto &pipeline : offscreen3DSurfacePipelines) {
223 if (pipeline) device->destroyPipeline(pipeline);
226 for (
auto &pipeline : hdrOffscreen3DSurfacePipelines) {
227 if (pipeline) device->destroyPipeline(pipeline);
230 for (
auto &pipeline : offscreenPrimitive3DPipelines) {
231 if (pipeline) device->destroyPipeline(pipeline);
234 for (
auto &pipeline : hdrOffscreenPrimitive3DPipelines) {
235 if (pipeline) device->destroyPipeline(pipeline);
238 offscreenPrimitive3DBufs.clear();
239 if (offscreen3DRenderPass) {
240 device->destroyRenderPass(offscreen3DRenderPass);
241 offscreen3DRenderPass = {};
243 if (hdrOffscreen3DRenderPass) {
244 device->destroyRenderPass(hdrOffscreen3DRenderPass);
245 hdrOffscreen3DRenderPass = {};
252 if (!initialized)
throw Exception(
"begin3DFrameToCanvas: graphics not initialized");
253 if (!canvas)
throw Exception(
"begin3DFrameToCanvas: null canvas");
254 createMesh3DPipeline();
256 if (!oc)
throw Exception(
"begin3DFrameToCanvas: not an offscreen canvas");
257 if (offscreen3DPassOpen)
throw Exception(
"begin3DFrameToCanvas: already open");
258 if (swapchainPassOpen) {
266 ensureOffscreen3DResources();
268 offscreen3DCanvas = oc;
269 offscreen3DHDRActive = oc->isHDR();
271 if (offscreen3DFence) (void)device->waitForFences(1, &offscreen3DFence, VK_TRUE, UINT64_MAX);
272 offscreenPrimitive3DDrawIndex = 0;
273 vk::CommandBufferBeginInfo beginInfo{};
274 offscreen3DCB.begin(beginInfo);
275 lastOffscreen3DGpuDurationMs = 0.f;
276 if (offscreen3DTimestampQueryPool) {
277 offscreen3DCB.resetQueryPool(offscreen3DTimestampQueryPool, 0, 2);
278 offscreen3DCB.writeTimestamp(vk::PipelineStageFlagBits::eTopOfPipe,
279 offscreen3DTimestampQueryPool, 0);
281 std::array<vk::ClearValue, 2> clears{};
283 vk::ClearColorValue(std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, 1.f});
284 clears[1].depthStencil = vk::ClearDepthStencilValue{1.0f, 0};
285 vk::RenderPassBeginInfo rpBegin{};
287 rpBegin.framebuffer = oc->framebuffer3D();
289 vk::Rect2D{{0, 0}, {uint32_t(oc->getWidth()), uint32_t(oc->getHeight())}};
290 rpBegin.clearValueCount = uint32_t(clears.size());
291 rpBegin.pClearValues = clears.data();
292 oc->colorImage().beginColorAttachment();
293 oc->depthImage().beginDepthAttachment();
294 offscreen3DCB.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
295 setViewportAndScissor(offscreen3DCB, oc->getWidth(), oc->getHeight());
298 auto &fslots = currentMesh3dFrameSlots();
299 fslots.lastDrawCount = fslots.drawIndex;
300 fslots.drawIndex = 0;
301 fslots.activePalette = 0;
302 ensureMesh3dRing(fslots);
303 auto &cslots = currentMesh3dClusteredFrameSlots();
304 cslots.lastDrawCount = cslots.drawIndex;
305 cslots.drawIndex = 0;
306 ensureMesh3dClusteredRing(cslots);
308 lastMesh3dPipeline =
nullptr;
309 lastMesh3dClusteredPipeline =
nullptr;
310 offscreen3DPassOpen =
true;
312 hasPendingClear =
false;
316 if (!offscreen3DPassOpen || !offscreen3DCB)
return;
317 offscreen3DCB.endRenderPass();
318 if (offscreen3DCanvas) {
320 offscreen3DCanvas->
colorImage().endSampledLayout();
322 if (offscreen3DTimestampQueryPool) {
323 offscreen3DCB.writeTimestamp(vk::PipelineStageFlagBits::eBottomOfPipe,
324 offscreen3DTimestampQueryPool, 1);
331 vk::SubmitInfo submitInfo{};
332 submitInfo.commandBufferCount = 1;
333 submitInfo.pCommandBuffers = &offscreen3DCB;
334 if (offscreen3DFence) (void)device->resetFences(1, &offscreen3DFence);
335 (void)device.getQueue(vkb::QueueType::graphics).submit(1, &submitInfo, offscreen3DFence);
336 if (offscreen3DFence)
337 (void)device->waitForFences(1, &offscreen3DFence, VK_TRUE, UINT64_MAX);
338 if (offscreen3DTimestampQueryPool && offscreen3DTimestampPeriodNs > 0.f) {
339 std::array<uint64_t, 2> ticks{};
340 const vk::Result queryResult = device->getQueryPoolResults(
341 offscreen3DTimestampQueryPool, 0, uint32_t(ticks.size()),
sizeof(ticks), ticks.data(),
342 sizeof(uint64_t), vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait);
343 if (queryResult == vk::Result::eSuccess && ticks[1] >= ticks[0]) {
344 const double nanoseconds =
345 double(ticks[1] - ticks[0]) * double(offscreen3DTimestampPeriodNs);
346 lastOffscreen3DGpuDurationMs = float(nanoseconds * 1.0e-6);
350 offscreen3DPassOpen =
false;
351 offscreen3DHDRActive =
false;
352 offscreen3DCanvas =
nullptr;
356 glm::vec3
d = glm::normalize(
dir);
357 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
367void Graphics::destroyVoxelRectResources() {
368 for (
auto &frame : voxelInstanceFrames) {
369 for (
auto &slot : frame.
slots) {
370 slot.buffer.release();
371 slot.capacityBytes = 0;
376 voxelInstanceFrames.clear();
377 voxelRectSets.clear();
378 auto destroyBuf = [&](vkb::GenericBuffer &
b) {
b.release(); };
379 if (voxelUnitQuadReady) {
380 destroyBuf(voxelUnitQuadVerts);
381 destroyBuf(voxelUnitQuadIndices);
382 voxelUnitQuadReady =
false;
384 if (voxelRectPipeline) {
385 device->destroyPipeline(voxelRectPipeline);
386 voxelRectPipeline = vk::Pipeline{};
388 if (voxelRectPipelineLayout) {
389 device->destroyPipelineLayout(voxelRectPipelineLayout);
390 voxelRectPipelineLayout = vk::PipelineLayout{};
392 voxelRectSetLayoutUnique.reset();
393 voxelRectSetLayout = vk::DescriptorSetLayout{};
396void Graphics::createVoxelRectPipeline() {
397 if (voxelRectPipeline)
return;
399 if (!voxelRectPipelineLayout) {
400 vkb::DescriptorSetLayoutBuilder layoutBuilder;
401 voxelRectSetLayoutUnique =
403 .image(0, vk::DescriptorType::eCombinedImageSampler,
404 vk::ShaderStageFlagBits::eFragment, 1)
405 .createUnique(device.instance);
406 voxelRectSetLayout = *voxelRectSetLayoutUnique;
408 const auto pcr = pushConstantRange(vk::ShaderStageFlagBits::eVertex,
sizeof(VoxelRectPC));
409 voxelRectPipelineLayout = createPipelineLayout(device, voxelRectSetLayout, &pcr);
412 voxelRectPipeline = buildVoxelRectPipeline(renderpass, vk::SampleCountFlagBits::e1);
413 ensureVoxelUnitQuad();
416vk::Pipeline Graphics::buildVoxelRectPipeline(
const vkb::BuiltRenderPass &rp,
417 vk::SampleCountFlagBits samples) {
418 auto vert = embeddedSpirv(voxel_rect_vert_spv);
419 auto frag = embeddedSpirv(voxel_rect_frag_spv);
420 ShaderModulePair modules(device,
vert,
frag);
422 vk::VertexInputBindingDescription
bindings[3]{};
424 bindings[0].stride =
sizeof(glm::vec2);
425 bindings[0].inputRate = vk::VertexInputRate::eVertex;
427 bindings[1].stride =
sizeof(uint32_t);
428 bindings[1].inputRate = vk::VertexInputRate::eInstance;
430 bindings[2].stride =
sizeof(uint32_t);
431 bindings[2].inputRate = vk::VertexInputRate::eInstance;
433 vk::VertexInputAttributeDescription
attrs[3]{};
434 attrs[0].location = 0;
435 attrs[0].binding = 0;
436 attrs[0].format = vk::Format::eR32G32Sfloat;
438 attrs[1].location = 1;
439 attrs[1].binding = 1;
440 attrs[1].format = vk::Format::eR32Uint;
442 attrs[2].location = 2;
443 attrs[2].binding = 2;
444 attrs[2].format = vk::Format::eR32Uint;
447 vk::PipelineVertexInputStateCreateInfo vi{};
448 vi.vertexBindingDescriptionCount = 3;
449 vi.pVertexBindingDescriptions =
bindings;
450 vi.vertexAttributeDescriptionCount = 3;
451 vi.pVertexAttributeDescriptions =
attrs;
459 return device.createPipeline()
460 .useClassicPipeline(modules.vert, modules.frag)
461 .setPipelineLayout(voxelRectPipelineLayout)
462 .setVertexInputState(vi)
463 .setDynamicStatesViewportScissor()
464 .setRasterizer(vk::PolygonMode::eFill,
false,
false, 1.0f, vk::CullModeFlagBits::eBack,
465 vk::FrontFace::eCounterClockwise)
466 .setMultisampler(
false, samples)
467 .setDepthStencil(
true,
true, vk::CompareOp::eLess)
468 .setColorAttachmentCount(1)
472void Graphics::ensureVoxelUnitQuad() {
473 if (voxelUnitQuadReady)
return;
474 const float corners[8] = {0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f};
475 voxelUnitQuadVerts.allocate(frameToken(), device, vk::BufferUsageFlagBits::eVertexBuffer,
sizeof(corners),
476 vk::MemoryPropertyFlagBits::eHostVisible |
477 vk::MemoryPropertyFlagBits::eHostCoherent);
478 voxelUnitQuadVerts.updateLocal(frameToken(), corners,
sizeof(corners));
479 const uint32_t
indices[6] = {0, 2, 1, 0, 3, 2};
480 voxelUnitQuadIndices.allocate(frameToken(), device, vk::BufferUsageFlagBits::eIndexBuffer,
sizeof(
indices),
481 vk::MemoryPropertyFlagBits::eHostVisible |
482 vk::MemoryPropertyFlagBits::eHostCoherent);
483 voxelUnitQuadIndices.updateLocal(frameToken(),
indices,
sizeof(
indices));
484 voxelUnitQuadReady =
true;
487vkb::BoundSet Graphics::voxelRectSetFor(GpuTexture *gpuTex) {
488 ASSERT(gpuTex !=
nullptr);
489 auto it = voxelRectSets.find(gpuTex);
490 if (it != voxelRectSets.end())
return it->second;
492 vk::DescriptorSetAllocateInfo alloc{};
493 alloc.descriptorPool = descriptorPool;
494 alloc.descriptorSetCount = 1;
495 alloc.pSetLayouts = &voxelRectSetLayout;
496 vkb::UnboundSet unbound{device->allocateDescriptorSets(alloc).front()};
498 vkb::DescriptorSetUpdater updater(4, 4, 0);
499 updater.beginDescriptorSet(unbound)
500 .beginImages(0, 0, vk::DescriptorType::eCombinedImageSampler)
501 .image(vkb::SampledImage::forLaterSample(gpuTex->sampler, gpuTex->imageView()))
502 .update(device.instance);
504 vkb::BoundSet bound = std::move(unbound).publish();
505 voxelRectSets.emplace(gpuTex, bound);
510 float originY,
float originZ,
const std::string &faceDir,
511 Texture *atlas,
int tilesPerRow,
const uint32_t *ao) {
513 if (!initialized)
throw Exception(
"drawVoxelFaceInstances: graphics not initialized");
514 if (!swapchainPassOpen)
throw Exception(
"drawVoxelFaceInstances: call begin3DFrame first");
515 if (!voxelRectPipeline) createVoxelRectPipeline();
516 if (!packed ||
count <= 0)
return;
519 if (faceDir ==
"posX" || faceDir ==
"+x")
521 else if (faceDir ==
"negX" || faceDir ==
"-x")
523 else if (faceDir ==
"posY" || faceDir ==
"+y")
525 else if (faceDir ==
"negY" || faceDir ==
"-y")
527 else if (faceDir ==
"posZ" || faceDir ==
"+z")
529 else if (faceDir ==
"negZ" || faceDir ==
"-z")
532 throw Exception(
"drawVoxelFaceInstances: unknown faceDir '%s'", faceDir.c_str());
534 Texture *tex = atlas ? atlas : whiteTexture;
535 if (!tex || !tex->
gpuHandle)
throw Exception(
"drawVoxelFaceInstances: missing texture");
538 ensureVoxelUnitQuad();
540 const vk::DeviceSize
bytes = vk::DeviceSize(
count) *
sizeof(uint32_t);
541 auto &vframe = currentVoxelInstanceFrame();
542 const size_t slotIndex = vframe.drawIndex++;
543 while (vframe.slots.size() <= slotIndex) vframe.slots.emplace_back();
544 auto &slot = vframe.slots[slotIndex];
545 if (slot.capacityBytes <
size_t(
bytes) || !slot.buffer.buffer) {
546 const size_t alloc = std::max(
size_t(
bytes), slot.capacityBytes ? slot.capacityBytes * 2 : size_t(
bytes));
547 slot.buffer.allocate(frameToken(), device, vk::BufferUsageFlagBits::eVertexBuffer, vk::DeviceSize(alloc),
548 vk::MemoryPropertyFlagBits::eHostVisible |
549 vk::MemoryPropertyFlagBits::eHostCoherent);
550 slot.capacityBytes = alloc;
552 slot.buffer.updateLocal(frameToken(), packed, size_t(
bytes));
555 const uint32_t defaultAO = 0xFFu;
556 std::vector<uint32_t> aoDefaults;
558 aoDefaults.assign(
size_t(
count), defaultAO);
559 ao = aoDefaults.data();
561 const vk::DeviceSize aoBytes = vk::DeviceSize(
count) *
sizeof(uint32_t);
562 if (slot.aoCapacityBytes <
size_t(aoBytes) || !slot.aoBuffer.buffer) {
563 const size_t alloc = std::max(
size_t(aoBytes),
564 slot.aoCapacityBytes ? slot.aoCapacityBytes * 2
566 slot.aoBuffer.allocate(frameToken(), device, vk::BufferUsageFlagBits::eVertexBuffer,
567 vk::DeviceSize(alloc),
568 vk::MemoryPropertyFlagBits::eHostVisible |
569 vk::MemoryPropertyFlagBits::eHostCoherent);
570 slot.aoCapacityBytes = alloc;
572 slot.aoBuffer.updateLocal(frameToken(), ao, size_t(aoBytes));
575 pc.viewProj = mesh3dFrameUbo.
mvp;
576 pc.chunkOrigin = glm::vec4(originX, originY, originZ,
float(face));
577 pc.atlasInfo = glm::vec4(
float(std::max(1, tilesPerRow)), 0.f, 0.f, 0.f);
578 pc.tint = mesh3dFrameUbo.
tint;
580 auto &cb = currentPresentCb();
581 vk::DescriptorSet set = voxelRectSetFor(gpuTex);
582 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, voxelRectPipeline);
583 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, voxelRectPipelineLayout, 0, 1, &set, 0,
585 cb.pushConstants(voxelRectPipelineLayout, vk::ShaderStageFlagBits::eVertex, 0,
sizeof(VoxelRectPC),
588 vk::Buffer vbufs[3] = {voxelUnitQuadVerts.buffer, slot.buffer.buffer, slot.aoBuffer.buffer};
589 vk::DeviceSize offsets[3] = {0, 0, 0};
590 cb.bindVertexBuffers(0, 3, vbufs, offsets);
591 cb.bindIndexBuffer(voxelUnitQuadIndices.buffer, 0, vk::IndexType::eUint32);
592 cb.drawIndexed(6, uint32_t(
count), 0, 0, 0);
598 mesh3dVirtualAtlas.z =
enabled ? 1.f : 0.f;
605 int atlasSlotsX,
int atlasSlotsY,
606 float borderFraction) {
607 mesh3dVirtualTexture =
608 enabled ? glm::vec4(1.f,
float(pageCountX),
float(pageCountY), borderFraction)
610 const float packedNormalMask = mesh3dVirtualAtlas.z;
611 mesh3dVirtualAtlas =
enabled ? glm::vec4(
float(atlasSlotsX),
float(atlasSlotsY), packedNormalMask, 0.f)
612 : glm::vec4(0.f, 0.f, packedNormalMask, 0.f);
623 if (!sceneColorPassOpen || sceneColorSlots.size() < 2 || !sceneColorResumeRenderPass ||
624 sceneColorSamples != vk::SampleCountFlagBits::e1)
628 const size_t sourceIndex = currentFrameSlot() % sceneColorSlots.size();
629 const size_t snapshotIndex = (sourceIndex + 1u) % sceneColorSlots.size();
630 SceneColorSlot &
source = sceneColorSlots[sourceIndex];
631 SceneColorSlot &snapshot = sceneColorSlots[snapshotIndex];
632 auto &cb = currentPresentCb();
635 sceneColorPassOpen =
false;
636 source.color.endSampledLayout();
637 source.color.setLayout(cb, vk::ImageLayout::eTransferSrcOptimal);
638 snapshot.color.setLayout(cb, vk::ImageLayout::eTransferDstOptimal);
639 vk::ImageCopy copy{};
640 copy.srcSubresource =
641 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1};
642 copy.dstSubresource =
643 vk::ImageSubresourceLayers{vk::ImageAspectFlagBits::eColor, 0, 0, 1};
644 copy.extent = vk::Extent3D{
static_cast<uint32_t
>(sceneColorWidth),
645 static_cast<uint32_t
>(sceneColorHeight), 1};
646 cb.copyImage(
source.color.image(), vk::ImageLayout::eTransferSrcOptimal,
647 snapshot.color.image(), vk::ImageLayout::eTransferDstOptimal, 1, ©);
648 snapshot.color.setLayout(cb, vk::ImageLayout::eShaderReadOnlyOptimal);
650 source.color.setLayout(cb, vk::ImageLayout::eColorAttachmentOptimal);
651 source.depth.setLayout(cb, vk::ImageLayout::eDepthStencilAttachmentOptimal,
652 vk::ImageAspectFlagBits::eDepth);
653 vk::RenderPassBeginInfo resume{};
654 resume.renderPass = sceneColorResumeRenderPass;
655 resume.framebuffer =
source.framebuffer;
657 vk::Rect2D{{0, 0}, {
static_cast<uint32_t
>(sceneColorWidth),
658 static_cast<uint32_t
>(sceneColorHeight)}};
659 cb.beginRenderPass(resume, vk::SubpassContents::eInline);
660 source.color.beginColorAttachment();
661 source.depth.beginDepthAttachment();
662 sceneColorPassOpen =
true;
663 completedSceneColorSlot = snapshotIndex;
664 sceneColorHistoryValid =
true;
669 mesh3dEnvTexture = cube;
670 mesh3dEnvIntensity = intensity < 0.f ? 0.f : intensity;
671 if (!cube) mesh3dEnvIntensity = 0.f;
675 mesh3dEnvProbeCenter =
center;
676 mesh3dEnvProbeExtent = glm::max(extent, glm::vec3(0.f));
677 mesh3dFrameUbo.
envProbeCenter = glm::vec4(mesh3dEnvProbeCenter, 1.f);
678 mesh3dFrameUbo.
envProbeExtent = glm::vec4(mesh3dEnvProbeExtent, 0.f);
682 mesh3dReflectionProbes = upload;
690 mesh3dSkinInfluenceLimit =
static_cast<int>(
count);
695 if (!shadowPipeline) createShadowResources();
697 throw Exception(
"beginShadowPass: cascadeIndex out of range");
699 shadowPassCascade = cascadeIndex;
700 shadowPassDraws.clear();
704 if (shadowPassCascade < 0)
throw Exception(
"drawMeshShadow: call beginShadowPass first");
710 prepareSkinPass(
mesh,
nullptr, lightMVP, glm::mat4(1.f), glm::vec4(0.f),
d.skinSet,
712 shadowPassDraws.push_back(
d);
718 if (shadowPassCascade < 0)
throw Exception(
"drawMeshShadowAlpha: call beginShadowPass first");
719 if (!
mesh || !
mesh->gpuHandle)
throw Exception(
"drawMeshShadowAlpha: null mesh");
727 lodDither ? 1.f : 0.f, 0.f);
728 prepareSkinPass(
mesh,
albedo, lightMVP, glm::mat4(1.f),
d.lodFade,
d.skinSet,
730 shadowPassDraws.push_back(
d);
734 if (shadowPassCascade < 0)
throw Exception(
"endShadowPass: no active shadow pass");
735 if (!shadowPipeline || !shadowRenderPass) {
736 shadowPassCascade = -1;
737 shadowPassDraws.clear();
740 const int cascade = shadowPassCascade;
741 shadowCascadeDraws[cascade] = std::move(shadowPassDraws);
742 shadowPassDraws.clear();
743 shadowPassCascade = -1;
744 shadowPendingMask |= (1u << cascade);
752 if (!initialized)
throw Exception(
"beginGBufferPass: graphics not initialized");
753 if (
w <= 0 ||
h <= 0)
throw Exception(
"beginGBufferPass: invalid size");
754 if (!texSetLayout || !descriptorPool)
755 throw Exception(
"beginGBufferPass: textured descriptor layout not ready");
756 createGBufferResources(
w,
h);
757 gbufferPassActive =
true;
758 gbufferPassDraws.clear();
762 float farZ,
Texture *
albedo,
float tintR,
float tintG,
float tintB,
764 if (!gbufferPassActive)
throw Exception(
"drawMeshGBuffer: call beginGBufferPass first");
773 auto u6 = [](
float x) -> uint32_t {
774 return uint32_t(std::lround(std::clamp(
x, 0.f, 1.f) * 63.f));
779 const uint32_t rough7 = uint32_t(std::lround(std::clamp(
roughness, 0.f, 1.f) * 127.f));
780 const uint32_t metal7 = uint32_t(std::lround(std::clamp(
metallic, 0.f, 1.f) * 127.f));
781 const uint32_t packedTint = u6(tintR) | (u6(tintG) << 6) | (u6(tintB) << 12) | (rough7 << 18) | (metal7 << 25);
782 auto motion12 = [](
float value) {
783 return uint32_t(std::lround(std::clamp(
value, -1.f, 1.f) * 2047.f)) + 2047u;
785 const uint32_t packedMotion = motion12(motionX) | (motion12(motionY) << 12);
786 d.push.clip = glm::vec4(nearZ, farZ, std::bit_cast<float>(packedTint),
float(packedMotion));
788 gbufferPassDraws.push_back(
d);
793 float tintG,
float tintB,
float motionX,
float motionY,
795 if (!gbufferPassActive)
throw Exception(
"drawMeshGBufferAlpha: call beginGBufferPass first");
796 if (!
mesh || !
mesh->gpuHandle)
throw Exception(
"drawMeshGBufferAlpha: null mesh");
805 auto u6 = [](
float x) -> uint32_t {
806 return uint32_t(std::lround(std::clamp(
x, 0.f, 1.f) * 63.f));
808 const uint32_t rough7 = uint32_t(std::lround(std::clamp(
roughness, 0.f, 1.f) * 127.f));
809 const uint32_t metal7 = uint32_t(std::lround(std::clamp(
metallic, 0.f, 1.f) * 127.f));
810 const uint32_t packedTint = u6(tintR) | (u6(tintG) << 6) | (u6(tintB) << 12) | (rough7 << 18) | (metal7 << 25);
811 auto motion12 = [](
float value) {
812 return uint32_t(std::lround(std::clamp(
value, -1.f, 1.f) * 2047.f)) + 2047u;
814 const uint32_t packedMotion = motion12(motionX) | (motion12(motionY) << 12);
815 d.push.clip = glm::vec4(nearZ, farZ, std::bit_cast<float>(packedTint),
float(packedMotion));
817 gbufferPassDraws.push_back(
d);
821 if (!gbufferPassActive)
throw Exception(
"endGBufferPass: no active gbuffer pass");
822 gbufferPassActive =
false;
824 auto *slot = currentGBufferSlot();
825 if (!gbufferPipeline || !gbufferRenderPass || !slot || !slot->framebuffer) {
826 gbufferPassDraws.clear();
827 gbufferPending =
false;
832 gbufferPending =
true;
834 rc->getGBuffer()->setTargets(gbufferWidth, gbufferHeight, &slot->depthColorTex, &slot->normalTex,
835 &slot->albedoTex, &slot->depthTex, &slot->pbrParamsTex, &slot->emissiveTex);
839void Graphics::ensureDecalPlaceholders() {
840 if (decalFlatAlbedo)
return;
841 const uint8_t transparent[4] = {0, 0, 0, 0};
842 const uint8_t flatNormal[4] = {128, 128, 255, 255};
843 const uint8_t neutralParams[4] = {128, 128, 0, 255};
844 decalFlatAlbedo =
newTexture(1, 1, transparent);
845 decalFlatNormal =
newTexture(1, 1, flatNormal);
846 decalFlatParams =
newTexture(1, 1, neutralParams);
851 if (!initialized)
throw Exception(
"beginDecalPass: graphics not initialized");
852 if (
w <= 0 ||
h <= 0)
throw Exception(
"beginDecalPass: invalid size");
853 if (!texSetLayout || !descriptorPool)
854 throw Exception(
"beginDecalPass: textured descriptor layout not ready");
855 createDecalResources(
w,
h);
856 ensureDecalPlaceholders();
857 decalPassActive =
true;
858 decalPassDraws.clear();
863 decalNear = nearZ > 1e-4f ? nearZ : 0.1f;
864 decalFar = farZ > decalNear ? farZ : decalNear + 1.f;
869 float normalStrength,
float roughnessStrength,
float metalStrength,
870 float emissiveStrength,
int blendMode,
int projectionMode,
871 float blendSharpness,
float parallaxScale,
float parallaxMinLayers,
872 float parallaxMaxLayers,
float edgeFadeWidth) {
873 if (!decalPassActive)
throw Exception(
"drawDecal: call beginDecalPass first");
880 for (
int i = 0; i < 4; ++i)
d.uvRect[i] = uvRect[i];
884 d.roughnessStrength = roughnessStrength;
885 d.metalStrength = metalStrength;
886 d.emissiveStrength = emissiveStrength;
887 d.blendMode = blendMode == 1 ? 1 : 0;
888 d.projectionMode = std::clamp(projectionMode, 0, 3);
889 d.blendSharpness = blendSharpness > 0.f ? blendSharpness : 4.f;
890 d.parallaxScale = std::clamp(parallaxScale, 0.f, 1.f);
891 d.parallaxMinLayers = std::clamp(parallaxMinLayers, 1.f, 64.f);
892 d.parallaxMaxLayers = std::clamp(parallaxMaxLayers,
d.parallaxMinLayers, 64.f);
893 d.edgeFadeWidth = std::clamp(edgeFadeWidth, 0.f, 0.49f);
894 if (decalPassDraws.size() >= kMaxDecalInstances)
return;
895 decalPassDraws.push_back(
d);
899 if (!decalPassActive)
throw Exception(
"endDecalPass: no active decal pass");
900 decalPassActive =
false;
901 auto *slot = currentDecalSlot();
902 if (!decalPipeline || !decalRenderPass || !slot || !slot->framebuffer) {
903 decalPassDraws.clear();
904 decalPending =
false;
910void Graphics::recordDecalPass() {
911 if (!decalPending)
return;
912 decalPending =
false;
913 auto *slot = currentDecalSlot();
914 if (!slot || !decalPipeline || !decalRenderPass || !slot->framebuffer) {
915 decalPassDraws.clear();
918 auto *gslot = currentGBufferSlot();
920 decalPassDraws.clear();
924 auto &cb = currentPresentCb();
925 recordDecalPassInto(cb, *slot, *gslot);
928void Graphics::recordDecalPassInto(vk::CommandBuffer cb, DecalSlot &slot, GBufferSlot &gslot) {
929 const uint32_t
w = uint32_t(decalWidth);
930 const uint32_t
h = uint32_t(decalHeight);
931 if (
w == 0 ||
h == 0) {
932 decalPassDraws.clear();
936 DecalCameraUBO cam{};
937 cam.viewProj = decalViewProj;
938 cam.invViewProj = glm::inverse(decalViewProj);
940 glm::vec4(decalNear, decalFar, 1.f /
float(
w), 1.f /
float(
h));
941 updateRingLocal(slot.cameraUbo, 0, &cam,
sizeof(cam));
943 slot.albedo.beginColorAttachment();
944 slot.normal.beginColorAttachment();
945 slot.params.beginColorAttachment();
946 std::array<vk::ClearValue, 3> clears{};
947 clears[0].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
948 clears[1].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
949 clears[2].color = vk::ClearColorValue(std::array<float, 4>{0, 0, 0, 0});
950 vk::RenderPassBeginInfo rpBegin{};
951 rpBegin.renderPass = decalRenderPass;
952 rpBegin.framebuffer = slot.framebuffer;
953 rpBegin.renderArea = vk::Rect2D{{0, 0}, {
w,
h}};
954 rpBegin.clearValueCount = uint32_t(clears.size());
955 rpBegin.pClearValues = clears.data();
956 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
957 setViewportAndScissor(cb,
w,
h);
958 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, decalPipeline);
959 decalLayerFresh =
true;
961 for (
const auto &
d : decalPassDraws) {
962 GpuTexture *gpuAlb =
d.albedo &&
d.albedo->gpuHandle
963 ?
static_cast<GpuTexture *
>(
d.albedo->gpuHandle)
964 : static_cast<GpuTexture *>(decalFlatAlbedo->gpuHandle);
965 GpuTexture *gpuNrm =
d.normal &&
d.normal->gpuHandle
966 ?
static_cast<GpuTexture *
>(
d.normal->gpuHandle)
967 : static_cast<GpuTexture *>(decalFlatNormal->gpuHandle);
968 GpuTexture *gpuPrm =
d.params &&
d.params->gpuHandle
969 ?
static_cast<GpuTexture *
>(
d.params->gpuHandle)
970 : static_cast<GpuTexture *>(decalFlatParams->gpuHandle);
971 DecalInstanceData inst{};
972 inst.model =
d.model;
973 inst.uvRect = glm::vec4(
d.uvRect[0],
d.uvRect[1],
d.uvRect[2],
d.uvRect[3]);
975 glm::vec4(
d.fade,
d.normalStrength,
d.roughnessStrength,
d.metalStrength);
976 inst.extraParams = glm::vec4(
d.emissiveStrength,
float(
d.blendMode),
977 float(
d.projectionMode),
d.blendSharpness);
979 glm::vec4(
d.parallaxScale,
d.parallaxMinLayers,
d.parallaxMaxLayers,
d.edgeFadeWidth);
980 const vkb::BoundSet set = decalSetFor(slot, gpuAlb, gpuNrm, gpuPrm, &gslot.depthGpu,
982 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, decalPipelineLayout, 0, 1,
983 set.ptr(), 0,
nullptr);
984 cb.pushConstants(decalPipelineLayout,
985 vk::ShaderStageFlagBits::eVertex |
986 vk::ShaderStageFlagBits::eFragment,
987 0,
sizeof(inst), &inst);
990 decalPassDraws.clear();
992 slot.albedo.endSampledLayout();
993 slot.normal.endSampledLayout();
994 slot.params.endSampledLayout();
997void Graphics::ensureDefaultEnvCubemap() {
998 if (defaultEnvCubemap)
return;
999 const uint8_t black[4] = {0, 0, 0, 255};
1000 std::vector<uint8_t> faces(6u * 4u);
1001 for (
int i = 0; i < 6; ++i) {
1002 faces[size_t(i) * 4u + 0] = black[0];
1003 faces[size_t(i) * 4u + 1] = black[1];
1004 faces[size_t(i) * 4u + 2] = black[2];
1005 faces[size_t(i) * 4u + 3] = black[3];
1007 defaultEnvCubemap =
newCubemap(1, faces.data());
1019 const std::string &alphaTechnique) {
1020 mesh3dSurfaceMode = mode;
1021 mesh3dSurfaceBlend =
blend;
1022 mesh3dSurfaceDepthWrite = depthWrite;
1024 mesh3dAlphaCutoff = std::clamp(
alphaCutoff, 0.f, 1.f);
1025 mesh3dAlphaTechnique = alphaTechnique;
1029 mesh3dTexBombScale = cellScale > 1e-3f ? cellScale : 1e-3f;
1031 mesh3dTexBombRot = rotAmount < 0.f ? 0.f : (rotAmount > 1.f ? 1.f : rotAmount);
1033 glm::vec4(mesh3dTexBombScale, mesh3dTexBombStrength, mesh3dTexBombRot, 0.f);
1037 mesh3dParallaxScale =
scale < 0.f ? 0.f : (
scale > 0.25f ? 0.25f :
scale);
1038 float minL = minLayers < 1.f ? 1.f : minLayers;
1039 float maxL = maxLayers < minL ? minL : maxLayers;
1040 if (maxL > 64.f) maxL = 64.f;
1041 mesh3dParallaxMinLayers = minL;
1042 mesh3dParallaxMaxLayers = maxL;
1044 glm::vec4(mesh3dParallaxScale, mesh3dParallaxMinLayers, mesh3dParallaxMaxLayers, 0.f);
1048 mesh3dLodFade = glm::vec4(std::clamp(
weight, 0.f, 1.f), reverse ? 1.f : 0.f,
1050 mesh3dFrameUbo.
lodFade = mesh3dLodFade;
1054 mesh3dLighting = pack;
1069 for (
int i = 0; i <
n; ++i) {
1077 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
1078 else d = glm::normalize(
d);
1079 mesh3dFrameUbo.
lightDir = glm::vec4(
d,
float(
n));
1084 mesh3dFrameUbo.
lightDir = glm::vec4(0.f, 1.f, 0.f,
float(
n));
1086 glm::vec4(0.f, 0.f, 0.f, mesh3dFrameUbo.
lightColor.w);
1091 float windAngle,
float coverage,
float detail) {
1092 mesh3dFrameUbo.
cloud = glm::vec4(std::clamp(
strength, 0.f, 1.f), std::max(worldCell, 1e-4f),
1094 mesh3dFrameUbo.
cloudWind = glm::vec4(std::cos(windAngle) * windSpeed,
1095 std::sin(windAngle) * windSpeed,
1096 std::clamp(coverage, 0.f, 1.f), std::clamp(
detail, 0.f, 1.f));
1099void Graphics::ensureFlatNormalTexture3D() {
1100 if (flatNormalTexture3D)
return;
1101 const uint8_t
px[4] = {128, 128, 255, 255};
1105void Graphics::ensureFlatHeightTexture3D() {
1106 if (flatHeightTexture3D)
return;
1108 const uint8_t
px[4] = {128, 128, 128, 255};
1112vkb::BoundSet Graphics::mesh3dSetFor(GpuTexture *gpuTex, GpuTexture *normalTex, GpuTexture *envTex,
1113 GpuTexture *heightTex, GpuTexture *depthTex, GpuTexture *sceneColorTex,
1114 GpuTexture *decalAlbedo, GpuTexture *decalNormal,
1115 GpuTexture *decalParams,
1116 Mesh3dFrameSlots &fslots) {
1117 ASSERT(gpuTex !=
nullptr);
1118 ASSERT(normalTex !=
nullptr);
1119 ASSERT(envTex !=
nullptr);
1120 ASSERT(heightTex !=
nullptr);
1121 ASSERT(sceneColorTex !=
nullptr);
1122 ASSERT(decalAlbedo !=
nullptr);
1123 ASSERT(decalNormal !=
nullptr);
1124 ASSERT(decalParams !=
nullptr);
1125 ASSERT(currentShadowArrayView());
1126 ASSERT(fslots.uboRing.buffer);
1127 ASSERT(fslots.shadowRing.buffer);
1129 auto probeTexture = [&](
int index) -> GpuTexture * {
1132 if (texture && texture->gpuHandle) {
1133 auto *gpu =
static_cast<GpuTexture *
>(texture->gpuHandle);
1134 if (gpu->isCube)
return gpu;
1137 return static_cast<GpuTexture *
>(defaultEnvCubemap->
gpuHandle);
1139 GpuTexture *probe0 = probeTexture(0);
1140 GpuTexture *probe1 = probeTexture(1);
1141 Mesh3dSetKey
key{gpuTex, normalTex, envTex, probe0, probe1, heightTex, depthTex, sceneColorTex,
1142 decalAlbedo, decalNormal, decalParams};
1143 key.paletteSlot = fslots.activePalette;
1144 if (fslots.palettes.empty()) uploadSkinPalette(
nullptr, fslots);
1145 auto it = fslots.sets.find(
key);
1146 if (it != fslots.sets.end())
return it->second;
1148 vk::DescriptorSetAllocateInfo alloc{};
1149 alloc.descriptorPool = descriptorPool;
1150 alloc.descriptorSetCount = 1;
1151 alloc.pSetLayouts = &mesh3dSetLayout;
1152 vkb::UnboundSet unbound{device->allocateDescriptorSets(alloc).front()};
1154 vkb::DescriptorSetUpdater updater(16, 16, 0);
1155 updater.beginDescriptorSet(unbound)
1156 .beginBuffers(0, 0, vk::DescriptorType::eUniformBufferDynamic)
1157 .buffer(fslots.uboRing.buffer, 0,
sizeof(Mesh3DUBO))
1158 .beginBuffers(21, 0, vk::DescriptorType::eStorageBuffer)
1159 .buffer(fslots.palettes[fslots.activePalette].buffer, 0, fslots.palettes[fslots.activePalette].capacity)
1160 .beginImages(1, 0, vk::DescriptorType::eCombinedImageSampler)
1161 .image(vkb::SampledImage::forLaterSample(gpuTex->sampler, gpuTex->imageView()))
1162 .beginImages(2, 0, vk::DescriptorType::eCombinedImageSampler)
1163 .image(vkb::SampledImage::forLaterSample(normalTex->sampler, normalTex->imageView()))
1164 .beginImages(3, 0, vk::DescriptorType::eCombinedImageSampler)
1165 .image(vkb::SampledImage::forLaterSample(envTex->sampler, envTex->imageView()))
1166 .beginBuffers(4, 0, vk::DescriptorType::eUniformBufferDynamic)
1167 .buffer(fslots.shadowRing.buffer, 0,
sizeof(ShadowUBO))
1168 .beginImages(5, 0, vk::DescriptorType::eCombinedImageSampler)
1169 .image(vkb::SampledImage::forLaterSample(shadowSampler, currentShadowArrayView()))
1170 .beginImages(6, 0, vk::DescriptorType::eCombinedImageSampler)
1171 .image(vkb::SampledImage::forLaterSample(heightTex->sampler, heightTex->imageView()))
1172 .beginImages(7, 0, vk::DescriptorType::eCombinedImageSampler)
1173 .image(vkb::SampledImage::forLaterSample(depthTex->sampler, depthTex->imageView()))
1174 .beginImages(8, 0, vk::DescriptorType::eCombinedImageSampler)
1175 .image(vkb::SampledImage::forLaterSample(decalAlbedo->sampler, decalAlbedo->imageView()))
1176 .beginImages(9, 0, vk::DescriptorType::eCombinedImageSampler)
1177 .image(vkb::SampledImage::forLaterSample(decalNormal->sampler, decalNormal->imageView()))
1178 .beginImages(10, 0, vk::DescriptorType::eCombinedImageSampler)
1179 .image(vkb::SampledImage::forLaterSample(decalParams->sampler, decalParams->imageView()))
1180 .beginImages(16, 0, vk::DescriptorType::eCombinedImageSampler)
1181 .image(vkb::SampledImage::forLaterSample(probe0->sampler, probe0->imageView()))
1182 .beginImages(17, 0, vk::DescriptorType::eCombinedImageSampler)
1183 .image(vkb::SampledImage::forLaterSample(probe1->sampler, probe1->imageView()))
1184 .beginImages(18, 0, vk::DescriptorType::eCombinedImageSampler)
1185 .image(vkb::SampledImage::forLaterSample(sceneColorTex->sampler, sceneColorTex->imageView()))
1186 .beginImages(20, 0, vk::DescriptorType::eCombinedImageSampler)
1187 .image(vkb::SampledImage::forLaterSample(shadowRawSampler, currentShadowArrayView()))
1188 .update(device.instance);
1190 vkb::BoundSet bound = std::move(unbound).publish();
1191 fslots.sets.emplace(
key, bound);
1204 const float n = nearZ > 1e-4f ? nearZ : 0.1f;
1205 const float f = farZ >
n ? farZ :
n + 1.f;
graphics::Texture * albedo
std::vector< std::uint32_t > indices
std::array< float, 3 > scale
std::vector< std::shared_ptr< DeviceBytes > > bindings
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
const UnitySourceAsset & source
const AssetImportLimits & limits
EVENGINE_API_FOUNDATION public API.
void ensureFileTexturesReady()
Finish CPU decode and GPU upload for outstanding newTextureFromFile results. @thread Game/render thre...
Mesh3DSceneColorCaptureStatus
Observable result of requesting a same-frame mesh SceneColor snapshot.
RenderControl * getRenderControl()
Shared compilable 3D render control (features → pass list + GBuffer). Owned by Graphics; valid for th...
GPU mesh handle (+ optional CPU morph targets).
Declarative, compilable 3D render control.
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
void drawMeshGBufferAlpha(Mesh *mesh, const glm::mat4 &mvp, const glm::mat4 &model, float nearZ, float farZ, Texture *albedo=nullptr, float tintR=1.f, float tintG=1.f, float tintB=1.f, float motionX=0.f, float motionY=0.f, float roughness=0.45f, float metallic=0.f) override
Draws mesh g buffer alpha.
void setMesh3DReflectionProbes(const ReflectionProbeUpload &upload) override
Sets the mesh 3 d reflection probes.
void beginShadowPass(int cascadeIndex) override
Begins shadow pass.
void begin3DFrame() override
Begins 3 d frame.
void setMesh3DParallax(float scale, float minLayers=8.f, float maxLayers=32.f) override
Sets the mesh 3 d parallax.
void setCloudShadows(float strength, float worldCell, float time, float windSpeed, float windAngle, float coverage, float detail) override
Sets the cloud shadows.
void drawMeshShadowAlpha(Mesh *mesh, const glm::mat4 &lightMVP, Texture *albedo=nullptr, bool doubleSided=true, float lodWeight=1.f, bool lodFadeReverse=false, bool lodDither=false) override
Draws mesh shadow alpha.
void drawVoxelFaceInstances(const uint32_t *packed, int count, float originX, float originY, float originZ, const std::string &faceDir, Texture *atlas, int tilesPerRow=16, const uint32_t *ao=nullptr) override
Draws voxel face instances.
void end3DFrameToCanvas() override
Ends 3 d frame to canvas.
void drawMeshGBuffer(Mesh *mesh, const glm::mat4 &mvp, const glm::mat4 &model, float nearZ, float farZ, Texture *albedo=nullptr, float tintR=1.f, float tintG=1.f, float tintB=1.f, float motionX=0.f, float motionY=0.f, float roughness=0.45f, float metallic=0.f) override
Draws mesh g buffer.
void endDecalPass() override
Ends decal pass.
void setMesh3DShadowReceive(bool receive) override
Sets the mesh 3 d shadow receive.
void setMesh3DLighting(const Lighting3DPack &pack) override
Sets the mesh 3 d lighting.
void setMesh3DEnv(Texture *cube, float intensity) override
Sets the mesh 3 d env.
Mesh3DSceneColorCaptureStatus captureMesh3DSceneColor() override
Capture mesh 3 d scene color.
void setDecalCamera(const glm::mat4 &viewProj, float nearZ, float farZ) override
Sets the decal camera.
void setMesh3DLodDither(float weight, bool reverse, bool enabled) override
Sets the mesh 3 d lod dither.
bool isCanvasActive() const override
True when canvas active.
void endGBufferPass() override
Ends g buffer pass.
void endShadowPass() override
Ends shadow pass.
void setMesh3DTexCellBomb(float cellScale, float strength, float rotAmount=1.f) override
Sets the mesh 3 d tex cell bomb.
void drawDecal(const glm::mat4 &model, Texture *albedo, Texture *normal, Texture *params, const float uvRect[4], float fade, float normalStrength, float roughnessStrength, float metalStrength, float emissiveStrength, int blendMode=0, int projectionMode=0, float blendSharpness=4.f, float parallaxScale=0.f, float parallaxMinLayers=8.f, float parallaxMaxLayers=24.f, float edgeFadeWidth=0.06f) override
Draws decal.
void setMesh3DSurface(SurfaceMode mode, BlendMode blend, bool depthWrite, bool doubleSided, float alphaCutoff, const std::string &alphaTechnique="cutoff") override
Sets the mesh 3 d surface.
void setMesh3DCameraPos(const glm::vec3 &eye) override
Sets the mesh 3 d camera pos.
void begin3DFrameToCanvas(Canvas *canvas) override
Begins 3 d frame to canvas.
void setMesh3DSceneColor(Texture *color) override
Sets the mesh 3 d scene color.
void setMesh3DViewProj(const glm::mat4 &viewProj) override
Sets the mesh 3 d view proj.
void drawMeshShadow(Mesh *mesh, const glm::mat4 &lightMVP, bool doubleSided=true) override
Draws mesh shadow.
void setMesh3DShadows(const ShadowUpload &upload) override
Sets the mesh 3 d shadows.
const vkb::BuiltRenderPass & getOffscreen3DRenderPass(bool hdr=false)
Returns the offscreen 3 d render pass.
void beginGBufferPass(int width, int height) override
Begins g buffer pass.
void setMesh3DLight(const glm::vec3 &dir, const glm::vec3 &color) override
Sets the mesh 3 d light.
void setMesh3DHeightTexture(Texture *height) override
Sets the mesh 3 d height texture.
Texture * newCubemap(int faceSize, const uint8_t *rgbaFaces) override
Creates a cubemap. @ownership Caller deletes unless documented otherwise.
void setMesh3DView(const glm::mat4 &view) override
Sets the mesh 3 d view.
void setMesh3DVirtualTexture(bool enabled, int pageCountX, int pageCountY, int atlasSlotsX, int atlasSlotsY, float borderFraction) override
Sets the mesh 3 d virtual texture.
vkb::Device & getDevice()
Returns the device.
void setMesh3DNormalTexture(Texture *normal) override
Sets the mesh 3 d normal texture.
void setMesh3DClip(float nearZ, float farZ) override
Sets the mesh 3 d clip.
Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false) override
Creates a texture. @ownership Caller deletes unless documented otherwise.
void setMesh3DSceneDepth(Texture *depth) override
Sets the mesh 3 d scene depth.
void setMesh3DEnvProbe(const glm::vec3 ¢er, const glm::vec3 &extent) override
Sets the mesh 3 d env probe.
void setMesh3DPackedNormalMask(bool enabled) override
Sets the mesh 3 d packed normal mask.
void setMesh3DMaterial(float metallic, float roughness) override
Sets the mesh 3 d material.
void setMesh3DSkinInfluenceLimit(SkinInfluenceLimit count) override
Sets the mesh 3 d skin influence limit.
FrameArena & currentFrameArena()
Per-frame arena for the current swapchain frame slot.
void beginDecalPass(int width, int height) override
Begins decal pass.
OffscreenCanvas public API.
vkb::ColorAttachmentImage & colorImage()
Color image.
std::vector< ParamSpec > params
SurfaceMode
How a 3D surface contributes to depth and color passes.
eve::BlendMode BlendMode
Compatibility alias for the shared 2D blend mode.
SkinInfluenceLimit
Number of strongest vertex influences retained by GPU skinning.
std::vector< int64_t > attrs(const Node &n, const char *key, std::vector< int64_t > fallback)
Attrs.
BlendMode
Render-neutral 2D blend mode shared by graphics-facing modules.
Lighting3DPack public API.
std::array< glm::vec4, kMaxDiffuseVolumeProbes > diffuseVolumePosition
std::array< glm::vec4, kMaxDiffuseVolumeProbes > diffuseVolumeExtent
bool diffuseVolumeTrilinearCell
std::array< glm::vec4, kMaxDiffuseVolumeProbes *9 > diffuseVolumeSh
static constexpr int kMaxLights
Light3DGpu lights[kMaxLights]
bool diffuseProbeShEnabled
int diffuseVolumeProbeCount
std::array< glm::vec4, 9 > diffuseProbeSh
ReflectionProbeUpload public API.
static constexpr int kTotalLayers
glm::vec4 diffuseVolumePosition[Lighting3DPack::kMaxDiffuseVolumeProbes]
glm::vec4 diffuseVolumeInfo
glm::vec4 diffuseProbeInfo
glm::vec4 diffuseProbeSh[9]
glm::vec4 diffuseVolumeExtent[Lighting3DPack::kMaxDiffuseVolumeProbes]
glm::vec4 diffuseVolumeSh[Lighting3DPack::kMaxDiffuseVolumeProbes *9]