#ifdef __EMSCRIPTEN__ #include #include "ThermionWebApi.h" #include #endif #include "c_api/TEngine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Log.hpp" #include "MathUtils.hpp" #ifdef __cplusplus namespace thermion { extern "C" { using namespace filament; #endif EMSCRIPTEN_KEEPALIVE uint64_t TSWAP_CHAIN_CONFIG_TRANSPARENT = filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT; EMSCRIPTEN_KEEPALIVE uint64_t TSWAP_CHAIN_CONFIG_READABLE = filament::backend::SWAP_CHAIN_CONFIG_READABLE; EMSCRIPTEN_KEEPALIVE uint64_t TSWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER; EMSCRIPTEN_KEEPALIVE uint64_t TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER = filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER; EMSCRIPTEN_KEEPALIVE TEngine *Engine_create( TBackend backend, void* tPlatform, void* tSharedContext, uint8_t stereoscopicEyeCount, bool disableHandleUseAfterFreeCheck) { #ifdef __EMSCRIPTEN__ auto handle = Thermion_createGLContext(); tSharedContext = (void*)handle; tPlatform = (backend::Platform *)new filament::backend::PlatformWebGL(); #endif filament::Engine::Config config; config.stereoscopicEyeCount = stereoscopicEyeCount; config.disableHandleUseAfterFreeCheck = disableHandleUseAfterFreeCheck; auto *platform = reinterpret_cast(tPlatform); auto *engine = filament::Engine::create( static_cast(backend), platform, tSharedContext, &config ); return reinterpret_cast(engine); } EMSCRIPTEN_KEEPALIVE void Engine_destroy(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); Engine::destroy(engine); TRACE("Engine destroyed"); } EMSCRIPTEN_KEEPALIVE TRenderer *Engine_createRenderer(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto *renderer = engine->createRenderer(); return reinterpret_cast(renderer); } EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags) { auto *engine = reinterpret_cast(tEngine); #ifdef ENABLE_TRACING if((flags & filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) == filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) { TRACE("SWAP_CHAIN_CONFIG_TRANSPARENT"); } if((flags & filament::backend::SWAP_CHAIN_CONFIG_READABLE) == filament::backend::SWAP_CHAIN_CONFIG_READABLE) { TRACE("SWAP_CHAIN_CONFIG_READABLE"); } if((flags & filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) == filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) { TRACE("SWAP_CHAIN_CONFIG_READABLE"); } #endif auto *swapChain = engine->createSwapChain(window, flags); return reinterpret_cast(swapChain); } EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags) { TRACE("Creating headless swapchain %dx%d, flags %flags", width, height, flags); auto *engine = reinterpret_cast(tEngine); auto *swapChain = engine->createSwapChain(width, height, flags); #ifdef ENABLE_TRACING if((flags & filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) == filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) { TRACE("SWAP_CHAIN_CONFIG_TRANSPARENT"); } if((flags & filament::backend::SWAP_CHAIN_CONFIG_READABLE) == filament::backend::SWAP_CHAIN_CONFIG_READABLE) { TRACE("SWAP_CHAIN_CONFIG_READABLE"); } if((flags & filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) == filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) { TRACE("SWAP_CHAIN_CONFIG_READABLE"); } #endif return reinterpret_cast(swapChain); } EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain) { auto *engine = reinterpret_cast(tEngine); auto *swapChain = reinterpret_cast(tSwapChain); engine->destroy(swapChain); } EMSCRIPTEN_KEEPALIVE void Engine_destroyView(TEngine *tEngine, TView *tView) { auto *engine = reinterpret_cast(tEngine); auto *view = reinterpret_cast(tView); engine->destroy(view); } EMSCRIPTEN_KEEPALIVE void Engine_destroyScene(TEngine *tEngine, TScene *tScene) { auto *engine = reinterpret_cast(tEngine); auto *scene = reinterpret_cast(tScene); engine->destroy(scene); } EMSCRIPTEN_KEEPALIVE void Engine_destroyColorGrading(TEngine *tEngine, TColorGrading *tColorGrading) { auto *engine = reinterpret_cast(tEngine); auto *colorGrading = reinterpret_cast(tColorGrading); engine->destroy(colorGrading); } EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto *view = engine->createView(); view->setShadowingEnabled(false); view->setAmbientOcclusionOptions({.enabled = false}); view->setDynamicResolutionOptions({.enabled = false}); return reinterpret_cast(view); } EMSCRIPTEN_KEEPALIVE TTransformManager *Engine_getTransformManager(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto &transformManager = engine->getTransformManager(); return reinterpret_cast(&transformManager); } EMSCRIPTEN_KEEPALIVE TRenderableManager *Engine_getRenderableManager(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto &renderableManager = engine->getRenderableManager(); return reinterpret_cast(&renderableManager); } EMSCRIPTEN_KEEPALIVE TLightManager *Engine_getLightManager(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto &lightManager = engine->getLightManager(); return reinterpret_cast(&lightManager); } EMSCRIPTEN_KEEPALIVE TCamera *Engine_createCamera(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); utils::Entity entity = utils::EntityManager::get().create(); auto *camera = engine->createCamera(entity); return reinterpret_cast(camera); } EMSCRIPTEN_KEEPALIVE void Engine_destroyCamera(TEngine *tEngine, TCamera *tCamera) { auto *engine = reinterpret_cast(tEngine); auto *camera = reinterpret_cast(tCamera); engine->destroyCameraComponent(camera->getEntity()); utils::EntityManager::get().destroy(camera->getEntity()); } EMSCRIPTEN_KEEPALIVE TCamera *Engine_getCameraComponent(TEngine *tEngine, EntityId entityId) { auto *engine = reinterpret_cast(tEngine); auto entity = utils::Entity::import(entityId); if (entity.isNull()) { return std::nullptr_t(); } auto *camera = engine->getCameraComponent(entity); return reinterpret_cast(camera); } EMSCRIPTEN_KEEPALIVE void Engine_setTransform(TEngine *tEngine, EntityId entity, double4x4 transform) { auto *engine = reinterpret_cast(tEngine); auto &transformManager = engine->getTransformManager(); auto transformInstance = transformManager.getInstance(utils::Entity::import(entity)); if (!transformInstance.isValid()) { Log("Transform instance not valid"); } transformManager.setTransform(transformInstance, convert_double4x4_to_mat4(transform)); } EMSCRIPTEN_KEEPALIVE TMaterial *Engine_buildMaterial(TEngine *tEngine, const uint8_t *materialData, size_t length) { auto *engine = reinterpret_cast(tEngine); auto *material = Material::Builder() .package(materialData, length) .build(*engine); return reinterpret_cast(material); } EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterial(TEngine *tEngine, TMaterial *tMaterial) { auto *engine = reinterpret_cast(tEngine); auto *material = reinterpret_cast(tMaterial); engine->destroy(material); } EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialInstance(TEngine *tEngine, TMaterialInstance *tMaterialInstance) { auto *engine = reinterpret_cast(tEngine); auto *mi = reinterpret_cast(tMaterialInstance); engine->destroy(mi); } EMSCRIPTEN_KEEPALIVE void Engine_destroyTexture(TEngine *tEngine, TTexture *tTexture) { auto *engine = reinterpret_cast(tEngine); auto *texture = reinterpret_cast(tTexture); engine->destroy(texture); } EMSCRIPTEN_KEEPALIVE TFence *Engine_createFence(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto *fence = engine->createFence(); return reinterpret_cast(fence); } EMSCRIPTEN_KEEPALIVE void Engine_destroyFence(TEngine *tEngine, TFence *tFence) { auto *engine = reinterpret_cast(tEngine); auto *fence = reinterpret_cast(tFence); Fence::waitAndDestroy(fence); } EMSCRIPTEN_KEEPALIVE void Engine_flushAndWait(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); engine->flushAndWait(); } EMSCRIPTEN_KEEPALIVE void Engine_execute(TEngine *tEngine) { #ifdef __EMSCRIPTEN__ // auto startTime = std::chrono::high_resolution_clock::now(); auto *engine = reinterpret_cast(tEngine); engine->execute(); // auto endTime = std::chrono::high_resolution_clock::now(); // auto durationNs = std::chrono::duration_cast(endTime - startTime).count(); // float durationMs = durationNs / 1e6f; // Log("Total Engine_execute() time: %.3f ms", durationMs); #else Log("WARNING - ignored on non-WASM"); #endif } EMSCRIPTEN_KEEPALIVE TScene *Engine_createScene(TEngine *tEngine) { auto *engine = reinterpret_cast(tEngine); auto *scene = engine->createScene(); return reinterpret_cast(scene); } EMSCRIPTEN_KEEPALIVE TSkybox *Engine_buildSkybox(TEngine *tEngine, uint8_t *ktxData, size_t length, void (*onTextureUploadComplete)()) { auto *engine = reinterpret_cast(tEngine); auto copy = new std::vector(ktxData, ktxData + length); image::Ktx1Bundle *skyboxBundle = new image::Ktx1Bundle(static_cast(copy->data()), static_cast(length)); std::vector *callbackData = new std::vector{ reinterpret_cast(onTextureUploadComplete), reinterpret_cast(skyboxBundle), reinterpret_cast(copy)}; auto *texture = ktxreader::Ktx1Reader::createTexture( engine, *skyboxBundle, false, [](void *userdata) { std::vector* vec = (std::vector*)userdata; void *callbackPtr = vec->at(0); image::Ktx1Bundle *skyboxBundle = reinterpret_cast(vec->at(1)); std::vector *copy = reinterpret_cast*>(vec->at(2)); delete vec; if (callbackPtr) { void (*callback)(void) = (void (*)(void))callbackPtr; callback(); } delete skyboxBundle; delete copy; }, (void *)callbackData); auto *skybox = filament::Skybox::Builder() .environment(texture) .build(*engine); return reinterpret_cast(skybox); } EMSCRIPTEN_KEEPALIVE TIndirectLight *Engine_buildIndirectLight(TEngine *tEngine, uint8_t *ktxData, size_t length, float intensity, void (*onTextureUploadComplete)()) { auto *engine = reinterpret_cast(tEngine); auto copy = new std::vector(ktxData, ktxData + length); image::Ktx1Bundle *iblBundle = new image::Ktx1Bundle(static_cast(copy->data()), static_cast(length)); filament::math::float3 harmonics[9]; iblBundle->getSphericalHarmonics(harmonics); std::vector *callbackData = new std::vector{ reinterpret_cast(onTextureUploadComplete), reinterpret_cast(iblBundle), reinterpret_cast(copy)}; auto *texture = ktxreader::Ktx1Reader::createTexture( engine, *iblBundle, false, [](void *userdata) { std::vector* vec = (std::vector*)userdata; void *callbackPtr = vec->at(0); image::Ktx1Bundle *iblBundle = reinterpret_cast(vec->at(1)); std::vector *copy = reinterpret_cast*>(vec->at(2)); delete vec; if (callbackPtr) { void (*callback)(void) = (void (*)(void))callbackPtr; callback(); } delete iblBundle; delete copy; }, (void *)callbackData); auto *indirectLight = filament::IndirectLight::Builder() .reflections(texture) .irradiance(3, harmonics) .intensity(intensity) .build(*engine); return reinterpret_cast(indirectLight); } EMSCRIPTEN_KEEPALIVE void Engine_destroySkybox(TEngine *tEngine, TSkybox *tSkybox) { auto *engine = reinterpret_cast(tEngine); auto *skybox = reinterpret_cast(tSkybox); if(skybox->getTexture()) { engine->destroy(skybox->getTexture()); } engine->destroy(skybox); } EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLight(TEngine *tEngine, TIndirectLight *tIndirectLight) { auto *engine = reinterpret_cast(tEngine); auto *indirectLight = reinterpret_cast(tIndirectLight); if(indirectLight->getReflectionsTexture()) { engine->destroy(indirectLight->getReflectionsTexture()); } if(indirectLight->getIrradianceTexture()) { engine->destroy(indirectLight->getIrradianceTexture()); } engine->destroy(indirectLight); } #ifdef __cplusplus } } #endif