Fix character preview facing and add 4x MSAA to preview render target

Character was facing stage-left (yaw 180) instead of toward camera;
corrected default yaw to 90. Added MSAA support to VkRenderTarget with
automatic resolve attachment, and enabled 4x MSAA for the character
preview off-screen pass.
This commit is contained in:
Kelsi 2026-02-23 10:48:26 -08:00
parent d65b170774
commit 9a1b78bffd
6 changed files with 229 additions and 108 deletions

View file

@ -89,7 +89,7 @@ private:
bool modelLoaded_ = false;
bool compositeRequested_ = false;
bool compositeRendered_ = false; // True after first successful compositePass
float modelYaw_ = 180.0f;
float modelYaw_ = 90.0f;
// Cached info from loadCharacter() for later recompositing.
game::Race race_ = game::Race::HUMAN;

View file

@ -45,7 +45,8 @@ public:
~CharacterRenderer();
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout, pipeline::AssetManager* am,
VkRenderPass renderPassOverride = VK_NULL_HANDLE);
VkRenderPass renderPassOverride = VK_NULL_HANDLE,
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT);
void shutdown();
void setAssetManager(pipeline::AssetManager* am) { assetManager = am; }
@ -230,6 +231,7 @@ public:
private:
VkContext* vkCtx_ = nullptr;
VkRenderPass renderPassOverride_ = VK_NULL_HANDLE;
VkSampleCountFlagBits msaaSamplesOverride_ = VK_SAMPLE_COUNT_1_BIT;
pipeline::AssetManager* assetManager = nullptr;
// Vulkan pipelines (one per blend mode)

View file

@ -13,6 +13,7 @@ class VkContext;
/**
* Off-screen render target encapsulating VkRenderPass + VkFramebuffer + color VkImage.
* Used for minimap compositing, world map compositing, and other off-screen passes.
* Supports optional MSAA with automatic resolve.
*/
class VkRenderTarget {
public:
@ -26,9 +27,11 @@ public:
* Create the render target with given dimensions and format.
* Creates: color image, image view, sampler, render pass, framebuffer.
* When withDepth is true, also creates a D32_SFLOAT depth attachment.
* When msaaSamples > 1, creates multisampled images and a resolve attachment.
*/
bool create(VkContext& ctx, uint32_t width, uint32_t height,
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, bool withDepth = false);
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, bool withDepth = false,
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT);
/**
* Destroy all Vulkan resources.
@ -48,14 +51,15 @@ public:
*/
void endPass(VkCommandBuffer cmd);
// Accessors
VkImage getColorImage() const { return colorImage_.image; }
VkImageView getColorImageView() const { return colorImage_.imageView; }
// Accessors - always return the resolved (single-sample) image for reading
VkImage getColorImage() const { return resolveImage_.image ? resolveImage_.image : colorImage_.image; }
VkImageView getColorImageView() const { return resolveImage_.imageView ? resolveImage_.imageView : colorImage_.imageView; }
VkSampler getSampler() const { return sampler_; }
VkRenderPass getRenderPass() const { return renderPass_; }
VkExtent2D getExtent() const { return { colorImage_.extent.width, colorImage_.extent.height }; }
VkFormat getFormat() const { return colorImage_.format; }
bool isValid() const { return framebuffer_ != VK_NULL_HANDLE; }
VkSampleCountFlagBits getSampleCount() const { return msaaSamples_; }
/**
* Descriptor info for binding the color attachment as a texture in a shader.
@ -63,9 +67,11 @@ public:
VkDescriptorImageInfo descriptorInfo() const;
private:
AllocatedImage colorImage_{};
AllocatedImage colorImage_{}; // MSAA color (or single-sample if no MSAA)
AllocatedImage resolveImage_{}; // Single-sample resolve target (only when MSAA)
AllocatedImage depthImage_{};
bool hasDepth_ = false;
VkSampleCountFlagBits msaaSamples_ = VK_SAMPLE_COUNT_1_BIT;
VkSampler sampler_ = VK_NULL_HANDLE;
VkRenderPass renderPass_ = VK_NULL_HANDLE;
VkFramebuffer framebuffer_ = VK_NULL_HANDLE;