enable setting output directory for recording
This commit is contained in:
@@ -46,6 +46,7 @@ typedef int32_t EntityId;
|
|||||||
namespace polyvox
|
namespace polyvox
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
enum ToneMapping
|
enum ToneMapping
|
||||||
{
|
{
|
||||||
ACES,
|
ACES,
|
||||||
@@ -124,6 +125,7 @@ namespace polyvox
|
|||||||
void setPostProcessing(bool enabled);
|
void setPostProcessing(bool enabled);
|
||||||
|
|
||||||
void setRecording(bool recording);
|
void setRecording(bool recording);
|
||||||
|
void setRecordingOutputDirectory(const char* path);
|
||||||
|
|
||||||
|
|
||||||
AssetManager *const getAssetManager()
|
AssetManager *const getAssetManager()
|
||||||
@@ -156,11 +158,11 @@ namespace polyvox
|
|||||||
Skybox *_skybox = nullptr;
|
Skybox *_skybox = nullptr;
|
||||||
Texture *_iblTexture = nullptr;
|
Texture *_iblTexture = nullptr;
|
||||||
IndirectLight *_indirectLight = nullptr;
|
IndirectLight *_indirectLight = nullptr;
|
||||||
|
|
||||||
bool _recomputeAabb = false;
|
bool _recomputeAabb = false;
|
||||||
|
|
||||||
bool _actualSize = false;
|
bool _actualSize = false;
|
||||||
|
|
||||||
|
float _frameInterval = 1000.0 / 60.0;
|
||||||
|
|
||||||
// Camera properties
|
// Camera properties
|
||||||
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
|
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
|
||||||
float _cameraFocalLength = 28.0f;
|
float _cameraFocalLength = 28.0f;
|
||||||
@@ -192,13 +194,18 @@ namespace polyvox
|
|||||||
void loadKtxTexture(string path, ResourceBuffer data);
|
void loadKtxTexture(string path, ResourceBuffer data);
|
||||||
void loadPngTexture(string path, ResourceBuffer data);
|
void loadPngTexture(string path, ResourceBuffer data);
|
||||||
void loadTextureFromPath(string path);
|
void loadTextureFromPath(string path);
|
||||||
void savePng(void* data, size_t size);
|
void savePng(void* data, size_t size, int frameNumber);
|
||||||
|
|
||||||
int _frameNumber = 0;
|
time_point_t _startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
uint32_t _lastFrameTimeInNanos;
|
|
||||||
|
|
||||||
bool _recording = false;
|
bool _recording = false;
|
||||||
|
std::string _recordingOutputDirectory = std::string("/tmp");
|
||||||
|
std::mutex _recordingMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameCallbackData {
|
||||||
|
FilamentViewer* viewer;
|
||||||
|
uint32_t frameNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ extern "C"
|
|||||||
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId);
|
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId);
|
||||||
FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name);
|
FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name);
|
||||||
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording);
|
FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording);
|
||||||
|
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory);
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy();
|
FLUTTER_PLUGIN_EXPORT void ios_dummy();
|
||||||
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
|
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void add_worker() {
|
void add_worker() {
|
||||||
std::thread t([this]() {
|
std::thread t([this]() {
|
||||||
while(!stop) {
|
while(!stop || tasks.size() > 0) {
|
||||||
std::function<void()> task;
|
std::function<void()> task;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(access);
|
std::unique_lock<std::mutex> lock(access);
|
||||||
|
|||||||
@@ -123,8 +123,6 @@ namespace polyvox
|
|||||||
: _resourceLoaderWrapper(resourceLoaderWrapper)
|
: _resourceLoaderWrapper(resourceLoaderWrapper)
|
||||||
{
|
{
|
||||||
|
|
||||||
_tp = new flutter_filament::ThreadPool(1);
|
|
||||||
|
|
||||||
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
ASSERT_POSTCONDITION(_resourceLoaderWrapper != nullptr, "Resource loader must be non-null");
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
@@ -139,12 +137,9 @@ namespace polyvox
|
|||||||
|
|
||||||
_renderer = _engine->createRenderer();
|
_renderer = _engine->createRenderer();
|
||||||
|
|
||||||
float fr = 60.0f;
|
_frameInterval = 1000.0f / 60.0f;
|
||||||
_renderer->setDisplayInfo({.refreshRate = fr});
|
|
||||||
|
|
||||||
Renderer::FrameRateOptions fro;
|
setFrameInterval(_frameInterval);
|
||||||
fro.interval = 1 / fr;
|
|
||||||
_renderer->setFrameRateOptions(fro);
|
|
||||||
|
|
||||||
_scene = _engine->createScene();
|
_scene = _engine->createScene();
|
||||||
|
|
||||||
@@ -307,6 +302,7 @@ namespace polyvox
|
|||||||
|
|
||||||
void FilamentViewer::setFrameInterval(float frameInterval)
|
void FilamentViewer::setFrameInterval(float frameInterval)
|
||||||
{
|
{
|
||||||
|
_frameInterval = frameInterval;
|
||||||
Renderer::FrameRateOptions fro;
|
Renderer::FrameRateOptions fro;
|
||||||
fro.interval = frameInterval;
|
fro.interval = frameInterval;
|
||||||
_renderer->setFrameRateOptions(fro);
|
_renderer->setFrameRateOptions(fro);
|
||||||
@@ -1020,13 +1016,23 @@ namespace polyvox
|
|||||||
size_t pixelBufferSize = vp.width * vp.height * 4;
|
size_t pixelBufferSize = vp.width * vp.height * 4;
|
||||||
auto* pixelBuffer = new uint8_t[pixelBufferSize];
|
auto* pixelBuffer = new uint8_t[pixelBufferSize];
|
||||||
auto callback = [](void *buf, size_t size, void *data) {
|
auto callback = [](void *buf, size_t size, void *data) {
|
||||||
((FilamentViewer*)data)->savePng(buf, size);
|
auto frameCallbackData = (FrameCallbackData*)data;
|
||||||
|
auto viewer = (FilamentViewer*)frameCallbackData->viewer;
|
||||||
|
viewer->savePng(buf, size, frameCallbackData->frameNumber);
|
||||||
|
delete frameCallbackData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - _startTime).count());
|
||||||
|
|
||||||
|
auto frameNumber = uint32_t(floor(elapsed / _frameInterval));
|
||||||
|
|
||||||
|
auto userData = new FrameCallbackData { this, frameNumber };
|
||||||
|
|
||||||
auto pbd = Texture::PixelBufferDescriptor(
|
auto pbd = Texture::PixelBufferDescriptor(
|
||||||
pixelBuffer, pixelBufferSize,
|
pixelBuffer, pixelBufferSize,
|
||||||
Texture::Format::RGBA,
|
Texture::Format::RGBA,
|
||||||
Texture::Type::UBYTE, nullptr, callback, this);
|
Texture::Type::UBYTE, nullptr, callback, userData);
|
||||||
|
|
||||||
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
_renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd));
|
||||||
}
|
}
|
||||||
@@ -1039,23 +1045,28 @@ namespace polyvox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::savePng(void* buf, size_t size) {
|
void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) {
|
||||||
|
std::lock_guard lock(_recordingMutex);
|
||||||
std::packaged_task<void()> lambda([=]() mutable {
|
if(!_recording) {
|
||||||
|
delete[] static_cast<uint8_t*>(buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Viewport const &vp = _view->getViewport();
|
Viewport const &vp = _view->getViewport();
|
||||||
|
|
||||||
|
std::packaged_task<void()> lambda([=]() mutable {
|
||||||
|
|
||||||
int digits = 6;
|
int digits = 6;
|
||||||
std::ostringstream stringStream;
|
std::ostringstream stringStream;
|
||||||
stringStream << "/tmp/output_";
|
stringStream << _recordingOutputDirectory << "/output_";
|
||||||
stringStream << std::setfill('0') << std::setw(digits);
|
stringStream << std::setfill('0') << std::setw(digits);
|
||||||
stringStream << std::to_string(_frameNumber);
|
stringStream << std::to_string(frameNumber);
|
||||||
stringStream << ".png";
|
stringStream << ".png";
|
||||||
|
|
||||||
std::string filename = stringStream.str();
|
std::string filename = stringStream.str();
|
||||||
|
|
||||||
ofstream wf(filename, ios::out | ios::binary);
|
ofstream wf(filename, ios::out | ios::binary);
|
||||||
// Log("%d %d %d", vp.width, vp.height, size);
|
|
||||||
LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
|
LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
|
||||||
static_cast<uint8_t*>(buf)));
|
static_cast<uint8_t*>(buf)));
|
||||||
|
|
||||||
@@ -1064,7 +1075,6 @@ namespace polyvox
|
|||||||
);
|
);
|
||||||
|
|
||||||
delete[] static_cast<uint8_t*>(buf);
|
delete[] static_cast<uint8_t*>(buf);
|
||||||
_frameNumber++;
|
|
||||||
|
|
||||||
if(!result) {
|
if(!result) {
|
||||||
Log("Failed to encode");
|
Log("Failed to encode");
|
||||||
@@ -1074,12 +1084,28 @@ namespace polyvox
|
|||||||
if(!wf.good()) {
|
if(!wf.good()) {
|
||||||
Log("Write failed!");
|
Log("Write failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
_tp->add_task(lambda);
|
_tp->add_task(lambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FilamentViewer::setRecordingOutputDirectory(const char* path) {
|
||||||
|
_recordingOutputDirectory = std::string(path);
|
||||||
|
auto outputDirAsPath = std::filesystem::path(path);
|
||||||
|
if(!std::filesystem::is_directory(outputDirAsPath)) {
|
||||||
|
std::filesystem::create_directories(outputDirAsPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FilamentViewer::setRecording(bool recording) {
|
void FilamentViewer::setRecording(bool recording) {
|
||||||
|
std::lock_guard lock(_recordingMutex);
|
||||||
this->_recording = recording;
|
this->_recording = recording;
|
||||||
|
if(recording) {
|
||||||
|
_tp = new flutter_filament::ThreadPool(8);
|
||||||
|
_startTime = std::chrono::high_resolution_clock::now();
|
||||||
|
} else {
|
||||||
|
delete _tp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::updateViewportAndCameraProjection(
|
void FilamentViewer::updateViewportAndCameraProjection(
|
||||||
|
|||||||
@@ -487,6 +487,10 @@ extern "C"
|
|||||||
((FilamentViewer*)viewer)->setRecording(recording);
|
((FilamentViewer*)viewer)->setRecording(recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory) {
|
||||||
|
((FilamentViewer*)viewer)->setRecordingOutputDirectory(outputDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy()
|
FLUTTER_PLUGIN_EXPORT void ios_dummy()
|
||||||
{
|
{
|
||||||
Log("Dummy called");
|
Log("Dummy called");
|
||||||
|
|||||||
@@ -498,4 +498,9 @@ abstract class FilamentController {
|
|||||||
/// This will impact performance; handle with care.
|
/// This will impact performance; handle with care.
|
||||||
///
|
///
|
||||||
Future setRecording(bool recording);
|
Future setRecording(bool recording);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the output directory where recorded PNGs will be placed.
|
||||||
|
///
|
||||||
|
Future setRecordingOutputDirectory(String outputDirectory);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1212,4 +1212,11 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
Future setRecording(bool recording) async {
|
Future setRecording(bool recording) async {
|
||||||
set_recording(_viewer!, recording);
|
set_recording(_viewer!, recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future setRecordingOutputDirectory(String outputDir) async {
|
||||||
|
var pathPtr = outputDir.toNativeUtf8(allocator: calloc);
|
||||||
|
set_recording_output_directory(_viewer!, pathPtr.cast<Char>());
|
||||||
|
calloc.free(pathPtr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -753,6 +753,14 @@ external void set_recording(
|
|||||||
bool recording,
|
bool recording,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>)>(
|
||||||
|
symbol: 'set_recording_output_directory',
|
||||||
|
assetId: 'flutter_filament_plugin')
|
||||||
|
external void set_recording_output_directory(
|
||||||
|
ffi.Pointer<ffi.Void> viewer,
|
||||||
|
ffi.Pointer<ffi.Char> outputDirectory,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function()>(
|
@ffi.Native<ffi.Void Function()>(
|
||||||
symbol: 'ios_dummy', assetId: 'flutter_filament_plugin')
|
symbol: 'ios_dummy', assetId: 'flutter_filament_plugin')
|
||||||
external void ios_dummy();
|
external void ios_dummy();
|
||||||
|
|||||||
Reference in New Issue
Block a user