Add playblast: viewport capture to H.264 MP4 via libavcodec
- TimelinePanel: Playblast button opens modal with output dir (native folder picker via IFileOpenDialog), custom resolution (HD/FHD/4K presets), frame range, and movie export toggle - UsdSceneRenderer: CaptureFrame() reads pixels from resolved MSAA FBO; Render() stores last dimensions for off-screen re-render at capture res - MovieEncoder: streams RGBA frames directly into MP4 using libavcodec/ libswscale (H.264, tries libx264 → nvenc → qsv → amf → openh264) - Application: CapturePlayblastFrame() re-renders at capture resolution each frame; opens/writes/closes MovieEncoder inline with the render loop - FileDialog: BrowseFolder() using Vista-style IFileOpenDialog (COM) - CMake: FindFFmpeg.cmake locates prebuilt BtbN GPL shared package in third_party/ffmpeg; links and copies DLLs for main and test targets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include <pxr/base/gf/frustum.h>
|
||||
#include <pxr/base/gf/rotation.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -496,6 +497,8 @@ bool UsdSceneRenderer::PickObjectsInRect(
|
||||
void UsdSceneRenderer::Render(int width, int height)
|
||||
{
|
||||
if (!m_stage || width <= 0 || height <= 0) return;
|
||||
m_lastRenderWidth = width;
|
||||
m_lastRenderHeight = height;
|
||||
|
||||
InitRenderer();
|
||||
if (!m_renderer) return;
|
||||
@@ -676,6 +679,40 @@ uint32_t UsdSceneRenderer::GetColorTextureID()
|
||||
return att ? static_cast<uint32_t>(att->GetGlTextureName()) : 0;
|
||||
}
|
||||
|
||||
bool UsdSceneRenderer::CaptureFrame(std::vector<uint8_t>& outRGBA)
|
||||
{
|
||||
if (!m_drawTarget || m_lastRenderWidth <= 0 || m_lastRenderHeight <= 0)
|
||||
return false;
|
||||
|
||||
int w = m_lastRenderWidth;
|
||||
int h = m_lastRenderHeight;
|
||||
|
||||
// Resolve MSAA so we read from the non-multisampled colour attachment.
|
||||
m_drawTarget->Resolve();
|
||||
|
||||
outRGBA.resize(static_cast<size_t>(w) * h * 4);
|
||||
|
||||
// Save/restore the read-framebuffer binding so we don't upset ImGui.
|
||||
GLint prevFbo = 0;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &prevFbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_drawTarget->GetFramebufferId());
|
||||
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, outRGBA.data());
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(prevFbo));
|
||||
|
||||
// OpenGL reads bottom-up; flip to top-down.
|
||||
int stride = w * 4;
|
||||
std::vector<uint8_t> row(stride);
|
||||
for (int y = 0; y < h / 2; ++y) {
|
||||
uint8_t* top = outRGBA.data() + y * stride;
|
||||
uint8_t* bot = outRGBA.data() + (h - 1 - y) * stride;
|
||||
std::copy(top, top + stride, row.data());
|
||||
std::copy(bot, bot + stride, top);
|
||||
std::copy(row.data(), row.data() + stride, bot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Axis Overlay (stageView.DrawAxis port)
|
||||
// ===========================================================================
|
||||
|
||||
Reference in New Issue
Block a user