diff --git a/README.md b/README.md index 1a8b110b..02b24840 100644 --- a/README.md +++ b/README.md @@ -1,168 +1,129 @@ -# OSPRay Studio +# OSPRay Studio on non-planar displays +> This project is part of a larger project called [Immersive OSPray Studio](https://github.com/jungwhonam/ImmersiveOSPRay). -This is release v1.0.0 of Intel® OSPRay Studio. It is released under the -Apache 2.0 license. + ## Overview +![TACC Rattler](docs/sample/rattler.png) -Visit [**OSPRay Studio**](http://www.ospray.org/ospray_studio) -(http://www.ospray.org/ospray_studio) for more information. +We extend [OSPRay Studio v1.0.0](https://github.com/RenderKit/ospray-studio/releases/tag/v1.0.0) to support these additional features: +* Support off-axis projection enabling us to display a single, coherent 3D virtual environemnt on non-planar, tiled-display walls +* Open multiple windows and arrange them based on specifications provided in a JSON file +* Synchronize application states across MPI processes -See [what's -new](https://github.com/ospray/ospray_studio/blob/master/CHANGELOG.md) -in this release. -## Overview +## Prerequisites +Make the CMake option `BUILD_OSPRAY_MODULE_MPI` is set to `ON` when building OSPRay, as this feature relies on [OSPRay’s MPI module](https://github.com/RenderKit/ospray?tab=readme-ov-file#mpi-distributed-rendering). -Intel OSPRay Studio is an open source and interactive visualization and -ray tracing application that leverages [Intel OSPRay](https://www.ospray.org) -as its core rendering engine. It can be used to load complex scenes requiring -high fidelity rendering or very large scenes requiring supercomputing resources. +## Setup +```shell +# clone this branch +git clone -b jungwho.nam-feature-multidisplays https://github.com/JungWhoNam/ospray_studio.git +cd ospray_studio -The main control structure is a *scene graph* which allows users to -create an abstract scene in a *directed acyclical graph* manner. Scenes -can either be imported or created using scene graph nodes and structure -support. The scenes can then be rendered either with OSPRay's pathtracer -or scivis renderer. +mkdir build +cd build +mkdir release +``` + +## CMake configuration and build +OSPRay Studio needs to be built with `-DUSE_MPI=ON` in CMake. -More information can be found in the [**high-level feature -description**](https://github.com/ospray/ospray_studio/blob/master/FEATURES.md). +Additionally, make sure to use the OSPRay version you have built. After building OSPRay with `BUILD_OSPRAY_MODULE_MPI`, set `ospray_DIR` so CMake can locate OSPRay, e.g., `/Users/jnam/Documents/dev/ospray/build/release/install/ospray/lib/cmake/ospray-3.1.0`. -Building OSPRay Studio -======================== +```shell +cmake -S .. \ +-B release \ +-DCMAKE_BUILD_TYPE=Release \ +-DUSE_MPI=ON \ +-Dospray_DIR="/Users/jnam/Documents/dev/ospray/build/release/install/ospray/lib/cmake/ospray-3.1.0" -CMake Superbuild ----------------- +cmake --build release -### Required dependencies for superbuild +cmake --install release +``` -- [CMake](https://www.cmake.org) (v3.15+) and any C++14 compiler +## Run `ospStudio` with an example display setting +![example](./docs/sample/example.png) -For convenience, OSPRay Studio provides a CMake Superbuild script which will -pull down its dependencies i.e. GLFW, OSPRay, rkcommon and TBB. It builds OSPRay -Studio without OpemImageIO and OpenEXR support. `stb_image` is used for all -image operations by default instead. +Run `ospStudio` with 3 ranks. Rank 0 will open a window and handle user inputs, as well as broadcast changes to other processes. Rank 1 and 2 will open windows without decorations such as a border, a close widget, etc. These two windows are placed right next to each other and utilize off-axis projection capabilities to appear as a single window. These specifications are written in the display setting file. -To use the superbuild run with: +> Download [the example display setting file](./docs/sample//display_settings.json). -``` sh -mkdir build -cd build -cmake .. -cmake --build . -``` +> Press 'r' to synchrnoize application states. -For other full set of options, run: +> Press 'q' to quit the application. -``` sh -ccmake .. +```shell +mpirun -n 3 \ +./release/ospStudio \ +--mpi \ +--scene multilevel_hierarchy \ +--displayConfig display_settings.json ``` -or +```--mpi```: This option enables the OSPRay Studio's built-in MPI support. -``` sh -cmake-gui .. -``` +```--scene multilevel_hierarchy```: *(Optional)* this option starts the application with the scene opened. + +````--displayConfig display_settings.json````: The JSON configuration file contains information about off-axis projection cameras and windows. + +## Support other display settings +Modify the JSON file specificed in the `--displayConfig` flag. Additionally, adjust the number for `mpirun -n` accordingly. + +> See [another example display setting file](./docs/sample/rattler.json) for the walls shown in the teaser image. + +### Display Configuration JSON File + +At the start, the application reads a JSON configuration file to set its cameras and arrange windows. This is a snippet of an example JSON file. -Standard CMake build --------------------- - -For standard cmake process turn off cmake option `OSPRAY_INSTALL` and provide -following required dependencies with their respective cmake options as will be -listed in OS-specific building process below. - -### Required dependencies - -- [CMake](https://www.cmake.org) (v3.15+) and any C++14 compiler -- Intel [OSPRay](https://www.github.com/ospray/ospray) (v3.1.0) and its - dependencies - OSPRay Studio builds on top of OSPRay. Instructions on - building OSPRay are provided - [here](http://www.ospray.org/downloads.html#building-and-finding-ospray). - OSPRay and OSPRay Studio have the following common dependencies which Studio - can hence leverage from an OSPRay build. - - Intel oneAPI Rendering Toolkit common library - [rkcommon](https://www.github.com/ospray/rkcommon) (v1.13.0) - - Intel [Threading Building Blocks](https://www.threadingbuildingblocks.org/) -- OpenGL and [GLFW](https://www.glfw.org) (v3.3.9) - for the windowing environment - - -### Optional Dependencies - -- Intel [Open Image Denoise](https://openimagedenoise.github.io) - (v2.2.0 or - newer) for denoising frames. To use with OSPRay Studio, OSPRay must be built - with `-DBUILD_OIDN=ON` in CMake. -- [OpenVDB](https://www.openvdb.org/) to support loading VDB formatted volume files. -- [OpenImageIO](http://openimageio.org/) and [OpenEXR](https://www.openexr.com/) - (either v2.x or v3.x) to support images in a variety of file formats. Set `OPENIMAGEIO_ROOT` - and `OPENEXR_ROOT` to the respective install directories to use these libraries. - (tested with OpenImageIO v2.3.16 and OpenEXR v2.5.8 and v3.3.0) -- [Python] (3.9.7) (https://python.org) for python bindings - -### Building on Linux and macOS - -- Follow OSPRay's build instructions to install it, which will also - fulfill most other required dependencies. Set the following - environment variables to easily locate OSPRay and - rkcommon during CMake. - - - - ``` bash - export ospray_DIR = ${OSPRAY_INSTALL_LOCATION} - export rkcommon_DIR = ${RKCOMMON_INSTALL_LOCATION} - export TBB_DIR = ${TBB_INSTALL_LOCATION} - ``` - - Alternatively, [CMAKE_PREFIX_PATH](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) - can be set to find the OSPRay install and other dependencies. - -- Clone OSPRay Studio - - ``` bash - git clone https://github.com/ospray/ospray_studio/ - ``` - -- Create build directory and change directory to it (we recommend - keeping a separate build directory) - - ``` bash - cd ospray_studio - mkdir build - cd build - ``` - -- Then run the typical CMake routine - - ``` bash - cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang ... # or use ccmake - make -j `nproc` # or cmake --build . - ``` - -- To run OSPRay Studio, make sure `LD_LIBRARY_PATH` (on Linux) or - `DYLD_LIBRARY_PATH` (on macOS) contains all dependencies. For - example, - - ``` bash - export LD_LIBRARY_PATH=${OSPRAY_INSTALL}/lib64:...:$LD_LIBRARY_PATH - # then run! - ./ospStudio - ``` - -### Building on Windows - -Use CMake (cmake-gui) to configure and generate a Microsoft Visual -Studio solution file for OSPRay Studio. - -- Specify the source folder and the build directory in CMake -- Specify `ospray_DIR`, `rkcommon_DIR` CMake - variables for the respective install locations -- Click 'Configure' and select the appropriate generator (we recommend - using at least Visual Studio 15 2017) -- Select x64 as an optional parameter for the generator (32-bit builds - are not supported) -- Click 'Generate' to create `ospray_studio.sln`. Open this in Visual - Studio and compile - -You can optionally use the CMake command line: - -``` pwsh -cmake --build . --config Release --target install ``` +[ + ... + + { + "hostName": "localhost", + + "topLeft": [-0.178950, 0.122950, 1.000000], + "botLeft": [-0.178950, -0.122950, 1.000000], + "botRight": [0.178950, -0.122950, 1.000000], + "eye": [0.000000, 0.000000, 2.000000], + "mullionLeft": 0.006320, + "mullionRight": 0.006320, + "mullionTop": 0.015056, + "mullionBottom": 0.015056, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 1024, + "screenHeight": 640, + "decorated": true, + "showUI": true, + "scaleRes": 0.250000, + "scaleResNav": 0.10000, + "lockAspectRatio": true + }, + + ... +] +``` + +The JSON configuration file comprises an array of JSON objects. Each object - surrounded by curly brackets - contains information about an off-axis projection camera and the window that shows the camera view. +- ```hostName``` specifies the host responsible for this JSON object. +- ```topLeft```, ```botLeft```, and ```botRight``` are positions of three corners of a projection plane, i.e., a physical display. +- ```eye``` is the camera position. +- Four keys that start with ```mullion``` are sizes of mullions on four sides of a display. These values are used to shrink the projection plane, accounting for display frames. +- ```display``` sets which display to show the window. +- Four keys that start with ```screen``` set the position and size of a window in screen coordinates. + +- `decorated` specifies whether the window will have decorations such as a border, a close widget, etc. +- `showUI` specifies whether the window will have the menu bar. +- `scaleRes` specifies the resolution scale of the rendering. +- `scaleResNav` specifies the resolution scale when the camera is moving. +- `lockAspectRatio` preserves the relative width and height when you resize the window. + +### Example display configuration JSON files +* [the example display setting file](./docs/sample//display_settings.json) for a single display setting shown in the `Run ospStudio with an example display setting` section. +* [another example display setting file](./docs/sample/rattler.json) for the walls shown in the teaser image. + +> [!NOTE] +> We provide [this Unity project](https://github.com/JungWhoNam/ConfigurationGenerator) to assist users in creating this JSON file. \ No newline at end of file diff --git a/app/GUIContext.cpp b/app/GUIContext.cpp index 36b28676..04fb264c 100644 --- a/app/GUIContext.cpp +++ b/app/GUIContext.cpp @@ -21,6 +21,7 @@ #include "sg/visitors/Commit.h" #include "sg/visitors/PrintNodes.h" #include "sg/visitors/Search.h" +#include "sg/Mpi.h" #include #include @@ -31,6 +32,43 @@ using namespace ospray; using namespace ospray::sg; +static +void offaxisStereoCamera(vec3f LL, vec3f LR, vec3f UR, vec3f eye, + vec3f &dirOUT, vec3f &upOUT, + float &fovyOUT, float &aspectOUT, + vec2f &imageStartOUT, vec2f &imageEndOUT) +{ + vec3f X = (LR-LL)/length(LR-LL); + vec3f Y = (UR-LR)/length(UR-LR); + vec3f Z = cross(X,Y); + + dirOUT = -Z; + upOUT = Y; + + // eye position relative to screen/wall + vec3f eyeP = eye-LL; + + // distance from eye to screen/wall + float dist = dot(eyeP,Z); + + float left = dot(eyeP,X); + float right = length(LR-LL)-left; + float bottom = dot(eyeP,Y); + float top = length(UR-LR)-bottom; + + float newWidth = leftmainLoop(); if (optSaveImageOnGUIExit) saveCurrentFrame(); + + if (!optDisplayJsonName.empty()) + MPI_Abort(MPI_COMM_WORLD, MPI_SUCCESS); } } @@ -184,6 +225,33 @@ void GUIContext::updateCamera() } camera->child("focusDistance").setValue(focusDistance); } + + if (!optDisplayJsonName.empty()) { // off-axis projection + affine3f t = mainWindow->arcballCamera->getTransform(); + vec3f tl = xfmPoint(t, topLeftLocal); + vec3f bl = xfmPoint(t, botLeftLocal); + vec3f br = xfmPoint(t, botRightLocal); + vec3f tr = (tl - bl) + br; + + vec3f eye = xfmPoint(t, eyeLocal); + vec3f dir, up; + float fovy, aspect; + vec2f imgStart, imgEnd; + + // LL, LR, UR + offaxisStereoCamera(bl, br, tr, eye, dir, up, fovy, aspect, imgStart, imgEnd); + + camera->child("fovy").setValue(fovy); + camera->child("aspect").setValue(aspect); + camera->child("position").setValue(eye); + camera->child("direction").setValue(dir); + camera->child("up").setValue(up); + camera->child("imageStart").setValue(imgStart); + camera->child("imageEnd").setValue(imgEnd); + } + + // indicate other processes to update camera + cameraUpdated = true; } void GUIContext::changeToDefaultCamera() @@ -317,6 +385,16 @@ void GUIContext::addToCommandLine(std::shared_ptr app) { optSaveImageOnGUIExit, "Save final image when exiting GUI mode" ); + app->add_option( + "--scene", + scene, + "Sets the opening scene name" + ); + app->add_option( + "--displayConfig", + optDisplayJsonName, + "JSON file name for display configurations" + ); } bool GUIContext::parseCommandLine() @@ -335,14 +413,147 @@ bool GUIContext::parseCommandLine() return false; } - // XXX: changing windowSize here messes causes some display scaling issues - // because it desyncs window and framebuffer size with any scaling - if (optResolution.x != 0) { - defaultSize = optResolution; - // since parseCommandLine happens after MainWindow object creation update the windowSize of that class - mainWindow->windowSize = defaultSize; + if (optDisplayJsonName.empty()) { + // XXX: changing windowSize here messes causes some display scaling issues + // because it desyncs window and framebuffer size with any scaling + if (optResolution.x != 0) { + defaultSize = optResolution; + // since parseCommandLine happens after MainWindow object creation update the windowSize of that class + mainWindow->windowSize = defaultSize; + mainWindow->reshape(true); + } + } + else { + // load the JSON configuration file + JSON config; + try { + std::ifstream configFile(optDisplayJsonName); + if (!configFile) { + std::cerr << "The display config file does not exist." << std::endl; + return false; + } + configFile >> config; + } catch (nlohmann::json::exception &e) { + std::cerr << "Failed to parse the display config file: " << e.what() << std::endl; + return false; + } + + // show/hide the frame and UIs + glfwSetWindowAttrib(mainWindow->glfwWindow, GLFW_DECORATED, config[sg::sgMpiRank()]["decorated"].get() ? GLFW_TRUE : GLFW_FALSE); + mainWindow->showUi = config[sg::sgMpiRank()]["showUI"].get(); + + // set window position + int numOfMonitors; + GLFWmonitor** monitors = glfwGetMonitors(&numOfMonitors); + int displayIndex = config[sg::sgMpiRank()]["display"]; + if (numOfMonitors <= displayIndex) { + std::cerr << "The display index should be less than numOfMonitors: " << std::to_string(numOfMonitors) << std::endl; + return false; + } + int xVirtual, yVirtual; + glfwGetMonitorPos(monitors[displayIndex], &xVirtual, &yVirtual); + int x = (int) config[sg::sgMpiRank()]["screenX"] + xVirtual; + int y = (int) config[sg::sgMpiRank()]["screenY"] + yVirtual; + glfwSetWindowPos(mainWindow->glfwWindow, x, y); + + // set the window size + mainWindow->windowSize.x = config[sg::sgMpiRank()]["screenWidth"]; + mainWindow->windowSize.y = config[sg::sgMpiRank()]["screenHeight"]; + optResolution.x = mainWindow->windowSize.x; + optResolution.y = mainWindow->windowSize.y; + + // keep the aspect ratio + lockAspectRatio = config[sg::sgMpiRank()]["lockAspectRatio"].get() ? + static_cast(mainWindow->windowSize.x) / static_cast(mainWindow->windowSize.y) : 0.f; + + // set the resolution scale + frame->child("scale") = config[sg::sgMpiRank()]["scaleRes"].get(); + frame->child("scaleNav") = config[sg::sgMpiRank()]["scaleResNav"].get(); + + // update three corners of the image plane + topLeftLocal = config[sg::sgMpiRank()]["topLeft"].get(); + botLeftLocal = config[sg::sgMpiRank()]["botLeft"].get(); + botRightLocal = config[sg::sgMpiRank()]["botRight"].get(); + eyeLocal = config[sg::sgMpiRank()]["eye"].get(); + vec4f mullion { + config[sg::sgMpiRank()]["mullionLeft"], + config[sg::sgMpiRank()]["mullionRight"], + config[sg::sgMpiRank()]["mullionTop"], + config[sg::sgMpiRank()]["mullionBottom"]}; + + // use mullion values to update the three corners + vec3f tl = topLeftLocal, bl = botLeftLocal, br = botRightLocal; + + float mullionLeft = mullion[0]; + botLeftLocal += normalize(br - bl) * mullionLeft; + topLeftLocal += normalize(br - bl) * mullionLeft; + + float mullionRight = mullion[1]; + botRightLocal += normalize(bl - br) * mullionRight; + + float mullionTop = mullion[2]; + topLeftLocal += normalize(bl - tl) * mullionTop; + + float mullionBottom = mullion[3]; + botLeftLocal += normalize(tl - bl) * mullionBottom; + botRightLocal += normalize(tl - bl) * mullionBottom; + mainWindow->reshape(true); + + // sync camera state + mainWindow->displayCallback = [&](MainWindow* mainWindow) { + MPI_Bcast(&cameraUpdated, 1, MPI_CXX_BOOL, 0, MPI_COMM_WORLD); + if (cameraUpdated) { + CameraState stateCamera = mainWindow->arcballCamera->getState(); + MPI_Bcast(&stateCamera, sizeof(stateCamera), MPI_BYTE, 0, MPI_COMM_WORLD); + mainWindow->arcballCamera->setState(stateCamera); + updateCamera(); + cameraUpdated = false; + } + + // sync scene state + MPI_Bcast(&syncScene, 1, MPI_CXX_BOOL, 0, MPI_COMM_WORLD); + if (syncScene) { + std::string sceneState = getSceneState(); + int sceneStateSize = sceneState.size(); + + // sync scene state size + MPI_Bcast(&sceneStateSize, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (sg::sgMpiRank() != 0) + sceneState.resize(sceneStateSize); + + // sync scene state + MPI_Bcast(const_cast(sceneState.c_str()), sceneStateSize, MPI_CHAR, 0, MPI_COMM_WORLD); + + // load scene state + if (sg::sgMpiRank() != 0) { + try { + clearScene(); + sg::importScene(shared_from_this(), sceneState, ""); + } catch (const std::exception &e) { + std::cerr << "Failed to sync the scene: '" << e.what() << "'!\n"; + std::cerr << " " << e.what() << std::endl; + } catch (...) { + std::cerr << "Failed to sync the scene!\n"; + } + } + + syncScene = false; + } + + // so nodes swap buffers at about the same time + mainWindow->waitOnOSPRayFrame(); + MPI_Barrier(MPI_COMM_WORLD); + }; + + // press 'r' to sync the scene + mainWindow->keyCallback = [](MainWindow* mainWindow, int key, int scancode, int action, int mod) { + if (action == GLFW_PRESS && key == GLFW_KEY_R) { + mainWindow->ctx->syncScene = true; + } + }; } + return true; } @@ -562,9 +773,7 @@ void GUIContext::useSceneCamera() } } -void GUIContext::saveSGScene() -{ - std::ofstream dump("studio_scene.sg"); +std::string GUIContext::getSceneState() { auto ¤tCamera = frame->child("camera"); JSON camera = {{"cameraIdx", currentCamera.child("cameraId").valueAs()}, {"cameraToWorld", mainWindow->arcballCamera->getTransform()}}; @@ -579,7 +788,13 @@ void GUIContext::saveSGScene() {"lightsManager", *lightsManager}, {"materialRegistry", *baseMaterialRegistry}, {"animation", animation}}; - dump << j.dump(); + return j.dump(); +} + +void GUIContext::saveSGScene() +{ + std::ofstream dump("studio_scene.sg"); + dump << getSceneState(); } void GUIContext::saveNodesJson(const std::string nodeTypeStr) diff --git a/app/GUIContext.h b/app/GUIContext.h index d35d4d58..9f444b44 100644 --- a/app/GUIContext.h +++ b/app/GUIContext.h @@ -74,7 +74,17 @@ class GUIContext : public StudioContext bool optDisplayBufferInvert{false}; bool optAutorotate{false}; bool optAnimate{false}; - + + // multiple windows and off-axis projection + std::string optDisplayJsonName{""}; // CLI + vec3f topLeftLocal; + vec3f botLeftLocal; + vec3f botRightLocal; + vec3f eyeLocal; + bool cameraUpdated; // the state to be sent out over MPI to the other rendering processes + bool syncScene; + std::string getSceneState(); + static MainWindow *mainWindow; std::shared_ptr framebuffer = nullptr; diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 6956f66b..f0072917 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -92,7 +92,7 @@ void MainWindow::initGLFW() } glfwSetKeyCallback( - glfwWindow, [](GLFWwindow *glfwWindow, int key, int, int action, int mod) { + glfwWindow, [](GLFWwindow *glfwWindow, int key, int scancode, int action, int mod) { auto pw = (MainWindow*)glfwGetWindowUserPointer(glfwWindow); auto &io = ImGui::GetIO(); if (!io.WantCaptureKeyboard) @@ -241,6 +241,8 @@ void MainWindow::initGLFW() break; } } + if (pw->keyCallback) + pw->keyCallback(pw, key, scancode, action, mod); }); // set GLFW callbacks diff --git a/app/ospStudio.cpp b/app/ospStudio.cpp index fb98e4ee..9cbfdf2f 100644 --- a/app/ospStudio.cpp +++ b/app/ospStudio.cpp @@ -347,15 +347,7 @@ int main(int argc, const char *argv[]) } if (use_mpi) { -#ifdef USE_MPI - if (mode != StudioMode::BATCH) { - std::cout - << "Error: ospStudio distributed rendering currently only enabled for " - << "batch mode." - << std::endl; - return 1; - } -#else +#ifndef USE_MPI std::cout << "Error: ospStudio launched with --mpi, but has not been compiled " << "with MPI support." diff --git a/app/ospStudio.h b/app/ospStudio.h index 435e18bb..eb8120fb 100644 --- a/app/ospStudio.h +++ b/app/ospStudio.h @@ -250,34 +250,19 @@ inline OSPError initializeOSPRay(int &argc, const char **argv, bool use_mpi) ospDeviceRelease(device); } else { - // - // MPI Distributed - // - // Initialize MPI and set rank and world size in sg sg::sgInitializeMPI(argc, argv); std::cout << "ospStudio --mpi, rank " << sg::sgMpiRank() << "/" << sg::sgMpiWorldSize() << "\n"; - // load the MPI module, and select the MPI distributed device. Here we - // do not call ospInit, as we want to explicitly pick the distributed - // device - auto OSPRAY_MPI_DISTRIBUTED_GPU = - utility::getEnvVar("OSPRAY_MPI_DISTRIBUTED_GPU").value_or(0); - - auto mpiModuleName = OSPRAY_MPI_DISTRIBUTED_GPU ? "mpi_distributed_gpu" - : "mpi_distributed_cpu"; - std::cout << "Loading OSPRay Module: " << mpiModuleName; - - use_mpi = ospLoadModule(mpiModuleName) == OSP_NO_ERROR; - if (!use_mpi) { - std::cout - << "Fatal: ospStudio launched with --mpi, but could not load the OSPRay MPI module." - << std::endl; - return OSP_UNKNOWN_ERROR; + OSPError initError = ospInit(&argc, argv); + + if (initError != OSP_NO_ERROR) { + std::cerr << "OSPRay not initialized correctly!" << std::endl; + return initError; } - cpp::Device mpiDevice("mpiDistributed"); + cpp::Device mpiDevice = cpp::Device::current(); mpiDevice.commit(); mpiDevice.setCurrent(); diff --git a/docs/sample/display_settings.json b/docs/sample/display_settings.json new file mode 100644 index 00000000..0ab78db5 --- /dev/null +++ b/docs/sample/display_settings.json @@ -0,0 +1,71 @@ +[ + { + "hostName": "localhost", + + "topLeft": [-0.178950, 0.122950, 1.000000], + "botLeft": [-0.178950, -0.122950, 1.000000], + "botRight": [0.178950, -0.122950, 1.000000], + "eye": [0.000000, 0.000000, 2.000000], + "mullionLeft": 0.006320, + "mullionRight": 0.006320, + "mullionTop": 0.015056, + "mullionBottom": 0.015056, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 1024, + "screenHeight": 640, + "decorated": true, + "showUI": true, + "scaleRes": 0.250000, + "scaleResNav": 0.10000, + "lockAspectRatio": true + }, + { + "hostName": "localhost", + + "topLeft": [-0.178950, 0.122950, -1.000000], + "botLeft": [-0.178950, -0.122950, -1.000000], + "botRight": [0.000000, -0.122950, -1.000000], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.006320, + "mullionRight": 0.000000, + "mullionTop": 0.015056, + "mullionBottom": 0.015056, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 896, + "screenHeight": 1120, + "decorated": false, + "showUI": false, + "scaleRes": 0.250000, + "scaleResNav": 0.10000, + "lockAspectRatio": true + }, + { + "hostName": "localhost", + + "topLeft": [0.000000, 0.122950, -1.000000], + "botLeft": [0.000000, -0.122950, -1.000000], + "botRight": [0.178950, -0.122950, -1.000000], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.000000, + "mullionRight": 0.006320, + "mullionTop": 0.015056, + "mullionBottom": 0.015056, + + "display": 0, + "screenX": 896, + "screenY": 0, + "screenWidth": 896, + "screenHeight": 1120, + "decorated": false, + "showUI": false, + "scaleRes": 0.250000, + "scaleResNav": 0.10000, + "lockAspectRatio": true + } + ] \ No newline at end of file diff --git a/docs/sample/example.png b/docs/sample/example.png new file mode 100644 index 00000000..13f1d8d9 Binary files /dev/null and b/docs/sample/example.png differ diff --git a/docs/sample/rattler.json b/docs/sample/rattler.json new file mode 100644 index 00000000..323af8de --- /dev/null +++ b/docs/sample/rattler.json @@ -0,0 +1,439 @@ +[ + { + "hostName": "localhost", + + "topLeft": [-1.887851, 1.078200, 0.613400], + "botLeft": [-1.887851, -1.078200, 0.613400], + "botRight": [1.887851, -1.078200, 0.613400], + "eye": [0.000000, 0.000000, 2.501251], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 1024, + "screenHeight": 577, + "decorated": true, + "showUI": true, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r07", + + "topLeft": [-1.887851, 1.078200, 0.613400], + "botLeft": [-1.887851, 0.359400, 0.613400], + "botRight": [-1.887851, 0.359400, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r08", + + "topLeft": [-1.887851, 0.359400, 0.613400], + "botLeft": [-1.887851, -0.359400, 0.613400], + "botRight": [-1.887851, -0.359400, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r09", + + "topLeft": [-1.887851, -0.359400, 0.613400], + "botLeft": [-1.887851, -1.078200, 0.613400], + "botRight": [-1.887851, -1.078200, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r04", + + "topLeft": [-1.887851, 1.078200, -0.613400], + "botLeft": [-1.887851, 0.359400, -0.613400], + "botRight": [-1.166756, 0.359400, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r05", + + "topLeft": [-1.887851, 0.359400, -0.613400], + "botLeft": [-1.887851, -0.359400, -0.613400], + "botRight": [-1.166756, -0.359400, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r06", + + "topLeft": [-1.887851, -0.359400, -0.613400], + "botLeft": [-1.887851, -1.078200, -0.613400], + "botRight": [-1.166756, -1.078200, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r10", + + "topLeft": [-1.166756, 1.078200, -1.605902], + "botLeft": [-1.166756, 0.359400, -1.605902], + "botRight": [0.000000, 0.359400, -1.985004], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r11", + + "topLeft": [-1.166756, 0.359400, -1.605902], + "botLeft": [-1.166756, -0.359400, -1.605902], + "botRight": [0.000000, -0.359400, -1.985004], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r12", + + "topLeft": [-1.166756, -0.359400, -1.605902], + "botLeft": [-1.166756, -1.078200, -1.605902], + "botRight": [0.000000, -1.078200, -1.985004], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r13", + + "topLeft": [0.000000, 1.078200, -1.985004], + "botLeft": [0.000000, 0.359400, -1.985004], + "botRight": [1.166756, 0.359400, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r14", + + "topLeft": [0.000000, 0.359400, -1.985004], + "botLeft": [0.000000, -0.359400, -1.985004], + "botRight": [1.166756, -0.359400, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r15", + + "topLeft": [0.000000, -0.359400, -1.985004], + "botLeft": [0.000000, -1.078200, -1.985004], + "botRight": [1.166756, -1.078200, -1.605902], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r16", + + "topLeft": [1.166756, 1.078200, -1.605902], + "botLeft": [1.166756, 0.359400, -1.605902], + "botRight": [1.887851, 0.359400, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r17", + + "topLeft": [1.166756, 0.359400, -1.605902], + "botLeft": [1.166756, -0.359400, -1.605902], + "botRight": [1.887851, -0.359400, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r18", + + "topLeft": [1.166756, -0.359400, -1.605902], + "botLeft": [1.166756, -1.078200, -1.605902], + "botRight": [1.887851, -1.078200, -0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r01", + + "topLeft": [1.887851, 1.078200, -0.613400], + "botLeft": [1.887851, 0.359400, -0.613400], + "botRight": [1.887851, 0.359400, 0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r02", + + "topLeft": [1.887851, 0.359400, -0.613400], + "botLeft": [1.887851, -0.359400, -0.613400], + "botRight": [1.887851, -0.359400, 0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + }, + { + "hostName": "r03", + + "topLeft": [1.887851, -0.359400, -0.613400], + "botLeft": [1.887851, -1.078200, -0.613400], + "botRight": [1.887851, -1.078200, 0.613400], + "eye": [0.000000, 0.000000, 0.000000], + "mullionLeft": 0.011326, + "mullionRight": 0.011326, + "mullionTop": 0.020733, + "mullionBottom": 0.020733, + + "display": 0, + "screenX": 0, + "screenY": 0, + "screenWidth": 3840, + "screenHeight": 2160, + "decorated": false, + "showUI": false, + "scaleRes": 0.500000, + "scaleResNav": 0.250000, + "lockAspectRatio": true + } + ] \ No newline at end of file diff --git a/docs/sample/rattler.png b/docs/sample/rattler.png new file mode 100644 index 00000000..934a0e0d Binary files /dev/null and b/docs/sample/rattler.png differ diff --git a/sg/importer/Importer.cpp b/sg/importer/Importer.cpp index a3ef0fbd..fc6ac88a 100644 --- a/sg/importer/Importer.cpp +++ b/sg/importer/Importer.cpp @@ -52,21 +52,9 @@ inline bool FindCameraNode::operator()(Node &node, TraversalContext &) return traverseChildren; } -OSPSG_INTERFACE void importScene( - std::shared_ptr context, rkcommon::FileName &sceneFileName) +void importScene( + std::shared_ptr context, const JSON &j, const std::string &filesToImportDir) { - std::cout << "Importing a scene" << std::endl; - context->filesToImport.clear(); - std::ifstream sgFile(sceneFileName.str()); - if (!sgFile) { - std::cerr << "Could not open " << sceneFileName << " for reading" - << std::endl; - return; - } - - JSON j; - sgFile >> j; - std::map jImporters; std::map jGenerators; sg::NodePtr lights; @@ -106,7 +94,7 @@ OSPSG_INTERFACE void importScene( // Try a couple different paths to find the file before giving up std::vector possibleFileNames = {fileName, // as imported - sceneFileName.path() + fileName.base(), // in scenefile directory + filesToImportDir + fileName.base(), // in scenefile directory fileName.base(), // in local directory ""}; @@ -314,6 +302,33 @@ OSPSG_INTERFACE void importScene( } } +OSPSG_INTERFACE void importScene( + std::shared_ptr context, rkcommon::FileName &sceneFileName) +{ + std::cout << "Importing a scene" << std::endl; + context->filesToImport.clear(); + std::ifstream sgFile(sceneFileName.str()); + if (!sgFile) { + std::cerr << "Could not open " << sceneFileName << " for reading" + << std::endl; + return; + } + + JSON j; + sgFile >> j; + + importScene(context, j, sceneFileName.path()); +} + +OSPSG_INTERFACE void importScene( + std::shared_ptr context, const std::string &sceneDesc, const std::string &filesToImportDir) +{ + JSON j; + j = JSON::parse(sceneDesc); + + importScene(context, j, filesToImportDir); +} + // global assets catalogue AssetsCatalogue cat; diff --git a/sg/importer/Importer.h b/sg/importer/Importer.h index c543828b..74493908 100644 --- a/sg/importer/Importer.h +++ b/sg/importer/Importer.h @@ -237,5 +237,9 @@ inline void clearAssets() OSPSG_INTERFACE void importScene( std::shared_ptr context, rkcommon::FileName &fileName); +// for loading scene +OSPSG_INTERFACE void importScene( + std::shared_ptr context, const std::string &sceneDesc, const std::string &filesToImportDir = ""); + } // namespace sg } // namespace ospray