From 6ff1a24fce99671dd4b258fb2d0bc7867cf1a202 Mon Sep 17 00:00:00 2001 From: Artur Sharafutdinov Date: Mon, 24 Nov 2025 09:33:46 +0100 Subject: [PATCH] Add a sample web app for example plugin --- example_plugin/CMakeLists.txt | 72 +++++++++++++++++++++++++++++ example_plugin/ViewerApp.cpp | 14 ++++++ example_plugin/wasm/compose_html.js | 49 ++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 example_plugin/CMakeLists.txt create mode 100644 example_plugin/ViewerApp.cpp create mode 100644 example_plugin/wasm/compose_html.js diff --git a/example_plugin/CMakeLists.txt b/example_plugin/CMakeLists.txt new file mode 100644 index 000000000000..011b3ab919da --- /dev/null +++ b/example_plugin/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(EMSCRIPTEN) + set(HTML_DIR "${CMAKE_BINARY_DIR}/html") + # place built Wasm/JS files to `html' directory + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${HTML_DIR}) +endif() + +project(example_plugin CXX) + +find_package(MeshLib REQUIRED) +include_directories(${MESHLIB_INCLUDE_DIR} ${MESHLIB_THIRDPARTY_INCLUDE_DIR}) +link_directories(${MESHLIB_THIRDPARTY_LIB_DIR}) + +add_library(MyPlugin MyPlugin.cpp) +target_link_libraries(MyPlugin + PRIVATE + MeshLib::MRViewer +) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MESHLIB_EMSCRIPTEN_CXX_FLAGS}") + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_executable(ViewerApp ViewerApp.cpp) + if(NOT EMSCRIPTEN) + target_link_libraries(ViewerApp + PRIVATE + MeshLib::MRViewer + ) + else() + # libraries and plugins must be linked with `--whole-archive' option for correct auto-initialization + target_link_libraries(ViewerApp + PRIVATE + -Wl,--whole-archive + MeshLib::MRViewer + MeshLib::MRIOExtras + MeshLib::MRCommonPlugins + MyPlugin + -Wl,--no-whole-archive + ) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS}") + + # place HTML/JS files to `html' directory + file(GLOB MESHLIB_WASM_FILES "${MESHLIB_DATA_DIR}/wasm/*.*") + file(GLOB LOCAL_WASM_FILES "${CMAKE_CURRENT_SOURCE_DIR}/wasm/*.*") + file( + COPY + ${MESHLIB_WASM_FILES} + ${LOCAL_WASM_FILES} + DESTINATION ${HTML_DIR} + ) + + # pack assets + file(GLOB MESHLIB_JSONS "${MESHLIB_DATA_DIR}/*.json") + file(GLOB MESHLIB_FONTS "${MESHLIB_DATA_DIR}/fonts/*.ttf") + file(GLOB LOCAL_JSONS "${CMAKE_CURRENT_SOURCE_DIR}/*.json") + file( + COPY + ${MESHLIB_DATA_DIR}/resource + ${MESHLIB_JSONS} + ${MESHLIB_FONTS} + resource + ${LOCAL_JSONS} + DESTINATION "${HTML_DIR}/assets" + ) + mr_emscripten_pack_directory("${HTML_DIR}/assets" "/") + + # enable Asyncify + mr_emscripten_set_async_func_list("${MESHLIB_DATA_DIR}/wasm/wasm_async_func_list.txt") + endif() +endif() diff --git a/example_plugin/ViewerApp.cpp b/example_plugin/ViewerApp.cpp new file mode 100644 index 000000000000..88be1a46cf10 --- /dev/null +++ b/example_plugin/ViewerApp.cpp @@ -0,0 +1,14 @@ +#include +#include + +int main( int argc, char** argv ) +{ + MR::Viewer::LaunchParams launchParams { + .name = "example_plugin", + .argc = argc, + .argv = argv, + }; + MR::Viewer::parseLaunchParams( launchParams ); + + return MR::launchDefaultViewer( launchParams, MR::ViewerSetup() ); +} diff --git a/example_plugin/wasm/compose_html.js b/example_plugin/wasm/compose_html.js new file mode 100644 index 000000000000..e8ea53401e1c --- /dev/null +++ b/example_plugin/wasm/compose_html.js @@ -0,0 +1,49 @@ +var ComposeHTMLHead = function (styles, images) { + for (let i = 0; i < styles.length; ++i) { + var link = document.createElement("link"); + link.setAttribute("rel", "stylesheet"); + link.setAttribute("href", styles[i] + window.location.search); + document.head.appendChild(link); + } + for (let i = 0; i < images.length; ++i) { + var link = document.createElement("link"); + link.setAttribute("rel", "preload"); + link.setAttribute("as", "image"); + link.setAttribute("href", images[i] + window.location.search); + document.head.appendChild(link); + } +} + +var ComposeHTMLBody = function (scripts, asyncScripts) { + for (let i = 0; i < scripts.length; ++i) { + var scr = document.createElement("script"); + scr.setAttribute("src", scripts[i] + window.location.search); + document.body.appendChild(scr); + } + + for (let i = 0; i < asyncScripts.length; ++i) { + var scr = document.createElement("script"); + scr.setAttribute("src", asyncScripts[i] + window.location.search); + scr.setAttribute("async", ""); + document.body.appendChild(scr); + } +} + +ComposeHTMLHead(["styles.css"], ["tool_not_supp.svg"]); +ComposeHTMLBody([ + "ios_detect.js", + "color_theme.js", + "reset_events.js", + "popups.js", + "io_files.js", + "open_link.js", + "resize.js", + "setter_cursor_type.js", + "web_request.js", + "version.js", + "post_load.js", + "wasm_loader.js", + "config.js", + "error.js", + "download_desktop_window.js" +], ["ViewerApp.js"]);