diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index 845c4657..e7db6721 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -131,6 +131,7 @@ namespace polyvox void setRecording(bool recording); void setRecordingOutputDirectory(const char* path); + EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, const char* materialPath); AssetManager *const getAssetManager() { diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index aa27bba0..8022c231 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -184,6 +184,8 @@ extern "C" FLUTTER_PLUGIN_EXPORT void ios_dummy(); FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr); FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const assetManager, EntityId entityId); + FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); + #ifdef __cplusplus } #endif diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 0d04e4ba..50122c15 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -93,6 +93,7 @@ FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enab FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId); FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); +FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); #ifdef __cplusplus } diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 0d0f64a4..5bfa55b4 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,6 @@ #include #include - #include "math.h" #include @@ -109,11 +109,6 @@ namespace polyvox // const float kAperture = 1.0f; // const float kShutterSpeed = 1.0f; // const float kSensitivity = 50.0f; - struct Vertex - { - filament::math::float2 position; - uint32_t color; - }; static constexpr float4 sFullScreenTriangleVertices[3] = { {-1.0f, -1.0f, 1.0f, 1.0f}, @@ -161,25 +156,25 @@ namespace polyvox setToneMapping(ToneMapping::ACES); Log("Set tone mapping"); - #ifdef __EMSCRIPTEN__ - Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers"); - decltype(_view->getBloomOptions()) opts; - opts.enabled = false; - _view->setBloomOptions(opts); +#ifdef __EMSCRIPTEN__ + Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers"); + decltype(_view->getBloomOptions()) opts; + opts.enabled = false; + _view->setBloomOptions(opts); - _view->setAmbientOcclusionOptions({.enabled=false}); + _view->setAmbientOcclusionOptions({.enabled = false}); - _view->setDynamicResolutionOptions({.enabled=false}); + _view->setDynamicResolutionOptions({.enabled = false}); - _view->setDithering(filament::Dithering::NONE); - _view->setAntiAliasing(filament::AntiAliasing::NONE); - _view->setShadowingEnabled(false); - _view->setScreenSpaceRefractionEnabled(false); + _view->setDithering(filament::Dithering::NONE); + _view->setAntiAliasing(filament::AntiAliasing::NONE); + _view->setShadowingEnabled(false); + _view->setScreenSpaceRefractionEnabled(false); - #else - setBloom(0.6f); - Log("Set bloom"); - #endif +#else + setBloom(0.6f); + Log("Set bloom"); +#endif _view->setScene(_scene); _view->setCamera(_mainCamera); @@ -276,7 +271,7 @@ namespace polyvox .culling(false) .build(*_engine, imageEntity); _imageEntity = &imageEntity; - _scene->addEntity(imageEntity); + // _scene->addEntity(imageEntity); } void FilamentViewer::setPostProcessing(bool enabled) @@ -286,14 +281,14 @@ namespace polyvox void FilamentViewer::setBloom(float strength) { - #ifdef __EMSCRIPTEN__ - Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored"); - #else - decltype(_view->getBloomOptions()) opts; - opts.enabled = true; - opts.strength = strength; - _view->setBloomOptions(opts); - #endif +#ifdef __EMSCRIPTEN__ + Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored"); +#else + decltype(_view->getBloomOptions()) opts; + opts.enabled = true; + opts.strength = strength; + _view->setBloomOptions(opts); +#endif } void FilamentViewer::setToneMapping(ToneMapping toneMapping) @@ -787,11 +782,13 @@ namespace polyvox Log("Set lens projection to focal length %f, near %f and far %f", _cameraFocalLength, _near, _far); } - double FilamentViewer::getCameraCullingNear() { + double FilamentViewer::getCameraCullingNear() + { Camera &cam = _view->getCamera(); return cam.getNear(); } - double FilamentViewer::getCameraCullingFar() { + double FilamentViewer::getCameraCullingFar() + { Camera &cam = _view->getCamera(); return cam.getCullingFar(); } @@ -952,11 +949,11 @@ namespace polyvox _scene->setIndirectLight(nullptr); } - void FilamentViewer::rotateIbl(const math::mat3f & matrix) { - _indirectLight->setRotation(matrix); + void FilamentViewer::rotateIbl(const math::mat3f &matrix) + { + _indirectLight->setRotation(matrix); } - void FilamentViewer::loadIbl(const char *const iblPath, float intensity) { removeIbl(); @@ -1005,7 +1002,6 @@ namespace polyvox } } - void FilamentViewer::render( uint64_t frameTimeInNanos, void *pixelBuffer, @@ -1029,7 +1025,8 @@ namespace polyvox auto now = std::chrono::high_resolution_clock::now(); auto secsSinceLastFpsCheck = float(std::chrono::duration_cast(now - _fpsCounterStartTime).count()); - if(secsSinceLastFpsCheck >= 1) { + if (secsSinceLastFpsCheck >= 1) + { auto fps = _frameCount / secsSinceLastFpsCheck; Log("%ffps (_frameCount %d, secs since last check %f)", fps, _frameCount, secsSinceLastFpsCheck); // Log("1 sec average for asset animation update %f", _elapsed / _frameCount); @@ -1046,9 +1043,10 @@ namespace polyvox _cumulativeAnimationUpdateTime += tmr.elapsed(); // if a manipulator is active, update the active camera orientation - if(_manipulator) { + if (_manipulator) + { math::double3 eye, target, upward; - Camera& cam =_view->getCamera(); + Camera &cam = _view->getCamera(); _manipulator->getLookAt(&eye, &target, &upward); cam.lookAt(eye, target, upward); } @@ -1069,109 +1067,119 @@ namespace polyvox // } // else // { - // Render the scene, unless the renderer wants to skip the frame. - bool beginFrame = _renderer->beginFrame(_swapChain, frameTimeInNanos); - - if (beginFrame) + // Render the scene, unless the renderer wants to skip the frame. + bool beginFrame = _renderer->beginFrame(_swapChain, frameTimeInNanos); + + if (beginFrame) + { + _renderer->render(_view); + _frameCount++; + + if (_recording) { - _renderer->render(_view); - _frameCount++; + Viewport const &vp = _view->getViewport(); + size_t pixelBufferSize = vp.width * vp.height * 4; + auto *pixelBuffer = new uint8_t[pixelBufferSize]; + auto callback = [](void *buf, size_t size, void *data) + { + 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(now - _recordingStartTime).count()); - if(_recording) { - Viewport const &vp = _view->getViewport(); - size_t pixelBufferSize = vp.width * vp.height * 4; - auto* pixelBuffer = new uint8_t[pixelBufferSize]; - auto callback = [](void *buf, size_t size, void *data) { - auto frameCallbackData = (FrameCallbackData*)data; - auto viewer = (FilamentViewer*)frameCallbackData->viewer; - viewer->savePng(buf, size, frameCallbackData->frameNumber); - delete frameCallbackData; - }; + auto frameNumber = uint32_t(floor(elapsed / _frameInterval)); - auto now = std::chrono::high_resolution_clock::now(); - auto elapsed = float(std::chrono::duration_cast(now - _recordingStartTime).count()); + auto userData = new FrameCallbackData{this, frameNumber}; - auto frameNumber = uint32_t(floor(elapsed / _frameInterval)); + auto pbd = Texture::PixelBufferDescriptor( + pixelBuffer, pixelBufferSize, + Texture::Format::RGBA, + Texture::Type::UBYTE, nullptr, callback, userData); - auto userData = new FrameCallbackData { this, frameNumber }; - - auto pbd = Texture::PixelBufferDescriptor( - pixelBuffer, pixelBufferSize, - Texture::Format::RGBA, - Texture::Type::UBYTE, nullptr, callback, userData); - - _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); - } - _renderer->endFrame(); - #ifdef __EMSCRIPTEN__ - _engine->execute(); - #endif - } else { - _skippedFrames++; + _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); } + _renderer->endFrame(); +#ifdef __EMSCRIPTEN__ + _engine->execute(); +#endif + } + else + { + _skippedFrames++; + } } - - void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) { + void FilamentViewer::savePng(void *buf, size_t size, int frameNumber) + { std::lock_guard lock(_recordingMutex); - if(!_recording) { - delete[] static_cast(buf); + if (!_recording) + { + delete[] static_cast(buf); return; } Viewport const &vp = _view->getViewport(); - std::packaged_task lambda([=]() mutable { + std::packaged_task lambda([=]() mutable + { + int digits = 6; + std::ostringstream stringStream; + stringStream << _recordingOutputDirectory << "/output_"; + stringStream << std::setfill('0') << std::setw(digits); + stringStream << std::to_string(frameNumber); + stringStream << ".png"; - int digits = 6; - std::ostringstream stringStream; - stringStream << _recordingOutputDirectory << "/output_"; - stringStream << std::setfill('0') << std::setw(digits); - stringStream << std::to_string(frameNumber); - 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); + LinearImage image(toLinearWithAlpha(vp.width, vp.height, vp.width * 4, + static_cast(buf))); - LinearImage image(toLinearWithAlpha(vp.width, vp.height, vp.width * 4, - static_cast(buf))); + auto result = image::ImageEncoder::encode( + wf, image::ImageEncoder::Format::PNG, image, std::string(""), std::string("")); - auto result = image::ImageEncoder::encode( - wf, image::ImageEncoder::Format::PNG, image, std::string(""), std::string("") - ); + delete[] static_cast(buf); - delete[] static_cast(buf); + if (!result) + { + Log("Failed to encode"); + } - if(!result) { - Log("Failed to encode"); - } - - wf.close(); - if(!wf.good()) { - Log("Write failed!"); - } - - }); - _tp->add_task(lambda); + wf.close(); + if (!wf.good()) + { + Log("Write failed!"); + } + }); + _tp->add_task(lambda); } - void FilamentViewer::setRecordingOutputDirectory(const char* path) { + void FilamentViewer::setRecordingOutputDirectory(const char *path) + { _recordingOutputDirectory = std::string(path); auto outputDirAsPath = std::filesystem::path(path); - if(!std::filesystem::is_directory(outputDirAsPath)) { + 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; - if(recording) { + if (recording) + { _tp = new flutter_filament::ThreadPool(8); _recordingStartTime = std::chrono::high_resolution_clock::now(); - } else { + } + else + { delete _tp; } } @@ -1284,7 +1292,7 @@ namespace polyvox matrix[13], matrix[14], matrix[15]); - cam.setCustomProjection(projectionMatrix,projectionMatrix, near, far); + cam.setCustomProjection(projectionMatrix, projectionMatrix, near, far); } const math::mat4 FilamentViewer::getCameraModelMatrix() @@ -1320,7 +1328,7 @@ namespace polyvox void FilamentViewer::_createManipulator() { Camera &cam = _view->getCamera(); - + math::double3 home = cam.getPosition(); math::double3 up = cam.getUpVector(); auto fv = cam.getForwardVector(); @@ -1330,7 +1338,7 @@ namespace polyvox _manipulator = Manipulator::Builder() .viewport(vp.width, vp.height) - .upVector(up.x, up.y, up.z) + .upVector(up.x, up.y, up.z) .zoomSpeed(_zoomSpeed) .targetPosition(target[0], target[1], target[2]) // only applicable to Mode::ORBIT @@ -1441,4 +1449,69 @@ namespace polyvox { *entityId = Entity::smuggle(result.renderable); }); } + EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, const char* materialPath) + { + + float *verticesCopy = (float*)malloc(numVertices * sizeof(float)); + memcpy(verticesCopy, vertices, numVertices * sizeof(float)); + VertexBuffer::BufferDescriptor::Callback vertexCallback = [](void *buf, size_t, + void *data) + { + free((void*)buf); + }; + + uint16_t *indicesCopy = (uint16_t*)malloc(numIndices * sizeof(uint16_t)); + memcpy(indicesCopy, indices, numIndices * sizeof(uint16_t)); + IndexBuffer::BufferDescriptor::Callback indexCallback = [](void *buf, size_t, + void *data) + { + free((void*)buf); + }; + + auto vb = VertexBuffer::Builder().vertexCount(numVertices).bufferCount(1).attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3).build(*_engine); + + vb->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor(verticesCopy, vb->getVertexCount() * sizeof(filament::math::float3), 0, vertexCallback)); + + auto ib = IndexBuffer::Builder().indexCount(numIndices).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine); + ib->setBuffer(*_engine, IndexBuffer::BufferDescriptor(indicesCopy, ib->getIndexCount() * sizeof(uint16_t), 0, indexCallback)); + + filament::Material* mat = nullptr; + if(materialPath) { + auto matData = _resourceLoaderWrapper->load(materialPath); + auto mat = Material::Builder().package(matData.data, matData.size).build(*_engine); + _resourceLoaderWrapper->free(matData); + } + + float minX, minY, minZ = 0.0f; + float maxX, maxY, maxZ = 0.0f; + + for(int i = 0; i < numVertices; i+=3) { + minX = std::min(vertices[i], minX); + minY = std::min(vertices[i+1], minY); + minZ = std::min(vertices[i+2], minZ); + maxX = std::max(vertices[i], maxX); + maxY = std::max(vertices[i+1], maxY); + maxZ = std::max(vertices[i+2], maxZ); + } + + auto renderable = utils::EntityManager::get().create(); + RenderableManager::Builder builder = RenderableManager::Builder(1); + builder + .boundingBox({{minX, minY, minZ}, {maxX, maxY, maxZ}}) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, + vb, ib, 0, numIndices) + .culling(false) + .receiveShadows(false) + .castShadows(false); + if(mat) { + builder.material(0, mat->getDefaultInstance()).build(*_engine, renderable); + } + auto result = builder.build(*_engine, renderable); + + _scene->addEntity(renderable); + Log("Created geometry with result %d", result); + + return Entity::smuggle(renderable); + } + } // namespace polyvox diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index 443cbd9d..a57e8f30 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -540,4 +540,10 @@ extern "C" FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const assetManager, EntityId entityId) { ((AssetManager*)assetManager)->addCollisionComponent(entityId); } + + FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { + return ((FilamentViewer*)viewer)->createGeometry(vertices, (size_t)numVertices, indices, numIndices, materialPath); + } + + } diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index 1da05d88..fc66da88 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -554,4 +554,15 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( } FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } + +FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { + std::packaged_task lambda( + [=] { + return create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath); + }); + auto fut = _rl->add_task(lambda); + fut.wait(); + return fut.get(); +} + } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 2fd6537d..006c1058 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -545,4 +545,10 @@ abstract class FilamentController { /// If there is a collision between the controlled entity and [collidableEntity], the transform will not be updated. /// Future addCollisionComponent(FilamentEntity collidableEntity); + + /// + /// Creates a (renderable) entity with the specified geometry and adds to the scene. + /// + Future createGeometry( + List vertices, List indices, String? materialPath); } diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 3559d377..ba50ae3d 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; -import 'dart:math'; import 'dart:ui' as ui; import 'dart:developer' as dev; import 'package:flutter/services.dart'; @@ -1367,4 +1366,39 @@ class FilamentControllerFFI extends FilamentController { add_collision_component(_assetManager!, entity); } + + @override + Future createGeometry( + List vertices, List indices, String? materialPath) async { + if (_viewer == null) { + throw Exception("Viewer must not be null"); + } + + final materialPathPtr = + materialPath?.toNativeUtf8(allocator: allocator) ?? nullptr; + final vertexPtr = allocator(vertices.length); + final indicesPtr = allocator(indices.length); + for (int i = 0; i < vertices.length; i++) { + vertexPtr.elementAt(i).value = vertices[i]; + } + + for (int i = 0; i < indices.length; i++) { + indicesPtr.elementAt(i).value = indices[i]; + } + + var entity = create_geometry_ffi(_viewer!, vertexPtr, vertices.length, + indicesPtr, indices.length, materialPathPtr.cast()); + if (entity == _FILAMENT_ASSET_ERROR) { + throw Exception("Failed to create geometry"); + } + + _entities.add(entity); + _onLoadController.sink.add(entity); + + allocator.free(materialPathPtr); + allocator.free(vertexPtr); + allocator.free(indicesPtr); + + return entity; + } } diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index 6e2cd923..55a03f77 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -835,6 +835,19 @@ external void add_collision_component( int entityId, ); +@ffi.Native< + EntityId Function(ffi.Pointer, ffi.Pointer, + ffi.Int, ffi.Pointer, ffi.Int, ffi.Pointer)>( + symbol: 'create_geometry', assetId: 'flutter_filament_plugin') +external int create_geometry( + ffi.Pointer viewer, + ffi.Pointer vertices, + int numVertices, + ffi.Pointer indices, + int numIndices, + ffi.Pointer materialPath, +); + @ffi.Native< ffi.Pointer Function( ffi.Pointer, @@ -1284,6 +1297,19 @@ external void reset_to_rest_pose_ffi( symbol: 'ios_dummy_ffi', assetId: 'flutter_filament_plugin') external void ios_dummy_ffi(); +@ffi.Native< + EntityId Function(ffi.Pointer, ffi.Pointer, + ffi.Int, ffi.Pointer, ffi.Int, ffi.Pointer)>( + symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin') +external int create_geometry_ffi( + ffi.Pointer viewer, + ffi.Pointer vertices, + int numVertices, + ffi.Pointer indices, + int numIndices, + ffi.Pointer materialPath, +); + final class __mbstate_t extends ffi.Union { @ffi.Array.multi([128]) external ffi.Array __mbstate8;