refactoring

This commit is contained in:
Nick Fisher
2025-03-18 16:26:47 +08:00
parent 07b80071a4
commit 77fe40848b
41 changed files with 1900 additions and 2342 deletions

View File

@@ -1,176 +0,0 @@
#pragma once
#include <filament/Camera.h>
#include <filament/Frustum.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <filament/LightManager.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <camutils/Manipulator.h>
#include <utils/NameComponentManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "ResourceBuffer.hpp"
#include "scene/SceneManager.hpp"
#include "ThreadPool.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
using namespace gltfio;
using namespace camutils;
class FilamentViewer
{
typedef int32_t EntityId;
public:
FilamentViewer(const void *context, const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
View* createView();
View* getViewAt(int index);
void loadSkybox(const char *const skyboxUri);
void removeSkybox();
void loadIbl(const char *const iblUri, float intensity);
void removeIbl();
void rotateIbl(const math::mat3f &matrix);
void createIbl(float r, float g, float b, float intensity);
void render(
uint64_t frameTimeInNanos
);
void setFrameInterval(float interval);
void setMainCamera(View *view);
EntityId getMainCamera();
float getCameraFov(bool horizontal);
void setCameraFov(double fovDegrees, bool horizontal);
SwapChain* createSwapChain(const void *surface);
SwapChain* createSwapChain(uint32_t width, uint32_t height);
void destroySwapChain(SwapChain* swapChain);
RenderTarget* createRenderTarget(intptr_t colorTextureId, intptr_t depthTextureId, uint32_t width, uint32_t height);
void destroyRenderTarget(RenderTarget* renderTarget);
Renderer *getRenderer();
std::map<SwapChain*, std::vector<View*>> _renderable;
void setRenderable(View* view, SwapChain* swapChain, bool renderable);
void setBackgroundColor(const float r, const float g, const float b, const float a);
void setBackgroundImage(const char *resourcePath, bool fillHeight, uint32_t width, uint32_t height);
void clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp, uint32_t width, uint32_t height);
Engine* getEngine() {
return _engine;
}
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)());
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)());
SceneManager *const getSceneManager()
{
return (SceneManager *const)_sceneManager;
}
SwapChain* getSwapChainAt(int index) {
if(index < _swapChains.size()) {
return _swapChains[index];
}
Log("Error: index %d is greater than available swapchains", index);
return nullptr;
}
private:
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
Scene *_scene = nullptr;
Engine *_engine = nullptr;
thermion::ThreadPool *_tp = nullptr;
Renderer *_renderer = nullptr;
SceneManager *_sceneManager = nullptr;
std::vector<RenderTarget*> _renderTargets;
std::vector<SwapChain*> _swapChains;
std::vector<View*> _views;
std::mutex _renderMutex; // mutex to ensure thread safety when removing assets
Texture *_skyboxTexture = nullptr;
Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr;
IndirectLight *_indirectLight = nullptr;
float _frameInterval = 1000.0 / 60.0;
// Camera properties
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
// background image properties
uint32_t _imageHeight = 0;
uint32_t _imageWidth = 0;
filament::math::mat4f _imageScale;
Texture *_imageTexture = nullptr;
Texture *_dummyImageTexture = nullptr;
utils::Entity _imageEntity;
VertexBuffer *_imageVb = nullptr;
IndexBuffer *_imageIb = nullptr;
Material *_imageMaterial = nullptr;
TextureSampler _imageSampler;
void loadKtx2Texture(std::string path, ResourceBuffer data);
void loadKtxTexture(std::string path, ResourceBuffer data);
void loadPngTexture(std::string path, ResourceBuffer data);
void loadTextureFromPath(std::string path);
void savePng(void *data, size_t size, int frameNumber);
void createBackgroundImage();
time_point_t _fpsCounterStartTime = std::chrono::high_resolution_clock::now();
std::mutex _imageMutex;
double _cumulativeAnimationUpdateTime = 0;
int _frameCount = 0;
int _skippedFrames = 0;
};
struct FrameCallbackData
{
FilamentViewer *viewer;
uint32_t frameNumber;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <filament/Engine.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "scene/SceneManager.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
class RenderTicker
{
public:
RenderTicker(filament::Renderer renderer, thermion::SceneManager sceneManager) : mRenderer(renderer), mSceneManager(sceneManager) { }
~RenderTicker();
void render(
uint64_t frameTimeInNanos
);
void setRenderable(SwapChain *swapChain, View **view, uint8_t numViews);
private:
std::mutex mMutex;
Renderer *mRenderer = nullptr;
SceneManager *mSceneManager = nullptr;
std::vector<SwapChain*> mSwapChains;
std::map<SwapChain*, std::vector<View*>> mRenderable;
};
}

View File

@@ -8,7 +8,7 @@ extern "C"
#include <stdint.h>
#include "APIExport.h"
typedef TRenderTicker TRenderTicker;
typedef int32_t EntityId;
typedef struct TCamera TCamera;
typedef struct TEngine TEngine;
@@ -110,6 +110,11 @@ extern "C"
double z;
} double3;
typedef struct {
double3 col1;
double3 col2;
double3 col3;
} double3x3;
typedef struct {
double x;

View File

@@ -21,10 +21,11 @@ enum TBackend {
BACKEND_NOOP = 4, //!< Selects the no-op driver for testing purposes.
};
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend);
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend, void* platform, void* sharedContext, uint8_t stereoscopicEyeCount, bool disableHandleUseAfterFreeCheck);
EMSCRIPTEN_KEEPALIVE TRenderer *Engine_createRenderer(TEngine *tEngine);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags);
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain);
EMSCRIPTEN_KEEPALIVE TCamera *Engine_createCamera(TEngine* tEngine);
EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine);

View File

@@ -23,6 +23,7 @@ EMSCRIPTEN_KEEPALIVE TFilamentAsset *GltfAssetLoader_load(
uint8_t numInstances
);
EMSCRIPTEN_KEEPALIVE TMaterialInstance *GltfAssetLoader_getMaterialInstance(TRenderableManager *tRenderableManager, TFilamentAsset *tAsset);
EMSCRIPTEN_KEEPALIVE TMaterialProvider *GltfAssetLoader_getMaterialProvider(TGltfAssetLoader *tAssetLoader);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "TTexture.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE void IndirectLight_setRotation(TIndirectLight TIndirectLight, double3x3 rotation);
#ifdef __cplusplus
}
#endif

View File

@@ -21,6 +21,11 @@ EMSCRIPTEN_KEEPALIVE TRenderTarget *RenderTarget_create(
TTexture *depth
);
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroy(
TEngine *tEngine,
TRenderTarget *tRenderTarget
);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,17 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TRenderTicker *RenderTicker_create(TRenderer *tRenderer, TSceneManager *tSceneManager);
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos);
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tFilamentRender, TSwapChain *swapChain, TView **views, uint8_t numViews);
#ifdef __cplusplus
}
#endif

View File

@@ -23,6 +23,13 @@ EMSCRIPTEN_KEEPALIVE void Renderer_readPixels(
TPixelDataType tPixelDataType,
uint8_t *out
);
EMSCRIPTEN_KEEPALIVE void Renderer_setFrameInterval(
TRenderer *tRenderer,
float headRoomRatio,
float scaleRate,
uint8_t history,
uint8_t interval
);

View File

@@ -1,85 +0,0 @@
#ifndef _FLUTTER_FILAMENT_API_H
#define _FLUTTER_FILAMENT_API_H
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath);
EMSCRIPTEN_KEEPALIVE TRenderer *Viewer_getRenderer(TViewer *tViewer);
EMSCRIPTEN_KEEPALIVE void Viewer_destroy(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *viewer, intptr_t colorTextureId, intptr_t depthTextureId, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *viewer, TRenderTarget* tRenderTarget);
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *viewer, const void *const window);
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *viewer, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *viewer, TSwapChain* swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_render(
TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
TRenderTarget *renderTarget,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE TView* Viewer_createView(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TView* Viewer_getViewAt(TViewer *viewer, int index);
EMSCRIPTEN_KEEPALIVE void Viewer_setMainCamera(TViewer *tViewer, TView *tView);
EMSCRIPTEN_KEEPALIVE TSwapChain* Viewer_getSwapChainAt(TViewer *tViewer, int index);
EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *viewer, TSwapChain *swapChain, TView* view, bool renderable);
// Engine
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer* viewer);
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight);
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE EntityId Viewer_getMainCamera(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkybox(TViewer *viewer, const char *skyboxPath);
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkybox(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity);
EMSCRIPTEN_KEEPALIVE void Viewer_removeIbl(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity);
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix);
EMSCRIPTEN_KEEPALIVE void set_frame_interval(TViewer *viewer, float interval);
EMSCRIPTEN_KEEPALIVE void queue_relative_position_update_world_axis(TSceneManager *sceneManager, EntityId entity, float viewportX, float viewportY, float x, float y, float z);
EMSCRIPTEN_KEEPALIVE void queue_position_update_from_viewport_coords(TSceneManager *sceneManager, TView *view, EntityId entity, float viewportX, float viewportY);
EMSCRIPTEN_KEEPALIVE void ios_dummy();
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr);
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
EMSCRIPTEN_KEEPALIVE void remove_collision_component(TSceneManager *sceneManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE void test_collisions(TSceneManager *sceneManager, EntityId entity);
EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(TSceneManager *sceneManager, TView *view, EntityId entity);
EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(TSceneManager *sceneManager, TView *view, EntityId entity, float *minX, float *minY, float *maxX, float *maxY);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,7 +1,5 @@
#ifndef _DART_FILAMENT_FFI_API_H
#define _DART_FILAMENT_FFI_API_H
#pragma once
#include "ThermionDartApi.h"
#include "TView.h"
#include "TTexture.h"
#include "TMaterialProvider.h"
@@ -13,49 +11,30 @@ namespace thermion
{
#endif
///
/// This header replicates most of the methods in ThermionDartApi.h.
/// It represents the interface for:
/// - invoking those methods that must be called on the main Filament engine thread
/// - setting up a render loop
///
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner);
EMSCRIPTEN_KEEPALIVE void RenderLoop_create();
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy();
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context,
void *const platform,
const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *viewer));
EMSCRIPTEN_KEEPALIVE void Viewer_createViewRenderThread(TViewer *viewer, void (*onComplete)(TView *tView));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyOnRenderThread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain, uint8_t *out, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain, TRenderTarget *renderTarget, uint8_t *out, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeIblRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_createRenderTargetRenderThread(TViewer *viewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height, void (*onComplete)(TRenderTarget *));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTargetRenderThread(TViewer *viewer, TRenderTarget *tRenderTarget, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkyboxRenderThread(TViewer *viewer, const char *skyboxPath, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkyboxRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos,);
// EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(TRenderLoop* tRenderLoop, void (*task)());
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(TBackend backend, void (*onComplete)(TEngine *));
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(
TBackend backend,
void* platform,
void* sharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck,
void (*onComplete)(TEngine *)
);
EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *));
EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *));
EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *));
EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, void (*onComplete)());
@@ -98,13 +77,6 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, void (*callback)());
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(TViewer *viewer, bool rendering, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_frame_interval_render_thread(TViewer *viewer, float frameInterval);
EMSCRIPTEN_KEEPALIVE void set_background_color_render_thread(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *));
@@ -323,7 +295,7 @@ namespace thermion
int boneIndex,
const float *const transform,
void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(TViewer *viewer, bool enabled);
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)());
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *));
@@ -345,4 +317,3 @@ namespace thermion
}
#endif
#endif // _DART_FILAMENT_FFI_API_H

View File

@@ -0,0 +1,87 @@
#pragma once
#include <chrono>
#include <condition_variable>
#include <deque>
#include <future>
#include <mutex>
#include <thread>
#include "RenderTicker.hpp"
namespace thermion {
/**
* @brief A render loop implementation that manages rendering on a separate thread.
*
* This class handles frame rendering requests, viewer creation, and maintains
* a task queue for rendering operations.
*/
class RenderLoop {
public:
/**
* @brief Constructs a new RenderLoop and starts the render thread.
*/
explicit RenderLoop();
/**
* @brief Destroys the RenderLoop and stops the render thread.
*/
~RenderLoop();
/**
* @brief Requests a frame to be rendered.
*
* @param callback Callback function to be called after rendering completes
*/
void requestFrame(void (*callback)());
/**
* @brief Sets the render ticker used.
*/
void setRenderTicker(RenderTicker *renderTicker) {
mRenderTicker = renderTicker;
}
/**
* @brief Adds a task to the render thread's task queue.
*
* @param pt The packaged task to be executed
* @return std::future<Rt> Future for the task result
*/
template <class Rt>
auto add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt>;
private:
/**
* @brief Main iteration of the render loop.
*/
void iter();
void (*_requestFrameRenderCallback)() = nullptr;
bool _stop = false;
std::mutex _mutex;
std::mutex _taskMutex;
std::condition_variable _cv;
std::deque<std::function<void()>> _tasks;
std::chrono::high_resolution_clock::time_point _lastFrameTime;
int _frameCount = 0;
float _accumulatedTime = 0.0f;
float _fps = 0.0f;
std::thread* t = nullptr;
RenderTicker* mRenderTicker = nullptr;
};
// Template implementation
template <class Rt>
auto RenderLoop::add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> {
std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cv.notify_one();
return ret;
}
} // namespace thermion

View File

@@ -0,0 +1,103 @@
#if __APPLE__
#include "TargetConditionals.h"
#endif
#ifdef _WIN32
#pragma comment(lib, "Ws2_32.lib")
#endif
#include <filament/Camera.h>
#include <filament/SwapChain.h>
#include <backend/DriverEnums.h>
#include <backend/platforms/OpenGLPlatform.h>
#ifdef __EMSCRIPTEN__
#include <backend/platforms/PlatformWebGL.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
#endif
#include <filament/Engine.h>
#include <filament/Options.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <filament/RenderableManager.h>
#include <iostream>
#include <streambuf>
#include <sstream>
#include <istream>
#include <fstream>
#include <filesystem>
#include <mutex>
#include <iomanip>
#include <unordered_set>
#include "Log.hpp"
#include "RenderTicker.hpp"
namespace thermion
{
using namespace filament;
using namespace filament::math;
using namespace utils;
using namespace std::chrono;
using std::string;
static constexpr filament::math::float4 sFullScreenTriangleVertices[3] = {
{-1.0f, -1.0f, 1.0f, 1.0f},
{3.0f, -1.0f, 1.0f, 1.0f},
{-1.0f, 3.0f, 1.0f, 1.0f}};
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
void RenderTicker::setRenderable(SwapChain *swapChain, View **views, uint8_t numViews) {
{
std::lock_guard lock(mMutex);
auto swapChainViews = mRenderable[swapChain];
swapChainViews.clear();
for(int i = 0; i < numViews; i++) {
swapChainViews.push_back(views[i]);
}
mRenderable[swapChain] = swapChainViews;
}
void RenderTicker::render(uint64_t frameTimeInNanos)
{
std::lock_guard lock(mMutex);
mSceneManager->update();
for (auto swapChain : mSwapChains)
{
auto views = mRenderable[swapChain];
if (views.size() > 0)
{
bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos);
if (beginFrame)
{
for (auto view : views)
{
mRenderer->render(view);
}
}
mRenderer->endFrame();
}
}
#ifdef __EMSCRIPTEN__
_engine->execute();
#endif
}
} // namespace thermion

View File

@@ -44,9 +44,17 @@ namespace thermion
uint64_t TSWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
uint64_t TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER = filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend)
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(
TBackend backend,
void* platform,
void* sharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck)
{
auto *engine = filament::Engine::create(static_cast<filament::Engine::Backend>(backend));
filament::Engine::Config config;
config.stereoscopicEyeCount = stereoscopicEyeCount;
config.disableHandleUseAfterFreeCheck = disableHandleUseAfterFreeCheck;
auto *engine = filament::Engine::create(static_cast<filament::Engine::Backend>(backend), platform, sharedContext, &config);
return reinterpret_cast<TEngine *>(engine);
}
@@ -71,6 +79,12 @@ namespace thermion
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain) {
auto *engine = reinterpret_cast<Engine *>(tEngine);
auto *swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
engine->destroy(swapChain);
}
EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine)
{
auto *engine = reinterpret_cast<Engine *>(tEngine);
@@ -279,12 +293,21 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void Engine_destroySkybox(TEngine *tEngine, TSkybox *tSkybox) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *skybox = reinterpret_cast<filament::Skybox *>(tSkybox);
if(skybox->getTexture()) {
engine->destroy(skybox->getTexture());
}
engine->destroy(skybox);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLight(TEngine *tEngine, TIndirectLight *tIndirectLight) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *indirectLight = reinterpret_cast<filament::IndirectLight *>(tIndirectLight);
if(indirectLight->getReflectionsTexture()) {
engine->destroy(indirectLight->getReflectionsTexture());
}
if(indirectLight->getIrradianceTexture()) {
engine->destroy(indirectLight->getIrradianceTexture());
}
engine->destroy(indirectLight);
}

View File

@@ -111,6 +111,12 @@ EMSCRIPTEN_KEEPALIVE TMaterialInstance *GltfAssetLoader_getMaterialInstance(TRen
return reinterpret_cast<TMaterialInstance*>(mi);
}
EMSCRIPTEN_KEEPALIVE TMaterialProvider *GltfAssetLoader_getMaterialProvider(TGltfAssetLoader *tAssetLoader) {
auto *assetLoader = reinterpret_cast<gltfio::AssetLoader>(tAssetLoader);
auto materialProvider = assetLoader->getMaterialProvider();
return reinterpret_cast<TMaterialProvider *>(&materialProvider);
}
#ifdef __cplusplus
}
}

View File

@@ -0,0 +1,41 @@
#include "c_api/TScene.h"
#include <filament/Engine.h>
#include <filament/Fence.h>
#include <filament/IndirectLight.h>
#include <filament/Material.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/Texture.h>
#include <filament/TextureSampler.h>
#include <filament/TransformManager.h>
#include <filament/View.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include "Log.hpp"
#ifdef __cplusplus
namespace thermion
{
extern "C"
{
using namespace filament;
#endif
EMSCRIPTEN_KEEPALIVE void IndirectLight_setRotation(TIndirectLight *tIndirectLight, double3x3 rotation)
{
auto *indirectLight = reinterpret_cast<filament::IndirectLight *>(tIndirectLight);
const filament::math::mat3f fRotation {
filament::math::float3 { rotation.col1.x, rotation.col1.y, rotation.col1.z },
filament::math::float3 { rotation.col2.x, rotation.col2.y, rotation.col2.z },
filament::math::float3 { rotation.col3.x, rotation.col3.y, rotation.col3.z },
};
indirectLight->setRotation(fRotation);
}
#ifdef __cplusplus
}
}
#endif

View File

@@ -34,6 +34,16 @@ namespace thermion
return reinterpret_cast<TRenderTarget *>(rt);
}
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroy(
TEngine *tEngine,
TRenderTarget *tRenderTarget
) {
auto engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *renderTarget = reinterpret_cast<filament::RenderTarget *>(tRenderTarget);
engine->destroy(renderTarget);
}
#ifdef __cplusplus
}

View File

@@ -0,0 +1,38 @@
#ifdef _WIN32
#include "ThermionWin32.h"
#endif
#include <thread>
#include <functional>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include "filament/LightManager.h"
#include "Log.hpp"
using namespace thermion;
extern "C"
{
#include "c_api/TRenderTicker.hpp"
EMSCRIPTEN_KEEPALIVE TRenderTicker *RenderTicker_create(TRenderer *tRenderer, TSceneManager *tSceneManager) {
auto *renderer = reinterpret_cast<filament::Renderer *>(tRenderer);
auto *sceneManager = reinterpret_cast<thermion::SceneManager *>(tSceneManager);
auto *renderTicker = new RenderTicker(renderer, sceneManager);
return reinterpret_cast<TRenderTicker *>(renderTicker);
}
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos) {
auto *renderTicker = reinterpret_cast<RenderTicker *>
renderTicker->render(frameTimeInNanos);
}
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tRenderTicker, TSwapChain *swapChain, TView **views, uint8_t numViews) {
auto *renderTicker = reinterpret_cast<RenderTicker *>
renderTicker->setRenderable(swapChain, views, numViews);
}
}

View File

@@ -61,6 +61,22 @@ EMSCRIPTEN_KEEPALIVE void Renderer_renderStandaloneView(TRenderer *tRenderer, TV
renderer->renderStandaloneView(view);
}
EMSCRIPTEN_KEEPALIVE void Renderer_setFrameRateOptions(
TRenderer *tRenderer,
float headRoomRatio,
float scaleRate,
uint8_t history,
uint8_t interval
) {
auto *renderer = reinterpret_cast<filament::Renderer *>(tRenderer);
filament::Renderer::FrameRateOptions fro;
fro.headRoomRatio = headRoomRatio;
fro.scaleRate = scaleRate;
fro.interval = interval;
fro.interval = interval;
renderer->setFrameRateOptions(fro);
}
class CaptureCallbackHandler : public filament::backend::CallbackHandler
{
void post(void *user, Callback callback)

View File

@@ -1,415 +0,0 @@
#ifdef _WIN32
#include "ThermionWin32.h"
#endif
#include <thread>
#include <functional>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include "filament/LightManager.h"
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
using namespace thermion;
extern "C"
{
#include "c_api/ThermionDartApi.h"
EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *context, const void *const loader, void *const platform, const char *uberArchivePath)
{
const auto *loaderImpl = new ResourceLoaderWrapperImpl((ResourceLoaderWrapper *)loader);
auto viewer = new FilamentViewer(context, loaderImpl, platform, uberArchivePath);
return reinterpret_cast<TViewer *>(viewer);
}
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer *viewer)
{
auto *engine = reinterpret_cast<FilamentViewer *>(viewer)->getEngine();
return reinterpret_cast<TEngine *>(engine);
}
EMSCRIPTEN_KEEPALIVE TRenderer *Viewer_getRenderer(TViewer *tViewer) {
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
return reinterpret_cast<TRenderer *>(viewer->getRenderer());
}
EMSCRIPTEN_KEEPALIVE TRenderTarget *Viewer_createRenderTarget(TViewer *tViewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto renderTarget = viewer->createRenderTarget(colorTexture, depthTexture, width, height);
return reinterpret_cast<TRenderTarget *>(renderTarget);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *tViewer, TRenderTarget *tRenderTarget)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto renderTarget = reinterpret_cast<RenderTarget *>(tRenderTarget);
viewer->destroyRenderTarget(renderTarget);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroy(TViewer *tViewer)
{
auto *viewer = reinterpret_cast<FilamentViewer*>(tViewer);
delete viewer;
}
EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a)
{
((FilamentViewer *)viewer)->setBackgroundColor(r, g, b, a);
}
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer)
{
((FilamentViewer *)viewer)->clearBackgroundImage();
}
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight)
{
((FilamentViewer *)viewer)->setBackgroundImage(path, fillHeight, 100, 100);
}
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp)
{
((FilamentViewer *)viewer)->setBackgroundImagePosition(x, y, clamp, 100, 100);
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkybox(TViewer *viewer, const char *skyboxPath)
{
((FilamentViewer *)viewer)->loadSkybox(skyboxPath);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkybox(TViewer *viewer)
{
((FilamentViewer *)viewer)->removeSkybox();
}
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity)
{
((FilamentViewer *)viewer)->createIbl(r, g, b, intensity);
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity)
{
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeIbl(TViewer *viewer)
{
((FilamentViewer *)viewer)->removeIbl();
}
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix)
{
math::mat3f matrix(rotationMatrix[0], rotationMatrix[1],
rotationMatrix[2],
rotationMatrix[3],
rotationMatrix[4],
rotationMatrix[5],
rotationMatrix[6],
rotationMatrix[7],
rotationMatrix[8]);
((FilamentViewer *)viewer)->rotateIbl(matrix);
}
EMSCRIPTEN_KEEPALIVE int get_instance_count(TSceneManager *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->getInstanceCount(entityId);
}
EMSCRIPTEN_KEEPALIVE void get_instances(TSceneManager *sceneManager, EntityId entityId, EntityId *out)
{
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
}
EMSCRIPTEN_KEEPALIVE void Viewer_setMainCamera(TViewer *tViewer, TView *tView)
{
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->setMainCamera(view);
}
EMSCRIPTEN_KEEPALIVE EntityId Viewer_getMainCamera(TViewer *viewer)
{
return ((FilamentViewer *)viewer)->getMainCamera();
}
EMSCRIPTEN_KEEPALIVE float get_camera_fov(TCamera *camera, bool horizontal)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getFieldOfViewInDegrees(horizontal ? Camera::Fov::HORIZONTAL : Camera::Fov::VERTICAL);
}
EMSCRIPTEN_KEEPALIVE double get_camera_focal_length(TCamera *const camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getFocalLength();
}
EMSCRIPTEN_KEEPALIVE void set_camera_projection_from_fov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setProjection(fovInDegrees, aspect, near, far, horizontal ? Camera::Fov::HORIZONTAL : Camera::Fov::VERTICAL);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getModelMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getViewMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getCullingProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
const auto &mat = convert_double4x4_to_mat4(matrix);
cam->setCustomProjection(mat, near, far);
}
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setLensProjection(focalLength, aspect, near, far);
}
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera *camera, double4x4 matrix)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setModelMatrix(convert_double4x4_to_mat4(matrix));
}
EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getNear();
}
EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getCullingFar();
}
EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *camera)
{
const auto frustum = reinterpret_cast<filament::Camera *>(camera)->getFrustum();
const math::float4 *planes = frustum.getNormalizedPlanes();
double *array = (double *)calloc(24, sizeof(double));
for (int i = 0; i < 6; i++)
{
auto plane = planes[i];
array[i * 4] = double(plane.x);
array[i * 4 + 1] = double(plane.y);
array[i * 4 + 2] = double(plane.z);
array[i * 4 + 3] = double(plane.w);
}
return array;
}
EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float distance)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
cam->setFocusDistance(distance);
}
EMSCRIPTEN_KEEPALIVE void set_camera_exposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
cam->setExposure(aperture, shutterSpeed, sensitivity);
}
EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(TCamera *camera, double4x4 matrix)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
const filament::math::mat4 &mat = convert_double4x4_to_mat4(matrix);
cam->setModelMatrix(mat);
}
EMSCRIPTEN_KEEPALIVE void Viewer_render(
TViewer *tViewer)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
viewer->render(0);
}
EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *tViewer, TSwapChain *tSwapChain, TView *tView, bool renderable)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto *view = reinterpret_cast<View *>(tView);
viewer->setRenderable(view, swapChain, renderable);
}
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
TViewer *tViewer,
TView *tView,
TSwapChain *tSwapChain,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void))
{
#ifdef __EMSCRIPTEN__
useFence = true;
#endif
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->capture(view, pixelBuffer, useFence, swapChain, callback);
};
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
TViewer *tViewer,
TView *tView,
TSwapChain *tSwapChain,
TRenderTarget *tRenderTarget,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void))
{
#ifdef __EMSCRIPTEN__
useFence = true;
#endif
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto renderTarget = reinterpret_cast<RenderTarget *>(tRenderTarget);
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->capture(view, pixelBuffer, useFence, swapChain, renderTarget, callback);
};
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
TViewer *viewer,
float frameInterval)
{
((FilamentViewer *)viewer)->setFrameInterval(frameInterval);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *tViewer, TSwapChain *tSwapChain)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
viewer->destroySwapChain(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *tViewer, uint32_t width, uint32_t height)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->createSwapChain(width, height);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *tViewer, const void *const window)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->createSwapChain(window);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_getSwapChainAt(TViewer *tViewer, int index)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->getSwapChainAt(index);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TView *Viewer_createView(TViewer *tViewer)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto view = viewer->createView();
return reinterpret_cast<TView *>(view);
}
EMSCRIPTEN_KEEPALIVE TView *Viewer_getViewAt(TViewer *tViewer, int32_t index)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto view = viewer->getViewAt(index);
return reinterpret_cast<TView *>(view);
}
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *tViewer)
{
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *sceneManager = viewer->getSceneManager();
return reinterpret_cast<TSceneManager *>(sceneManager);
}
EMSCRIPTEN_KEEPALIVE void queue_position_update_from_viewport_coords(TSceneManager *sceneManager, TView *tView, EntityId entity, float viewportX, float viewportY)
{
auto *view = reinterpret_cast<View *>(tView);
((SceneManager *)sceneManager)->queueRelativePositionUpdateFromViewportVector(view, entity, viewportX, viewportY);
}
EMSCRIPTEN_KEEPALIVE void ios_dummy()
{
Log("Dummy called");
}
EMSCRIPTEN_KEEPALIVE void thermion_filament_free(void *ptr)
{
free(ptr);
}
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform)
{
((SceneManager *)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform);
}
EMSCRIPTEN_KEEPALIVE void remove_collision_component(TSceneManager *sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->removeCollisionComponent(entityId);
}
EMSCRIPTEN_KEEPALIVE void test_collisions(TSceneManager *sceneManager, EntityId entity)
{
((SceneManager *)sceneManager)->testCollisions(entity);
}
EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(TSceneManager *sceneManager, TView *tView, EntityId entity)
{
auto view = reinterpret_cast<View *>(tView);
return ((SceneManager *)sceneManager)->getScreenSpaceBoundingBox(view, entity);
}
EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(TSceneManager *sceneManager, TView *tView, EntityId entity, float *minX, float *minY, float *maxX, float *maxY)
{
auto view = reinterpret_cast<View *>(tView);
auto box = ((SceneManager *)sceneManager)->getScreenSpaceBoundingBox(view, entity);
*minX = box.minX;
*minY = box.minY;
*maxX = box.maxX;
*maxY = box.maxY;
}
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr)
{
free(ptr);
}
}

View File

@@ -6,7 +6,6 @@
#include <filament/LightManager.h>
#include "c_api/APIBoundaryTypes.h"
#include "c_api/TAnimationManager.h"
#include "c_api/TEngine.h"
#include "c_api/TGltfAssetLoader.h"
@@ -19,7 +18,8 @@
#include "c_api/TView.h"
#include "c_api/ThermionDartRenderThreadApi.h"
#include "FilamentViewer.hpp"
#include "RenderTicker.hpp"
#include "rendering/RenderLoop.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
@@ -28,165 +28,6 @@ using namespace thermion;
using namespace std::chrono_literals;
#include <time.h>
class RenderLoop
{
public:
explicit RenderLoop()
{
srand(time(NULL));
t = new std::thread([this]()
{ start(); });
}
~RenderLoop()
{
TRACE("Destroying RenderLoop");
_stop = true;
_cv.notify_one();
TRACE("Joining RenderLoop thread..");
t->join();
TRACE("RenderLoop destructor complete");
}
void start()
{
while (!_stop)
{
iter();
}
}
void destroyViewer() {
std::packaged_task<void()> lambda([=]() mutable
{
if(viewer) {
Viewer_destroy(viewer);
}
viewer = nullptr;
_renderCallback = nullptr;
_renderCallbackOwner = nullptr;
});
auto fut = add_task(lambda);
fut.wait();
}
void createViewer(
void *const context,
void *const platform,
const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *),
void *const owner,
void (*callback)(TViewer *))
{
_renderCallback = renderCallback;
_renderCallbackOwner = owner;
std::packaged_task<void()> lambda([=]() mutable
{
if(viewer) {
Viewer_destroy(viewer);
}
viewer = Viewer_create(context, loader, platform, uberArchivePath);
callback(viewer); });
add_task(lambda);
}
void requestFrame(void (*callback)())
{
std::unique_lock<std::mutex> lock(_mutex);
this->_requestFrameRenderCallback = callback;
_cv.notify_one();
}
void iter()
{
{
std::unique_lock<std::mutex> lock(_mutex);
if (_requestFrameRenderCallback)
{
doRender();
lock.unlock();
this->_requestFrameRenderCallback();
this->_requestFrameRenderCallback = nullptr;
// Calculate and print FPS
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - _lastFrameTime).count();
_lastFrameTime = currentTime;
_frameCount++;
_accumulatedTime += deltaTime;
if (_accumulatedTime >= 1.0f) // Update FPS every second
{
_fps = _frameCount / _accumulatedTime;
// std::cout << "FPS: " << _fps << std::endl;
_frameCount = 0;
_accumulatedTime = 0.0f;
}
}
}
std::unique_lock<std::mutex> taskLock(_taskMutex);
if (!_tasks.empty())
{
auto task = std::move(_tasks.front());
_tasks.pop_front();
taskLock.unlock();
task();
taskLock.lock();
}
_cv.wait_for(taskLock, std::chrono::microseconds(2000), [this]
{ return !_tasks.empty() || _stop; });
}
void doRender()
{
Viewer_render(viewer);
if (_renderCallback)
{
_renderCallback(_renderCallbackOwner);
}
}
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds)
{
_frameIntervalInMicroseconds = static_cast<int>(1000.0f * frameIntervalInMilliseconds);
}
template <class Rt>
auto add_task(std::packaged_task<Rt()> &pt) -> std::future<Rt>
{
std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cv.notify_one();
return ret;
}
TViewer *viewer = std::nullptr_t();
private:
void (*_requestFrameRenderCallback)() = nullptr;
bool _stop = false;
int _frameIntervalInMicroseconds = 1000000 / 60;
std::mutex _mutex;
std::mutex _taskMutex;
std::condition_variable _cv;
void (*_renderCallback)(void *const) = nullptr;
void *_renderCallbackOwner = nullptr;
std::deque<std::function<void()>> _tasks;
std::chrono::high_resolution_clock::time_point _lastFrameTime;
int _frameCount = 0;
float _accumulatedTime = 0.0f;
float _fps = 0.0f;
std::thread *t = nullptr;
};
extern "C"
{
@@ -205,145 +46,21 @@ extern "C"
TRACE("RenderLoop_destroy");
if (_rl)
{
_rl->destroyViewer();
_rl = nullptr;
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context, void *const platform, const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *))
{
TRACE("Viewer_createOnRenderThread");
_rl->createViewer(
context,
platform,
uberArchivePath,
loader,
renderCallback,
renderCallbackOwner,
callback
);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyOnRenderThread(TViewer *viewer)
{
TRACE("Viewer_destroyOnRenderThread");
if (!_rl)
{
Log("Warning - cannot destroy viewer, no RenderLoop has been created");
} else {
_rl->destroyViewer();
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_createViewRenderThread(TViewer *viewer, void (*onComplete)(TView *tView)) {
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, , uint64_t frameTimeInNanos, void (*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *view = Viewer_createView(viewer);
onComplete(view);
RenderTicker_render(tRenderTicker, frameTimeInNanos);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer,
uint32_t width,
uint32_t height,
void (*onComplete)(TSwapChain *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *swapChain = Viewer_createHeadlessSwapChain(viewer, width, height);
onComplete(swapChain);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer,
void *const surface,
void (*onComplete)(TSwapChain *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *swapChain = Viewer_createSwapChain(viewer, surface);
onComplete(swapChain);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_destroySwapChain(viewer, swapChain);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void (*onComplete)())
{
if (!_rl)
{
Log("No render loop!"); // PANIC?
}
else
{
_rl->requestFrame(onComplete);
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_loadIbl(viewer, iblPath, intensity);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeIblRenderThread(TViewer *viewer, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_removeIbl(viewer);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createRenderTargetRenderThread(TViewer *viewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height, void (*onComplete)(TRenderTarget *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto renderTarget = Viewer_createRenderTarget(viewer, colorTexture, depthTexture, width, height);
onComplete(renderTarget);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTargetRenderThread(TViewer *tViewer, TRenderTarget *tRenderTarget, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_destroyRenderTarget(tViewer, tRenderTarget);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(TBackend backend, void (*onComplete)(TEngine *)) {
std::packaged_task<void()> lambda(
[=]() mutable
@@ -383,6 +100,16 @@ extern "C"
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
Engine_destroySwapChain(tEngine);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *)) {
std::packaged_task<void()> lambda(
@@ -446,6 +173,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, void (*onComplete)())
{
std::packaged_task<void()> lambda(
@@ -592,15 +320,6 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
set_frame_interval_render_thread(TViewer *viewer, float frameIntervalInMilliseconds)
{
_rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds);
std::packaged_task<void()> lambda([=]() mutable
{ ((FilamentViewer *)viewer)->setFrameInterval(frameIntervalInMilliseconds); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView *tView, TSwapChain *tSwapChain)
{
std::packaged_task<void()> lambda([=]() mutable
@@ -608,30 +327,6 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, uint8_t *pixelBuffer, bool useFence, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_capture(viewer, view, tSwapChain, pixelBuffer, useFence, onComplete); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, TRenderTarget *tRenderTarget, uint8_t *pixelBuffer, bool useFence, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_captureRenderTarget(viewer, view, tSwapChain, tRenderTarget, pixelBuffer, useFence, onComplete); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
set_background_color_render_thread(TViewer *viewer, const float r, const float g,
const float b, const float a)
{
std::packaged_task<void()> lambda(
[=]() mutable
{ set_background_color(viewer, r, g, b, a); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *))
{
std::packaged_task<void()> lambda([=]() mutable

View File

@@ -0,0 +1,83 @@
#include "RenderLoop.hpp"
#include <functional>
#include <stdlib.h>
#include <time.h>
#include "Log.hpp"
namespace thermion {
RenderLoop::RenderLoop()
{
srand(time(NULL));
t = new std::thread([this]() {
while (!_stop) {
iter();
}
});
}
RenderLoop::~RenderLoop()
{
TRACE("Destroying RenderLoop");
_stop = true;
_cv.notify_one();
TRACE("Joining RenderLoop thread..");
t->join();
delete t;
TRACE("RenderLoop destructor complete");
}
void RenderLoop::requestFrame(void (*callback)())
{
std::unique_lock<std::mutex> lock(_mutex);
this->_requestFrameRenderCallback = callback;
_cv.notify_one();
}
void RenderLoop::iter()
{
{
std::unique_lock<std::mutex> lock(_mutex);
if (_requestFrameRenderCallback)
{
mRenderTicker->render();
lock.unlock();
this->_requestFrameRenderCallback();
this->_requestFrameRenderCallback = nullptr;
// Calculate and print FPS
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - _lastFrameTime).count();
_lastFrameTime = currentTime;
_frameCount++;
_accumulatedTime += deltaTime;
if (_accumulatedTime >= 1.0f) // Update FPS every second
{
_fps = _frameCount / _accumulatedTime;
_frameCount = 0;
_accumulatedTime = 0.0f;
}
}
}
std::unique_lock<std::mutex> taskLock(_taskMutex);
if (!_tasks.empty())
{
auto task = std::move(_tasks.front());
_tasks.pop_front();
taskLock.unlock();
task();
taskLock.lock();
}
_cv.wait_for(taskLock, std::chrono::microseconds(2000), [this]
{ return !_tasks.empty() || _stop; });
}
} // namespace thermion