diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fa3cfc619c..f517ed379e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -71,12 +71,30 @@ build-osx-clang: build-windows-msvc: type: build script: - - call scripts\build_gitlab\win.bat + - call scripts\build_gitlab\win.bat "Visual Studio 12 2013 Win64" "v120" tags: - msvc - build - win7 +build-windows-msvc14: + type: build + script: + - call scripts\build_gitlab\win.bat "Visual Studio 14 2015 Win64" "v140" + tags: + - msvc14 + - build + - win7 + +build-windows-icc: + type: build + script: + - call scripts\build_gitlab\win.bat "Visual Studio 12 2013 Win64" "Intel C++ Compiler 16.0" + tags: + - icc + - build + - win7 + ### RELEASE JOBS ### # NOTE(jda) - Current CentOS 6 release machine needs gcc environment @@ -96,6 +114,7 @@ build-windows-msvc: # only: # - devel # - master +# - release-1.0 # artifacts: # paths: # - build_release/*.gz @@ -114,7 +133,7 @@ release-linux-icc: only: - devel - master - - release-0.10 + - release-1.0 artifacts: paths: - build_release/*.gz @@ -132,7 +151,7 @@ release-osx-clang: only: - devel - master - - release-0.10 + - release-1.0 artifacts: paths: - build_release/*.gz @@ -149,7 +168,7 @@ release-windows: only: - devel - master - - release-0.10 + - release-1.0 artifacts: paths: - build_release\ospray*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f668bd809..79ab98b953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,50 @@ Version History --------------- +### Changes in v1.0.0: + +- New OSPRay 'SDK' + - OSPRay internal headers are now installed, enabling applications + to extend OSPRay from a binary install + - CMake macros for OSPRay and ISPC configuration now a part of + binary releases + - CMake clients use them by calling + `include(${OSPRAY_USE_FILE})` in their CMake code after + calling `find_package(ospray)` + - New OSPRay C++ wrapper classes + - These act as a thin layer on top of OSPRay object handles, + where multiple wrappers will share the same underlying + handle when assigned, copied, or moved + - New OSPRay objects are only created when a class instance is + explicity constructed + - C++ users are encouraged to use these over the `ospray.h` + API +- Complete rework of sample applications + - New shared code for parsing the `commandline` + - Save/load of transfer functions now handled through a separate + library which does not depend on Qt + - Added `ospCvtParaViewTfcn` utility, which enables + `ospVolumeViewer` to load color maps from ParaView + - GLUT based sample viewer updates + - Rename of `ospModelViewer` to `ospGlutViewer` + - GLUT viewer now supports volume rendering + - Command mode with preliminary scripting capabilities, + enabled by pressing '`:`' key (not available when using + Intel C++ compiler (icc)) + - Enhanced support of sample applications on Windows +- New minimum ISPC version is 1.9.0 +- Support of Intel® AVX-512 for second generation Intel® Xeon Phi™ + processor (codename Knights Landing) is now a part of the + `OSPRAY_BUILD_ISA` CMake build configuration + - Compiling AVX-512 requires icc to be enabled as a build option +- Enhanced error messages when `ospLoadModule()` fails +- Added `OSP_FB_RGBA32F` support in the `DistributedFrameBuffer` +- Updated Glass shader in the PathTracer +- Many miscellaneous cleanups, bugfixes, and improvements + ### Changes in v0.10.1: -- Fixed support of first generation Intel® Xeon Phi™ coprocessor + +- Fixed support of first generation Intel Xeon Phi coprocessor (codename Knights Corner) - Restored missing implementation of `ospRemoveVolume()` @@ -11,7 +53,7 @@ Version History - Added new tasking options: `Cilk`, `Internal`, and `Debug` - Provides more ways for OSPRay to interact with calling application tasking systems - - `Cilk`: Use Intel® Cilk™ Plus language extensions (ICC only) + - `Cilk`: Use Intel® Cilk™ Plus language extensions (icc only) - `Internal`: Use hand written OSPRay tasking system - `Debug`: All tasks are run in serial (useful for debugging) - In most cases, Intel Threading Building Blocks (Intel `TBB`) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5b3791088..8b37283a42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,14 +29,28 @@ IF(POLICY CMP0054) ENDIF() SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) -SET(OSPRAY_VERSION_MAJOR 0) -SET(OSPRAY_VERSION_MINOR 10) -SET(OSPRAY_VERSION_PATCH 1) +SET(OSPRAY_VERSION_MAJOR 1) +SET(OSPRAY_VERSION_MINOR 0) +SET(OSPRAY_VERSION_PATCH 0) +SET(OSPRAY_VERSION_GITHASH 0) +IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + FIND_PACKAGE(Git) + IF(GIT_FOUND) + EXECUTE_PROCESS( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE "OSPRAY_VERSION_GITHASH" + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + ENDIF() +ENDIF() + SET(OSPRAY_VERSION ${OSPRAY_VERSION_MAJOR}.${OSPRAY_VERSION_MINOR}.${OSPRAY_VERSION_PATCH} ) SET(OSPRAY_SOVERSION 0) + SET(CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo") IF (WIN32) IF (NOT OSPRAY_DEFAULT_CMAKE_CONFIGURATION_TYPES_SET) @@ -68,17 +82,12 @@ IF(NOT WIN32) INCLUDE(cmake/doxygen.cmake) ENDIF() -OSPRAY_CONFIGURE_COMPILER() - ############################################################## # OSPRay specific build options and configuration selection ############################################################## OPTION(OSPRAY_USE_EXTERNAL_EMBREE - "Use a pre-built Embree instead of the internally built version") - -OPTION(OSPRAY_BUILD_ENABLE_KNL "Enable experimental 'Knights Landing' build?") -MARK_AS_ADVANCED(OSPRAY_BUILD_ENABLE_KNL) + "Use a pre-built Embree instead of the internally built version" ON) OPTION(OSPRAY_VOLUME_VOXELRANGE_IN_APP "Move 'voxelrange' computations to app?") MARK_AS_ADVANCED(OSPRAY_VOLUME_VOXELRANGE_IN_APP) @@ -95,17 +104,6 @@ ENDIF() OPTION(OSPRAY_BUILD_MPI_DEVICE "Add MPI Remote/Distributed rendering support?") -# the arch we're targeting for the non-MIC/non-xeon phi part of ospray -SET(OSPRAY_BUILD_ISA "ALL" CACHE STRING "Target ISA (SSE, AVX, AVX2, or ALL)") -IF (OSPRAY_BUILD_ENABLE_KNL) - SET_PROPERTY(CACHE OSPRAY_BUILD_ISA PROPERTY STRINGS ALL SSE AVX AVX2 AVX512) -ELSE() - SET_PROPERTY(CACHE OSPRAY_BUILD_ISA PROPERTY STRINGS ALL SSE AVX AVX2) -ENDIF() - -# make ISA target case-insensitive, used in configure_ospray() macro -STRING(TOUPPER ${OSPRAY_BUILD_ISA} OSPRAY_BUILD_ISA) - SET(OSPRAY_MIC ${OSPRAY_BUILD_MIC_SUPPORT}) SET(OSPRAY_MPI ${OSPRAY_BUILD_MPI_DEVICE}) @@ -113,19 +111,22 @@ SET(OSPRAY_MPI ${OSPRAY_BUILD_MPI_DEVICE}) # create binary packages; before any INSTALL() invocation/definition ############################################################## +OPTION(OSPRAY_ZIP_MODE "Use tarball/zip CPack generator instead of RPM" ON) +MARK_AS_ADVANCED(OSPRAY_ZIP_MODE) + INCLUDE(package) ############################################################## # the OSPRay 'common' library ############################################################## -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) +CONFIGURE_OSPRAY() + SET(OSPRAY_TARGET "intel64") -ADD_SUBDIRECTORY(common builddir/ospray_common/intel64) +ADD_SUBDIRECTORY(ospcommon builddir/ospray_common/intel64) IF (OSPRAY_MIC) SET(OSPRAY_TARGET "mic") - ADD_SUBDIRECTORY(common builddir/ospray_common/mic) + ADD_SUBDIRECTORY(ospcommon builddir/ospray_common/mic) ENDIF() ############################################################## diff --git a/README.md b/README.md index c2a3efe06b..5268985a61 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OSPRay ====== -This is release v0.10.1 of OSPRay. For changes and new features see the +This is release v1.0.0 of OSPRay. For changes and new features see the [changelog](CHANGELOG.md). Also visit http://www.ospray.org for more information. @@ -29,10 +29,10 @@ and AVX-512 to achieve high rendering performance. OSPRay Support and Contact -------------------------- -OSPRay is still in beta stage, and though we do our best to +OSPRay is under active development, and though we do our best to guarantee stable release versions a certain number of bugs, as-yet-missing features, inconsistencies, or any other issues are -unavoidable at this stage. Should you find any such issues please report +still possible. Should you find any such issues please report them immediately via [OSPRay's GitHub Issue Tracker](https://github.com/ospray/OSPRay/issues) (or, if you should happen to have a fix for it,you can also send us a pull request); for @@ -67,7 +67,7 @@ following prerequisites: - To build OSPRay you need [CMake](http://www.cmake.org), any form of C++ compiler (we recommend using the [Intel® C++ compiler (icc)](https://software.intel.com/en-us/c-compilers), but also - support GCC and clang-cc), and standard Linux development tools. + support GCC and Clang), and standard Linux development tools. To build the demo viewers, you should also have some version of OpenGL and the GL Utility Toolkit (GLUT or freeglut), as well as Qt 4.6 or higher. @@ -83,7 +83,7 @@ following prerequisites: - Per default OSPRay uses the Intel® Threading Building Blocks (TBB) as tasking system, which we recommend for performance and flexibility reasons. Alternatively you can set CMake variable - `OSPRAY_TASKING_SYSTEM` to `OpenMP`. + `OSPRAY_TASKING_SYSTEM` to `OpenMP`, `Internal`, or `Cilk` (icc only). - OSPRay also heavily uses [Embree](http://embree.github.io); however, OSPRay directly includes its own copy of Embree, so a special installation of Embree is *not* required. @@ -129,13 +129,13 @@ CMake is easy: - The compiler CMake will use will default to whatever the `CC` and `CXX` environment variables point to. Should you want to specify a different compiler, run cmake manually while specifying the desired - compiler. The default compiler on most linux machines is 'gcc', but - it can be pointed to 'clang' instead by executing the following: + compiler. The default compiler on most linux machines is `gcc`, but + it can be pointed to `clang` instead by executing the following: user@mymachine[~/Projects/ospray/release]: cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang .. - CMake will now use clang instead of gcc. If you are ok with using + CMake will now use Clang instead of GCC. If you are ok with using the default compiler on your system, then simply skip this step. Note that the compiler variables cannot be changed after the first `cmake` or `ccmake` run. @@ -216,3 +216,6 @@ at the [OSPRay Demos and Examples] page. [imgTutorial2]: https://ospray.github.io/images/tutorial_accumulatedframe.png [imgQtViewer]: https://ospray.github.io/images/QtViewer.jpg [imgVolumeViewer]: https://ospray.github.io/images/VolumeViewer.png +[imgSpotLight]: https://ospray.github.io/images/spot_light.png +[imgQuadLight]: https://ospray.github.io/images/quad_light.png +[imgHDRILight]: https://ospray.github.io/images/hdri_light.png diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 1ba1c8e506..f95371d59b 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -14,33 +14,56 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - IF(NOT THIS_IS_MIC) - # common xml utility classes + INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/ospray/include + ${CMAKE_SOURCE_DIR} + ${CMAKE_CURRENT_LIST_DIR} + common + ) + + # common utilities + ADD_SUBDIRECTORY(common/commandline) + ADD_SUBDIRECTORY(common/importer) + ADD_SUBDIRECTORY(common/ospray_cpp) + ADD_SUBDIRECTORY(common/tfn_lib) + ADD_SUBDIRECTORY(common/miniSG) ADD_SUBDIRECTORY(common/xml) - # mini-scene graph viewer for _geometry_ (intentionally simple) - OPTION(OSPRAY_APPS_MODELVIEWER "Build ospModelViewer application." ON) + # benchmark application which has no windowing framework dependencies + OPTION(OSPRAY_APPS_BENCHMARK "Build ospBenchmark application." ON) + + IF(OSPRAY_APPS_BENCHMARK) + ADD_SUBDIRECTORY(bench) + ENDIF() + + # determine if we can enable scripting features (can't with icc) + IF(NOT OSPRAY_COMPILER_ICC) + OPTION(OSPRAY_APPS_ENABLE_SCRIPTING + "Enable scripting features in viewer apps." + ON) + MARK_AS_ADVANCED(OSPRAY_APPS_ENABLE_SCRIPTING) + ELSE() + SET(OSPRAY_APPS_ENABLE_SCRIPTING OFF) + ENDIF() + + IF(OSPRAY_APPS_ENABLE_SCRIPTING) + ADD_DEFINITIONS(-DOSPRAY_APPS_ENABLE_SCRIPTING) + INCLUDE_DIRECTORIES(common/script/chaiscript) + ADD_SUBDIRECTORY(common/script) + ENDIF() - # include glut and widgets only when needed - # needs to be before the first application, such that the GLUT include path is known - # XXX if the defaults change, include could be missing on the first run of CMake - IF(OSPRAY_APPS_MODELVIEWER OR - OSPRAY_APPS_STREAMLINEVIEWER OR - OSPRAY_APPS_PARTICLEVIEWER) - # GLUT is used by apps as well + # mini-scene graph viewer implemented with GLUT + OPTION(OSPRAY_APPS_GLUTVIEWER "Build ospGlutViewer application." ON) + + IF(OSPRAY_APPS_GLUTVIEWER) INCLUDE(${PROJECT_SOURCE_DIR}/cmake/glut.cmake) # common utility classes for GLUT-based 3D viewer widget ADD_SUBDIRECTORY(common/widgets) + ADD_SUBDIRECTORY(glutViewer) ENDIF() - IF (OSPRAY_APPS_MODELVIEWER) - ADD_SUBDIRECTORY(modelViewer) - ENDIF() - - IF(NOT WIN32) # NOTE(jda) - Disable Qt based viewers when on OS X using ICC due to # unresolved issues # qt-based viewer for geometry (and soon volumes) @@ -51,19 +74,6 @@ IF(NOT THIS_IS_MIC) ENDIF() ENDIF() - # stream line viewer for NASA Stream Line Demo - OPTION(OSPRAY_APPS_STREAMLINEVIEWER "Build ospStreamLineViewer application." OFF) - MARK_AS_ADVANCED(OSPRAY_APPS_STREAMLINEVIEWER) - IF(OSPRAY_APPS_STREAMLINEVIEWER) - ADD_SUBDIRECTORY(streamLineViewer) - ENDIF() - - # xyz atom model viewer (one sphere per atom) - OPTION(OSPRAY_APPS_PARTICLEVIEWER "Build ospParticleViewer application." OFF) - IF(OSPRAY_APPS_PARTICLEVIEWER) - ADD_SUBDIRECTORY(particleViewer) - ENDIF() - # volume viewer application IF(NOT (APPLE AND (${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel"))) OPTION(OSPRAY_APPS_VOLUMEVIEWER "Build ospVolumeViewer application." ON) @@ -71,5 +81,4 @@ IF(NOT THIS_IS_MIC) ADD_SUBDIRECTORY(volumeViewer) ENDIF() ENDIF() - ENDIF(NOT WIN32) ENDIF() diff --git a/apps/bench/CMakeLists.txt b/apps/bench/CMakeLists.txt new file mode 100644 index 0000000000..fbb5f8c32f --- /dev/null +++ b/apps/bench/CMakeLists.txt @@ -0,0 +1,30 @@ +# ======================================================================== ## +## Copyright 2009-2016 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}) + +OSPRAY_CREATE_APPLICATION(Benchmark + bench.cpp + OSPRayFixture.cpp + OSPRayFixture.h + simple_outputter.hpp +LINK + ospray + ospray_commandline + ospray_common + ospray_minisg + ospray_importer +) diff --git a/apps/bench/OSPRayFixture.cpp b/apps/bench/OSPRayFixture.cpp new file mode 100644 index 0000000000..8aba6c9afe --- /dev/null +++ b/apps/bench/OSPRayFixture.cpp @@ -0,0 +1,90 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "OSPRayFixture.h" + +using std::string; + +using namespace ospcommon; + +std::unique_ptr OSPRayFixture::renderer; +std::unique_ptr OSPRayFixture::camera; +std::unique_ptr OSPRayFixture::model; +std::unique_ptr OSPRayFixture::fb; + +string OSPRayFixture::imageOutputFile; + +std::vector OSPRayFixture::benchmarkModelFiles; + +int OSPRayFixture::width = 1024; +int OSPRayFixture::height = 1024; + +int OSPRayFixture::numWarmupFrames = 10; + +vec3f OSPRayFixture::bg_color = {1.f, 1.f, 1.f}; + +// helper function to write the rendered image as PPM file +static void writePPM(const string &fileName, const int sizeX, const int sizeY, + const uint32_t *pixel) +{ + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); +} + +static void createFramebuffer(OSPRayFixture *f) +{ + *f->fb = ospray::cpp::FrameBuffer(osp::vec2i{f->width, f->height}, + OSP_FB_SRGBA, + OSP_FB_COLOR|OSP_FB_ACCUM); + f->fb->clear(OSP_FB_ACCUM | OSP_FB_COLOR); +} + +void OSPRayFixture::SetUp() +{ + createFramebuffer(this); + + renderer->set("world", *model); + renderer->set("model", *model); + renderer->set("camera", *camera); + renderer->set("spp", 1); + + renderer->commit(); + + for (int i = 0; i < numWarmupFrames; ++i) { + renderer->renderFrame(*fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } +} + +void OSPRayFixture::TearDown() +{ + if (!imageOutputFile.empty()) { + auto *lfb = (uint32_t*)fb->map(OSP_FB_COLOR); + writePPM(imageOutputFile + ".ppm", width, height, lfb); + fb->unmap(lfb); + } +} diff --git a/apps/bench/OSPRayFixture.h b/apps/bench/OSPRayFixture.h new file mode 100644 index 0000000000..9723537182 --- /dev/null +++ b/apps/bench/OSPRayFixture.h @@ -0,0 +1,50 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + + +#include "hayai/hayai.hpp" + +#include +#include +#include + +struct OSPRayFixture : public hayai::Fixture +{ + // Fixture hayai interface // + + void SetUp() override; + void TearDown() override; + + // Fixture data // + + static std::unique_ptr renderer; + static std::unique_ptr camera; + static std::unique_ptr model; + static std::unique_ptr fb; + + // Command-line configuration data // + + static std::string imageOutputFile; + + static std::vector benchmarkModelFiles; + + static int width; + static int height; + + static int numWarmupFrames; + + static ospcommon::vec3f bg_color; +}; diff --git a/apps/bench/bench.cpp b/apps/bench/bench.cpp new file mode 100644 index 0000000000..0d5dce458a --- /dev/null +++ b/apps/bench/bench.cpp @@ -0,0 +1,204 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "hayai/hayai.hpp" +#include "simple_outputter.hpp" + +#include "OSPRayFixture.h" + +#include "commandline/Utility.h" + +using std::cout; +using std::endl; +using std::string; + +BENCHMARK_F(OSPRayFixture, test1, 1, 100) +{ + renderer->renderFrame(*fb, OSP_FB_COLOR | OSP_FB_ACCUM); +} + +// NOTE(jda) - Implement make_unique() as it didn't show up until C++14... +template +std::unique_ptr make_unique(Args&& ...args) +{ + return std::unique_ptr(new T( std::forward(args)... )); +} + +void printUsageAndExit() +{ + cout << "Usage: ospBenchmark [options] model_file" << endl; + + cout << endl << "Args:" << endl; + + cout << endl; + cout << " model_file --> Scene used for benchmarking, supported types" + << " are:" << endl; + cout << " stl, msg, tri, xml, obj, hbp, x3d" << endl; + + cout << endl; + cout << "Options:" << endl; + + cout << endl; + cout << "**generic rendering options**" << endl; + + cout << endl; + cout << " -i | --image --> Specify the base filename to write the" + << " framebuffer to a file." << endl; + cout << " If ommitted, no file will be written." << endl; + cout << " NOTE: this option adds '.ppm' to the end of the" + << " filename" << endl; + + cout << endl; + cout << " -w | --width --> Specify the width of the benchmark frame" + << endl; + cout << " default: 1024" << endl; + + cout << endl; + cout << " -h | --height --> Specify the height of the benchmark frame" + << endl; + cout << " default: 1024" << endl; + + cout << endl; + cout << " -r | --renderer --> Specify the renderer to be benchmarked." + << endl; + cout << " Ex: -r pathtracer" << endl; + cout << " default: ao1" << endl; + + cout << endl; + cout << " -bg | --background --> Specify the background color: R G B" + << endl; + + cout << endl; + cout << " -wf | --warmup --> Specify the number of warmup frames: N" + << endl; + cout << " default: 10" << endl; + + cout << endl; + cout << "**camera rendering options**" << endl; + + cout << endl; + cout << " -vp | --eye --> Specify the camera eye as: ex ey ez " << endl; + + cout << endl; + cout << " -vi | --gaze --> Specify the camera gaze point as: ix iy iz " + << endl; + + cout << endl; + cout << " -vu | --up --> Specify the camera up as: ux uy uz " << endl; + + + cout << endl; + cout << "**volume rendering options**" << endl; + + cout << endl; + cout << " -s | --sampling-rate --> Specify the sampling rate for volumes." + << endl; + cout << " default: 0.125" << endl; + + cout << endl; + cout << " -dr | --data-range --> Specify the data range for volumes." + << " If not specified, then the min and max data" << endl + << " values will be used when reading the data into memory." << endl; + cout << " Format: low high" << endl; + + cout << endl; + cout << " -tfc | --tf-color --> Specify the next color to in the transfer" + << " function for volumes. Each entry will add to the total list of" + << " colors in the order they are specified." << endl; + cout << " Format: R G B A" << endl; + cout << " Value Range: [0,1]" << endl; + + cout << " -tfs | --tf-scale --> Specify the opacity the transfer function" + << " will scale to: [0,x] where x is the input value." << endl; + cout << " default: 1.0" << endl; + + cout << " -tff | --tf-file --> Specify the transfer function file to use" + << endl; + + cout << endl; + cout << " -is | --surface --> Specify an isosurface at value: val " + << endl; + + exit(0); +} + +void parseCommandLine(int argc, const char *argv[]) +{ + if (argc <= 1) { + printUsageAndExit(); + } + + for (int i = 1; i < argc; ++i) { + string arg = argv[i]; + if (arg == "-i" || arg == "--image") { + OSPRayFixture::imageOutputFile = argv[++i]; + } else if (arg == "-w" || arg == "--width") { + OSPRayFixture::width = atoi(argv[++i]); + } else if (arg == "-h" || arg == "--height") { + OSPRayFixture::height = atoi(argv[++i]); + } else if (arg == "-wf" || arg == "--warmup") { + OSPRayFixture::numWarmupFrames = atoi(argv[++i]); + } else if (arg == "-bg" || arg == "--background") { + ospcommon::vec3f &color = OSPRayFixture::bg_color; + color.x = atof(argv[++i]); + color.y = atof(argv[++i]); + color.z = atof(argv[++i]); + } + } + + auto ospObjs = parseWithDefaultParsers(argc, argv); + + ospcommon::box3f bbox; + std::tie(bbox, + *OSPRayFixture::model, + *OSPRayFixture::renderer, + *OSPRayFixture::camera) = ospObjs; + + float width = OSPRayFixture::width; + float height = OSPRayFixture::height; + auto &camera = *OSPRayFixture::camera; + camera.set("aspect", width/height); + camera.commit(); +} + +void allocateFixtureObjects() +{ + // NOTE(jda) - Have to allocate objects here, because we can't allocate them + // statically (before ospInit) and can't in the fixture's + // constructor because they need to exist during parseCommandLine. + OSPRayFixture::renderer = make_unique(); + OSPRayFixture::camera = make_unique(); + OSPRayFixture::model = make_unique(); + OSPRayFixture::fb = make_unique(); +} + +int main(int argc, const char *argv[]) +{ + ospInit(&argc, argv); + allocateFixtureObjects(); + parseCommandLine(argc, argv); + +# if 0 + hayai::ConsoleOutputter outputter; +#else + hayai::SimpleOutputter outputter; +#endif + + hayai::Benchmarker::AddOutputter(outputter); + + hayai::Benchmarker::RunAllTests(); + return 0; +} diff --git a/apps/bench/hayai/hayai.hpp b/apps/bench/hayai/hayai.hpp new file mode 100644 index 0000000000..0396751064 --- /dev/null +++ b/apps/bench/hayai/hayai.hpp @@ -0,0 +1,136 @@ +#ifndef __HAYAI +#define __HAYAI + +#include "hayai_benchmarker.hpp" +#include "hayai_test.hpp" +#include "hayai_default_test_factory.hpp" +#include "hayai_fixture.hpp" +#include "hayai_console_outputter.hpp" +#include "hayai_json_outputter.hpp" +#include "hayai_junit_xml_outputter.hpp" + + +#define HAYAI_VERSION "1.0.1" + + +#define BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) \ + fixture_name ## _ ## benchmark_name ## _Benchmark + +#define BENCHMARK_(fixture_name, \ + benchmark_name, \ + fixture_class_name, \ + runs, \ + iterations) \ + class BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) \ + : public fixture_class_name \ + { \ + public: \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)() \ + { \ + \ + } \ + protected: \ + virtual void TestBody(); \ + private: \ + static const ::hayai::TestDescriptor* _descriptor; \ + }; \ + \ + const ::hayai::TestDescriptor* \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::_descriptor = \ + ::hayai::Benchmarker::Instance().RegisterTest( \ + #fixture_name, \ + #benchmark_name, \ + runs, \ + iterations, \ + new ::hayai::TestFactoryDefault< \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) \ + >(), \ + ::hayai::TestParametersDescriptor()); \ + \ + void BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::TestBody() + +#define BENCHMARK_F(fixture_name, \ + benchmark_name, \ + runs, \ + iterations) \ + BENCHMARK_(fixture_name, \ + benchmark_name, \ + fixture_name, \ + runs, \ + iterations) + +#define BENCHMARK(fixture_name, \ + benchmark_name, \ + runs, \ + iterations) \ + BENCHMARK_(fixture_name, \ + benchmark_name, \ + ::hayai::Test, \ + runs, \ + iterations) + +// Parametrized benchmarks. +#define BENCHMARK_P_(fixture_name, \ + benchmark_name, \ + fixture_class_name, \ + runs, \ + iterations, \ + arguments) \ + class BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) \ + : public fixture_class_name { \ + public: \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) () {} \ + virtual ~ BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) () {} \ + static const std::size_t _runs = runs; \ + static const std::size_t _iterations = iterations; \ + static const char* _argumentsDeclaration() { return #arguments; } \ + protected: \ + inline void TestPayload arguments; \ + }; \ + void BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::TestPayload arguments + +#define BENCHMARK_P(fixture_name, \ + benchmark_name, \ + runs, \ + iterations, \ + arguments) \ + BENCHMARK_P_(fixture_name, \ + benchmark_name, \ + hayai::Fixture, \ + runs, \ + iterations, \ + arguments) + +#define BENCHMARK_P_F(fixture_name, benchmark_name, runs, iterations, arguments) \ + BENCHMARK_P_(fixture_name, benchmark_name, fixture_name, runs, iterations, arguments) + +#define BENCHMARK_P_CLASS_NAME_(fixture_name, benchmark_name, id) \ + fixture_name ## _ ## benchmark_name ## _Benchmark_ ## id + +#define BENCHMARK_P_INSTANCE1(fixture_name, benchmark_name, arguments, id) \ + class BENCHMARK_P_CLASS_NAME_(fixture_name, benchmark_name, id): \ + public BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name) { \ + protected: \ + virtual void TestBody() { this->TestPayload arguments; } \ + private: \ + static const ::hayai::TestDescriptor* _descriptor; \ + }; \ + const ::hayai::TestDescriptor* BENCHMARK_P_CLASS_NAME_(fixture_name, benchmark_name, id)::_descriptor = \ + ::hayai::Benchmarker::Instance().RegisterTest( \ + #fixture_name, #benchmark_name, \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::_runs, \ + BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::_iterations, \ + new ::hayai::TestFactoryDefault< BENCHMARK_P_CLASS_NAME_(fixture_name, benchmark_name, id) >(), \ + ::hayai::TestParametersDescriptor(BENCHMARK_CLASS_NAME_(fixture_name, benchmark_name)::_argumentsDeclaration(), #arguments)) + +#if defined(__COUNTER__) +# define BENCHMARK_P_ID_ __COUNTER__ +#else +# define BENCHMARK_P_ID_ __LINE__ +#endif + +#define BENCHMARK_P_INSTANCE(fixture_name, benchmark_name, arguments) \ + BENCHMARK_P_INSTANCE1(fixture_name, benchmark_name, arguments, BENCHMARK_P_ID_) + + +#endif diff --git a/apps/bench/hayai/hayai_benchmarker.hpp b/apps/bench/hayai/hayai_benchmarker.hpp new file mode 100644 index 0000000000..3250f02aa7 --- /dev/null +++ b/apps/bench/hayai/hayai_benchmarker.hpp @@ -0,0 +1,541 @@ +#ifndef __HAYAI_BENCHMARKER +#define __HAYAI_BENCHMARKER +#include +#include +#include +#include +#include +#include + +#include "hayai_test_factory.hpp" +#include "hayai_test_descriptor.hpp" +#include "hayai_test_result.hpp" +#include "hayai_console_outputter.hpp" + + +namespace hayai +{ + /// Benchmarking execution controller singleton. + class Benchmarker + { + public: + /// Get the singleton instance of @ref Benchmarker. + + /// @returns a reference to the singleton instance of the + /// benchmarker execution controller. + static Benchmarker& Instance() + { + static Benchmarker singleton; + return singleton; + } + + + /// Register a test with the benchmarker instance. + + /// @param fixtureName Name of the fixture. + /// @param testName Name of the test. + /// @param runs Number of runs for the test. + /// @param iterations Number of iterations per run. + /// @param testFactory Test factory implementation for the test. + /// @returns a pointer to a @ref TestDescriptor instance + /// representing the given test. + static TestDescriptor* RegisterTest( + const char* fixtureName, + const char* testName, + std::size_t runs, + std::size_t iterations, + TestFactory* testFactory, + TestParametersDescriptor parameters + ) + { + // Determine if the test has been disabled. + static const char* disabledPrefix = "DISABLED_"; + bool isDisabled = ((::strlen(testName) >= 9) && + (!::memcmp(testName, disabledPrefix, 9))); + + if (isDisabled) + testName += 9; + + // Add the descriptor. + TestDescriptor* descriptor = new TestDescriptor(fixtureName, + testName, + runs, + iterations, + testFactory, + parameters, + isDisabled); + + Instance()._tests.push_back(descriptor); + + return descriptor; + } + + + /// Add an outputter. + + /// @param outputter Outputter. The caller must ensure that the + /// outputter remains in existence for the entire benchmark run. + static void AddOutputter(Outputter& outputter) + { + Instance()._outputters.push_back(&outputter); + } + + + /// Apply a pattern filter to the tests. + + /// --gtest_filter-compatible pattern: + /// + /// https://code.google.com/p/googletest/wiki/AdvancedGuide + /// + /// @param pattern Filter pattern compatible with gtest. + static void ApplyPatternFilter(const char* pattern) + { + Benchmarker& instance = Instance(); + + // Split the filter at '-' if it exists. + const char* const dash = strchr(pattern, '-'); + + std::string positive; + std::string negative; + + if (dash == NULL) + positive = pattern; + else + { + positive = std::string(pattern, dash); + negative = std::string(dash + 1); + if (positive.empty()) + positive = "*"; + } + + // Iterate across all tests and test them against the patterns. + std::size_t index = 0; + while (index < instance._tests.size()) + { + TestDescriptor* desc = instance._tests[index]; + + if ((!FilterMatchesString(positive.c_str(), + desc->CanonicalName)) || + (FilterMatchesString(negative.c_str(), + desc->CanonicalName))) + { + instance._tests.erase( + instance._tests.begin() + + std::vector::difference_type(index) + ); + delete desc; + } + else + ++index; + } + } + + + /// Run all benchmarking tests. + static void RunAllTests() + { + ConsoleOutputter defaultOutputter; + std::vector defaultOutputters; + defaultOutputters.push_back(&defaultOutputter); + + Benchmarker& instance = Instance(); + std::vector& outputters = + (instance._outputters.empty() ? + defaultOutputters : + instance._outputters); + + // Get the tests for execution. + std::vector tests = instance.GetTests(); + + const std::size_t totalCount = tests.size(); + std::size_t disabledCount = 0; + + std::vector::const_iterator testsIt = + tests.begin(); + + while (testsIt != tests.end()) + { + if ((*testsIt)->IsDisabled) + ++disabledCount; + ++testsIt; + } + + const std::size_t enabledCount = totalCount - disabledCount; + + // Calibrate the tests. + const CalibrationModel calibrationModel = GetCalibrationModel(); + + // Begin output. + for (std::size_t outputterIndex = 0; + outputterIndex < outputters.size(); + outputterIndex++) + outputters[outputterIndex]->Begin(enabledCount, disabledCount); + + // Run through all the tests in ascending order. + std::size_t index = 0; + + while (index < tests.size()) + { + // Get the test descriptor. + TestDescriptor* descriptor = tests[index++]; + + // Check if test matches include filters + if (instance._include.size() > 0) + { + bool included = false; + std::string name = + descriptor->FixtureName + "." + + descriptor->TestName; + + for (std::size_t i = 0; i < instance._include.size(); i++) + { + if (name.find(instance._include[i]) != + std::string::npos) + { + included = true; + break; + } + } + + if (!included) + continue; + } + + // Check if test is not disabled. + if (descriptor->IsDisabled) + { + for (std::size_t outputterIndex = 0; + outputterIndex < outputters.size(); + outputterIndex++) + outputters[outputterIndex]->SkipDisabledTest( + descriptor->FixtureName, + descriptor->TestName, + descriptor->Parameters, + descriptor->Runs, + descriptor->Iterations + ); + + continue; + } + + // Describe the beginning of the run. + for (std::size_t outputterIndex = 0; + outputterIndex < outputters.size(); + outputterIndex++) + outputters[outputterIndex]->BeginTest( + descriptor->FixtureName, + descriptor->TestName, + descriptor->Parameters, + descriptor->Runs, + descriptor->Iterations + ); + + // Execute each individual run. + std::vector runTimes(descriptor->Runs); + uint64_t overheadCalibration = + calibrationModel.GetCalibration(descriptor->Iterations); + + std::size_t run = 0; + while (run < descriptor->Runs) + { + // Construct a test instance. + Test* test = descriptor->Factory->CreateTest(); + + // Run the test. + uint64_t time = test->Run(descriptor->Iterations); + + // Store the test time. + runTimes[run] = (time > overheadCalibration ? + time - overheadCalibration : + 0); + + // Dispose of the test instance. + delete test; + + ++run; + } + + // Calculate the test result. + TestResult testResult(runTimes, descriptor->Iterations); + + // Describe the end of the run. + for (std::size_t outputterIndex = 0; + outputterIndex < outputters.size(); + outputterIndex++) + outputters[outputterIndex]->EndTest( + descriptor->FixtureName, + descriptor->TestName, + descriptor->Parameters, + testResult + ); + + } + + // End output. + for (std::size_t outputterIndex = 0; + outputterIndex < outputters.size(); + outputterIndex++) + outputters[outputterIndex]->End(enabledCount, + disabledCount); + } + + + /// List tests. + static std::vector ListTests() + { + std::vector tests; + Benchmarker& instance = Instance(); + + std::size_t index = 0; + while (index < instance._tests.size()) + tests.push_back(instance._tests[index++]); + + return tests; + } + + + /// Shuffle tests. + + /// Randomly shuffles the order of tests. + static void ShuffleTests() + { + Benchmarker& instance = Instance(); + std::random_shuffle(instance._tests.begin(), + instance._tests.end()); + } + private: + /// Calibration model. + + /// Describes a linear calibration model for test runs. + struct CalibrationModel + { + public: + CalibrationModel(std::size_t scale, + uint64_t slope, + uint64_t yIntercept) + : Scale(scale), + Slope(slope), + YIntercept(yIntercept) + { + + } + + + /// Scale. + + /// Number of iterations per slope unit. + const std::size_t Scale; + + + /// Slope. + const uint64_t Slope; + + + /// Y-intercept; + const uint64_t YIntercept; + + + /// Get calibration value for a run. + int64_t GetCalibration(std::size_t iterations) const + { + return YIntercept + (iterations * Slope) / Scale; + } + }; + + + /// Private constructor. + Benchmarker() + { + + } + + + /// Private destructor. + ~Benchmarker() + { + // Release all test descriptors. + std::size_t index = _tests.size(); + while (index--) + delete _tests[index]; + } + + + /// Get the tests to be executed. + std::vector GetTests() const + { + std::vector tests; + + std::size_t index = 0; + while (index < _tests.size()) + tests.push_back(_tests[index++]); + + return tests; + } + + + /// Test if a filter matches a string. + + /// Adapted from gtest. All rights reserved by original authors. + static bool FilterMatchesString(const char* filter, + const std::string& str) + { + const char *patternStart = filter; + + while (true) + { + if (PatternMatchesString(patternStart, str.c_str())) + return true; + + // Finds the next pattern in the filter. + patternStart = strchr(patternStart, ':'); + + // Returns if no more pattern can be found. + if (!patternStart) + return false; + + // Skips the pattern separater (the ':' character). + patternStart++; + } + } + + + /// Test if pattern matches a string. + + /// Adapted from gtest. All rights reserved by original authors. + static bool PatternMatchesString(const char* pattern, const char *str) + { + switch (*pattern) + { + case '\0': + case ':': + return (*str == '\0'); + case '?': // Matches any single character. + return ((*str != '\0') && + (PatternMatchesString(pattern + 1, str + 1))); + case '*': // Matches any string (possibly empty) of characters. + return (((*str != '\0') && + (PatternMatchesString(pattern, str + 1))) || + (PatternMatchesString(pattern + 1, str))); + default: + return ((*pattern == *str) && + (PatternMatchesString(pattern + 1, str + 1))); + } + } + + + /// Get calibration model. + + /// Returns an average linear calibration model. + static CalibrationModel GetCalibrationModel() + { + // We perform a number of runs of varying iterations with an empty + // test body. The assumption here is, that the time taken for the + // test run is linear with regards to the number of iterations, ie. + // some constant overhead with a per-iteration overhead. This + // hypothesis has been manually validated by linear regression over + // sample data. + // + // In order to avoid losing too much precision, we are going to + // calibrate in terms of the overhead of some x n iterations, + // where n must be a sufficiently large number to produce some + // significant runtime. On a high-end 2012 Retina MacBook Pro with + // -O3 on clang-602.0.53 (LLVM 6.1.0) n = 1,000,000 produces + // run times of ~1.9 ms, which should be sufficiently precise. + // + // However, as the constant overhead is mostly related to + // retrieving the system clock, which under the same conditions + // clocks in at around 17 ms, we run the risk of winding up with + // a negative y-intercept if we do not fix the y-intercept. This + // intercept is therefore fixed by a large number of runs of 0 + // iterations. + ::hayai::Test* test = new Test(); + +#define HAYAI_CALIBRATION_INTERESECT_RUNS 10000 + +#define HAYAI_CALIBRATION_RUNS 10 +#define HAYAI_CALIBRATION_SCALE 1000000 +#define HAYAI_CALIBRATION_PPR 6 + + // Determine the intercept. + uint64_t + interceptSum = 0, + interceptMin = std::numeric_limits::min(), + interceptMax = 0; + + for (std::size_t run = 0; + run < HAYAI_CALIBRATION_INTERESECT_RUNS; + ++run) + { + uint64_t intercept = test->Run(0); + interceptSum += intercept; + if (intercept < interceptMin) + interceptMin = intercept; + if (intercept > interceptMax) + interceptMax = intercept; + } + + uint64_t interceptAvg = + interceptSum / HAYAI_CALIBRATION_INTERESECT_RUNS; + + // Produce a series of sample points. + std::vector x(HAYAI_CALIBRATION_RUNS * + HAYAI_CALIBRATION_PPR); + std::vector t(HAYAI_CALIBRATION_RUNS * + HAYAI_CALIBRATION_PPR); + + std::size_t point = 0; + + for (std::size_t run = 0; run < HAYAI_CALIBRATION_RUNS; ++run) + { +#define HAYAI_CALIBRATION_POINT(_x) \ + x[point] = _x; \ + t[point++] = \ + test->Run(_x * std::size_t(HAYAI_CALIBRATION_SCALE)) + + HAYAI_CALIBRATION_POINT(1); + HAYAI_CALIBRATION_POINT(2); + HAYAI_CALIBRATION_POINT(5); + HAYAI_CALIBRATION_POINT(10); + HAYAI_CALIBRATION_POINT(15); + HAYAI_CALIBRATION_POINT(20); + +#undef HAYAI_CALIBRATION_POINT + } + + // As we have a fixed y-intercept, b, the optimal slope for a line + // fitting the sample points will be + // $\frac {\sum_{i=1}^{n} x_n \cdot (y_n - b)} + // {\sum_{i=1}^{n} {x_n}^2}$. + uint64_t + sumProducts = 0, + sumXSquared = 0; + + std::size_t p = x.size(); + while (p--) + { + sumXSquared += x[p] * x[p]; + sumProducts += x[p] * (t[p] - interceptAvg); + } + + uint64_t slope = sumProducts / sumXSquared; + + delete test; + + return CalibrationModel(HAYAI_CALIBRATION_SCALE, + slope, + interceptAvg); + +#undef HAYAI_CALIBRATION_INTERESECT_RUNS + +#undef HAYAI_CALIBRATION_RUNS +#undef HAYAI_CALIBRATION_SCALE +#undef HAYAI_CALIBRATION_PPR + } + + + std::vector _outputters; ///< Registered outputters. + std::vector _tests; ///< Registered tests. + std::vector _include; ///< Test filters. + }; +} +#endif diff --git a/apps/bench/hayai/hayai_clock.hpp b/apps/bench/hayai/hayai_clock.hpp new file mode 100644 index 0000000000..33702c5a1a --- /dev/null +++ b/apps/bench/hayai/hayai_clock.hpp @@ -0,0 +1,362 @@ +// +// System-specific implementation of the clock functions. +// +// Copyright (C) 2011 Nick Bruun +// Copyright (C) 2013 Vlad Lazarenko +// Copyright (C) 2014 Nicolas Pauss +// +// Implementation notes: +// +// On Windows, QueryPerformanceCounter() is used. It gets +// real-time clock with up to nanosecond precision. +// +// On Apple (OS X, iOS), mach_absolute_time() is used. It gets +// CPU/bus dependent real-time clock with up to nanosecond precision. +// +// On Unix, gethrtime() is used with HP-UX and Solaris. Otherwise, +// clock_gettime() is used to access monotonic real-time clock +// with up to nanosecond precision. On kernels 2.6.28 and newer, the ticks +// are also raw and are not subject to NTP and/or adjtime(3) adjustments. +// +// Other POSIX compliant platforms resort to using gettimeofday(). It is +// subject to clock adjustments, does not allow for higher than microsecond +// resolution and is also declared obsolete by POSIX.1-2008. +// +// Note on C++11: +// +// Starting with C++11, we could use std::chrono. However, the details of +// what clock is really being used is implementation-specific. For example, +// Visual Studio 2012 defines "high_resolution_clock" as system clock with +// ~1 millisecond precision that is not acceptable for performance +// measurements. Therefore, we are much better off having full control of what +// mechanism we use to obtain the system clock. +// +// Note on durations: it is assumed that end times passed to the clock methods +// are all after the start time. Wrap-around of clocks is not tested, as +// nanosecond precision of unsigned 64-bit integers would require an uptime of +// almost 585 years for this to happen. Let's call ourselves safe on that one. +// +#ifndef __HAYAI_CLOCK +#define __HAYAI_CLOCK + +#include "hayai_compatibility.hpp" + + +// Win32 +#if defined(_WIN32) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +// Apple +#elif defined(__APPLE__) && defined(__MACH__) +#include + +// Unix +#elif defined(__unix__) || defined(__unix) || defined(unix) + +// gethrtime +# if (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) +# include + +// clock_gettime +# elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) +# include + +// gettimeofday +# else +# include + +# endif +#else +#error "Unable to define high resolution timer for an unknown OS." +#endif + +#include +#include + + +namespace hayai +{ +// Win32 +#if defined(_WIN32) + class Clock + { + public: + /// Time point. + + /// Opaque representation of a point in time. + typedef LARGE_INTEGER TimePoint; + + + /// Get the current time as a time point. + + /// @returns the current time point. + static TimePoint Now() + { + TimePoint result; + QueryPerformanceCounter(&result); + return result; + } + + + /// Get the duration between two time points. + + /// @param startTime Start time point. + /// @param endTime End time point. + /// @returns the number of nanoseconds elapsed between the two time + /// points. + static uint64_t Duration(const TimePoint& startTime, + const TimePoint& endTime) + { + const static double performanceFrequencyNs = + PerformanceFrequencyNs(); + + return static_cast( + (endTime.QuadPart - startTime.QuadPart) + * performanceFrequencyNs + ); + } + + + /// Clock implementation description. + + /// @returns a description of the clock implementation used. + static const char* Description() + { + return "QueryPerformanceCounter"; + } + private: + static double PerformanceFrequencyNs() + { + TimePoint result; + QueryPerformanceFrequency(&result); + return 1e9 / static_cast(result.QuadPart); + } + }; + +// Mach kernel. +#elif defined(__APPLE__) && defined(__MACH__) + class Clock + { + public: + /// Time point. + + /// Opaque representation of a point in time. + typedef uint64_t TimePoint; + + + /// Get the current time as a time point. + + /// @returns the current time point. + static TimePoint Now() __hayai_noexcept + { + return mach_absolute_time(); + } + + + /// Get the duration between two time points. + + /// @param startTime Start time point. + /// @param endTime End time point. + /// @returns the number of nanoseconds elapsed between the two time + /// points. + static uint64_t Duration(const TimePoint& startTime, + const TimePoint& endTime) __hayai_noexcept + { + mach_timebase_info_data_t time_info; + mach_timebase_info(&time_info); + + return (endTime - startTime) * time_info.numer / time_info.denom; + } + + + /// Clock implementation description. + + /// @returns a description of the clock implementation used. + static const char* Description() + { + return "mach_absolute_time"; + } + }; + +// Unix +#elif defined(__unix__) || defined(__unix) || defined(unix) + +// gethrtime +# if (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) + class Clock + { + public: + /// Time point. + + /// Opaque representation of a point in time. + typedef hrtime_t TimePoint; + + + /// Get the current time as a time point. + + /// @returns the current time point. + static TimePoint Now() __hayai_noexcept + { + return gethrtime(); + } + + + /// Get the duration between two time points. + + /// @param startTime Start time point. + /// @param endTime End time point. + /// @returns the number of nanoseconds elapsed between the two time + /// points. + static uint64_t Duration(const TimePoint& startTime, + const TimePoint& endTime) __hayai_noexcept + { + return static_cast(endTime - startTime); + } + + + /// Clock implementation description. + + /// @returns a description of the clock implementation used. + static const char* Description() + { + return "gethrtime"; + } + }; + + +// clock_gettime +# elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + class Clock + { + public: + /// Time point. + + /// Opaque representation of a point in time. + typedef struct timespec TimePoint; + + + /// Get the current time as a time point. + + /// @returns the current time point. + static TimePoint Now() __hayai_noexcept + { + TimePoint result; +# if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &result); +# elif defined(CLOCK_MONOTONIC) + clock_gettime(CLOCK_MONOTONIC, &result); +# elif defined(CLOCK_REALTIME) + clock_gettime(CLOCK_REALTIME, &result); +# else + clock_gettime((clockid_t)-1, &result); +# endif + return result; + } + + + /// Get the duration between two time points. + + /// @param startTime Start time point. + /// @param endTime End time point. + /// @returns the number of nanoseconds elapsed between the two time + /// points. + static uint64_t Duration(const TimePoint& startTime, + const TimePoint& endTime) __hayai_noexcept + { + TimePoint timeDiff; + + timeDiff.tv_sec = endTime.tv_sec - startTime.tv_sec; + if (endTime.tv_nsec < startTime.tv_nsec) + { + timeDiff.tv_nsec = endTime.tv_nsec + 1000000000LL - + startTime.tv_nsec; + timeDiff.tv_sec--; + } + else + timeDiff.tv_nsec = endTime.tv_nsec - startTime.tv_nsec; + + return static_cast(timeDiff.tv_sec * 1000000000L + + timeDiff.tv_nsec); + } + + + /// Clock implementation description. + + /// @returns a description of the clock implementation used. + static const char* Description() + { +# if defined(CLOCK_MONOTONIC_RAW) + return "clock_gettime(CLOCK_MONOTONIC_RAW)"; +# elif defined(CLOCK_MONOTONIC) + return "clock_gettime(CLOCK_MONOTONIC)"; +# elif defined(CLOCK_REALTIME) + return "clock_gettime(CLOCK_REALTIME)"; +# else + return "clock_gettime(-1)"; +# endif + } + }; + +// gettimeofday +# else + class Clock + { + public: + /// Time point. + + /// Opaque representation of a point in time. + typedef struct timeval TimePoint; + + + /// Get the current time as a time point. + + /// @returns the current time point. + static TimePoint Now() __hayai_noexcept + { + TimePoint result; + gettimeofday(&result, NULL); + return result; + } + + + /// Get the duration between two time points. + + /// @param startTime Start time point. + /// @param endTime End time point. + /// @returns the number of nanoseconds elapsed between the two time + /// points. + static uint64_t Duration(const TimePoint& startTime, + const TimePoint& endTime) __hayai_noexcept + { + TimePoint timeDiff; + + timeDiff.tv_sec = endTime.tv_sec - startTime.tv_sec; + if (endTime.tv_usec < startTime.tv_usec) + { + timeDiff.tv_usec = endTime.tv_usec + 1000000L - + startTime.tv_usec; + timeDiff.tv_sec--; + } + else + timeDiff.tv_usec = endTime.tv_usec - startTime.tv_usec; + + return static_cast(timeDiff.tv_sec * 1000000000LL + + timeDiff.tv_usec * 1000); + } + + + /// Clock implementation description. + + /// @returns a description of the clock implementation used. + static const char* Description() + { + return "gettimeofday"; + } + }; +# endif +#endif +} +#endif diff --git a/apps/bench/hayai/hayai_compatibility.hpp b/apps/bench/hayai/hayai_compatibility.hpp new file mode 100644 index 0000000000..8cb53072cf --- /dev/null +++ b/apps/bench/hayai/hayai_compatibility.hpp @@ -0,0 +1,10 @@ +#ifndef __HAYAI_COMPATIBILITY +#define __HAYAI_COMPATIBILITY + +# if __cplusplus > 201100L +# define __hayai_noexcept noexcept +# else +# define __hayai_noexcept +# endif + +#endif diff --git a/apps/bench/hayai/hayai_console.hpp b/apps/bench/hayai/hayai_console.hpp new file mode 100644 index 0000000000..6639890b0c --- /dev/null +++ b/apps/bench/hayai/hayai_console.hpp @@ -0,0 +1,199 @@ +#ifndef __HAYAI_CONSOLE +#define __HAYAI_CONSOLE + +#include + +#if !defined(HAYAI_NO_COLOR) +# if defined(_WIN32) +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +# else +# include +# include +# endif +#endif + + +namespace hayai +{ + /// Static helper class for outputting to a terminal/console. + class Console + { + public: + /// Console text colors. + enum TextColor + { + /// Default console color. Used for resets. + TextDefault, + + /// Black. + /// + /// @warning Avoid using black unless it is absolutely necesssary. + TextBlack, + + /// Blue. + TextBlue, + + /// Green. + TextGreen, + + /// Cyan. + TextCyan, + + /// Red. + TextRed, + + /// Purple. + TextPurple, + + /// Yellow. + TextYellow, + + /// White. + /// + /// @warning Avoid using white unless it is absolutely necessary. + TextWhite + }; + + + /// Get the singleton instance of @ref Console. + + /// @returns a reference to the singleton instance of the + /// benchmarker execution controller. + inline static Console& Instance() + { + static Console singleton; + return singleton; + } + + + /// Test if formatting is enabled. + inline static bool IsFormattingEnabled() + { + return Instance()._formattingEnabled; + } + + + /// Set whether formatting is enabled. + inline static void SetFormattingEnabled(bool enabled) + { + Instance()._formattingEnabled = enabled; + } + private: + inline Console() + : _formattingEnabled(true) + { + + } + + + bool _formattingEnabled; + }; + +#if defined(_WIN32) && !defined(HAYAI_NO_COLOR) // Windows + static inline WORD GetConsoleAttributes() + { + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), + &consoleInfo); + return consoleInfo.wAttributes; + } + + inline std::ostream& operator <<(std::ostream& stream, + const Console::TextColor& color) + { + static const WORD defaultConsoleAttributes = + GetConsoleAttributes(); + WORD newColor; + + if ((!Console::IsFormattingEnabled()) || + ((stream.rdbuf() != std::cout.rdbuf()) && + (stream.rdbuf() != std::cerr.rdbuf()))) + return stream; + + switch(color) + { + case Console::TextDefault: + newColor = defaultConsoleAttributes; + break; + case Console::TextBlack: + newColor = 0; + break; + case Console::TextBlue: + newColor = FOREGROUND_BLUE; + break; + case Console::TextGreen: + newColor = FOREGROUND_GREEN; + break; + case Console::TextCyan: + newColor = FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case Console::TextRed: + newColor = FOREGROUND_RED; + break; + case Console::TextPurple: + newColor = FOREGROUND_RED | FOREGROUND_BLUE; + break; + case Console::TextYellow: + newColor = + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_INTENSITY; + break; + case Console::TextWhite: + newColor = + FOREGROUND_RED | + FOREGROUND_GREEN | + FOREGROUND_BLUE | + FOREGROUND_INTENSITY; + break; + } + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), newColor); + return stream; + } +#elif !defined(HAYAI_NO_COLOR) // Linux or others + inline std::ostream& operator <<(std::ostream& stream, + const Console::TextColor& color) + { + static const bool outputNoColor = isatty(fileno(stdout)) != 1; + + if ((!Console::IsFormattingEnabled()) || + (outputNoColor) || + ((stream.rdbuf() != std::cout.rdbuf()) && + (stream.rdbuf() != std::cerr.rdbuf()))) + return stream; + + const char* value = ""; + switch(color) { + case Console::TextDefault: + value = "\033[m"; break; + case Console::TextBlack: + value = "\033[0;30m"; break; + case Console::TextBlue: + value = "\033[0;34m"; break; + case Console::TextGreen: + value = "\033[0;32m"; break; + case Console::TextCyan: + value = "\033[0;36m"; break; + case Console::TextRed: + value = "\033[0;31m"; break; + case Console::TextPurple: + value = "\033[0;35m"; break; + case Console::TextYellow: + value = "\033[0;33m"; break; + case Console::TextWhite: + value = "\033[0;37m"; break; + } + return stream << value; + } +#else // No color + inline std::ostream& operator <<(std::ostream& stream, + const Console::TextColor&) + { + return stream; + } +#endif +} +#endif diff --git a/apps/bench/hayai/hayai_console_outputter.hpp b/apps/bench/hayai/hayai_console_outputter.hpp new file mode 100644 index 0000000000..3f904f578f --- /dev/null +++ b/apps/bench/hayai/hayai_console_outputter.hpp @@ -0,0 +1,251 @@ +#ifndef __HAYAI_CONSOLEOUTPUTTER +#define __HAYAI_CONSOLEOUTPUTTER +#include "hayai_outputter.hpp" +#include "hayai_console.hpp" + + +namespace hayai +{ + /// Console outputter. + + /// Prints the result to standard output. + class ConsoleOutputter + : public Outputter + { + public: + /// Initialize console outputter. + + /// @param stream Output stream. Must exist for the entire duration of + /// the outputter's use. + ConsoleOutputter(std::ostream& stream = std::cout) + : _stream(stream) + { + + } + + + virtual void Begin(const std::size_t& enabledCount, + const std::size_t& disabledCount) + { + _stream << std::fixed; + _stream << Console::TextGreen << "[==========]" + << Console::TextDefault << " Running " + << enabledCount + << (enabledCount == 1 ? " benchmark." : " benchmarks"); + + if (disabledCount) + _stream << ", skipping " + << disabledCount + << (disabledCount == 1 ? + " benchmark." : + " benchmarks"); + else + _stream << "."; + + _stream << std::endl; + } + + + virtual void End(const std::size_t& executedCount, + const std::size_t& disabledCount) + { + _stream << Console::TextGreen << "[==========]" + << Console::TextDefault << " Ran " << executedCount + << (executedCount == 1 ? + " benchmark." : + " benchmarks"); + + if (disabledCount) + _stream << ", skipped " + << disabledCount + << (disabledCount == 1 ? + " benchmark." : + " benchmarks"); + else + _stream << "."; + + _stream << std::endl; + } + + + inline void BeginOrSkipTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount, + const bool skip) + { + if (skip) + _stream << Console::TextCyan << "[ DISABLED ]"; + else + _stream << Console::TextGreen << "[ RUN ]"; + + _stream << Console::TextYellow << " "; + WriteTestNameToStream(_stream, fixtureName, testName, parameters); + _stream << Console::TextDefault + << " (" << runsCount + << (runsCount == 1 ? " run, " : " runs, ") + << iterationsCount + << (iterationsCount == 1 ? + " iteration per run)" : + " iterations per run)") + << std::endl; + } + + + virtual void BeginTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) + { + BeginOrSkipTest(fixtureName, + testName, + parameters, + runsCount, + iterationsCount, + false); + } + + + virtual void SkipDisabledTest( + const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount + ) + { + BeginOrSkipTest(fixtureName, + testName, + parameters, + runsCount, + iterationsCount, + true); + } + + + virtual void EndTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const TestResult& result) + { +#define PAD(x) _stream << std::setw(34) << x << std::endl; +#define PAD_DEVIATION(description, \ + deviated, \ + average, \ + unit) \ + { \ + double _d_ = \ + double(deviated) - double(average); \ + \ + PAD(description << \ + deviated << " " << unit << " (" << \ + (deviated < average ? \ + Console::TextRed : \ + Console::TextGreen) << \ + (deviated > average ? "+" : "") << \ + _d_ << " " << unit << " / " << \ + (deviated > average ? "+" : "") << \ + (_d_ * 100.0 / average) << " %" << \ + Console::TextDefault << ")"); \ + } +#define PAD_DEVIATION_INVERSE(description, \ + deviated, \ + average, \ + unit) \ + { \ + double _d_ = \ + double(deviated) - double(average); \ + \ + PAD(description << \ + deviated << " " << unit << " (" << \ + (deviated > average ? \ + Console::TextRed : \ + Console::TextGreen) << \ + (deviated > average ? "+" : "") << \ + _d_ << " " << unit << " / " << \ + (deviated > average ? "+" : "") << \ + (_d_ * 100.0 / average) << " %" << \ + Console::TextDefault << ")"); \ + } + + _stream << Console::TextGreen << "[ DONE ]" + << Console::TextYellow << " "; + WriteTestNameToStream(_stream, fixtureName, testName, parameters); + _stream << Console::TextDefault << " (" + << std::setprecision(6) + << (result.TimeTotal() / 1000000.0) << " ms)" + << std::endl; + + _stream << Console::TextBlue << "[ RUNS ] " + << Console::TextDefault + << " Average time: " + << std::setprecision(3) + << result.RunTimeAverage() / 1000.0 << " us" + << std::endl; + + PAD_DEVIATION_INVERSE("Fastest: ", + (result.RunTimeMinimum() / 1000.0), + (result.RunTimeAverage() / 1000.0), + "us"); + PAD_DEVIATION_INVERSE("Slowest: ", + (result.RunTimeMaximum() / 1000.0), + (result.RunTimeAverage() / 1000.0), + "us"); + + _stream << std::setprecision(5); + + PAD(""); + PAD("Average performance: " << + result.RunsPerSecondAverage() << " runs/s"); + PAD_DEVIATION("Best performance: ", + result.RunsPerSecondMaximum(), + result.RunsPerSecondAverage(), + "runs/s"); + PAD_DEVIATION("Worst performance: ", + result.RunsPerSecondMinimum(), + result.RunsPerSecondAverage(), + "runs/s"); + + _stream << Console::TextBlue << "[ITERATIONS] " + << Console::TextDefault + << std::setprecision(3) + << " Average time: " + << result.IterationTimeAverage() / 1000.0 << " us" + << std::endl; + + PAD_DEVIATION_INVERSE("Fastest: ", + (result.IterationTimeMinimum() / 1000.0), + (result.IterationTimeAverage() / 1000.0), + "us"); + PAD_DEVIATION_INVERSE("Slowest: ", + (result.IterationTimeMaximum() / 1000.0), + (result.IterationTimeAverage() / 1000.0), + "us"); + + _stream << std::setprecision(5); + + PAD(""); + PAD("Average performance: " << + result.IterationsPerSecondAverage() << + " iterations/s"); + PAD_DEVIATION("Best performance: ", + (result.IterationsPerSecondMaximum()), + (result.IterationsPerSecondAverage()), + "iterations/s"); + PAD_DEVIATION("Worst performance: ", + (result.IterationsPerSecondMinimum()), + (result.IterationsPerSecondAverage()), + "iterations/s"); + +#undef PAD_DEVIATION_INVERSE +#undef PAD_DEVIATION +#undef PAD + } + + + std::ostream& _stream; + }; +} +#endif diff --git a/apps/bench/hayai/hayai_default_test_factory.hpp b/apps/bench/hayai/hayai_default_test_factory.hpp new file mode 100644 index 0000000000..34f66ac108 --- /dev/null +++ b/apps/bench/hayai/hayai_default_test_factory.hpp @@ -0,0 +1,27 @@ +#ifndef __HAYAI_DEFAULTTESTFACTORY +#define __HAYAI_DEFAULTTESTFACTORY +#include "hayai_test_factory.hpp" + +namespace hayai +{ + /// Default test factory implementation. + + /// Simply constructs an instance of a the test of class @ref T with no + /// constructor parameters. + /// + /// @tparam T Test class. + template + class TestFactoryDefault + : public TestFactory + { + public: + /// Create a test instance with no constructor parameters. + + /// @returns a pointer to an initialized test. + virtual Test* CreateTest() + { + return new T(); + } + }; +} +#endif diff --git a/apps/bench/hayai/hayai_fixture.hpp b/apps/bench/hayai/hayai_fixture.hpp new file mode 100644 index 0000000000..315c234d56 --- /dev/null +++ b/apps/bench/hayai/hayai_fixture.hpp @@ -0,0 +1,9 @@ +#ifndef __HAYAI_FIXTURE +#define __HAYAI_FIXTURE +#include "hayai_test.hpp" + +namespace hayai +{ + typedef Test Fixture; +} +#endif diff --git a/apps/bench/hayai/hayai_json_outputter.hpp b/apps/bench/hayai/hayai_json_outputter.hpp new file mode 100644 index 0000000000..6637564966 --- /dev/null +++ b/apps/bench/hayai/hayai_json_outputter.hpp @@ -0,0 +1,333 @@ +#ifndef __HAYAI_JSONOUTPUTTER +#define __HAYAI_JSONOUTPUTTER +#include +#include + +#include "hayai_outputter.hpp" + + +#define JSON_OBJECT_BEGIN "{" +#define JSON_OBJECT_END "}" +#define JSON_ARRAY_BEGIN "[" +#define JSON_ARRAY_END "]" +#define JSON_STRING_BEGIN "\"" +#define JSON_STRING_END "\"" +#define JSON_NAME_SEPARATOR ":" +#define JSON_VALUE_SEPARATOR "," +#define JSON_TRUE "true" +#define JSON_FALSE "false" + +namespace hayai +{ + /// JSON outputter. + + /// Outputs the result of benchmarks in JSON format with the following + /// structure: + /// + /// { + /// "format_version": 1, + /// "benchmarks": [{ + /// "fixture": "DeliveryMan", + /// "name": "DeliverPackage", + /// "parameters": { + /// "declaration": "std::size_t distance", + /// "value": "1" + /// }, + /// "iterations_per_run": 10, + /// "disabled": false, + /// "runs": [{ + /// "duration": 3801.889831 + /// }, ..] + /// }, { + /// "fixture": "DeliveryMan", + /// "name": "DisabledTest", + /// "iterations_per_run": 10, + /// "disabled": true + /// }, ..] + /// } + /// + /// All durations are represented as milliseconds. + class JsonOutputter + : public Outputter + { + public: + /// Initialize JSON outputter. + + /// @param stream Output stream. Must exist for the entire duration of + /// the outputter's use. + JsonOutputter(std::ostream& stream) + : _stream(stream), + _firstTest(true) + { + + } + + + virtual void Begin(const std::size_t& enabledCount, + const std::size_t& disabledCount) + { + (void)enabledCount; + (void)disabledCount; + + _stream << + JSON_OBJECT_BEGIN + + JSON_STRING_BEGIN "format_version" JSON_STRING_END + JSON_NAME_SEPARATOR + "1" + + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "benchmarks" JSON_STRING_END + JSON_NAME_SEPARATOR + JSON_ARRAY_BEGIN; + } + + + virtual void End(const std::size_t& executedCount, + const std::size_t& disabledCount) + { + (void)executedCount; + (void)disabledCount; + + _stream << + JSON_ARRAY_END + JSON_OBJECT_END; + } + + + virtual void BeginTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) + { + BeginTestObject(fixtureName, + testName, + parameters, + runsCount, + iterationsCount, + false); + } + + + virtual void SkipDisabledTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) + { + BeginTestObject(fixtureName, + testName, + parameters, + runsCount, + iterationsCount, + true); + EndTestObject(); + } + + + virtual void EndTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const TestResult& result) + { + (void)fixtureName; + (void)testName; + (void)parameters; + + _stream << + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "runs" JSON_STRING_END + JSON_NAME_SEPARATOR + JSON_ARRAY_BEGIN; + + const std::vector& runTimes = result.RunTimes(); + + for (std::vector::const_iterator it = runTimes.begin(); + it != runTimes.end(); + ++it) + { + if (it != runTimes.begin()) + _stream << JSON_VALUE_SEPARATOR; + + _stream << JSON_OBJECT_BEGIN + + JSON_STRING_BEGIN "duration" JSON_STRING_END + JSON_NAME_SEPARATOR + << std::fixed + << std::setprecision(6) + << (double(*it) / 1000000.0) + << JSON_OBJECT_END; + } + + _stream << + JSON_ARRAY_END; + + EndTestObject(); + } + private: + void BeginTestObject(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount, + bool disabled) + { + (void)runsCount; + + if (_firstTest) + _firstTest = false; + else + _stream << JSON_VALUE_SEPARATOR; + + _stream << + JSON_OBJECT_BEGIN + + JSON_STRING_BEGIN "fixture" JSON_STRING_END + JSON_NAME_SEPARATOR; + + WriteString(fixtureName); + + _stream << + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "name" JSON_STRING_END + JSON_NAME_SEPARATOR; + + WriteString(testName); + + _stream << + JSON_VALUE_SEPARATOR; + + const std::vector& descs = + parameters.Parameters(); + + if (!descs.empty()) + { + _stream << + JSON_STRING_BEGIN "parameters" JSON_STRING_END + JSON_NAME_SEPARATOR + JSON_ARRAY_BEGIN; + + for (std::size_t i = 0; i < descs.size(); ++i) + { + if (i) + _stream << JSON_VALUE_SEPARATOR; + + const TestParameterDescriptor& desc = descs[i]; + + _stream << + JSON_OBJECT_BEGIN + + JSON_STRING_BEGIN "declaration" JSON_STRING_END + JSON_NAME_SEPARATOR; + + WriteString(desc.Declaration); + + _stream << + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "value" JSON_STRING_END + JSON_NAME_SEPARATOR; + + WriteString(desc.Value); + + _stream << + JSON_OBJECT_END; + } + + _stream << + JSON_ARRAY_END + JSON_VALUE_SEPARATOR; + } + + _stream << + JSON_STRING_BEGIN "iterations_per_run" JSON_STRING_END + JSON_NAME_SEPARATOR << iterationsCount << + + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "disabled" JSON_STRING_END + JSON_NAME_SEPARATOR << (disabled ? JSON_TRUE : JSON_FALSE); + } + + + inline void EndTestObject() + { + _stream << + JSON_OBJECT_END; + } + + + /// Write an escaped string. + + /// The escaping is currently very rudimentary and assumes that names, + /// parameters etc. are ASCII. + /// + /// @param str String to write. + void WriteString(const std::string& str) + { + _stream << JSON_STRING_BEGIN; + + std::string::const_iterator it = str.begin(); + while (it != str.end()) + { + char c = *it++; + + switch (c) + { + case '\\': + case '"': + case '/': + _stream << "\\" << c; + break; + + case '\b': + _stream << "\\b"; + break; + + case '\f': + _stream << "\\f"; + break; + + case '\n': + _stream << "\\n"; + break; + + case '\r': + _stream << "\\r"; + break; + + case '\t': + _stream << "\\t"; + break; + + default: + _stream << c; + break; + } + } + + _stream << JSON_STRING_END; + } + + + std::ostream& _stream; + bool _firstTest; + }; +} + +#undef JSON_OBJECT_BEGIN +#undef JSON_OBJECT_END +#undef JSON_ARRAY_BEGIN +#undef JSON_ARRAY_END +#undef JSON_STRING_BEGIN +#undef JSON_STRING_END +#undef JSON_NAME_SEPARATOR +#undef JSON_VALUE_SEPARATOR +#undef JSON_TRUE +#undef JSON_FALSE + +#endif diff --git a/apps/bench/hayai/hayai_junit_xml_outputter.hpp b/apps/bench/hayai/hayai_junit_xml_outputter.hpp new file mode 100644 index 0000000000..e7e116483c --- /dev/null +++ b/apps/bench/hayai/hayai_junit_xml_outputter.hpp @@ -0,0 +1,260 @@ +#ifndef __HAYAI_JUNITXMLOUTPUTTER +#define __HAYAI_JUNITXMLOUTPUTTER +#include +#include +#include +#include +#include + +#include "hayai_outputter.hpp" + + +namespace hayai +{ + /// JUnit-compatible XML outputter. + class JUnitXmlOutputter + : public Outputter + { + private: + /// Test case. + class TestCase + { + public: + TestCase(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const TestResult* result) + { + // Derive a pretty name. + std::stringstream nameStream; + WriteTestNameToStream(nameStream, + fixtureName, + testName, + parameters); + Name = nameStream.str(); + + // Derive the result. + Skipped = !result; + + if (result) + { + std::stringstream timeStream; + timeStream << std::fixed + << std::setprecision(9) + << (result->IterationTimeAverage() / 1e9); + Time = timeStream.str(); + } + } + + + std::string Name; + std::string Time; + bool Skipped; + }; + + + /// Test suite map. + typedef std::map > TestSuiteMap; + public: + /// Initialize outputter. + + /// @param stream Output stream. Must exist for the entire duration of + /// the outputter's use. + JUnitXmlOutputter(std::ostream& stream) + : _stream(stream) + { + + } + + + virtual void Begin(const std::size_t& enabledCount, + const std::size_t& disabledCount) + { + (void)enabledCount; + (void)disabledCount; + } + + + virtual void End(const std::size_t& executedCount, + const std::size_t& disabledCount) + { + (void)executedCount; + (void)disabledCount; + + // Write the header. + _stream << "" + << std::endl + << "" << std::endl; + + // Write out each test suite (fixture.) + for (TestSuiteMap::iterator testSuiteIt = _testSuites.begin(); + testSuiteIt != _testSuites.end(); + ++testSuiteIt) + { + _stream << " first + << "\" tests=\"" << testSuiteIt->second.size() << "\">" + << std::endl; + + // Write out each test case. + for (std::vector::iterator testCaseIt = + testSuiteIt->second.begin(); + testCaseIt != testSuiteIt->second.end(); + ++testCaseIt) + { + _stream << " Name); + _stream << "\""; + + if (!testCaseIt->Skipped) + _stream << " time=\"" << testCaseIt->Time << "\" />" + << std::endl; + else + { + _stream << ">" << std::endl + << " " << std::endl + << " " << std::endl; + } + } + + _stream << " " << std::endl; + } + + _stream << "" << std::endl; + } + + + virtual void BeginTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) + { + (void)fixtureName; + (void)testName; + (void)parameters; + (void)runsCount; + (void)iterationsCount; + } + + + virtual void SkipDisabledTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) + { + (void)fixtureName; + (void)testName; + (void)parameters; + (void)runsCount; + (void)iterationsCount; + } + + + virtual void EndTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const TestResult& result) + { + (void)fixtureName; + (void)testName; + (void)parameters; + + TestSuiteMap::iterator fixtureIt = + _testSuites.find(fixtureName); + if (fixtureIt == _testSuites.end()) + { + _testSuites[fixtureName] = std::vector(); + fixtureIt = _testSuites.find(fixtureName); + } + + std::vector& testCases = fixtureIt->second; + + testCases.push_back(TestCase(fixtureName, + testName, + parameters, + &result)); + + /* + _stream << + JSON_VALUE_SEPARATOR + + JSON_STRING_BEGIN "runs" JSON_STRING_END + JSON_NAME_SEPARATOR + JSON_ARRAY_BEGIN; + + const std::vector& runTimes = result.RunTimes(); + + for (std::vector::const_iterator it = runTimes.begin(); + it != runTimes.end(); + ++it) + { + if (it != runTimes.begin()) + _stream << JSON_VALUE_SEPARATOR; + + _stream << JSON_OBJECT_BEGIN + + JSON_STRING_BEGIN "duration" JSON_STRING_END + JSON_NAME_SEPARATOR + << std::fixed + << std::setprecision(6) + << (double(*it) / 1000000.0) + << JSON_OBJECT_END; + } + + _stream << + JSON_ARRAY_END; + + EndTestObject(); + */ + } + private: + /// Write an escaped string. + + /// The escaping is currently very rudimentary and assumes that names, + /// parameters etc. are ASCII. + /// + /// @param str String to write. + void WriteEscapedString(const std::string& str) + { + std::string::const_iterator it = str.begin(); + while (it != str.end()) + { + char c = *it++; + + switch (c) + { + case '"': + _stream << """; + break; + + case '\'': + _stream << "'"; + break; + + case '<': + _stream << "<"; + break; + + case '>': + _stream << ">"; + break; + + case '&': + _stream << "&"; + break; + + default: + _stream << c; + break; + } + } + } + + + std::ostream& _stream; + TestSuiteMap _testSuites; + }; +} + +#endif diff --git a/apps/bench/hayai/hayai_outputter.hpp b/apps/bench/hayai/hayai_outputter.hpp new file mode 100644 index 0000000000..94055b2b1b --- /dev/null +++ b/apps/bench/hayai/hayai_outputter.hpp @@ -0,0 +1,113 @@ +#ifndef __HAYAI_OUTPUTTER +#define __HAYAI_OUTPUTTER +#include +#include + +#include "hayai_test_result.hpp" + + +namespace hayai +{ + /// Outputter. + + /// Abstract base class for outputters. + class Outputter + { + public: + /// Begin benchmarking. + + /// The total number of benchmarks registred is the sum of the two + /// counts passed to the outputter. + /// + /// @param enabledCount Number of benchmarks to be executed. + /// @param disabledCount Number of disabled benchmarks to be skipped. + virtual void Begin(const std::size_t& enabledCount, + const std::size_t& disabledCount) = 0; + + + /// End benchmarking. + + /// @param executedCount Number of benchmarks that have been executed. + /// @param disabledCount Number of benchmarks that have been skipped + /// because they are disabled. + virtual void End(const std::size_t& executedCount, + const std::size_t& disabledCount) = 0; + + + /// Begin benchmark test run. + + /// @param fixtureName Fixture name. + /// @param testName Test name. + /// @param parameters Test parameter description. + /// @param runsCount Number of runs to be executed. + /// @param iterationsCount Number of iterations per run. + virtual void BeginTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) = 0; + + + /// End benchmark test run. + + /// @param fixtureName Fixture name. + /// @param testName Test name. + /// @param parameters Test parameter description. + /// @param result Test result. + virtual void EndTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& parameters, + const TestResult& result) = 0; + + + /// Skip disabled benchmark test run. + + /// @param fixtureName Fixture name. + /// @param testName Test name. + /// @param parameters Test parameter description. + /// @param runsCount Number of runs to be executed. + /// @param iterationsCount Number of iterations per run. + virtual void SkipDisabledTest(const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& + parameters, + const std::size_t& runsCount, + const std::size_t& iterationsCount) = 0; + + + virtual ~Outputter() + { + + } + protected: + /// Write a nicely formatted test name to a stream. + static void WriteTestNameToStream(std::ostream& stream, + const std::string& fixtureName, + const std::string& testName, + const TestParametersDescriptor& + parameters) + { + stream << fixtureName << "." << testName; + + const std::vector& descs = + parameters.Parameters(); + + if (descs.empty()) + return; + + stream << "("; + + for (std::size_t i = 0; i < descs.size(); ++i) + { + if (i) + stream << ", "; + + const TestParameterDescriptor& desc = descs[i]; + stream << desc.Declaration << " = " << desc.Value; + } + + stream << ")"; + } + }; +} +#endif diff --git a/apps/bench/hayai/hayai_test.hpp b/apps/bench/hayai/hayai_test.hpp new file mode 100644 index 0000000000..36d25fdbc2 --- /dev/null +++ b/apps/bench/hayai/hayai_test.hpp @@ -0,0 +1,83 @@ +#ifndef __HAYAI_TEST +#define __HAYAI_TEST +#include + +#include "hayai_clock.hpp" +#include "hayai_test_result.hpp" + + +namespace hayai +{ + /// Base test class. + + /// @ref SetUp is invoked before each run, and @ref TearDown is invoked + /// once the run is finished. Iterations rely on the same fixture + /// for every run. + /// + /// The default test class does not contain any actual code in the + /// SetUp and TearDown methods, which means that tests can inherit + /// this class directly for non-fixture based benchmarking tests. + class Test + { + public: + /// Set up the testing fixture for execution of a run. + virtual void SetUp() + { + + } + + + /// Tear down the previously set up testing fixture after the + /// execution run. + virtual void TearDown() + { + + } + + + /// Run the test. + + /// @param iterations Number of iterations to gather data for. + /// @returns the number of nanoseconds the run took. + uint64_t Run(std::size_t iterations) + { + std::size_t iteration = iterations; + + // Set up the testing fixture. + SetUp(); + + // Get the starting time. + Clock::TimePoint startTime, endTime; + + startTime = Clock::Now(); + + // Run the test body for each iteration. + while (iteration--) + TestBody(); + + // Get the ending time. + endTime = Clock::Now(); + + // Tear down the testing fixture. + TearDown(); + + // Return the duration in nanoseconds. + return Clock::Duration(startTime, endTime); + } + + + virtual ~Test() + { + + } + protected: + /// Test body. + + /// Executed for each iteration the benchmarking test is run. + virtual void TestBody() + { + + } + }; +} +#endif diff --git a/apps/bench/hayai/hayai_test_descriptor.hpp b/apps/bench/hayai/hayai_test_descriptor.hpp new file mode 100644 index 0000000000..529744e847 --- /dev/null +++ b/apps/bench/hayai/hayai_test_descriptor.hpp @@ -0,0 +1,365 @@ +#ifndef __HAYAI_TESTDESCRIPTOR +#define __HAYAI_TESTDESCRIPTOR +#include +#include +#include +#include + +#include "hayai_test.hpp" +#include "hayai_test_factory.hpp" + + +namespace hayai +{ + /// Parameter declaration. + + /// Describes parameter type and name. + class TestParameterDescriptor + { + public: + TestParameterDescriptor(std::string declaration, + std::string value) + : Declaration(declaration), + Value(value) + { + + } + + + /// Declaration. + std::string Declaration; + + + /// Value. + std::string Value; + }; + + + /// Test parameters descriptor. + class TestParametersDescriptor + { + private: + /// Quoting state. + enum QuotingState + { + /// Unquoted. + Unquoted, + + + /// Single quoted. + SingleQuoted, + + + /// Double quoted. + DoubleQuoted + }; + + + /// Trimmed string. + + /// @param start Start character. + /// @param end Character one position beyond end. + inline static std::string TrimmedString(const char* start, + const char* end) + { + while (start < end) + { + if ((*start == ' ') || + (*start == '\r') || + (*start == '\n') || + (*start == '\t')) + ++start; + else + break; + } + + while (end > start) + { + const char c = *(end - 1); + + if ((c != ' ') && + (c != '\r') && + (c != '\n') && + (c != '\t')) + break; + + --end; + } + + return std::string(start, std::string::size_type(end - start)); + } + + + /// Parse comma separated parentherized value. + + /// @param separated Separated values as "(..[, ..])". + /// @returns the individual values with white space trimmed. + static std::vector + ParseCommaSeparated(const char* separated) + { + std::vector result; + + if (*separated) + ++separated; + + while ((*separated) && (*separated != ')')) + { + std::size_t escapeCounter = 0; + const char* start = separated; + QuotingState state = Unquoted; + bool escaped = false; + + while (*separated) + { + const char c = *separated++; + + if (state == Unquoted) + { + if ((c == '"') || (c == '\'')) + { + state = (c == '"' ? DoubleQuoted : SingleQuoted); + escaped = false; + } + else if ((c == '<') || + (c == '(') || + (c == '[') || + (c == '{')) + ++escapeCounter; + else if ((escapeCounter) && + ((c == '>') || + (c == ')') || + (c == ']') || + (c == '}'))) + --escapeCounter; + else if ((!escapeCounter) && + ((c == ',') || (c == ')'))) + { + result.push_back(TrimmedString(start, + separated - 1)); + break; + } + } + else + { + if (escaped) + escaped = false; + else if (c == '\\') + escaped = true; + else if (c == (state == DoubleQuoted ? '"' : '\'')) + state = Unquoted; + } + } + } + + return result; + } + + + /// Parse parameter declaration. + + /// @param raw Raw declaration. + TestParameterDescriptor ParseDescriptor(const std::string& raw) + { + const char* position = raw.c_str(); + + // Split the declaration into its declaration and its default + // type. + const char* equalPosition = NULL; + std::size_t escapeCounter = 0; + QuotingState state = Unquoted; + bool escaped = false; + + while (*position) + { + const char c = *position++; + + if (state == Unquoted) + { + if ((c == '"') || (c == '\'')) + { + state = (c == '"' ? DoubleQuoted : SingleQuoted); + escaped = false; + } + else if ((c == '<') || + (c == '(') || + (c == '[') || + (c == '{')) + ++escapeCounter; + else if ((escapeCounter) && + ((c == '>') || + (c == ')') || + (c == ']') || + (c == '}'))) + --escapeCounter; + else if ((!escapeCounter) && + (c == '=')) + { + equalPosition = position; + break; + } + } + else + { + if (escaped) + escaped = false; + else if (c == '\\') + escaped = true; + else if (c == (state == DoubleQuoted ? '"' : '\'')) + state = Unquoted; + } + } + + // Construct the parameter descriptor. + if (equalPosition) + { + const char* start = raw.c_str(); + const char* end = start + raw.length(); + + return TestParameterDescriptor( + std::string(TrimmedString(start, + equalPosition - 1)), + std::string(TrimmedString(equalPosition, + end)) + ); + } + else + return TestParameterDescriptor(raw, std::string()); + } + public: + TestParametersDescriptor() + { + + } + + + TestParametersDescriptor(const char* rawDeclarations, + const char* rawValues) + { + // Parse the declarations. + std::vector declarations = + ParseCommaSeparated(rawDeclarations); + + for (std::vector::const_iterator it = + declarations.begin(); + it != declarations.end(); + ++it) + _parameters.push_back(ParseDescriptor(*it)); + + // Parse the values. + std::vector values = ParseCommaSeparated(rawValues); + + std::size_t + straightValues = (_parameters.size() > values.size() ? + values.size() : + _parameters.size()), + variadicValues = 0; + + if (values.size() > _parameters.size()) + { + if (straightValues > 0) + --straightValues; + variadicValues = values.size() - _parameters.size() + 1; + } + + for (std::size_t i = 0; i < straightValues; ++i) + _parameters[i].Value = values[i]; + + if (variadicValues) + { + std::stringstream variadic; + + for (std::size_t i = 0; i < variadicValues; ++i) + { + if (i) + variadic << ", "; + variadic << values[straightValues + i]; + } + + _parameters[_parameters.size() - 1].Value = variadic.str(); + } + } + + + inline const std::vector& Parameters() const + { + return _parameters; + } + private: + std::vector _parameters; + }; + + + /// Test descriptor. + class TestDescriptor + { + public: + /// Initialize a new test descriptor. + + /// @param fixtureName Name of the fixture. + /// @param testName Name of the test. + /// @param runs Number of runs for the test. + /// @param iterations Number of iterations per run. + /// @param testFactory Test factory implementation for the test. + /// @param parameters Parametrized test parameters. + TestDescriptor(const char* fixtureName, + const char* testName, + std::size_t runs, + std::size_t iterations, + TestFactory* testFactory, + TestParametersDescriptor parameters, + bool isDisabled = false) + : FixtureName(fixtureName), + TestName(testName), + CanonicalName(std::string(fixtureName) + "." + testName), + Runs(runs), + Iterations(iterations), + Factory(testFactory), + Parameters(parameters), + IsDisabled(isDisabled) + { + + } + + + /// Dispose of a test descriptor. + ~TestDescriptor() + { + delete this->Factory; + } + + + /// Fixture name. + std::string FixtureName; + + + /// Test name. + std::string TestName; + + + /// Canonical name. + + /// As: .. + std::string CanonicalName; + + + /// Test runs. + std::size_t Runs; + + + /// Iterations per test run. + std::size_t Iterations; + + + /// Test factory. + TestFactory* Factory; + + + /// Parameters for parametrized tests + TestParametersDescriptor Parameters; + + + /// Disabled. + bool IsDisabled; + }; +} +#endif diff --git a/apps/bench/hayai/hayai_test_factory.hpp b/apps/bench/hayai/hayai_test_factory.hpp new file mode 100644 index 0000000000..1c5f469e0c --- /dev/null +++ b/apps/bench/hayai/hayai_test_factory.hpp @@ -0,0 +1,26 @@ +#ifndef __HAYAI_TESTFACTORY +#define __HAYAI_TESTFACTORY +#include "hayai_test.hpp" + +namespace hayai +{ + /// Base class for test factory implementations. + class TestFactory + { + public: + /// Virtual destructor + + /// Has no function in the base class. + virtual ~TestFactory() + { + + } + + + /// Creates a test instance to run. + + /// @returns a pointer to an initialized test. + virtual Test* CreateTest() = 0; + }; +} +#endif diff --git a/apps/bench/hayai/hayai_test_result.hpp b/apps/bench/hayai/hayai_test_result.hpp new file mode 100644 index 0000000000..6d8288bc1f --- /dev/null +++ b/apps/bench/hayai/hayai_test_result.hpp @@ -0,0 +1,153 @@ +#ifndef __HAYAI_TESTRESULT +#define __HAYAI_TESTRESULT +#include +#include +#include + +#include "hayai_clock.hpp" + + +namespace hayai +{ + /// Test result descriptor. + + /// All durations are expressed in nanoseconds. + struct TestResult + { + public: + /// Initialize test result descriptor. + + /// @param runTimes Timing for the individual runs. + /// @param iterations Number of iterations per run. + TestResult(const std::vector& runTimes, + std::size_t iterations) + : _runTimes(runTimes), + _iterations(iterations), + _timeTotal(0), + _timeRunMin(std::numeric_limits::max()), + _timeRunMax(std::numeric_limits::min()) + { + // Summarize under the assumption of values being accessed more + // than once. + std::vector::iterator runIt = _runTimes.begin(); + + while (runIt != _runTimes.end()) + { + const uint64_t run = *runIt; + + _timeTotal += run; + if ((runIt == _runTimes.begin()) || (run > _timeRunMax)) + _timeRunMax = run; + if ((runIt == _runTimes.begin()) || (run < _timeRunMin)) + _timeRunMin = run; + + ++runIt; + } + } + + + /// Total time. + inline double TimeTotal() const + { + return _timeTotal; + } + + + /// Run times. + inline const std::vector& RunTimes() const + { + return _runTimes; + } + + + /// Average time per run. + inline double RunTimeAverage() const + { + return double(_timeTotal) / double(_runTimes.size()); + } + + + /// Maximum time per run. + inline double RunTimeMaximum() const + { + return _timeRunMax; + } + + + /// Minimum time per run. + inline double RunTimeMinimum() const + { + return _timeRunMin; + } + + + /// Average runs per second. + inline double RunsPerSecondAverage() const + { + return 1000000000.0 / RunTimeAverage(); + } + + + /// Maximum runs per second. + inline double RunsPerSecondMaximum() const + { + return 1000000000.0 / _timeRunMin; + } + + + /// Minimum runs per second. + inline double RunsPerSecondMinimum() const + { + return 1000000000.0 / _timeRunMax; + } + + + /// Average time per iteration. + inline double IterationTimeAverage() const + { + return RunTimeAverage() / double(_iterations); + } + + + /// Minimum time per iteration. + inline double IterationTimeMinimum() const + { + return _timeRunMin / double(_iterations); + } + + + /// Maximum time per iteration. + inline double IterationTimeMaximum() const + { + return _timeRunMax / double(_iterations); + } + + + /// Average iterations per second. + inline double IterationsPerSecondAverage() const + { + return 1000000000.0 / IterationTimeAverage(); + } + + + /// Minimum iterations per second. + inline double IterationsPerSecondMinimum() const + { + return 1000000000.0 / IterationTimeMaximum(); + } + + + /// Maximum iterations per second. + inline double IterationsPerSecondMaximum() const + { + return 1000000000.0 / IterationTimeMinimum(); + } + private: + std::vector _runTimes; + std::size_t _iterations; + uint64_t _timeTotal; + uint64_t _timeRunMin; + uint64_t _timeRunMax; + }; +} +#endif diff --git a/apps/bench/simple_outputter.hpp b/apps/bench/simple_outputter.hpp new file mode 100644 index 0000000000..6e19d3fefe --- /dev/null +++ b/apps/bench/simple_outputter.hpp @@ -0,0 +1,72 @@ +#ifndef __HAYAI_SIMPLEOUTPUTTER +#define __HAYAI_SIMPLEOUTPUTTER +#include "hayai/hayai_outputter.hpp" +#include "hayai/hayai_console.hpp" + + +namespace hayai +{ + /// Console outputter. + + /// Prints the result to standard output. + class SimpleOutputter + : public Outputter + { + public: + /// Initialize console outputter. + + /// @param stream Output stream. Must exist for the entire duration of + /// the outputter's use. + SimpleOutputter(std::ostream& stream = std::cout) + : _stream(stream) + { + + } + + + void Begin(const std::size_t&, const std::size_t&) override + { + } + + + void End(const std::size_t&, const std::size_t&) override + { + } + + + void BeginTest(const std::string&, + const std::string&, + const TestParametersDescriptor&, + const std::size_t&, + const std::size_t&) override + { + } + + + void SkipDisabledTest(const std::string&, + const std::string&, + const TestParametersDescriptor&, + const std::size_t&, + const std::size_t&) override + { + } + + + void EndTest(const std::string&, + const std::string&, + const TestParametersDescriptor&, + const TestResult& result) override + { + _stream << Console::TextBlue + << Console::TextDefault + << std::setprecision(5) + << std::setw(9) + << result.IterationsPerSecondAverage() + << " fps"; + } + + + std::ostream& _stream; + }; +} +#endif diff --git a/modules/tachyon/CMakeLists.txt b/apps/common/commandline/CMakeLists.txt similarity index 60% rename from modules/tachyon/CMakeLists.txt rename to apps/common/commandline/CMakeLists.txt index 52e0513520..1f86bafb3c 100644 --- a/modules/tachyon/CMakeLists.txt +++ b/apps/common/commandline/CMakeLists.txt @@ -14,27 +14,27 @@ ## limitations under the License. ## ## ======================================================================== ## -OPTION(OSPRAY_MODULE_TACHYON "Build 'TACH' (tachyon model viewer)") +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}) -IF (OSPRAY_MODULE_TACHYON) - - CONFIGURE_OSPRAY() +OPTION(OSPRAY_COMMANDLINE_TACHYON_SUPPORT "Support Tachyon scene parser" ) +MARK_AS_ADVANCED(OSPRAY_COMMANDLINE_TACHYON_SUPPORT) +IF(OSPRAY_COMMANDLINE_TACHYON_SUPPORT) # ------------------------------------------------------- find_program(LEX_EXE flex) if(LEX_EXE STREQUAL "LEX_EXE-NOTFOUND") message(FATAL_ERROR "dear user, plase install flex!") - endif(LEX_EXE STREQUAL "LEX_EXE-NOTFOUND") + endif() find_program(YACC_EXE bison) if(YACC_EXE STREQUAL "YACC_EXE-NOTFOUND") message(FATAL_ERROR "dear user, plase install bison!") - endif(YACC_EXE STREQUAL "YACC_EXE-NOTFOUND") + endif() # reuseable cmake macro for yacc - MACRO(YACC_FILE _filename) - GET_FILENAME_COMPONENT(_basename ${_filename} NAME_WE) - ADD_CUSTOM_COMMAND( + macro(yacc_file _filename) + get_filename_component(_basename ${_filename} NAME_WE) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_basename}_y.cpp ${CMAKE_CURRENT_BINARY_DIR}/${_basename}_y.hpp COMMAND ${YACC_EXE} -d --debug --verbose @@ -42,46 +42,65 @@ IF (OSPRAY_MODULE_TACHYON) ${CMAKE_CURRENT_SOURCE_DIR}/${_filename} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_filename} ) - ENDMACRO(YACC_FILE) + endmacro() # reuseable cmake macro for lex - MACRO(LEX_FILE _filename) - GET_FILENAME_COMPONENT(_basename ${_filename} NAME_WE) - ADD_CUSTOM_COMMAND( + macro(lex_file _filename) + get_filename_component(_basename ${_filename} NAME_WE) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_basename}_l.cpp COMMAND ${LEX_EXE} -o${_basename}_l.cpp ${CMAKE_CURRENT_SOURCE_DIR}/${_filename} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_filename} ) - ENDMACRO(LEX_FILE) + endmacro() # ------------------------------------------------------- - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/) - INCLUDE_DIRECTORIES(${EMBREE_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}) - INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}/ospray) - INCLUDE_DIRECTORIES_ISPC(${EMBREE_INCLUDE_DIRS}) + yacc_file(SceneParser/tachyon/parser.yy) + lex_file(SceneParser/tachyon/lexer.ll) - YACC_FILE(parser.yy) - LEX_FILE(lexer.ll) + ADD_DEFINITIONS(-DOSPRAY_TACHYON_SUPPORT) +ENDIF() - OSPRAY_ADD_LIBRARY(ospray_module_tachyon SHARED - TachyonRenderer.cpp - TachyonRenderer.ispc - ) +SET(SOURCE_FILES + CommandLineExport.h + CommandLineParser.h + CameraParser.cpp + LightsParser.cpp + RendererParser.cpp + + SceneParser/SceneParser.h + SceneParser/MultiSceneParser.cpp + + SceneParser/particle/ParticleSceneParser.cpp + SceneParser/particle/Model.cpp + SceneParser/particle/uintah.cpp + + SceneParser/streamlines/StreamLineSceneParser.cpp - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray_module_tachyon ospray ospray_common) - - IF (NOT THIS_IS_MIC) - ADD_EXECUTABLE(ospTachyon - Viewer - Model - lexer_l.cpp - parser_y.cpp - Loc.cpp - ) - TARGET_LINK_LIBRARIES(ospTachyon ospray ospray_common ospray_glut3d) - ENDIF() + SceneParser/trianglemesh/TriangleMeshSceneParser.cpp + + SceneParser/volume/VolumeSceneParser.cpp + + Utility.h +) + +IF(OSPRAY_COMMANDLINE_TACHYON_SUPPORT) + LIST(APPEND SOURCE_FILES + SceneParser/tachyon/TachyonSceneParser.cpp + SceneParser/tachyon/Model.cpp + SceneParser/tachyon/Loc.cpp + lexer_l.cpp + parser_y.cpp + ) ENDIF() + +OSPRAY_CREATE_LIBRARY(commandline + ${SOURCE_FILES} +LINK + ospray_common + ospray_minisg + ospray_xml + ospray_importer + ospray_tfn +) diff --git a/apps/common/commandline/CameraParser.cpp b/apps/common/commandline/CameraParser.cpp new file mode 100644 index 0000000000..fac5366b9f --- /dev/null +++ b/apps/common/commandline/CameraParser.cpp @@ -0,0 +1,63 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "CameraParser.h" + +bool DefaultCameraParser::parse(int ac, const char **&av) +{ + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--camera" || arg == "-c") { + m_cameraType = av[++i]; + } else if (arg == "-vp" || arg == "--eye") { + auto &pos = m_eye; + pos.x = atof(av[++i]); + pos.y = atof(av[++i]); + pos.z = atof(av[++i]); + } else if (arg == "-vu" || arg == "--up") { + auto &up = m_up; + up.x = atof(av[++i]); + up.y = atof(av[++i]); + up.z = atof(av[++i]); + } else if (arg == "-vi" || arg == "--gaze") { + auto &at = m_gaze; + at.x = atof(av[++i]); + at.y = atof(av[++i]); + at.z = atof(av[++i]); + } + } + + finalize(); + + return true; +} + +ospray::cpp::Camera DefaultCameraParser::camera() +{ + return m_camera; +} + +void DefaultCameraParser::finalize() +{ + if (m_cameraType.empty()) + m_cameraType = "perspective"; + + m_camera = ospray::cpp::Camera(m_cameraType.c_str()); + m_camera.set("pos", m_eye); + m_camera.set("up", m_up); + m_camera.set("dir", m_gaze - m_eye); + m_camera.commit(); +} diff --git a/apps/common/commandline/CameraParser.h b/apps/common/commandline/CameraParser.h new file mode 100644 index 0000000000..fea3adc2a0 --- /dev/null +++ b/apps/common/commandline/CameraParser.h @@ -0,0 +1,51 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include + +class OSPRAY_COMMANDLINE_INTERFACE CameraParser : public CommandLineParser +{ +public: + virtual ~CameraParser() = default; + virtual ospray::cpp::Camera camera() = 0; +}; + +class OSPRAY_COMMANDLINE_INTERFACE DefaultCameraParser : public CameraParser +{ +public: + DefaultCameraParser() = default; + bool parse(int ac, const char **&av) override; + ospray::cpp::Camera camera() override; + +protected: + + std::string m_cameraType; + ospray::cpp::Camera m_camera; + + ospcommon::vec3f m_eye {-1, 1, -1}; + ospcommon::vec3f m_up { 1, -1, 1}; + ospcommon::vec3f m_gaze{ 0, 1, 0}; + +private: + + void finalize(); +}; diff --git a/ospray/math/operator_vec3T.ih b/apps/common/commandline/CommandLineExport.h similarity index 72% rename from ospray/math/operator_vec3T.ih rename to apps/common/commandline/CommandLineExport.h index ee613b8354..67277a5ce2 100644 --- a/ospray/math/operator_vec3T.ih +++ b/apps/common/commandline/CommandLineExport.h @@ -14,22 +14,14 @@ // limitations under the License. // // ======================================================================== // -#define __AWIDTH varying -#define __BWIDTH varying -#define __RWIDTH varying -#include "operator_vec3TW.ih" -#define __AWIDTH uniform -#define __BWIDTH varying -#define __RWIDTH varying -#include "operator_vec3TW.ih" -#define __AWIDTH varying -#define __BWIDTH uniform -#define __RWIDTH varying -#include "operator_vec3TW.ih" -#define __AWIDTH uniform -#define __BWIDTH uniform -#define __RWIDTH uniform -#include "operator_vec3TW.ih" +#pragma once -#undef __vecT -#undef __scaT +#ifdef _WIN32 +# ifdef ospray_commandline_EXPORTS +# define OSPRAY_COMMANDLINE_INTERFACE __declspec(dllexport) +# else +# define OSPRAY_COMMANDLINE_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPRAY_COMMANDLINE_INTERFACE +#endif diff --git a/ospray/math/operator_vec3.ih b/apps/common/commandline/CommandLineParser.h similarity index 84% rename from ospray/math/operator_vec3.ih rename to apps/common/commandline/CommandLineParser.h index 40411c6631..bfe5261799 100644 --- a/ospray/math/operator_vec3.ih +++ b/apps/common/commandline/CommandLineParser.h @@ -14,13 +14,13 @@ // limitations under the License. // // ======================================================================== // -#define __scaT float -#define __vecT vec3f -#include "operator_vec3T.ih" +#pragma once -#define __scaT int -#define __vecT vec3i -#include "operator_vec3T.ih" +#include -#undef __OPFCT -#undef __OP +class OSPRAY_COMMANDLINE_INTERFACE CommandLineParser +{ +public: + virtual ~CommandLineParser() {} + virtual bool parse(int ac, const char **&av) = 0; +}; diff --git a/apps/common/commandline/LightsParser.cpp b/apps/common/commandline/LightsParser.cpp new file mode 100644 index 0000000000..8b78850cc4 --- /dev/null +++ b/apps/common/commandline/LightsParser.cpp @@ -0,0 +1,111 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "LightsParser.h" + +#include + +#include + +#include + +using namespace ospcommon; + +DefaultLightsParser::DefaultLightsParser(ospray::cpp::Renderer renderer) : + m_renderer(renderer), + m_defaultDirLight_direction(.3, -1, -.2) +{ +} + +bool DefaultLightsParser::parse(int ac, const char **&av) +{ + std::vector lights; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--sun-dir") { + if (!strcmp(av[i+1],"none")) { + ++i; + m_defaultDirLight_direction = vec3f(0.f); + } else { + m_defaultDirLight_direction.x = atof(av[++i]); + m_defaultDirLight_direction.y = atof(av[++i]); + m_defaultDirLight_direction.z = atof(av[++i]); + } + } else if (arg == "--hdri-light") { + if (i+2 >= ac) + throw std::runtime_error("Not enough arguments! Usage:\n\t" + "--hdri-light .(pfm|ppm)"); + auto ospHdri = m_renderer.newLight("hdri"); + ospHdri.set("name", "hdri light"); + if (1) {//TODO: add commandline option for up direction. + ospHdri.set("up", 0.f, 1.f, 0.f); + ospHdri.set("dir", 1.f, 0.f, 0.0f); + } else {// up = z + ospHdri.set("up", 0.f, 0.f, 1.f); + ospHdri.set("dir", 0.f, 1.f, 0.0f); + } + ospHdri.set( "intensity", atof(av[++i])); + FileName imageFile(av[++i]); + ospray::miniSG::Texture2D *lightMap = ospray::miniSG::loadTexture(imageFile.path(), imageFile.base()); + if (lightMap == NULL){ + std::cout << "Failed to load hdri-light texture '" << imageFile << "'" << std::endl; + } else { + std::cout << "Successfully loaded hdri-light texture '" << imageFile << "'" << std::endl; + } + OSPTexture2D ospLightMap = ospray::miniSG::createTexture2D(lightMap); + ospHdri.set( "map", ospLightMap); + ospHdri.commit(); + lights.push_back(ospHdri.handle()); + } else if (arg == "--backplate") { + FileName imageFile(av[++i]); + ospray::miniSG::Texture2D *backplate = ospray::miniSG::loadTexture(imageFile.path(), imageFile.base()); + if (backplate == NULL){ + std::cout << "Failed to load backplate texture '" << imageFile << "'" << std::endl; + } + OSPTexture2D ospBackplate = ospray::miniSG::createTexture2D(backplate); + m_renderer.set("backplate", ospBackplate); + } + } + + //TODO: Need to figure out where we're going to read lighting data from + + if (m_defaultDirLight_direction != vec3f(0.f)) { + auto ospLight = m_renderer.newLight("directional"); + if (ospLight.handle() == nullptr) { + throw std::runtime_error("Failed to create a 'DirectionalLight'!"); + } + ospLight.set("name", "sun"); + ospLight.set("color", 1.f, 1.f, 1.f); + ospLight.set("direction", m_defaultDirLight_direction); + ospLight.set("angularDiameter", 0.53f); + ospLight.commit(); + lights.push_back(ospLight.handle()); + } + + auto lightArray = ospray::cpp::Data(lights.size(), OSP_OBJECT, lights.data()); + //lightArray.commit(); + m_renderer.set("lights", lightArray); + + finalize(); + + return true; +} + +void DefaultLightsParser::finalize() +{ + +} diff --git a/apps/common/commandline/LightsParser.h b/apps/common/commandline/LightsParser.h new file mode 100644 index 0000000000..49397573d3 --- /dev/null +++ b/apps/common/commandline/LightsParser.h @@ -0,0 +1,47 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE LightsParser : public CommandLineParser +{ +public: + virtual ~LightsParser() = default; +}; + +class OSPRAY_COMMANDLINE_INTERFACE DefaultLightsParser : public LightsParser +{ +public: + DefaultLightsParser(ospray::cpp::Renderer renderer); + bool parse(int ac, const char **&av) override; + +protected: + + ospray::cpp::Renderer m_renderer; + + /*! when using the OBJ renderer, we create a automatic dirlight with this + * direction; use ''--sun-dir x y z' to change */ + ospcommon::vec3f m_defaultDirLight_direction; +private: + + void finalize(); +}; diff --git a/apps/common/commandline/RendererParser.cpp b/apps/common/commandline/RendererParser.cpp new file mode 100644 index 0000000000..34846aad82 --- /dev/null +++ b/apps/common/commandline/RendererParser.cpp @@ -0,0 +1,61 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "RendererParser.h" + +bool DefaultRendererParser::parse(int ac, const char **&av) +{ + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--renderer" || arg == "-r") { + assert(i+1 < ac); + m_rendererType = av[++i]; + } else if (arg == "--spp" || arg == "-spp") { + m_spp = atoi(av[++i]); + } else if (arg == "--max-depth") { + maxDepth = atoi(av[++i]); + } + } + + finalize(); + + return true; +} + +ospray::cpp::Renderer DefaultRendererParser::renderer() +{ + return m_renderer; +} + +void DefaultRendererParser::finalize() +{ + if (m_rendererType.empty()) + m_rendererType = "scivis"; + + m_renderer = ospray::cpp::Renderer(m_rendererType.c_str()); + + // Set renderer defaults (if not using 'aoX' renderers) + if (m_rendererType[0] != 'a' && m_rendererType[1] != 'o') + { + m_renderer.set("aoSamples", 1); + m_renderer.set("shadowsEnabled", 1); + } + + m_renderer.set("spp", m_spp); + m_renderer.set("maxDepth", maxDepth); + + m_renderer.commit(); +} diff --git a/apps/common/commandline/RendererParser.h b/apps/common/commandline/RendererParser.h new file mode 100644 index 0000000000..007107db83 --- /dev/null +++ b/apps/common/commandline/RendererParser.h @@ -0,0 +1,50 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include + +class OSPRAY_COMMANDLINE_INTERFACE RendererParser : public CommandLineParser +{ +public: + virtual ~RendererParser() = default; + virtual ospray::cpp::Renderer renderer() = 0; +}; + +class OSPRAY_COMMANDLINE_INTERFACE DefaultRendererParser : public RendererParser +{ +public: + DefaultRendererParser() = default; + bool parse(int ac, const char **&av) override; + ospray::cpp::Renderer renderer() override; + +protected: + + std::string m_rendererType; + ospray::cpp::Renderer m_renderer; + + int m_spp{1}; + int maxDepth{5}; + +private: + + void finalize(); +}; diff --git a/apps/common/commandline/SceneParser/MultiSceneParser.cpp b/apps/common/commandline/SceneParser/MultiSceneParser.cpp new file mode 100644 index 0000000000..dc196e4dee --- /dev/null +++ b/apps/common/commandline/SceneParser/MultiSceneParser.cpp @@ -0,0 +1,92 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "MultiSceneParser.h" + +#include "particle/ParticleSceneParser.h" +#include "streamlines/StreamLineSceneParser.h" +#ifdef OSPRAY_TACHYON_SUPPORT +# include "tachyon/TachyonSceneParser.h" +#endif +#include "trianglemesh/TriangleMeshSceneParser.h" +#ifndef _WIN32 +# include "volume/VolumeSceneParser.h" +#endif + +using namespace ospray; +using namespace ospcommon; + +MultiSceneParser::MultiSceneParser(cpp::Renderer renderer) : + m_renderer(renderer) +{ +} + +bool MultiSceneParser::parse(int ac, const char **&av) +{ + TriangleMeshSceneParser triangleMeshParser(m_renderer); +#ifdef OSPRAY_TACHYON_SUPPORT + TachyonSceneParser tachyonParser(m_renderer); +#endif + ParticleSceneParser particleParser(m_renderer); + StreamLineSceneParser streamlineParser(m_renderer); +#ifndef _WIN32 + VolumeSceneParser volumeParser(m_renderer); +#endif + + bool gotTriangleMeshScene = triangleMeshParser.parse(ac, av); +#ifdef OSPRAY_TACHYON_SUPPORT + bool gotTachyonScene = tachyonParser.parse(ac, av); +#endif + bool gotPartileScene = particleParser.parse(ac, av); + bool gotStreamLineScene = streamlineParser.parse(ac, av); +#ifndef _WIN32 + bool gotVolumeScene = volumeParser.parse(ac, av); +#endif + + SceneParser *parser = nullptr; + + if (gotTriangleMeshScene) + parser = &triangleMeshParser; +#ifdef OSPRAY_TACHYON_SUPPORT + else if (gotTachyonScene) + parser = &tachyonParser; +#endif + else if (gotPartileScene) + parser = &particleParser; + else if (gotStreamLineScene) + parser = &streamlineParser; +#ifndef _WIN32 + else if (gotVolumeScene) + parser = &volumeParser; +#endif + + if (parser) { + m_model = parser->model(); + m_bbox = parser->bbox(); + } + + return parser != nullptr; +} + +cpp::Model MultiSceneParser::model() const +{ + return m_model; +} + +box3f MultiSceneParser::bbox() const +{ + return m_bbox; +} diff --git a/apps/common/commandline/SceneParser/MultiSceneParser.h b/apps/common/commandline/SceneParser/MultiSceneParser.h new file mode 100644 index 0000000000..ea37a8ebfd --- /dev/null +++ b/apps/common/commandline/SceneParser/MultiSceneParser.h @@ -0,0 +1,42 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE MultiSceneParser : public SceneParser +{ +public: + MultiSceneParser(ospray::cpp::Renderer); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +protected: + + ospray::cpp::Renderer m_renderer; + ospray::cpp::Model m_model; + ospcommon::box3f m_bbox; + +private: + + void finalize(); +}; diff --git a/apps/common/commandline/SceneParser/SceneParser.h b/apps/common/commandline/SceneParser/SceneParser.h new file mode 100644 index 0000000000..02760c9576 --- /dev/null +++ b/apps/common/commandline/SceneParser/SceneParser.h @@ -0,0 +1,30 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE SceneParser : public CommandLineParser +{ +public: + virtual ~SceneParser() = default; + virtual ospray::cpp::Model model() const = 0; + virtual ospcommon::box3f bbox() const = 0; +}; diff --git a/apps/particleViewer/Model.cpp b/apps/common/commandline/SceneParser/particle/Model.cpp similarity index 100% rename from apps/particleViewer/Model.cpp rename to apps/common/commandline/SceneParser/particle/Model.cpp diff --git a/apps/particleViewer/Model.h b/apps/common/commandline/SceneParser/particle/Model.h similarity index 98% rename from apps/particleViewer/Model.h rename to apps/common/commandline/SceneParser/particle/Model.h index 63c353f70b..74a3f8c46a 100644 --- a/apps/particleViewer/Model.h +++ b/apps/common/commandline/SceneParser/particle/Model.h @@ -20,8 +20,8 @@ // #include "common/OSPCommon.h" // // embree // #include "common/sys/filename.h" -#include "common/FileName.h" -#include "common/box.h" +#include "ospcommon/FileName.h" +#include "ospcommon/box.h" // stl #include #include diff --git a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp new file mode 100644 index 0000000000..8d7390c5aa --- /dev/null +++ b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp @@ -0,0 +1,189 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "ParticleSceneParser.h" + +// particle viewer +#include "Model.h" +#include "uintah.h" + +#include + +using namespace ospray; +using namespace ospcommon; + +// TODO: Need to convert ospray.h calls to ospray::cpp objects! + +// Helper functions /////////////////////////////////////////////////////////// + +particle::Model *createTestCube(int numPerSide) +{ + particle::Model *m = new particle::Model; + int type = m->getAtomType("testParticle"); + for (int z=0;zatom.push_back(a); + } + return m; +} + +OSPData makeMaterials(OSPRenderer renderer, particle::Model *model) +{ + int numMaterials = model->atomType.size(); + std::vector matArray(numMaterials); + for (int i = 0; i < numMaterials; i++) { + OSPMaterial mat = ospNewMaterial(renderer,"OBJMaterial"); + assert(mat); + ospSet3fv(mat,"kd",&model->atomType[i]->color.x); + ospCommit(mat); + matArray[i] = mat; + } + OSPData data = ospNewData(numMaterials,OSP_OBJECT,matArray.data()); + ospCommit(data); + return data; +} + +// Helper types /////////////////////////////////////////////////////////////// + +struct DeferredLoadJob { + DeferredLoadJob(particle::Model *model, + const FileName &xyzFileName, + const FileName &defFileName) + : model(model), xyzFileName(xyzFileName), defFileName(defFileName) + {} + + //! the mode we still have to load + particle::Model *model; + //! file name of xyz file to be loaded into this model + FileName xyzFileName; + //! name of atom type defintion file active when this xyz file was added + FileName defFileName; +}; + +// Class definitions ////////////////////////////////////////////////////////// + +ParticleSceneParser::ParticleSceneParser(cpp::Renderer renderer) : + m_renderer(renderer) +{ +} + +bool ParticleSceneParser::parse(int ac, const char **&av) +{ + bool loadedScene = false; + std::vector particleModel; + std::vector deferredLoadingListXYZ; + FileName defFileName = ""; + std::vector modelTimeStep; + int timeStep = 0; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--radius") { + ospray::particle::Model::defaultRadius = atof(av[++i]); + } else if (arg == "--atom-defs") { + defFileName = av[++i]; + } else if (arg == "--particle-timestep") { + timeStep = atoi(av[++i]); + } else { + FileName fn = arg; + if (fn.str() == "___CUBE_TEST___") { + int numPerSide = atoi(av[++i]); + particle::Model *m = createTestCube(numPerSide); + particleModel.push_back(m); + loadedScene = true; + } else if (fn.ext() == "xyz") { + particle::Model *m = new particle::Model; + deferredLoadingListXYZ.push_back(new DeferredLoadJob(m,fn,defFileName)); + particleModel.push_back(m); + loadedScene = true; + } else if (fn.ext() == "xyz2") { + particle::Model *m = new particle::Model; + m->loadXYZ2(fn); + particleModel.push_back(m); + loadedScene = true; +#if 1 // NOTE(jda) - The '.xml' file extension conflicts with RIVL files in + // TriangleMeshSceneParser...disabling here for now until the + // the problem requires a solution. + } +#else + } else if (fn.ext() == "xml") { + particle::Model *m = particle::parse__Uintah_timestep_xml(fn); + particleModel.push_back(m); + loadedScene = true; + } +#endif + } + } + + if (loadedScene) { + //TODO: this needs parallelized as it was in ospParticleViewer... + for (int i = 0; i < deferredLoadingListXYZ.size(); ++i) { + FileName defFileName = deferredLoadingListXYZ[i]->defFileName; + FileName xyzFileName = deferredLoadingListXYZ[i]->xyzFileName; + particle::Model *model = deferredLoadingListXYZ[i]->model; + + if (defFileName.str() != "") + model->readAtomTypeDefinitions(defFileName); + model->loadXYZ(xyzFileName); + } + + for (int i = 0; i < particleModel.size(); i++) { + OSPModel model = ospNewModel(); + OSPData materialData = makeMaterials(m_renderer.handle(), particleModel[i]); + + OSPData data = ospNewData(particleModel[i]->atom.size()*5,OSP_FLOAT, + &particleModel[i]->atom[0],OSP_DATA_SHARED_BUFFER); + ospCommit(data); + + OSPGeometry geom = ospNewGeometry("spheres"); + ospSet1i(geom,"bytes_per_sphere",sizeof(particle::Model::Atom)); + ospSet1i(geom,"offset_center",0); + ospSet1i(geom,"offset_radius",3*sizeof(float)); + ospSet1i(geom,"offset_materialID",4*sizeof(float)); + ospSetData(geom,"spheres",data); + ospSetData(geom,"materialList",materialData); + ospCommit(geom); + + ospAddGeometry(model,geom); + ospCommit(model); + + modelTimeStep.push_back(model); + } + + m_model = modelTimeStep[timeStep]; + m_bbox = particleModel[0]->getBBox(); + } + + return loadedScene; +} + +ospray::cpp::Model ParticleSceneParser::model() const +{ + return m_model; +} + +ospcommon::box3f ParticleSceneParser::bbox() const +{ + return m_bbox; +} + diff --git a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h new file mode 100644 index 0000000000..4277af7f5b --- /dev/null +++ b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h @@ -0,0 +1,38 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE ParticleSceneParser : public SceneParser +{ +public: + ParticleSceneParser(ospray::cpp::Renderer); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +private: + + ospray::cpp::Renderer m_renderer; + ospray::cpp::Model m_model; + ospcommon::box3f m_bbox; +}; diff --git a/apps/particleViewer/lexer.ll b/apps/common/commandline/SceneParser/particle/lexer.ll similarity index 100% rename from apps/particleViewer/lexer.ll rename to apps/common/commandline/SceneParser/particle/lexer.ll diff --git a/apps/particleViewer/uintah.cpp b/apps/common/commandline/SceneParser/particle/uintah.cpp similarity index 99% rename from apps/particleViewer/uintah.cpp rename to apps/common/commandline/SceneParser/particle/uintah.cpp index 62fa19e025..0d4a80f4b2 100644 --- a/apps/particleViewer/uintah.cpp +++ b/apps/common/commandline/SceneParser/particle/uintah.cpp @@ -16,10 +16,10 @@ #undef NDEBUG -#include "apps/common/xml/XML.h" +#include "common/xml/XML.h" #include "Model.h" // embree -#include "common/FileName.h" +#include "ospcommon/FileName.h" // std #include diff --git a/apps/particleViewer/uintah.h b/apps/common/commandline/SceneParser/particle/uintah.h similarity index 100% rename from apps/particleViewer/uintah.h rename to apps/common/commandline/SceneParser/particle/uintah.h diff --git a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp new file mode 100644 index 0000000000..53e5473e63 --- /dev/null +++ b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp @@ -0,0 +1,617 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "StreamLineSceneParser.h" + +#include "common/xml/XML.h" + +using namespace ospray; +using namespace ospcommon; + +#include +using std::cout; +using std::endl; + +// Helper types /////////////////////////////////////////////////////////////// + +struct Triangles { + std::vector vertex; + std::vector color; // vertex color, from sv's 'v' value + std::vector index; + + struct SVVertex { + float v; + vec3f pos; //float x,y,z; + }; + + struct SVTriangle { + SVVertex vertex[3]; + }; + + vec3f lerpf(float x, float x0,float x1,vec3f y0, vec3f y1) + { + float f = (x-x0)/(x1-x0); + return f*y1+(1-f)*y0; + } + vec3f colorOf(const float f) + { + if (f < .5f) + return vec3f(lerpf(f, 0.f,.5f,vec3f(0),vec3f(0,1,0))); + else + return vec3f(lerpf(f, .5f,1.f,vec3f(0,1,0),vec3f(1,0,0))); + } + void parseSV(const FileName &fn) + { + FILE *file = fopen(fn.str().c_str(),"rb"); + if (!file) return; + SVTriangle triangle; + while (fread(&triangle,sizeof(triangle),1,file)) { + index.push_back(vec3i(0,1,2)+vec3i(vertex.size())); + vertex.push_back(vec3fa(triangle.vertex[0].pos)); + vertex.push_back(vec3fa(triangle.vertex[1].pos)); + vertex.push_back(vec3fa(triangle.vertex[2].pos)); + color.push_back(colorOf(triangle.vertex[0].v)); + color.push_back(colorOf(triangle.vertex[1].v)); + color.push_back(colorOf(triangle.vertex[2].v)); + } + fclose(file); + } + + box3f getBounds() const + { + box3f bounds = empty; + for (int i=0;i vertex; + std::vector index; + float radius; + + StreamLines() : radius(0.001f) {} + + void parsePNT(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"r"); + if (!file) { + cout << "WARNING: could not open file " << fn << endl; + return; + } + vec3fa pnt; + static size_t totalSegments = 0; + size_t segments = 0; + // cout << "parsing file " << fn << ":" << std::flush; + int rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z); + vertex.push_back(pnt); + Assert(rc == 3); + while ((rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z)) == 3) { + index.push_back(vertex.size()-1); + vertex.push_back(pnt); + segments++; + } + totalSegments += segments; + fclose(file); + } + + + void parsePNTlist(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + char *eol = strstr(line,"\n"); if (eol) *eol = 0; + parsePNT(line); + } + fclose(file); + } + + void parseSLRAW(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"rb"); + + if (!file) { + cout << "WARNING: could not open file " << fn << endl; + return; + } + + int numStreamlines; + int rc = fread(&numStreamlines, sizeof(int), 1, file); + Assert(rc == 1); + + for(int s=0; s filePoints; + + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + radius = 99.f; + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + if (line[0] == '#') continue; + + vec3fa v; float f1; int ID, i, j; + sscanf(line,"%i %i %f %f %f %f %i\n",&ID,&i,&v.x,&v.y,&v.z,&f1,&j); + if (f1 < radius) + radius = f1; + filePoints.push_back(v); + + index.push_back(vertex.size()); + vertex.push_back(v); + vertex.push_back(filePoints[j-1]); + } + fclose(file); + } + + void parse(const FileName &fn) + { + if (fn.ext() == "pnt") + parsePNT(fn); + else if (fn.ext() == "pntlist") { + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + char *eol = strstr(line,"\n"); if (eol) *eol = 0; + parsePNT(line); + } + fclose(file); + } else if (fn.ext() == "slraw") { + parseSLRAW(fn); + } else + throw std::runtime_error("unknown input file format "+fn.str()); + } + box3f getBounds() const + { + box3f bounds = empty; + for (int i=0;i spheres[3]; + std::vector cylinders[3]; + box3f bounds; + + void parse(const FileName &fn) + { + std::vector filePoints; + + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + bounds = empty; + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + if (line[0] == '#') continue; + + vec3f v; float radius; int id, parent, type; + sscanf(line,"%i %i %f %f %f %f %i\n", + &id, &type, &v.x, &v.y, &v.z, &radius, &parent); + filePoints.push_back(v); + Assert(filePoints.size() == id); // assumes index-1==id + bounds.extend(v); // neglects radius + + if (parent == -1) // root soma, just one sphere + spheres[0].push_back(Sphere{v, radius}); + else { // cylinder with sphere at end + int idx; + switch (type) { + case 3: idx = 1; break; // basal dendrite + case 4: idx = 2; break; // apical dendrite + default: idx = 0; break; // soma / axon + } + spheres[idx].push_back(Sphere{v, radius}); + cylinders[idx].push_back(Cylinder{filePoints[parent-1], v, radius}); + } + } + fclose(file); + } + + box3f getBounds() const + { + return bounds; + } +}; + +void osxParseInts(std::vector &vec, const std::string &content) +{ + char *s = strdup(content.c_str()); + const char *delim = "\n\t\r "; + char *tok = strtok(s,delim); + while (tok) { + vec.push_back(atol(tok)); + tok = strtok(NULL,delim); + } + free(s); +} + +void osxParseVec3is(std::vector &vec, const std::string &content) +{ + char *s = strdup(content.c_str()); + const char *delim = "\n\t\r "; + char *tok = strtok(s,delim); + while (tok) { + vec3i v; + assert(tok); + v.x = atol(tok); + tok = strtok(NULL,delim); + + assert(tok); + v.y = atol(tok); + tok = strtok(NULL,delim); + + assert(tok); + v.z = atol(tok); + tok = strtok(NULL,delim); + + vec.push_back(v); + } + free(s); +} + +void osxParseVec3fas(std::vector &vec, const std::string &content) +{ + char *s = strdup(content.c_str()); + const char *delim = "\n\t\r "; + char *tok = strtok(s,delim); + while (tok) { + vec3fa v; + assert(tok); + v.x = atof(tok); + tok = strtok(NULL,delim); + + assert(tok); + v.y = atof(tok); + tok = strtok(NULL,delim); + + assert(tok); + v.z = atof(tok); + tok = strtok(NULL,delim); + + vec.push_back(v); + } + free(s); +} + +/*! parse ospray xml file */ +void parseOSX(StreamLines *streamLines, + Triangles *triangles, + const std::string &fn) +{ + xml::XMLDoc *doc = xml::readXML(fn); + assert(doc); + if (doc->child.size() != 1 || doc->child[0]->name != "OSPRay") + throw std::runtime_error("could not parse osx file: Not in OSPRay format!?"); + xml::Node *root_element = doc->child[0]; + for (int childID=0;childIDchild.size();childID++) { + xml::Node *node = root_element->child[childID]; + if (node->name == "Info") { + // ignore + continue; + } + + if (node->name == "Model") { + xml::Node *model_node = node; + for (int childID=0;childIDchild.size();childID++) { + xml::Node *node = model_node->child[childID]; + + if (node->name == "StreamLines") { + + xml::Node *sl_node = node; + for (int childID=0;childIDchild.size();childID++) { + xml::Node *node = sl_node->child[childID]; + if (node->name == "vertex") { + osxParseVec3fas(streamLines->vertex,node->content); + continue; + }; + if (node->name == "index") { + osxParseInts(streamLines->index,node->content); + continue; + }; + } + continue; + } + + if (node->name == "TriangleMesh") { + xml::Node *tris_node = node; + for (int childID=0;childIDchild.size();childID++) { + xml::Node *node = tris_node->child[childID]; + if (node->name == "vertex") { + osxParseVec3fas(triangles->vertex,node->content); + continue; + }; + if (node->name == "color") { + osxParseVec3fas(triangles->color,node->content); + continue; + }; + if (node->name == "index") { + osxParseVec3is(triangles->index,node->content); + continue; + }; + } + continue; + } + } + } + } +} + +void exportOSX(const char *fn,StreamLines *streamLines, Triangles *triangles) +{ + FILE *file = fopen(fn,"w"); + fprintf(file,"\n\n"); + fprintf(file,"\n"); + { + fprintf(file,"\n"); + { + fprintf(file,"\n"); + { + fprintf(file,"\n"); + for (int i=0;ivertex.size();i++) + fprintf(file,"%f %f %f\n", + streamLines->vertex[i].x, + streamLines->vertex[i].y, + streamLines->vertex[i].z); + fprintf(file,"\n"); + + fprintf(file,"\n"); + for (int i=0;iindex.size();i++) + fprintf(file,"%i ",streamLines->index[i]); + fprintf(file,"\n\n"); + } + fprintf(file,"\n"); + + + fprintf(file,"\n"); + { + fprintf(file,"\n"); + for (int i=0;ivertex.size();i++) + fprintf(file,"%f %f %f\n", + triangles->vertex[i].x, + triangles->vertex[i].y, + triangles->vertex[i].z); + fprintf(file,"\n"); + + fprintf(file,"\n"); + for (int i=0;icolor.size();i++) + fprintf(file,"%f %f %f\n", + triangles->color[i].x, + triangles->color[i].y, + triangles->color[i].z); + fprintf(file,"\n"); + + fprintf(file,"\n"); + for (int i=0;iindex.size();i++) + fprintf(file,"%i %i %i\n", + triangles->index[i].x, + triangles->index[i].y, + triangles->index[i].z); + fprintf(file,"\n"); + + } + fprintf(file,"\n"); + } + fprintf(file,"\n"); + } + fprintf(file,"\n"); + fclose(file); +} + +// Class definitions ////////////////////////////////////////////////////////// + +StreamLineSceneParser::StreamLineSceneParser(cpp::Renderer renderer) : + m_renderer(renderer) +{ +} + +bool StreamLineSceneParser::parse(int ac, const char **&av) +{ + bool loadedScene = false; + + StreamLines *streamLines = nullptr;//new StreamLines; + Triangles *triangles = nullptr;//new Triangles; + StockleyWhealCannon *swc = nullptr;//new StockleyWhealCannon; + + for (int i = 1; i < ac; i++) { + std::string arg = av[i]; + if (arg[0] != '-') { + const FileName fn = arg; + if (fn.ext() == "osx") { + streamLines = new StreamLines; + triangles = new Triangles; + parseOSX(streamLines, triangles, fn); + loadedScene = true; + } + else if (fn.ext() == "pnt") { + streamLines = new StreamLines; + streamLines->parsePNT(fn); + loadedScene = true; + } + else if (fn.ext() == "swc") { + swc = new StockleyWhealCannon; + swc->parse(fn); + loadedScene = true; + } + else if (fn.ext() == "pntlist") { + streamLines = new StreamLines; + streamLines->parsePNTlist(fn); + loadedScene = true; + } + else if (fn.ext() == "slraw") { + streamLines = new StreamLines; + streamLines->parseSLRAW(fn); + loadedScene = true; + } + else if (fn.ext() == "sv") { + triangles = new Triangles; + triangles->parseSV(fn); + loadedScene = true; + } + } else if (arg == "--streamline-radius") { + streamLines->radius = atof(av[++i]); +#if 1 + } +#else + } else if (arg == "--streamline-export") { + exportOSX(av[++i], streamLines, triangles); + } +#endif + } + + if (loadedScene) { + m_model = ospNewModel(); + + OSPMaterial mat = ospNewMaterial(m_renderer.handle(), "default"); + if (mat) { + ospSet3f(mat, "kd", .7, .7, .7); + ospCommit(mat); + } + + box3f bounds(empty); + + if (streamLines && !streamLines->index.empty()) { + OSPGeometry geom = ospNewGeometry("streamlines"); + Assert(geom); + OSPData vertex = ospNewData(streamLines->vertex.size(), + OSP_FLOAT3A, &streamLines->vertex[0]); + OSPData index = ospNewData(streamLines->index.size(), + OSP_UINT, &streamLines->index[0]); + ospSetObject(geom,"vertex",vertex); + ospSetObject(geom,"index",index); + ospSet1f(geom,"radius",streamLines->radius); + if (mat) + ospSetMaterial(geom,mat); + ospCommit(geom); + ospAddGeometry(m_model.handle(), geom); + bounds.extend(streamLines->getBounds()); + } + + if (triangles && !triangles->index.empty()) { + OSPGeometry geom = ospNewGeometry("triangles"); + Assert(geom); + OSPData vertex = ospNewData(triangles->vertex.size(), + OSP_FLOAT3A, &triangles->vertex[0]); + OSPData index = ospNewData(triangles->index.size(), + OSP_INT3, &triangles->index[0]); + OSPData color = ospNewData(triangles->color.size(), + OSP_FLOAT3A, &triangles->color[0]); + ospSetObject(geom, "vertex", vertex); + ospSetObject(geom, "index", index); + ospSetObject(geom, "vertex.color", color); + ospSetMaterial(geom, mat); + ospCommit(geom); + ospAddGeometry(m_model.handle(), geom); + bounds.extend(triangles->getBounds()); + } + + if (swc && !swc->bounds.empty()) { + OSPMaterial material[3]; + material[0] = mat; + material[1] = ospNewMaterial(m_renderer.handle(), "default"); + if (material[1]) { + ospSet3f(material[1], "kd", .0, .7, .0); // OBJ renderer, green + ospCommit(material[1]); + } + material[2] = ospNewMaterial(m_renderer.handle(), "default"); + if (material[2]) { + ospSet3f(material[2], "kd", .7, .0, .7); // OBJ renderer, magenta + ospCommit(material[2]); + } + + OSPGeometry spheres[3], cylinders[3]; + for (int i = 0; i < 3; i++) { + spheres[i] = ospNewGeometry("spheres"); + Assert(spheres[i]); + + OSPData data = ospNewData(swc->spheres[i].size(), OSP_FLOAT4, + &swc->spheres[i][0]); + ospSetObject(spheres[i], "spheres", data); + ospSet1i(spheres[i], "offset_radius", 3*sizeof(float)); + + if (material[i]) + ospSetMaterial(spheres[i], material[i]); + + ospCommit(spheres[i]); + ospAddGeometry(m_model.handle(), spheres[i]); + + + cylinders[i] = ospNewGeometry("cylinders"); + Assert(cylinders[i]); + + data = ospNewData(swc->cylinders[i].size()*7, OSP_FLOAT, + &swc->cylinders[i][0]); + ospSetObject(cylinders[i], "cylinders", data); + + if (material[i]) + ospSetMaterial(cylinders[i], material[i]); + + ospCommit(cylinders[i]); + ospAddGeometry(m_model.handle(), cylinders[i]); + } + + bounds.extend(swc->getBounds()); + } + + m_model.commit(); + m_bbox = bounds; + } + + if (streamLines) delete streamLines; + if (triangles) delete triangles; + if (swc) delete swc; + + return loadedScene; +} + +cpp::Model StreamLineSceneParser::model() const +{ + return m_model; +} + +box3f StreamLineSceneParser::bbox() const +{ + return m_bbox; +} diff --git a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h new file mode 100644 index 0000000000..eb8fd67ee0 --- /dev/null +++ b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h @@ -0,0 +1,40 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE StreamLineSceneParser : public SceneParser +{ +public: + StreamLineSceneParser(ospray::cpp::Renderer); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +private: + + ospray::cpp::Model m_model; + ospray::cpp::Renderer m_renderer; + ospcommon::box3f m_bbox; + + void finalize(); +}; diff --git a/modules/tachyon/Loc.cpp b/apps/common/commandline/SceneParser/tachyon/Loc.cpp similarity index 100% rename from modules/tachyon/Loc.cpp rename to apps/common/commandline/SceneParser/tachyon/Loc.cpp diff --git a/modules/tachyon/Loc.h b/apps/common/commandline/SceneParser/tachyon/Loc.h similarity index 100% rename from modules/tachyon/Loc.h rename to apps/common/commandline/SceneParser/tachyon/Loc.h diff --git a/modules/tachyon/Model.cpp b/apps/common/commandline/SceneParser/tachyon/Model.cpp similarity index 99% rename from modules/tachyon/Model.cpp rename to apps/common/commandline/SceneParser/tachyon/Model.cpp index 69cbe1bd47..94d76484fc 100644 --- a/modules/tachyon/Model.cpp +++ b/apps/common/commandline/SceneParser/tachyon/Model.cpp @@ -16,6 +16,8 @@ #undef NDEBUG +// ospray +#include "common/AffineSpace.h" // tachyon module #include "Model.h" #include "Loc.h" diff --git a/modules/tachyon/Model.h b/apps/common/commandline/SceneParser/tachyon/Model.h similarity index 95% rename from modules/tachyon/Model.h rename to apps/common/commandline/SceneParser/tachyon/Model.h index 57ebbe1b82..7c8b242762 100644 --- a/modules/tachyon/Model.h +++ b/apps/common/commandline/SceneParser/tachyon/Model.h @@ -17,7 +17,8 @@ #pragma once // ospray -#include "ospray/common/OSPCommon.h" +#include "common/vec.h" +#include "common/box.h" // std #include #include @@ -25,6 +26,8 @@ namespace ospray { namespace tachyon { + using namespace ospcommon; + struct Phong { float plastic; float size; @@ -73,10 +76,10 @@ namespace ospray { vec3f n0,n1,n2; }; struct VertexArray : public Primitive { - std::vector coord; - std::vector color; - std::vector normal; - std::vector triangle; + std::vector coord; + std::vector color; + std::vector normal; + std::vector triangle; /*! for smoothtris (which we put into a vertex array), each primitive may have a different texture ID. since 'regular' vertex arrays don't, this array _may_ be empty */ @@ -146,7 +149,6 @@ namespace ospray { }; void importFile(tachyon::Model &model, const std::string &fileName); - void error(const std::string &vol); } // ::ospray::tachyon } // ::ospray diff --git a/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.cpp b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.cpp new file mode 100644 index 0000000000..fb8d0d3b0b --- /dev/null +++ b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.cpp @@ -0,0 +1,197 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "TachyonSceneParser.h" +#include "Model.h" +#include "common/FileName.h" + +#include +using std::cout; +using std::endl; + +using namespace ospcommon; +using namespace ospray; +using namespace ospray::tachyon; + +// Helper types /////////////////////////////////////////////////////////////// + +struct TimeStep +{ + std::string modelName; + tachyon::Model tm; // tachyon model + OSPModel om; // ospray model + + TimeStep(const std::string &modelName) : modelName(modelName), om(nullptr) {} +}; + +// Helper functions /////////////////////////////////////////////////////////// + +OSPModel specifyModel(tachyon::Model &tm) +{ + OSPModel ospModel = ospNewModel(); + + if (tm.numSpheres()) { + OSPData sphereData = ospNewData(tm.numSpheres()*sizeof(Sphere)/sizeof(float), + OSP_FLOAT,tm.getSpheresPtr()); + ospCommit(sphereData); + OSPGeometry sphereGeom = ospNewGeometry("spheres"); + ospSetData(sphereGeom,"spheres",sphereData); + ospSet1i(sphereGeom,"bytes_per_sphere",sizeof(Sphere)); + ospSet1i(sphereGeom,"offset_materialID",0*sizeof(float)); + ospSet1i(sphereGeom,"offset_center",1*sizeof(float)); + ospSet1i(sphereGeom,"offset_radius",4*sizeof(float)); + ospCommit(sphereGeom); + ospAddGeometry(ospModel,sphereGeom); + } + + if (tm.numCylinders()) { + OSPData cylinderData = ospNewData(tm.numCylinders()*sizeof(Cylinder)/sizeof(float), + OSP_FLOAT,tm.getCylindersPtr()); + ospCommit(cylinderData); + OSPGeometry cylinderGeom = ospNewGeometry("cylinders"); + ospSetData(cylinderGeom,"cylinders",cylinderData); + ospSet1i(cylinderGeom,"bytes_per_cylinder",sizeof(Cylinder)); + ospSet1i(cylinderGeom,"offset_materialID",0*sizeof(float)); + ospSet1i(cylinderGeom,"offset_v0",1*sizeof(float)); + ospSet1i(cylinderGeom,"offset_v1",4*sizeof(float)); + ospSet1i(cylinderGeom,"offset_radius",7*sizeof(float)); + ospCommit(cylinderGeom); + ospAddGeometry(ospModel,cylinderGeom); + } + + cout << "#osp:tachyon: creating " << tm.numVertexArrays() << " vertex arrays" << endl; + long numTriangles = 0; + for (int vaID=0;vaIDtriangle.size(); + if (va->triangle.size()) { + OSPData data = ospNewData(va->triangle.size(),OSP_INT3,&va->triangle[0]); + ospCommit(data); + ospSetData(geom,"triangle",data); + } + if (va->coord.size()) { + OSPData data = ospNewData(va->coord.size(),OSP_FLOAT3A,&va->coord[0]); + ospCommit(data); + ospSetData(geom,"vertex",data); + } + if (va->normal.size()) { + OSPData data = ospNewData(va->normal.size(),OSP_FLOAT3A,&va->normal[0]); + ospCommit(data); + ospSetData(geom,"vertex.normal",data); + } + if (va->color.size()) { + OSPData data = ospNewData(va->color.size(),OSP_FLOAT3A,&va->color[0]); + ospCommit(data); + ospSetData(geom,"vertex.color",data); + } + if (va->perTriTextureID.size()) { + OSPData data = ospNewData(va->perTriTextureID.size(),OSP_UINT, + &va->perTriTextureID[0]); + ospCommit(data); + ospSetData(geom,"prim.materialID",data); + } else { + ospSet1i(geom,"geom.materialID",va->textureID); + } + ospCommit(geom); + ospAddGeometry(ospModel,geom); + } + + + cout << "#osp:tachyon: specifying " << tm.numTextures() << " materials..." << endl; + { + OSPData data = ospNewData(tm.numTextures()*sizeof(Texture), + OSP_UCHAR,tm.getTexturesPtr()); + ospCommit(data); + ospSetData(ospModel,"textureArray",data); + } + + cout << "#osp:tachyon: specifying " << tm.numPointLights() + << " point lights..." << endl; + if (tm.numPointLights() > 0) + { + OSPData data + = ospNewData(tm.numPointLights()*sizeof(PointLight), + OSP_UCHAR,tm.getPointLightsPtr()); + ospCommit(data); + ospSetData(ospModel,"pointLights",data); + } + + cout << "#osp:tachyon: specifying " << tm.numDirLights() + << " dir lights..." << endl; + if (tm.numDirLights() > 0) + { + OSPData data + = ospNewData(tm.numDirLights()*sizeof(DirLight), + OSP_UCHAR,tm.getDirLightsPtr()); + ospCommit(data); + ospSetData(ospModel,"dirLights",data); + } + + std::cout << "=======================================" << std::endl; + std::cout << "Tachyon Renderer: Done specifying model" << std::endl; + std::cout << "num spheres: " << tm.numSpheres() << std::endl; + std::cout << "num cylinders: " << tm.numCylinders() << std::endl; + std::cout << "num triangles: " << numTriangles << std::endl; + std::cout << "=======================================" << std::endl; + + + ospCommit(ospModel); + return ospModel; +} + +// Class definitions ////////////////////////////////////////////////////////// + +TachyonSceneParser::TachyonSceneParser(cpp::Renderer renderer) : + m_renderer(renderer) +{ +} + +bool TachyonSceneParser::parse(int ac, const char **&av) +{ + bool loadedScene = false; + + for (int i = 1; i < ac; i++) { + std::string arg = av[i]; + if (arg[0] != '-') { + FileName fn = arg; + if (fn.ext() == "tachy") { + loadedScene = true; + TimeStep ts(arg); + importFile(ts.tm, arg); + ts.om = specifyModel(ts.tm); + m_model = ts.om; + m_bbox = ts.tm.getBounds(); + break; + } + } + } + + return loadedScene; +} + +cpp::Model TachyonSceneParser::model() const +{ + return m_model; +} + +box3f TachyonSceneParser::bbox() const +{ + return m_bbox; +} diff --git a/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h new file mode 100644 index 0000000000..bd18c482e5 --- /dev/null +++ b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h @@ -0,0 +1,37 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +class TachyonSceneParser : public SceneParser +{ +public: + TachyonSceneParser(ospray::cpp::Renderer); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +private: + + ospray::cpp::Renderer m_renderer; + ospray::cpp::Model m_model; + ospcommon::box3f m_bbox; +}; diff --git a/modules/tachyon/lexer.ll b/apps/common/commandline/SceneParser/tachyon/lexer.ll similarity index 98% rename from modules/tachyon/lexer.ll rename to apps/common/commandline/SceneParser/tachyon/lexer.ll index e0e19a0230..1f58552b3b 100644 --- a/modules/tachyon/lexer.ll +++ b/apps/common/commandline/SceneParser/tachyon/lexer.ll @@ -20,8 +20,8 @@ #define YY_MAIN 1 #define YY_NEVER_INTERACTIVE 0 -#include "Loc.h" -#include "Model.h" +#include "SceneParser/tachyon/Loc.h" +#include "SceneParser/tachyon/Model.h" #include "parser_y.hpp" // (iw) use auto-generated one, not checked-in one #include diff --git a/modules/tachyon/parser.yy b/apps/common/commandline/SceneParser/tachyon/parser.yy similarity index 92% rename from modules/tachyon/parser.yy rename to apps/common/commandline/SceneParser/tachyon/parser.yy index 587aa03489..7fff5b5b46 100644 --- a/modules/tachyon/parser.yy +++ b/apps/common/commandline/SceneParser/tachyon/parser.yy @@ -18,12 +18,15 @@ %{ -#include "Model.h" -#include "Loc.h" +#include "SceneParser/tachyon/Model.h" +#include "SceneParser/tachyon/Loc.h" + +#include "common/vec.h" extern int yydebug; using namespace ospray; + using namespace ospcommon; extern int yylex(); void yyerror(const char *s); @@ -40,14 +43,13 @@ extern char *yytext; int intVal; float floatVal; char *identifierVal; - std::vector *vec3fVector; - std::vector *intVector; - ospray::tachyon::VertexArray *vertexArray; - // PolyMesh *poly; - ospray::tachyon::Texture *texture; - ospray::tachyon::DirLight *dirLight; - ospray::tachyon::PointLight *pointLight; - ospray::vec3f *Vec3f; + std::vector *vec3fVector; + std::vector *intVector; + ospray::tachyon::VertexArray *vertexArray; + ospray::tachyon::Texture *texture; + ospray::tachyon::DirLight *dirLight; + ospray::tachyon::PointLight *pointLight; + ospcommon::vec3f *Vec3f; } // DUMMY @@ -183,8 +185,8 @@ vertexarray_body vec3f_vector -: /* eol */ { $$ = new std::vector; } -| vec3f_vector Float Float Float { $$ = $1; $$->push_back(ospray::vec3f($2,$3,$4)); } +: /* eol */ { $$ = new std::vector; } +| vec3f_vector Float Float Float { $$ = $1; $$->push_back(vec3f($2,$3,$4)); } ; @@ -204,13 +206,13 @@ TOKEN_N1 Float Float Float TOKEN_N2 Float Float Float texture { - ospray::vec3f v0($3,$4,$5); - ospray::vec3f v1($7,$8,$9); - ospray::vec3f v2($11,$12,$13); - ospray::vec3f n0($15,$16,$17); - ospray::vec3f n1($19,$20,$21); - ospray::vec3f n2($23,$24,$25); - ospray::tachyon::VertexArray *sva = ospray::tachyon::parserModel->getSTriVA(1); + vec3f v0($3,$4,$5); + vec3f v1($7,$8,$9); + vec3f v2($11,$12,$13); + vec3f n0($15,$16,$17); + vec3f n1($19,$20,$21); + vec3f n2($23,$24,$25); + tachyon::VertexArray *sva = ospray::tachyon::parserModel->getSTriVA(1); int textureID = ospray::tachyon::parserModel->addTexture($26); delete $26; diff --git a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp new file mode 100644 index 0000000000..422c9c3d3d --- /dev/null +++ b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp @@ -0,0 +1,372 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "TriangleMeshSceneParser.h" + +#include +#include + +using namespace ospray; +using namespace ospcommon; + +#include +#include +using std::cerr; +using std::endl; + +// Static local helper functions ////////////////////////////////////////////// + +static void warnMaterial(const std::string &type) +{ + static std::map numOccurances; + if (numOccurances[type] == 0) + { + cerr << "could not create material type '"<< type << + "'. Replacing with default material." << endl; + } + numOccurances[type]++; +} + +// SceneParser definitions //////////////////////////////////////////////////// + +TriangleMeshSceneParser::TriangleMeshSceneParser(cpp::Renderer renderer, + std::string geometryType) : + m_renderer(renderer), + m_geometryType(geometryType), + m_alpha(false), + m_createDefaultMaterial(true), + m_maxObjectsToConsider((uint32_t)-1), + m_forceInstancing(false), + m_msgModel(new miniSG::Model) +{ +} + +bool TriangleMeshSceneParser::parse(int ac, const char **&av) +{ + bool loadedScene = false; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--max-objects") { + m_maxObjectsToConsider = atoi(av[++i]); + } else if (arg == "--force-instancing") { + m_forceInstancing = true; + } else if (arg == "--alpha") { + m_alpha = true; + } else if (arg == "--no-default-material") { + m_createDefaultMaterial = false; + } else { + FileName fn = arg; + if (fn.ext() == "stl") { + miniSG::importSTL(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "msg") { + miniSG::importMSG(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "tri") { + miniSG::importTRI(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "xml") { + miniSG::importRIVL(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "obj") { + miniSG::importOBJ(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "hbp") { + miniSG::importHBP(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "x3d") { + miniSG::importX3D(*m_msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "astl") { + miniSG::importSTL(m_msgAnimation,fn); + loadedScene = true; + } + } + } + + if (loadedScene) finalize(); + return loadedScene; +} + +cpp::Model TriangleMeshSceneParser::model() const +{ + return m_model; +} + +ospcommon::box3f TriangleMeshSceneParser::bbox() const +{ + return m_msgModel.ptr->getBBox(); +} + +cpp::Material +TriangleMeshSceneParser::createDefaultMaterial(cpp::Renderer renderer) +{ + if(!m_createDefaultMaterial) return nullptr; + + static auto ospMat = cpp::Material(nullptr); + + if (ospMat.handle()) return ospMat; + + ospMat = renderer.newMaterial("OBJMaterial"); + + ospMat.set("Kd", .8f, 0.f, 0.f); + ospMat.commit(); + return ospMat; +} + +cpp::Material TriangleMeshSceneParser::createMaterial(cpp::Renderer renderer, + miniSG::Material *mat) +{ + if (mat == nullptr) return createDefaultMaterial(renderer); + + static std::map alreadyCreatedMaterials; + + if (alreadyCreatedMaterials.find(mat) != alreadyCreatedMaterials.end()) { + return alreadyCreatedMaterials[mat]; + } + + const char *type = mat->getParam("type", "OBJMaterial"); + assert(type); + + cpp::Material ospMat; + try { + ospMat = alreadyCreatedMaterials[mat] = renderer.newMaterial(type); + } catch (const std::runtime_error &/*e*/) { + warnMaterial(type); + return createDefaultMaterial(renderer); + } + + const bool isOBJMaterial = !strcmp(type, "OBJMaterial"); + + for (auto it = mat->params.begin(); it != mat->params.end(); ++it) { + const char *name = it->first.c_str(); + const miniSG::Material::Param *p = it->second.ptr; + + switch(p->type) { + case miniSG::Material::Param::INT: + ospMat.set(name, p->i[0]); + break; + case miniSG::Material::Param::FLOAT: { + float f = p->f[0]; + /* many mtl materials of obj models wrongly store the phong exponent + 'Ns' in range [0..1], whereas OSPRay's material implementations + correctly interpret it to be in [0..inf), thus we map ranges here */ + if (isOBJMaterial && + (!strcmp(name, "Ns") || !strcmp(name, "ns")) && + f < 1.f) { + f = 1.f/(1.f - f) - 1.f; + } + ospMat.set(name, f); + } break; + case miniSG::Material::Param::FLOAT_3: + ospMat.set(name, p->f[0], p->f[1], p->f[2]); + break; + case miniSG::Material::Param::STRING: + ospMat.set(name, p->s); + break; + case miniSG::Material::Param::TEXTURE: + { + miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; + if (tex) { + OSPTexture2D ospTex = miniSG::createTexture2D(tex); + assert(ospTex); + ospCommit(ospTex); + ospMat.set(name, ospTex); + } + break; + } + default: + throw std::runtime_error("unknown material parameter type"); + }; + } + + ospMat.commit(); + return ospMat; +} + +void TriangleMeshSceneParser::finalize() +{ + // code does not yet do instancing ... check that the model doesn't + // contain instances + bool doesInstancing = 0; + + if (m_forceInstancing) { + doesInstancing = true; + } else if (m_msgModel->instance.size() > m_msgModel->mesh.size()) { + doesInstancing = true; + } else { + doesInstancing = false; + } + + if (m_msgModel->instance.size() > m_maxObjectsToConsider) { + m_msgModel->instance.resize(m_maxObjectsToConsider); + + if (!doesInstancing) { + m_msgModel->mesh.resize(m_maxObjectsToConsider); + } + } + + std::vector instanceModels; + + for (size_t i=0;imesh.size();i++) { + Ref msgMesh = m_msgModel->mesh[i]; + + // create ospray mesh + auto ospMesh = m_alpha ? cpp::Geometry("alpha_aware_triangle_mesh") : + cpp::Geometry(m_geometryType); + + // check if we have to transform the vertices: + if (doesInstancing == false && + m_msgModel->instance[i] != miniSG::Instance(i)) { + for (size_t vID = 0; vID < msgMesh->position.size(); vID++) { + msgMesh->position[vID] = xfmPoint(m_msgModel->instance[i].xfm, + msgMesh->position[vID]); + } + } + + // add position array to mesh + OSPData position = ospNewData(msgMesh->position.size(), + OSP_FLOAT3A, + &msgMesh->position[0]); + ospMesh.set("position", position); + + // add triangle index array to mesh + if (!msgMesh->triangleMaterialId.empty()) { + OSPData primMatID = ospNewData(msgMesh->triangleMaterialId.size(), + OSP_INT, + &msgMesh->triangleMaterialId[0]); + ospMesh.set("prim.materialID", primMatID); + } + + // add triangle index array to mesh + OSPData index = ospNewData(msgMesh->triangle.size(), + OSP_INT3, + &msgMesh->triangle[0]); + assert(msgMesh->triangle.size() > 0); + ospMesh.set("index", index); + + // add normal array to mesh + if (!msgMesh->normal.empty()) { + OSPData normal = ospNewData(msgMesh->normal.size(), + OSP_FLOAT3A, + &msgMesh->normal[0]); + assert(msgMesh->normal.size() > 0); + ospMesh.set("vertex.normal", normal); + } + + // add color array to mesh + if (!msgMesh->color.empty()) { + OSPData color = ospNewData(msgMesh->color.size(), + OSP_FLOAT3A, + &msgMesh->color[0]); + assert(msgMesh->color.size() > 0); + ospMesh.set("vertex.color", color); + } + // add texcoord array to mesh + if (!msgMesh->texcoord.empty()) { + OSPData texcoord = ospNewData(msgMesh->texcoord.size(), + OSP_FLOAT2, + &msgMesh->texcoord[0]); + assert(msgMesh->texcoord.size() > 0); + ospMesh.set("vertex.texcoord", texcoord); + } + + ospMesh.set("alpha_type", 0); + ospMesh.set("alpha_component", 4); + + // add triangle material id array to mesh + if (msgMesh->materialList.empty()) { + // we have a single material for this mesh... + auto singleMaterial = createMaterial(m_renderer, msgMesh->material.ptr); + ospMesh.setMaterial(singleMaterial); + } else { + // we have an entire material list, assign that list + std::vector materialList; + std::vector alphaMaps; + std::vector alphas; + for (size_t i = 0; i < msgMesh->materialList.size(); i++) { + auto m = createMaterial(m_renderer, msgMesh->materialList[i].ptr); + auto handle = m.handle(); + materialList.push_back(handle); + + for (miniSG::Material::ParamMap::const_iterator it = + msgMesh->materialList[i]->params.begin(); + it != msgMesh->materialList[i]->params.end(); it++) { + const char *name = it->first.c_str(); + const miniSG::Material::Param *p = it->second.ptr; + if(p->type == miniSG::Material::Param::TEXTURE) { + if(!strcmp(name, "map_kd") || !strcmp(name, "map_Kd")) { + miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; + OSPTexture2D ospTex = createTexture2D(tex); + ospCommit(ospTex); + alphaMaps.push_back(ospTex); + } + } else if(p->type == miniSG::Material::Param::FLOAT) { + if(!strcmp(name, "d")) alphas.push_back(p->f[0]); + } + } + + while(materialList.size() > alphaMaps.size()) { + alphaMaps.push_back(nullptr); + } + while(materialList.size() > alphas.size()) { + alphas.push_back(0.f); + } + } + auto ospMaterialList = cpp::Data(materialList.size(), + OSP_OBJECT, + &materialList[0]); + ospMesh.set("materialList", ospMaterialList); + + // only set these if alpha aware mode enabled + // this currently doesn't work on the MICs! + if(m_alpha) { + auto ospAlphaMapList = cpp::Data(alphaMaps.size(), + OSP_OBJECT, + &alphaMaps[0]); + ospMesh.set("alpha_maps", ospAlphaMapList); + + auto ospAlphaList = cpp::Data(alphas.size(), + OSP_OBJECT, + &alphas[0]); + ospMesh.set("alphas", ospAlphaList); + } + } + + ospMesh.commit(); + + if (doesInstancing) { + cpp::Model model_i; + model_i.addGeometry(ospMesh); + model_i.commit(); + instanceModels.push_back(model_i.handle()); + } else { + m_model.addGeometry(ospMesh); + } + } + + if (doesInstancing) { + for (size_t i = 0; i < m_msgModel->instance.size(); i++) { + OSPGeometry inst = + ospNewInstance(instanceModels[m_msgModel->instance[i].meshID], + reinterpret_cast(m_msgModel->instance[i].xfm)); + m_model.addGeometry(inst); + } + } + + m_model.commit(); +} diff --git a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h new file mode 100644 index 0000000000..f1f907c369 --- /dev/null +++ b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h @@ -0,0 +1,58 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE TriangleMeshSceneParser : public SceneParser +{ +public: + TriangleMeshSceneParser(ospray::cpp::Renderer renderer, + std::string geometryType = "triangles"); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +private: + + ospray::cpp::Material createDefaultMaterial(ospray::cpp::Renderer renderer); + ospray::cpp::Material createMaterial(ospray::cpp::Renderer renderer, + ospray::miniSG::Material *mat); + + ospray::cpp::Model m_model; + ospray::cpp::Renderer m_renderer; + + std::string m_geometryType; + + bool m_alpha; + bool m_createDefaultMaterial; + unsigned int m_maxObjectsToConsider; + + // if turned on, we'll put each triangle mesh into its own instance, + // no matter what + bool m_forceInstancing; + + ospcommon::Ref m_msgModel; + std::vector m_msgAnimation; + + void finalize(); +}; diff --git a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp new file mode 100644 index 0000000000..3714ed018b --- /dev/null +++ b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp @@ -0,0 +1,246 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "VolumeSceneParser.h" + +#include + +using namespace ospray; +using namespace ospcommon; + +#include "common/importer/Importer.h" +#include "common/tfn_lib/tfn_lib.h" + +#include +using std::cerr; +using std::endl; + +#include + +// SceneParser definitions //////////////////////////////////////////////////// + +VolumeSceneParser::VolumeSceneParser(cpp::Renderer renderer) : + m_renderer(renderer) +{ +} + +bool VolumeSceneParser::parse(int ac, const char **&av) +{ + bool loadedScene = false; + bool loadedTransferFunction = false; + + FileName scene; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "-s" || arg == "--sampling-rate") { + m_samplingRate = atof(av[++i]); + } else if (arg == "-tfc" || arg == "--tf-color") { + ospcommon::vec4f color; + color.x = atof(av[++i]); + color.y = atof(av[++i]); + color.z = atof(av[++i]); + color.w = atof(av[++i]); + m_tf_colors.push_back(color); + } else if (arg == "-tfs" || arg == "--tf-scale") { + m_tf_scale = atof(av[++i]); + } else if (arg == "-tff" || arg == "--tf-file") { + importTransferFunction(std::string(av[++i])); + loadedTransferFunction = true; + } else if (arg == "-is" || arg == "--surface") { + m_isosurfaces.push_back(atof(av[++i])); + } else { + FileName fn = arg; + if (fn.ext() == "osp") { + scene = arg; + loadedScene = true; + } + } + } + + if (loadedScene) { + if (!loadedTransferFunction) { + createDefaultTransferFunction(); + } + importObjectsFromFile(scene, loadedTransferFunction); + } + + return loadedScene; +} + +cpp::Model VolumeSceneParser::model() const +{ + return m_model; +} + +ospcommon::box3f VolumeSceneParser::bbox() const +{ + return m_bbox; +} + +void VolumeSceneParser::importObjectsFromFile(const std::string &filename, + bool loadedTransferFunction) +{ + auto &model = m_model; + + // Load OSPRay objects from a file. + ospray::importer::Group *imported = ospray::importer::import(filename); + + // Iterate over geometries + for (size_t i = 0; i < imported->geometry.size(); i++) { + auto geometry = ospray::cpp::Geometry(imported->geometry[i]->handle); + geometry.commit(); + model.addGeometry(geometry); + } + + // Iterate over volumes + for (size_t i = 0 ; i < imported->volume.size(); i++) { + ospray::importer::Volume *vol = imported->volume[i]; + auto volume = ospray::cpp::Volume(vol->handle); + + // For now we set the same transfer function on all volumes. + volume.set("transferFunction", m_tf); + volume.set("samplingRate", m_samplingRate); + volume.commit(); + + // Add the loaded volume(s) to the model. + model.addVolume(volume); + + // Set the minimum and maximum values in the domain for both color and + // opacity components of the transfer function if we didn't load a transfer + // function for a file (in that case this is already set) + if (!loadedTransferFunction) { + m_tf.set("valueRange", vol->voxelRange.x, vol->voxelRange.y); + m_tf.commit(); + } + + //m_bbox.extend(vol->bounds); + m_bbox = vol->bounds; + + // Create any specified isosurfaces + if (!m_isosurfaces.empty()) { + auto isoValueData = ospray::cpp::Data(m_isosurfaces.size(), OSP_FLOAT, + m_isosurfaces.data()); + auto isoGeometry = ospray::cpp::Geometry("isosurfaces"); + + isoGeometry.set("isovalues", isoValueData); + isoGeometry.set("volume", volume); + isoGeometry.commit(); + + model.addGeometry(isoGeometry); + } + } + + model.commit(); +} + +void VolumeSceneParser::importTransferFunction(const std::string &filename) +{ + tfn::TransferFunction fcn(filename); + auto colorsData = ospray::cpp::Data(fcn.rgbValues.size(), OSP_FLOAT3, + fcn.rgbValues.data()); + m_tf.set("colors", colorsData); + + m_tf_scale = fcn.opacityScaling; + // Sample the opacity values, taking 256 samples to match the volume viewer + // the volume viewer does the sampling a bit differently so we match that + // instead of what's done in createDefault + std::vector opacityValues; + const int N_OPACITIES = 256; + size_t lo = 0; + size_t hi = 1; + for (int i = 0; i < N_OPACITIES; ++i) { + const float x = float(i) / float(N_OPACITIES - 1); + float opacity = 0; + if (i == 0) { + opacity = fcn.opacityValues[0].y; + } else if (i == N_OPACITIES - 1) { + opacity = fcn.opacityValues.back().y; + } else { + // If we're over this val, find the next segment + if (x > fcn.opacityValues[lo].x) { + for (size_t j = lo; j < fcn.opacityValues.size() - 1; ++j) { + if (x <= fcn.opacityValues[j + 1].x) { + lo = j; + hi = j + 1; + break; + } + } + } + const float delta = x - fcn.opacityValues[lo].x; + const float interval = fcn.opacityValues[hi].x - fcn.opacityValues[lo].x; + if (delta == 0 || interval == 0) { + opacity = fcn.opacityValues[lo].y; + } else { + opacity = fcn.opacityValues[lo].y + delta / interval + * (fcn.opacityValues[hi].y - fcn.opacityValues[lo].y); + } + } + opacityValues.push_back(m_tf_scale * opacity); + } + + auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), + OSP_FLOAT, + opacityValues.data()); + m_tf.set("opacities", opacityValuesData); + m_tf.set("valueRange", vec2f(fcn.dataValueMin, fcn.dataValueMax)); + + // Commit transfer function + m_tf.commit(); +} +void VolumeSceneParser::createDefaultTransferFunction() +{ + // Add colors + std::vector colors; + if (m_tf_colors.empty()) { + colors.emplace_back(0.f, 0.f, 0.f, 0.f); + colors.emplace_back(0.9f, 0.9f, 0.9f, 1.f); + } else { + colors = m_tf_colors; + } + std::vector colorsAsVec3; + for (auto &c : colors) colorsAsVec3.emplace_back(c.x, c.y, c.z); + auto colorsData = ospray::cpp::Data(colors.size(), OSP_FLOAT3, + colorsAsVec3.data()); + m_tf.set("colors", colorsData); + + // Add opacities + std::vector opacityValues; + + const int N_OPACITIES = 64;//NOTE(jda) - This affects image quality and + // performance! + const int N_INTERVALS = colors.size() - 1; + const float OPACITIES_PER_INTERVAL = N_OPACITIES / float(N_INTERVALS); + for (int i = 0; i < N_OPACITIES; ++i) { + int lcolor = static_cast(i/OPACITIES_PER_INTERVAL); + int hcolor = lcolor + 1; + + float v0 = colors[lcolor].w; + float v1 = colors[hcolor].w; + float t = (i / OPACITIES_PER_INTERVAL) - lcolor; + + float opacity = (1-t)*v0 + t*v1; + if (opacity > 1.f) opacity = 1.f; + opacityValues.push_back(m_tf_scale*opacity); + } + auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), + OSP_FLOAT, + opacityValues.data()); + m_tf.set("opacities", opacityValuesData); + + // Commit transfer function + m_tf.commit(); +} diff --git a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h new file mode 100644 index 0000000000..aaca3ba36c --- /dev/null +++ b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h @@ -0,0 +1,61 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +class OSPRAY_COMMANDLINE_INTERFACE VolumeSceneParser : public SceneParser +{ +public: + VolumeSceneParser(ospray::cpp::Renderer); + + bool parse(int ac, const char **&av) override; + + ospray::cpp::Model model() const override; + ospcommon::box3f bbox() const override; + +private: + + // Helper functions // + + void importObjectsFromFile(const std::string &filename, + bool loadedTransferFunction); + void importTransferFunction(const std::string &filename); + void createDefaultTransferFunction(); + + // Data // + + ospray::cpp::Renderer m_renderer; + ospray::cpp::Model m_model; + ospcommon::box3f m_bbox; + + float m_samplingRate{0.125f}; + + float m_tf_scale{1.f}; + std::vector m_tf_colors; + std::vector m_isosurfaces; + + ospray::cpp::TransferFunction m_tf{"piecewise_linear"}; +}; diff --git a/apps/common/commandline/Utility.h b/apps/common/commandline/Utility.h new file mode 100644 index 0000000000..d67424af2f --- /dev/null +++ b/apps/common/commandline/Utility.h @@ -0,0 +1,86 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +#include "commandline/CameraParser.h" +#include "commandline/LightsParser.h" +#include "commandline/SceneParser/MultiSceneParser.h" +#include "commandline/RendererParser.h" + +#include +#include + +inline void parseForLoadingModules(int ac, const char**& av) +{ + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--module" || arg == "-m") { + ospLoadModule(av[++i]); + } + } +} + +using ParsedOSPObjects = std::tuple; + +template +inline ParsedOSPObjects parseCommandLine(int ac, const char **&av) +{ + static_assert(std::is_base_of::value, + "RendererParser_T is not a subclass of RendererParser."); + static_assert(std::is_base_of::value, + "CameraParser_T is not a subclass of CameraParser."); + static_assert(std::is_base_of::value, + "SceneParser_T is not a subclass of SceneParser."); + static_assert(std::is_base_of::value, + "LightsParser_T is not a subclass of LightsParser."); + + parseForLoadingModules(ac, av); + + CameraParser_T cameraParser; + cameraParser.parse(ac, av); + auto camera = cameraParser.camera(); + + RendererParser_T rendererParser; + rendererParser.parse(ac, av); + auto renderer = rendererParser.renderer(); + + SceneParser_T sceneParser{rendererParser.renderer()}; + sceneParser.parse(ac, av); + auto model = sceneParser.model(); + auto bbox = sceneParser.bbox(); + + LightsParser_T lightsParser(renderer); + lightsParser.parse(ac, av); + + return std::make_tuple(bbox, model, renderer, camera); +} + +inline ParsedOSPObjects parseWithDefaultParsers(int ac, const char**& av) +{ + return parseCommandLine(ac, av); +} \ No newline at end of file diff --git a/apps/common/importer/CMakeLists.txt b/apps/common/importer/CMakeLists.txt new file mode 100644 index 0000000000..834a688018 --- /dev/null +++ b/apps/common/importer/CMakeLists.txt @@ -0,0 +1,26 @@ +## ======================================================================== ## +## Copyright 2009-2016 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +OSPRAY_CREATE_LIBRARY(importer + importOSP.cpp + importRAW.cpp + importRM.cpp + Importer.cpp + TinyXML2.cpp +LINK + ospray_common + ospray +) diff --git a/apps/common/importer/Importer.cpp b/apps/common/importer/Importer.cpp new file mode 100644 index 0000000000..24281db1ff --- /dev/null +++ b/apps/common/importer/Importer.cpp @@ -0,0 +1,47 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// own +#include "Importer.h" +// ospcommon +#include "ospcommon/FileName.h" + +namespace ospray { + namespace importer { + + void importOSP(const FileName &fileName, Group *existingGroupToAddTo); + void importRM(const FileName &fileName, Group *existingGroupToAddTo); + + Group *import(const std::string &fn, Group *existingGroupToAddTo) + { + FileName fileName = fn; + Group *group = existingGroupToAddTo; + if (!group) group = new Group; + + if (fileName.ext() == "osp") { + importOSP(fn, group); + } else if (fileName.ext() == "bob") { + importRM(fn, group); + } else { + throw std::runtime_error("#ospray:importer: do not know how to import file of type " + + fileName.ext()); + } + + return group; + } + + } +} diff --git a/apps/common/importer/Importer.h b/apps/common/importer/Importer.h new file mode 100644 index 0000000000..7b9c5b9988 --- /dev/null +++ b/apps/common/importer/Importer.h @@ -0,0 +1,145 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +// ospray API +#include "ospray/ospray.h" +// ospcommon +#include "ospcommon/box.h" +// std +#include + +#ifdef _WIN32 +# ifdef ospray_importer_EXPORTS +# define OSPIMPORTER_INTERFACE __declspec(dllexport) +# else +# define OSPIMPORTER_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPIMPORTER_INTERFACE +#endif + +namespace ospray { + namespace importer { + using namespace ospcommon; + + /*! helper function to help build voxel ranges during parsing */ + template + inline void extendVoxelRange(ospcommon::vec2f &voxelRange, const T *voxel, size_t num) + { + for (size_t i=0;i unsigned char + //! voxelSize = 2 -> uint16_t + //! voxelSize = 4 -> float + //! voxelSize = 8 -> double + inline void extendVoxelRange(ospcommon::vec2f &voxelRange, const size_t voxelSize, const unsigned char *voxels, + const size_t numVoxels) + { + switch (voxelSize) { + case sizeof(unsigned char): + extendVoxelRange(voxelRange, (unsigned char*)voxels, numVoxels); + break; + case sizeof(float): + extendVoxelRange(voxelRange, (float*)voxels, numVoxels); + break; + case sizeof(double): + extendVoxelRange(voxelRange, (double*)voxels, numVoxels); + break; + default: + std::cerr << "ERROR: unsupported voxel type with size " << voxelSize << std::endl; + exit(1); + } + } + + /*! handle for any sort of geometry object. the loader will create + the geometry, create all the required data arrays, etc, and + set the appropriate bounding box field - but will NOT commit + it, yet */ + struct Geometry { + box3f bounds; + OSPGeometry handle {nullptr}; + }; + + /*! abstraction for a volume object whose values are already + set. the importer will create the volume, will set issue all + the required ospSetRegion()'s on it, but will NOT commit + it. */ + struct Volume { + OSPVolume handle {nullptr}; + /* range of voxel values in the given volume. TODO: should be + moved to a "ospray::range1f" type, not a implicit min/max + stored in the x/y coordinates of a vec2f. */ + vec2f voxelRange {1e8, -1e8}; + //! desired sampling rate for this volume + float samplingRate {1.f}; + //! world space bounding box of this volume + box3f bounds; + + size_t fileOffset {0}; + vec3f gridOrigin {0}; + vec3f gridSpacing {1}; + vec3i subVolumeOffsets {0}; + vec3i subVolumeDimensions {-1}; + vec3i subVolumeSteps {-1}; + vec3i dimensions {-1}; + vec3f scaleFactor{1}; + + std::string voxelType; + }; + + struct Group { + std::vector geometry; + std::vector volume; + }; + + OSPIMPORTER_INTERFACE Group *import(const std::string &fileName, + Group *existingGroupToAddTo=nullptr); + } + + //! Print an error message. + inline void emitMessage(const std::string &kind, const std::string &message) + { std::cerr << "#osp:vv:importer " + kind + ": " + message + "." << std::endl; } + + //! Error checking. + inline void exitOnCondition(bool condition, const std::string &message) + { if (!condition) return; emitMessage("ERROR", message); exit(1); } + + //! Warning condition. + inline void warnOnCondition(bool condition, const std::string &message) + { if (!condition) return; emitMessage("WARNING", message); } + +#if 0 + //! Get the absolute file path. + static std::string getFullFilePath(const std::string &filename) + { +#ifdef _WIN32 + //getfullpathname + throw std::runtime_error("no realpath() under windows"); +#else + char *fullpath = realpath(filename.c_str(), nullptr); + return(fullpath != nullptr ? fullpath : filename); +#endif + } +#endif + +} diff --git a/apps/common/importer/TinyXML2.cpp b/apps/common/importer/TinyXML2.cpp new file mode 100644 index 0000000000..4a351353f8 --- /dev/null +++ b/apps/common/importer/TinyXML2.cpp @@ -0,0 +1,2226 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "TinyXML2.h" +#include "ospcommon/common.h" + +#include // yes, this one new style header, is in the Android SDK. +# ifdef ANDROID_NDK +# include +#else +# include +#endif + +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +#define DELETE_NODE( node ) { \ + if ( node ) { \ + MemPool* pool = node->_memPool; \ + node->~XMLNode(); \ + pool->Free( node ); \ + } \ +} + +#define DELETE_ATTRIBUTE( attrib ) { \ + if ( attrib ) { \ + MemPool* pool = attrib->_memPool; \ + attrib->~XMLAttribute(); \ + pool->Free( attrib ); \ + } \ +} + +namespace tinyxml2 { + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; + +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + Reset(); + size_t len = strlen( str ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) +{ + TIXMLASSERT( endTag && *endTag ); + + char* start = p; // fixme: hides a member + char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } + ++p; + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + char* start = p; + + if ( !start || !(*start) ) { + return 0; + } + + while( *p && ( p == start ? XMLUtil::IsNameStartChar( *p ) : XMLUtil::IsNameChar( *p ) )) { + ++p; + } + + if ( p > start ) { + Set( start, p, 0 ); + return p; + } + return 0; +} + + +void StrPair::CollapseWhitespace() +{ + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start ); + + if ( _start && *_start ) { + char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q++ = LF; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q++ = LF; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + char buf[10] = { 0 }; + int len; + p = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + for( int i=0; i(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't covert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs. + switch (*length) { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + default: + break; + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + if ( !*(p+3) ) { + return 0; + } + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) { + return 0; + } + + delta = q-p; + --q; + + while ( *q != 'x' ) { + if ( *q >= '0' && *q <= '9' ) { + ucs += mult * (*q - '0'); + } + else if ( *q >= 'a' && *q <= 'f' ) { + ucs += mult * (*q - 'a' + 10); + } + else if ( *q >= 'A' && *q <= 'F' ) { + ucs += mult * (*q - 'A' + 10 ); + } + else { + return 0; + } + mult *= 16; + --q; + } + } + else { + // Decimal. + if ( !*(p+2) ) { + return 0; + } + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) { + return 0; + } + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + ucs += mult * (*q - '0'); + } + else { + return 0; + } + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); +} + +/* + ToStr() of a number is a very tricky topic. + https://github.com/leethomason/tinyxml2/issues/106 +*/ +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); +} + + +bool XMLUtil::ToInt( const char* str, int* value ) +{ + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) +{ + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + if ( StringEqual( str, "true" ) ) { + *value = true; + return true; + } + else if ( StringEqual( str, "false" ) ) { + *value = false; + return true; + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node ) +{ + XMLNode* returnNode = 0; + char* start = p; + p = XMLUtil::SkipWhiteSpace( p ); + if( !p || !*p ) { + return p; + } + + // What is this thing? + // These strings define the matching patters: + static const char* xmlHeader = { "_memPool = &_commentPool; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + returnNode = new (_commentPool.Alloc()) XMLComment( this ); + returnNode->_memPool = &_commentPool; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + XMLText* text = new (_textPool.Alloc()) XMLText( this ); + returnNode = text; + returnNode->_memPool = &_textPool; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); + returnNode->_memPool = &_commentPool; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + returnNode = new (_elementPool.Alloc()) XMLElement( this ); + returnNode->_memPool = &_elementPool; + p += elementHeaderLen; + } + else { + returnNode = new (_textPool.Alloc()) XMLText( this ); + returnNode->_memPool = &_textPool; + p = start; // Back it up, all the text counts. + } + + *node = returnNode; + return p; +} + + +bool XMLDocument::Accept( XMLVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLNode ----------- // + +XMLNode::XMLNode( XMLDocument* doc ) : + _document( doc ), + _parent( 0 ), + _firstChild( 0 ), _lastChild( 0 ), + _prev( 0 ), _next( 0 ), + _memPool( 0 ) +{ +} + + +XMLNode::~XMLNode() +{ + DeleteChildren(); + if ( _parent ) { + _parent->Unlink( this ); + } +} + +const char* XMLNode::Value() const +{ + return _value.GetStr(); +} + +void XMLNode::SetValue( const char* str, bool staticMem ) +{ + if ( staticMem ) { + _value.SetInternedStr( str ); + } + else { + _value.SetStr( str ); + } +} + + +void XMLNode::DeleteChildren() +{ + while( _firstChild ) { + XMLNode* node = _firstChild; + Unlink( node ); + + DELETE_NODE( node ); + } + _firstChild = _lastChild = 0; +} + + +void XMLNode::Unlink( XMLNode* child ) +{ + if ( child == _firstChild ) { + _firstChild = _firstChild->_next; + } + if ( child == _lastChild ) { + _lastChild = _lastChild->_prev; + } + + if ( child->_prev ) { + child->_prev->_next = child->_next; + } + if ( child->_next ) { + child->_next->_prev = child->_prev; + } + child->_parent = 0; +} + + +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node->_parent == this ); + DELETE_NODE( node ); +} + + +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) +{ + if (addThis->_document != _document) + return 0; + + if (addThis->_parent) + addThis->_parent->Unlink( addThis ); + else + addThis->_memPool->SetTracked(); + + if ( _lastChild ) { + TIXMLASSERT( _firstChild ); + TIXMLASSERT( _lastChild->_next == 0 ); + _lastChild->_next = addThis; + addThis->_prev = _lastChild; + _lastChild = addThis; + + addThis->_next = 0; + } + else { + TIXMLASSERT( _firstChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) +{ + if (addThis->_document != _document) + return 0; + + if (addThis->_parent) + addThis->_parent->Unlink( addThis ); + else + addThis->_memPool->SetTracked(); + + if ( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_prev == 0 ); + + _firstChild->_prev = addThis; + addThis->_next = _firstChild; + _firstChild = addThis; + + addThis->_prev = 0; + } + else { + TIXMLASSERT( _lastChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) +{ + if (addThis->_document != _document) + return 0; + + TIXMLASSERT( afterThis->_parent == this ); + + if ( afterThis->_parent != this ) { + return 0; + } + + if ( afterThis->_next == 0 ) { + // The last node or the only node. + return InsertEndChild( addThis ); + } + if (addThis->_parent) + addThis->_parent->Unlink( addThis ); + else + addThis->_memPool->SetTracked(); + addThis->_prev = afterThis; + addThis->_next = afterThis->_next; + afterThis->_next->_prev = addThis; + afterThis->_next = addThis; + addThis->_parent = this; + return addThis; +} + + + + +const XMLElement* XMLNode::FirstChildElement( const char* value ) const +{ + for( XMLNode* node=_firstChild; node; node=node->_next ) { + XMLElement* element = node->ToElement(); + if ( element ) { + if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { + return element; + } + } + } + return 0; +} + + +const XMLElement* XMLNode::LastChildElement( const char* value ) const +{ + for( XMLNode* node=_lastChild; node; node=node->_prev ) { + XMLElement* element = node->ToElement(); + if ( element ) { + if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { + return element; + } + } + } + return 0; +} + + +const XMLElement* XMLNode::NextSiblingElement( const char* value ) const +{ + for( XMLNode* element=this->_next; element; element = element->_next ) { + if ( element->ToElement() + && (!value || XMLUtil::StringEqual( value, element->Value() ))) { + return element->ToElement(); + } + } + return 0; +} + + +const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const +{ + for( XMLNode* element=_prev; element; element = element->_prev ) { + if ( element->ToElement() + && (!value || XMLUtil::StringEqual( value, element->Value() ))) { + return element->ToElement(); + } + } + return 0; +} + + +char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) +{ + // This is a recursive method, but thinking about it "at the current level" + // it is a pretty simple flat list: + // + // + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node ); + if ( p == 0 || node == 0 ) { + break; + } + + StrPair endTag; + p = node->ParseDeep( p, &endTag ); + if ( !p ) { + DELETE_NODE( node ); + node = 0; + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, 0, 0 ); + } + break; + } + + // We read the end tag. Return it to the parent. + if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { + if ( parentEnd ) { + *parentEnd = static_cast(node)->_value; + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DELETE_NODE( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + XMLElement* ele = node->ToElement(); + if ( ele ) { + if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + else if ( !endTag.Empty() ) { + if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); + p = 0; + } + } + } + if ( p == 0 ) { + DELETE_NODE( node ); + node = 0; + } + if ( node ) { + this->InsertEndChild( node ); + } + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair* ) +{ + const char* start = p; + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::COLLAPSE_WHITESPACE; + } + + p = _value.ParseText( p, "<", flags ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); + } + if ( p && *p ) { + return p-1; + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair* ) +{ + // Comment parses as text. + const char* start = p; + p = _value.ParseText( p, "-->", StrPair::COMMENT ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair* ) +{ + // Declaration parses as text. + const char* start = p; + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair* ) +{ + // Unknown parses as text. + const char* start = p; + + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // + +const char* XMLAttribute::Name() const +{ + return _name.GetStr(); +} + +const char* XMLAttribute::Value() const +{ + return _value.GetStr(); +} + +char* XMLAttribute::ParseDeep( char* p, bool processEntities ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p || *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_NO_ERROR; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( 0 ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DELETE_ATTRIBUTE( _rootAttribute ); + _rootAttribute = next; + } +} + + +XMLAttribute* XMLElement::FindAttribute( const char* name ) +{ + XMLAttribute* a = 0; + for( a=_rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + XMLAttribute* a = 0; + for( a=_rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + + +const char* XMLElement::GetText() const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + return FirstChild()->ToText()->Value(); + } + return 0; +} + + +void XMLElement::SetText( const char* inText ) +{ + if ( FirstChild() && FirstChild()->ToText() ) + FirstChild()->SetValue( inText ); + else { + XMLText* theText = GetDocument()->NewText( inText ); + InsertFirstChild( theText ); + } +} + + +void XMLElement::SetText( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryBoolText( bool* bval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->ToText()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + attrib->_memPool = &_document->_attributePool; + if ( last ) { + last->_next = attrib; + } + else { + _rootAttribute = attrib; + } + attrib->SetName( name ); + attrib->_memPool->SetTracked(); // always created and linked. + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DELETE_ATTRIBUTE( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p ) +{ + const char* start = p; + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p || !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); + return 0; + } + + // attribute. + if (XMLUtil::IsNameStartChar( *p ) ) { + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + + p = attrib->ParseDeep( p, _document->ProcessEntities() ); + if ( !p || Attribute( attrib->Name() ) ) { + DELETE_ATTRIBUTE( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + prevAttribute->_next = attrib; + } + else { + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); + return 0; + } + } + return p; +} + + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* strPair ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p ); + if ( !p ) { + return 0; + } + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p ); + if ( !p || !*p || _closingType ) { + return p; + } + + p = XMLNode::ParseDeep( p, strPair ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID( XML_NO_ERROR ), + _whitespace( whitespace ), + _errorStr1( 0 ), + _errorStr2( 0 ), + _charBuffer( 0 ) +{ + _document = this; // avoid warning about 'this' in initializer list +} + + +XMLDocument::~XMLDocument() +{ + DeleteChildren(); + delete [] _charBuffer; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef DEBUG + if ( Error() == false ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +void XMLDocument::Clear() +{ + DeleteChildren(); + + _errorID = XML_NO_ERROR; + _errorStr1 = 0; + _errorStr2 = 0; + + delete [] _charBuffer; + _charBuffer = 0; +} + + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); + ele->_memPool = &_elementPool; + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); + comment->_memPool = &_commentPool; + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = new (_textPool.Alloc()) XMLText( this ); + text->_memPool = &_textPool; + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); + dec->_memPool = &_commentPool; + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); + unk->_memPool = &_commentPool; + unk->SetValue( str ); + return unk; +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + Clear(); + FILE* fp = 0; + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + errno_t err = fopen_s(&fp, filename, "rb" ); + if ( !fp || err) { +#else + fp = fopen( filename, "rb" ); + if ( !fp) { +#endif + SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + Clear(); + + fseek( fp, 0, SEEK_SET ); + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + fseek( fp, 0, SEEK_END ); + const ssize_t filelength = +#ifdef _WIN32 + _ftelli64( fp ); +#else + ftell( fp ); +#endif + fseek( fp, 0, SEEK_SET ); + if ( filelength == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + const size_t size = filelength; + if ( size == 0 ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + _charBuffer = new char[size+1]; + size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + const char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p ); + p = XMLUtil::ReadBOM( p, &_writeBOM ); + if ( !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + ParseDeep( _charBuffer + (p-_charBuffer), 0 ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + FILE* fp = 0; +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + errno_t err = fopen_s(&fp, filename, "w" ); + if ( !fp || err) { +#else + fp = fopen( filename, "w" ); + if ( !fp) { +#endif + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* p, size_t len ) +{ + const char* start = p; + Clear(); + + if ( len == 0 || !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + if ( len == (size_t)(-1) ) { + len = strlen( p ); + } + _charBuffer = new char[ len+1 ]; + memcpy( _charBuffer, p, len ); + _charBuffer[len] = 0; + + p = XMLUtil::SkipWhiteSpace( p ); + p = XMLUtil::ReadBOM( p, &_writeBOM ); + if ( !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + ptrdiff_t delta = p - start; // skip initial whitespace, BOM, etc. + ParseDeep( _charBuffer+delta, 0 ); + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) const +{ + XMLPrinter stdStreamer( stdout ); + if ( !streamer ) { + streamer = &stdStreamer; + } + Accept( streamer ); +} + + +void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) +{ + _errorID = error; + _errorStr1 = str1; + _errorStr2 = str2; +} + + +void XMLDocument::PrintError() const +{ + if ( _errorID ) { + static const int LEN = 20; + char buf1[LEN] = { 0 }; + char buf2[LEN] = { 0 }; + + if ( _errorStr1 ) { + TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); + } + if ( _errorStr2 ) { + TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); + } + + printf( "XMLDocument error id=%d str1=%s str2=%s\n", + _errorID, buf1, buf2 ); + } +} + + +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : + _elementJustOpened( false ), + _firstElement( true ), + _fp( file ), + _depth( depth ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ) +{ + for( int i=0; i'] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + #if defined(WINCE) + int len = 512; + do { + len = len*2; + char* str = new char[len](); + len = _vsnprintf(str, len, format, va); + delete[] str; + }while (len < 0); + #else + int len = _vscprintf( format, va ); + #endif +#else + int len = vsnprintf( 0, 0, format, va ); +#endif + // Close out and re-start the va-args + va_end( va ); + va_start( va, format ); + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + #if defined(WINCE) + _vsnprintf( p, len+1, format, va ); + #else + vsnprintf_s( p, len+1, _TRUNCATE, format, va ); + #endif +#else + vsnprintf( p, len+1, format, va ); +#endif + } + va_end( va ); +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[(unsigned)(*q)] ) { + while ( p < q ) { + Print( "%c", *p ); + ++p; + } + for( int i=0; i 0) ) { + Print( "%s", p ); + } +} + + +void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) +{ + if ( writeBOM ) { + static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; + Print( "%s", bom ); + } + if ( writeDec ) { + PushDeclaration( "xml version=\"1.0\"" ); + } +} + + +void XMLPrinter::OpenElement( const char* name, bool compactMode ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + _stack.Push( name ); + + if ( _textDepth < 0 && !_firstElement && !compactMode ) { + Print( "\n" ); + } + if ( !compactMode ) { + PrintSpace( _depth ); + } + + Print( "<%s", name ); + _elementJustOpened = true; + _firstElement = false; + ++_depth; +} + + +void XMLPrinter::PushAttribute( const char* name, const char* value ) +{ + TIXMLASSERT( _elementJustOpened ); + Print( " %s=\"", name ); + PrintString( value, false ); + Print( "\"" ); +} + + +void XMLPrinter::PushAttribute( const char* name, int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::CloseElement( bool compactMode ) +{ + --_depth; + const char* name = _stack.Pop(); + + if ( _elementJustOpened ) { + Print( "/>" ); + } + else { + if ( _textDepth < 0 && !compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + Print( "", name ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !compactMode) { + Print( "\n" ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElement() +{ + _elementJustOpened = false; + Print( ">" ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + if ( _elementJustOpened ) { + SealElement(); + } + if ( cdata ) { + Print( "" ); + } + else { + PrintString( text, true ); + } +} + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", comment ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + if ( _elementJustOpened ) { + SealElement(); + } + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Print( "\n" ); + PrintSpace( _depth ); + } + _firstElement = false; + Print( "", value ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + const XMLElement* parentElem = element.Parent()->ToElement(); + bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode; + OpenElement( element.Name(), compactMode ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& element ) +{ + CloseElement( CompactMode(element) ); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 + diff --git a/apps/volumeViewer/loaders/TinyXML2.h b/apps/common/importer/TinyXML2.h similarity index 100% rename from apps/volumeViewer/loaders/TinyXML2.h rename to apps/common/importer/TinyXML2.h diff --git a/apps/common/importer/importOSP.cpp b/apps/common/importer/importOSP.cpp new file mode 100644 index 0000000000..c49d53b08d --- /dev/null +++ b/apps/common/importer/importOSP.cpp @@ -0,0 +1,279 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// own +#include "Importer.h" +// ospcommon +#include "ospcommon/FileName.h" +// tinyxml +#include "TinyXML2.h" +// std +#include + +namespace ospray { + namespace importer { + + void importVolumeRAW(const FileName &fileName, Volume *volume); + void importVolumeRM(const FileName &fileName, Volume *volume); + + void importVolume(const FileName &fileName, Volume *volume) + { + const std::string ext = fileName.ext(); + if (ext == "raw" || ext == "gz") { + importVolumeRAW(fileName, volume); + } else if (ext == "bob") { + importVolumeRM(fileName, volume); + } else { + throw std::runtime_error("unknown volume format '"+ext+"'"); + } + + // post-checks + assert(volume->handle != nullptr); + assert(!volume->bounds.empty()); + } + + vec2i parseInt2(const tinyxml2::XMLNode *node) + { + vec2i v; + int rc = sscanf(node->ToElement()->GetText(),"%i %i",&v.x,&v.y); + (void)rc; + assert(rc == 2); + return v; + } + vec3i parseInt3(const tinyxml2::XMLNode *node) + { + vec3i v; + int rc = sscanf(node->ToElement()->GetText(),"%i %i %i",&v.x,&v.y,&v.z); + (void)rc; + assert(rc == 3); + return v; + } + + float parseFloat1(const tinyxml2::XMLNode *node) + { + float v; + int rc = sscanf(node->ToElement()->GetText(),"%f",&v); + (void)rc; + assert(rc == 1); + return v; + } + vec2f parseFloat2(const tinyxml2::XMLNode *node) + { + vec2f v; + int rc = sscanf(node->ToElement()->GetText(),"%f %f",&v.x,&v.y); + (void)rc; + assert(rc == 2); + return v; + } + vec3f parseFloat3(const tinyxml2::XMLNode *node) + { + vec3f v; + int rc = sscanf(node->ToElement()->GetText(),"%f %f %f",&v.x,&v.y,&v.z); + (void)rc; + assert(rc == 3); + return v; + } + + void importVolume(const FileName &orgFileName, + Group *group, + const tinyxml2::XMLNode *root) + { + const char *dpFromEnv = getenv("OSPRAY_DATA_PARALLEL"); + + Volume *volume = new Volume; + if (dpFromEnv) { + // Create the OSPRay object. + osp::vec3i blockDims; + int rc = sscanf(dpFromEnv, "%dx%dx%d", + &blockDims.x, &blockDims.y, &blockDims.z); + if (rc != 3) { + throw std::runtime_error("could not parse OSPRAY_DATA_PARALLEL " + "env-var. Must be of format xx<>Z " + "(e.g., '4x4x4'"); + } + volume->handle = ospNewVolume("data_distributed_volume"); + if (volume->handle == nullptr) { + throw std::runtime_error("#loaders.ospObjectFile: could not create " + "volume ..."); + } + ospSetVec3i(volume->handle,"num_dp_blocks",blockDims); + } else { + // Create the OSPRay object. + volume->handle = ospNewVolume("block_bricked_volume"); + } + + if (volume->handle == nullptr) { + throw std::runtime_error("#loaders.ospObjectFile: could not create " + "volume ..."); + } + + // Temporary storage for the file name attribute if specified. + const char *volumeFilename = nullptr; + + // Iterate over object attributes. + for (const tinyxml2::XMLNode *node = root->FirstChild(); + node; + node = node->NextSibling()) { + + // Volume size in voxels per dimension. + if (!strcmp(node->ToElement()->Name(), "dimensions")) { + volume->dimensions = parseInt3(node); + continue; + } + + // File containing a volume specification and / or voxel data. + if (!strcmp(node->ToElement()->Name(), "filename")) { + volumeFilename = node->ToElement()->GetText(); + continue; + } + + // Grid origin in world coordinates. + if (!strcmp(node->ToElement()->Name(), "gridOrigin")) { + volume->gridOrigin = parseFloat3(node); + ospSetVec3f(volume->handle, "gridOrigin", (osp::vec3f&)volume->gridOrigin); + continue; + } + + // Grid spacing in each dimension in world coordinates. + if (!strcmp(node->ToElement()->Name(), "gridSpacing")) { + volume->gridSpacing = parseFloat3(node); + ospSetVec3f(volume->handle, "gridSpacing", (osp::vec3f&)volume->gridSpacing); + continue; + } + + // Sampling rate for ray casting based renderers. + if (!strcmp(node->ToElement()->Name(), "samplingRate")) { + volume->samplingRate = parseFloat1(node); + ospSet1f(volume->handle, "samplingRate", volume->samplingRate); + continue; + } + + // Volume scale factor. + if (!strcmp(node->ToElement()->Name(), "scaleFactor")) { + volume->scaleFactor = parseFloat3(node); + std::cout << "Got scaleFactor = " << volume->scaleFactor << std::endl; + ospSetVec3f(volume->handle, "scaleFactor", (osp::vec3f&)volume->scaleFactor); + continue; + } + + // Subvolume offset from origin within the full volume. + if (!strcmp(node->ToElement()->Name(), "subvolumeOffsets")) { + volume->subVolumeOffsets = parseInt3(node); + continue; + } + + // Subvolume dimensions within the full volume. + if (!strcmp(node->ToElement()->Name(), "subvolumeDimensions")) { + volume->subVolumeDimensions = parseInt3(node); + continue; + } + + // Subvolume steps in each dimension; can be used to subsample. + if (!strcmp(node->ToElement()->Name(), "subvolumeSteps")) { + volume->subVolumeSteps = parseInt3(node); + continue; + } + + // Voxel value range. + if (!strcmp(node->ToElement()->Name(), "voxelRange")) { + volume->voxelRange = parseFloat2(node); + continue; + } + + // Voxel type string. + if (!strcmp(node->ToElement()->Name(), "voxelType")) { + volume->voxelType = node->ToElement()->GetText(); + continue; + } + + // Error check. + exitOnCondition(true, "unrecognized XML element type '" + + std::string(node->ToElement()->Name()) + "'"); + } + + // Load the contents of the volume file if specified. + if (volumeFilename != nullptr) { + // The volume file path is absolute. + if (volumeFilename[0] == '/') + importVolume(volumeFilename, volume); + else { + importVolume((orgFileName.path().str() + + "/" + volumeFilename).c_str(), volume); + } + } + + group->volume.push_back(volume); + } + + void importTriangleMesh(Group *group, const tinyxml2::XMLNode *node) + { + throw std::runtime_error("importTriangleMesh: not yet implemented"); + } + + void importLight(Group *group, const tinyxml2::XMLNode *node) + { + throw std::runtime_error("importLight: not yet implemented"); + } + + void importObject(const FileName &orgFileName, + Group *group, + const tinyxml2::XMLNode *node) + { + // OSPRay light object. + if (!strcmp(node->ToElement()->Name(), "light")) + importLight(group,node); + + // OSPRay triangle mesh object. + else if (!strcmp(node->ToElement()->Name(), "triangleMesh")) + importTriangleMesh(group,node); + + // OSPRay volume object. + else if (!strcmp(node->ToElement()->Name(), "volume")) + importVolume(orgFileName,group,node); + + // No other object types are currently supported. + else + exitOnCondition(true, "unrecognized XML element type '" + + std::string(node->ToElement()->Name()) + "'"); + } + + void importOSP(const FileName &fileName, Group *existingGroupToAddTo) + { + // The XML document container. + tinyxml2::XMLDocument xml(true, tinyxml2::COLLAPSE_WHITESPACE); + + // Read the XML object file. + exitOnCondition(xml.LoadFile(fileName.str().c_str()) != tinyxml2::XML_SUCCESS, + "unable to read object file '" + fileName.str() + "'"); + + Group *group = existingGroupToAddTo ? existingGroupToAddTo : new Group; + + // Iterate over the object entries, skip the XML declaration and comments. + for (const tinyxml2::XMLNode *node = xml.FirstChild(); + node; + node = node->NextSibling()) { + if (node->ToElement()) + importObject(fileName,group,node); + } + + // post-checks: + for (size_t i = 0; i < group->volume.size(); i++) { + assert(group->volume[i]->handle != nullptr); + assert(!group->volume[i]->bounds.empty()); + } + } + } +} diff --git a/apps/common/importer/importRAW.cpp b/apps/common/importer/importRAW.cpp new file mode 100644 index 0000000000..ce4aa49d20 --- /dev/null +++ b/apps/common/importer/importRAW.cpp @@ -0,0 +1,346 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// own +#include "Importer.h" +// ospcommon +#include "ospcommon/FileName.h" +// ospray api +#include "ospray/ospray.h" + +namespace ospray { + namespace importer { + + void importVolumeRAW(const FileName &fileName, Volume *volume) + { + std::string filename = fileName.str(); + // Look for the volume data file at the given path. + FILE *file = NULL; + FileName fn = fileName; + bool gzipped = fn.ext() == "gz"; + if (gzipped) { +#ifdef _WIN32 + exitOnCondition(true, "Transparent handling of zipped files not yet supported on Windows"); +#else + std::string cmd = "/usr/bin/gunzip -c " + filename; + file = popen(cmd.c_str(),"r"); +#endif + } else { + file = fopen(filename.c_str(),"rb"); + } + //FILE *file = fopen(filename.c_str(), "rb"); + exitOnCondition(!file, "unable to open file '" + filename + "'"); + + // Offset into the volume data file if any. + if (volume->fileOffset > 0) + fseek(file, volume->fileOffset, SEEK_SET); + + // Volume dimensions. + ospcommon::vec3i volumeDimensions = volume->dimensions; + assert(volumeDimensions != vec3i(0) && volumeDimensions != vec3i(-1)); + + // Voxel type string. + const char *voxelType = volume->voxelType.c_str(); + // Voxel size in bytes. + size_t voxelSize; + + if (strcmp(voxelType, "uchar") == 0) + voxelSize = sizeof(unsigned char); + else if (strcmp(voxelType, "float") == 0) + voxelSize = sizeof(float); + else if (strcmp(voxelType, "double") == 0) + voxelSize = sizeof(double); + else + exitOnCondition(true, "unsupported voxel type"); + ospSetString(volume->handle,"voxelType",voxelType); + + // Check if a subvolume of the volume has been specified. + // Subvolume params: subvolumeOffsets, subvolumeDimensions, subvolumeSteps. + // The subvolume defaults to full dimensions (allowing for just subsampling, + // for example). + ospcommon::vec3i subvolumeOffsets = volume->subVolumeOffsets; + exitOnCondition(reduce_min(subvolumeOffsets) < 0 || + reduce_max(subvolumeOffsets - volumeDimensions) >= 0, + "invalid subvolume offsets"); + + ospcommon::vec3i subvolumeDimensions = volumeDimensions - subvolumeOffsets; + if (volume->subVolumeDimensions != vec3i(-1)) + subvolumeDimensions = volume->subVolumeDimensions; + + exitOnCondition(reduce_min(subvolumeDimensions) < 1 || + reduce_max(subvolumeDimensions - + (volumeDimensions - subvolumeOffsets)) > 0, + "invalid subvolume dimension(s) specified"); + + ospcommon::vec3i subvolumeSteps = ospcommon::vec3i(1); + if (volume->subVolumeSteps != vec3i(-1)) + subvolumeSteps = volume->subVolumeSteps; + exitOnCondition(reduce_min(subvolumeSteps) < 1 || + reduce_max(subvolumeSteps - + (volumeDimensions - subvolumeOffsets)) >= 0, + "invalid subvolume steps"); + + bool useSubvolume = false; + + // Check for volume scale factor from the environment + const char *scaleFactorEnv = getenv("OSPRAY_VOLUME_SCALE_FACTOR"); + if (scaleFactorEnv){ + std::cout << "#importRAW: found OSPRAY_VOLUME_SCALE_FACTOR env-var\n"; + vec3f scaleFactor; + if (sscanf(scaleFactorEnv, "%fx%fx%f", &scaleFactor.x, &scaleFactor.y, &scaleFactor.z) != 3){ + throw std::runtime_error("Could not parse OSPRAY_RM_SCALE_FACTOR env-var. Must be of format" + "xx (e.g '1.5x2x0.5')"); + } + std::cout << "#importRAW: got OSPRAY_VOLUME_SCALE_FACTOR env-var = {" + << scaleFactor.x << ", " << scaleFactor.y << ", " << scaleFactor.z + << "}\n"; + volume->scaleFactor = scaleFactor; + ospSetVec3f(volume->handle, "scaleFactor", (osp::vec3f&)volume->scaleFactor); + } + + // The dimensions of the volume to be imported; this will be changed if a + // subvolume is specified. + ospcommon::vec3i importVolumeDimensions = volumeDimensions; + + if (reduce_max(subvolumeOffsets) > 0 || + subvolumeDimensions != volumeDimensions || + reduce_max(subvolumeSteps) > 1) { + + useSubvolume = true; + + // The dimensions of the volume to be imported, considering the subvolume + // specified. + int xdim = subvolumeDimensions.x / subvolumeSteps.x + + (subvolumeDimensions.x % subvolumeSteps.x != 0); + int ydim = subvolumeDimensions.y / subvolumeSteps.y + + (subvolumeDimensions.y % subvolumeSteps.y != 0); + int zdim = subvolumeDimensions.z / subvolumeSteps.z + + (subvolumeDimensions.z % subvolumeSteps.z != 0); + importVolumeDimensions = ospcommon::vec3i(xdim, ydim, zdim); + + // Range check. + exitOnCondition(reduce_min(importVolumeDimensions) <= 0, + "invalid import volume dimensions"); + + // Update the provided dimensions of the volume for the subvolume specified. + ospSetVec3i(volume->handle, "dimensions", (osp::vec3i&)importVolumeDimensions); + } + else { + vec3i dims = volumeDimensions; + if (volume->scaleFactor != vec3f(1.f)) { + dims = vec3i(vec3f(dims) * volume->scaleFactor); + } + ospSetVec3i(volume->handle, "dimensions", (osp::vec3i&)dims); + } + PRINT(volumeDimensions); + + // To avoid hitting memory limits or exceeding the 2GB limit in MPIDevice::ospSetRegion we + // set the volume data in at 1.5GB chunks + // TODO How to compute these chunks, they must be convex as well, e.g. we can't set + // 2.5 scanlines of the data b/c of the params we give to setRegion are the start & size of the chunk. + // For testing try with super tiny 1k chunks + const int SET_REGION_CHUNK_SIZE = 1512e6; + const int MAX_CHUNK_VOXELS = SET_REGION_CHUNK_SIZE / voxelSize; + // For chunk dims we must step biggest along X until we hit chunkDim.x == volumeDimensions.x + // then increase chunk size along Y until we hit chunkDim.y == volumeDimensions.y and then + // we can increase chunk size along Z (assumes row order is XYZ which should be fine for any sane raw file) + osp::vec3i chunkDimensions; + chunkDimensions.x = MAX_CHUNK_VOXELS; + chunkDimensions.y = 1; + chunkDimensions.z = 1; + if (chunkDimensions.x > volumeDimensions.x) { + chunkDimensions.x = volumeDimensions.x; + chunkDimensions.y = MAX_CHUNK_VOXELS / chunkDimensions.x; + if (chunkDimensions.y > volumeDimensions.y) { + chunkDimensions.y = volumeDimensions.y; + chunkDimensions.z = std::min(volumeDimensions.z, MAX_CHUNK_VOXELS / (chunkDimensions.x * chunkDimensions.y)); + } + } + + std::cout << "#importRAW: Reading volume in chunks of size {" << chunkDimensions.x << ", " << chunkDimensions.y + << ", " << chunkDimensions.z << "}" << std::endl; + + if (!useSubvolume) { + // Log out some progress stats after we've read LOG_PROGRESS_SIZE bytes (25GB) + const size_t LOG_PROGRESS_SIZE = 25e9; + const size_t VOLUME_TOTAL_SIZE = voxelSize * volumeDimensions.x * volumeDimensions.y * volumeDimensions.z; + size_t totalDataRead = 0; + size_t dataSizeRead = 0; + + // Allocate memory for a single chunk + const size_t chunkVoxels = chunkDimensions.x * chunkDimensions.y * chunkDimensions.z; + unsigned char *voxelData = new unsigned char[chunkVoxels * voxelSize]; + osp::vec3i numChunks; + numChunks.x = volumeDimensions.x / chunkDimensions.x; + numChunks.y = volumeDimensions.y / chunkDimensions.y; + numChunks.z = volumeDimensions.z / chunkDimensions.z; + osp::vec3i remainderVoxels; + remainderVoxels.x = volumeDimensions.x % chunkDimensions.x; + remainderVoxels.y = volumeDimensions.y % chunkDimensions.y; + remainderVoxels.z = volumeDimensions.z % chunkDimensions.z; + std::cout << "#importRAW: Number of chunks on each axis = {" << numChunks.x << ", " << numChunks.y << ", " + << numChunks.z << "}, remainderVoxels {" << remainderVoxels.x + << ", " << remainderVoxels.y << ", " << remainderVoxels.z << "}, each chunk is " + << chunkVoxels << " voxels " << std::endl; + // Load and copy in each chunk of the volume data into the OSPRay volume + for (int chunkz = 0; chunkz < numChunks.z; ++chunkz) { + for (int chunky = 0; chunky < numChunks.y; ++chunky) { + for (int chunkx = 0; chunkx < numChunks.x; ++chunkx) { + size_t voxelsRead = fread(voxelData, voxelSize, chunkVoxels, file); + + dataSizeRead += voxelsRead * voxelSize; + if (dataSizeRead >= LOG_PROGRESS_SIZE){ + totalDataRead += dataSizeRead; + dataSizeRead = 0; + float percent = 100.0 * totalDataRead / static_cast(VOLUME_TOTAL_SIZE); + std::cout << "#importRAW: Have read " << totalDataRead * 1e-9 << "GB of " + << VOLUME_TOTAL_SIZE * 1e-9 << "GB (" << percent << "%)" << std::endl; + } + + // The end of the file may have been reached unexpectedly. + exitOnCondition(voxelsRead != chunkVoxels, "end of volume file reached before read completed"); + + extendVoxelRange(volume->voxelRange, voxelSize, voxelData, voxelsRead); + ospcommon::vec3i region_lo(chunkx * chunkDimensions.x, chunky * chunkDimensions.y, + chunkz * chunkDimensions.z); + + ospSetRegion(volume->handle, voxelData, (osp::vec3i&)region_lo, chunkDimensions); + } + // Read any remainder voxels on the scanline + if (remainderVoxels.x > 0) { + // We should only have remainder along x if we couldn't fit a scanline in SET_REGION_CHUNK_SIZE + assert(chunkDimensions.y == 1 && chunkDimensions.z == 1); + size_t remainder = remainderVoxels.x; + size_t voxelsRead = fread(voxelData, voxelSize, remainder, file); + dataSizeRead += voxelsRead; + ospcommon::vec3i region_lo(numChunks.x * chunkDimensions.x, chunky * chunkDimensions.y, + chunkz * chunkDimensions.z); + ospcommon::vec3i region_sz(remainderVoxels.x, chunkDimensions.y, chunkDimensions.z); + + extendVoxelRange(volume->voxelRange, voxelSize, voxelData, voxelsRead); + ospSetRegion(volume->handle, voxelData, (osp::vec3i&)region_lo, (osp::vec3i&)region_sz); + } + } + if (remainderVoxels.y > 0) { + // We should only have remainder along y if we couldn't fit a slice in SET_REGION_CHUNK_SIZE + assert(chunkDimensions.x == volumeDimensions.x && chunkDimensions.z == 1); + size_t remainder = chunkDimensions.x * remainderVoxels.y; + size_t voxelsRead = fread(voxelData, voxelSize, remainder, file); + dataSizeRead += voxelsRead; + ospcommon::vec3i region_lo(0, numChunks.y * chunkDimensions.y, + chunkz * chunkDimensions.z); + ospcommon::vec3i region_sz(chunkDimensions.x, remainderVoxels.y, chunkDimensions.z); + + extendVoxelRange(volume->voxelRange, voxelSize, voxelData, voxelsRead); + ospSetRegion(volume->handle, voxelData, (osp::vec3i&)region_lo, (osp::vec3i&)region_sz); + } + } + if (remainderVoxels.z > 0) { + // We should only have remainder along z if we couldn't fit the volume in SET_REGION_CHUNK_SIZE + assert(chunkDimensions.x == volumeDimensions.x && chunkDimensions.y == volumeDimensions.y); + size_t remainder = chunkDimensions.x * chunkDimensions.y * remainderVoxels.z; + size_t voxelsRead = fread(voxelData, voxelSize, remainder, file); + dataSizeRead += voxelsRead; + ospcommon::vec3i region_lo(0, 0, numChunks.z * chunkDimensions.z); + ospcommon::vec3i region_sz(chunkDimensions.x, chunkDimensions.y, remainderVoxels.z); + + extendVoxelRange(volume->voxelRange, voxelSize, voxelData, voxelsRead); + ospSetRegion(volume->handle, voxelData, (osp::vec3i&)region_lo, (osp::vec3i&)region_sz); + } + ospSet2f(volume->handle,"voxelRange",volume->voxelRange.x,volume->voxelRange.y); + + // Clean up. + delete [] voxelData; + } else { + + throw std::runtime_error("subvolumes not yet implemented for RAW files ..."); + + // // Allocate memory for a single row of voxel data. + // unsigned char *rowData = new unsigned char[volumeDimensions.x * voxelSize]; + + // // Allocate memory for a single row of voxel data for the subvolume. + // unsigned char *subvolumeRowData = + // new unsigned char[importVolumeDimensions.x * voxelSize]; + + // // Read the subvolume data from the full volume. + // for(long i3 = subvolumeOffsets.z; + // i3 < subvolumeOffsets.z + subvolumeDimensions.z; + // i3 += subvolumeSteps.z) { + + // for(long i2 = subvolumeOffsets.y; + // i2 < subvolumeOffsets.y + subvolumeDimensions.y; + // i2+=subvolumeSteps.y) { + + // // Seek to appropriate location in file. + // fseek(file, + // volume->offset + + // (i3 * volumeDimensions.y * volumeDimensions.x * voxelSize) + + // (i2 * volumeDimensions.x * voxelSize), SEEK_SET); + + // // Read row from volume. + // size_t voxelsRead = fread(rowData, voxelSize, volumeDimensions.x, file); + + // // The end of the file may have been reached unexpectedly. + // exitOnCondition(voxelsRead != volumeDimensions.x, + // "end of volume file reached before read completed"); + + // // Resample row for the subvolume. + // for(long i1 = subvolumeOffsets.x; + // i1 < subvolumeOffsets.x+subvolumeDimensions.x; + // i1 += subvolumeSteps.x) { + // memcpy(&subvolumeRowData[(i1 - subvolumeOffsets.x) / + // subvolumeSteps.x * voxelSize], + // &rowData[i1 * voxelSize], + // voxelSize); + // } + + // // Copy subvolume row into the volume. + // ospcommon::vec3i region_lo(0, + // (i2 - subvolumeOffsets.y) / subvolumeSteps.y, + // (i3 - subvolumeOffsets.z) / subvolumeSteps.z); + // ospcommon::vec3i region_sz(importVolumeDimensions.x, 1, 1); + // ospSetRegion(volume, + // &subvolumeRowData[0], + // (osp::vec3i&)region_lo, + // (osp::vec3i&)region_sz); + // } + // } + + // // Clean up. + // delete [] rowData; + // delete [] subvolumeRowData; + } + + if (volume->scaleFactor != vec3f(1.f)) { + volume->dimensions = vec3i(vec3f(volume->dimensions) * volume->scaleFactor); + std::cout << "#importRAW: scaled volume to " << volume->dimensions << std::endl; + } + volume->bounds = ospcommon::empty; + volume->bounds.extend(volume->gridOrigin); + volume->bounds.extend(volume->gridOrigin+ vec3f(volume->dimensions) * volume->gridSpacing); + +#ifndef _WIN32 + if (gzipped) + pclose(file); + else +#endif + fclose(file); + // Return the volume. + + } + + } // ::ospray::vv +} // ::ospray diff --git a/apps/common/importer/importRM.cpp b/apps/common/importer/importRM.cpp new file mode 100644 index 0000000000..7e65450b28 --- /dev/null +++ b/apps/common/importer/importRM.cpp @@ -0,0 +1,237 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// own +#include "Importer.h" +// ospcommon +#include "ospcommon/FileName.h" +#include "ospcommon/sysinfo.h" +// ospray api +#include "ospray/ospray.h" + +// std:: +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ospray { + namespace importer { + struct RMLoaderThreads { + Volume *volume; + + std::mutex mutex; + std::atomic nextBlockID; + std::atomic nextPinID; + int numThreads; + int timeStep; + std::vector threads; + std::string inFilesDir; + bool useGZip; + + struct Block { + uint8_t voxel[256*256*128]; + }; + + RMLoaderThreads(Volume *volume, const std::string &fileName, int numThreads=10) + : volume(volume), nextBlockID(0), nextPinID(0), numThreads(numThreads) + { + inFilesDir = fileName.substr(0, fileName.rfind('.')); + std::cout << "Reading LLNL Richtmyer-Meshkov bob from " << inFilesDir + << " with " << numThreads << " threads" << std::endl; + + useGZip = (getenv("OSPRAY_RM_NO_GZIP") == NULL); + + const size_t slash = fileName.rfind('/'); + std::string base = slash != std::string::npos ? fileName.substr(slash + 1) : fileName; + + int rc = sscanf(base.c_str(),"bob%03d.bob",&timeStep); + if (rc != 1) + throw std::runtime_error("could not extract time step from bob file name "+base); + + volume->voxelRange.x = +std::numeric_limits::infinity(); + volume->voxelRange.y = -std::numeric_limits::infinity(); + + threads.reserve(numThreads); + for (int i = 0; i < numThreads; ++i) { + threads.push_back(std::thread([&](){ run(); })); + } + for (size_t i = 0; i < threads.size(); ++i) { + threads[i].join(); + } + }; + + void loadBlock(Block &block, const std::string &fileNameBase, size_t blockID){ + char fileName[10000]; + FILE *file; + if (useGZip) { +#ifndef _WIN32 + sprintf(fileName,"%s/d_%04d_%04li.gz", + fileNameBase.c_str(),timeStep,blockID); + const std::string cmd = "gunzip -c "+std::string(fileName); + file = popen(cmd.c_str(),"r"); + if (!file) + throw std::runtime_error("could not open file in popen command '"+cmd+"'"); +#else + throw std::runtime_error("gzipped RM bob's aren't supported on Windows!"); +#endif + } else { + sprintf(fileName,"%s/d_%04d_%04li", + fileNameBase.c_str(),timeStep,blockID); + file = fopen(fileName,"rb"); + if (!file) + throw std::runtime_error("could not open '"+std::string(fileName)+"'"); + } + + assert(file); + int rc = fread(block.voxel,sizeof(uint8_t),256*256*128,file); + if (rc != 256*256*128) { + PRINT(rc); + PRINT(256*256*128); + throw std::runtime_error("could not read enough data from "+std::string(fileName)); + } + + if (useGZip) { +#ifndef _WIN32 + pclose(file); +#else + throw std::runtime_error("gzipped RM bob's aren't supported on Windows!"); +#endif + } else { + fclose(file); + } + } + + void run() + { + int threadID = nextPinID.fetch_add(1); + + Block *block = new Block; + while(1) { + int blockID = nextBlockID.fetch_add(1); + if (blockID >= 8*8*15) break; + + // int b = K*64+J*8+I; + int I = blockID % 8; + int J = (blockID / 8) % 8; + int K = (blockID / 64); + + + printf("[b%i:%i,%i,%i,(%i)]",blockID,I,J,K,threadID); + loadBlock(*block,inFilesDir,blockID); + + ospcommon::vec2f blockRange(block->voxel[0]); + extendVoxelRange(blockRange,&block->voxel[0],256*256*128); + ospcommon::vec3i region_lo(I*256,J*256,K*128); + ospcommon::vec3i region_sz(256,256,128); + { + std::lock_guard lock(mutex); + ospSetRegion(volume->handle,block->voxel,(osp::vec3i&)region_lo,(osp::vec3i&)region_sz); + + volume->voxelRange.x = std::min(volume->voxelRange.x,blockRange.x); + volume->voxelRange.y = std::max(volume->voxelRange.y,blockRange.y); + } + } + delete block; + } + }; + + // Just import the RM file into some larger scene, just loads the volume + void importVolumeRM(const FileName &fileName, Volume *volume) { + const char *scaleFactorEnv = getenv("OSPRAY_VOLUME_SCALE_FACTOR"); + if (scaleFactorEnv){ + std::cout << "#importRM: found OSPRAY_VOLUME_SCALE_FACTOR env-var\n"; + vec3f scaleFactor; + if (sscanf(scaleFactorEnv, "%fx%fx%f", &scaleFactor.x, &scaleFactor.y, &scaleFactor.z) != 3){ + throw std::runtime_error("Could not parse OSPRAY_RM_SCALE_FACTOR env-var. Must be of format" + "xx (e.g '1.5x2x0.5')"); + } + std::cout << "#importRM: got OSPRAY_VOLUME_SCALE_FACTOR env-var = {" + << scaleFactor.x << ", " << scaleFactor.y << ", " << scaleFactor.z + << "}\n"; + volume->scaleFactor = scaleFactor; + ospSetVec3f(volume->handle, "scaleFactor", (osp::vec3f&)volume->scaleFactor); + } + + // Update the provided dimensions of the volume for the subvolume specified. + ospcommon::vec3i dims(2048,2048,1920); + volume->dimensions = dims; + if (volume->scaleFactor != vec3f(1.f)) { + dims = vec3i(vec3f(dims) * volume->scaleFactor); + } + ospSetVec3i(volume->handle, "dimensions", (osp::vec3i&)dims); + ospSetString(volume->handle,"voxelType", "uchar"); + + const int numThreads = ospcommon::getNumberOfLogicalThreads(); + + double t0 = ospcommon::getSysTime(); + + RMLoaderThreads(volume,fileName,numThreads); + double t1 = ospcommon::getSysTime(); + std::cout << "done loading " << fileName + << ", needed " << (t1-t0) << " seconds" << std::endl; + + ospSet2f(volume->handle,"voxelRange",volume->voxelRange.x,volume->voxelRange.y); + volume->dimensions = dims; + volume->bounds = ospcommon::empty; + volume->bounds.extend(volume->gridOrigin); + volume->bounds.extend(volume->gridOrigin+ vec3f(volume->dimensions) * volume->gridSpacing); + } + + // Treat a single RM file as a "scene" + void importRM(const FileName &fileName, Group *group) { + const char *dpFromEnv = getenv("OSPRAY_DATA_PARALLEL"); + // Same as OSPObjectFile::importVolume for creating the initial OSPVolume and importRAW + Volume *volume = new Volume; + if (dpFromEnv) { + // Create the OSPRay object. + std::cout << "#osp.loader: found OSPRAY_DATA_PARALLEL env-var, " + << "#osp.loader: trying to use data _parallel_ mode..." << std::endl; + osp::vec3i blockDims; + int rc = sscanf(dpFromEnv,"%dx%dx%d",&blockDims.x,&blockDims.y,&blockDims.z); + if (rc != 3){ + throw std::runtime_error("could not parse OSPRAY_DATA_PARALLEL env-var." + "Must be of format xx<>Z (e.g., '4x4x4'"); + } + volume->handle = ospNewVolume("data_distributed_volume"); + if (volume->handle == NULL){ + throw std::runtime_error("#loaders.ospObjectFile: could not create volume ..."); + } + ospSetVec3i(volume->handle,"num_dp_blocks",blockDims); + } else { + // Create the OSPRay object. + std::cout << "#osp.loader: no OSPRAY_DATA_PARALLEL dimensions set, " + << "#osp.loader: assuming data replicated mode is desired" << std::endl; + std::cout << "#osp.loader: to use data parallel mode, set OSPRAY_DATA_PARALLEL env-var to xx" << std::endl; + std::cout << "#osp.loader: where X, Y, and Z are the desired _number_ of data parallel blocks" << std::endl; + volume->handle = ospNewVolume("block_bricked_volume"); + } + if (volume->handle == NULL){ + throw std::runtime_error("#loaders.ospObjectFile: could not create volume ..."); + } + + importVolumeRM(fileName, volume); + + // Add the volume to the group + group->volume.push_back(volume); + } + } +} + diff --git a/apps/streamLineViewer/CMakeLists.txt b/apps/common/loaders/CMakeLists.txt similarity index 80% rename from apps/streamLineViewer/CMakeLists.txt rename to apps/common/loaders/CMakeLists.txt index be04fbad67..17029cf56d 100644 --- a/apps/streamLineViewer/CMakeLists.txt +++ b/apps/common/loaders/CMakeLists.txt @@ -16,15 +16,16 @@ CONFIGURE_OSPRAY() -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - -IF (NOT THIS_IS_MIC) - ADD_EXECUTABLE(ospStreamLineViewer StreamLineViewer.cpp) - TARGET_LINK_LIBRARIES(ospStreamLineViewer - ospray_glut3d - ospray - ospray_xml - ${TBB_LIBRARY_MALLOC} - ${TBB_LIBRARY} - ) -ENDIF() +OSPRAY_CREATE_LIBRARY(loaders + ObjectFile.cpp + OSPObjectFile.cpp + PLYTriangleMeshFile.cpp + RawVolumeFile.cpp + RMVolumeFile.cpp + SymbolRegistry.cpp + TinyXML2.cpp + TriangleMeshFile.cpp + VolumeFile.cpp +LINK + ${OSPRAY_LIBRARIES} +) diff --git a/apps/volumeViewer/loaders/OSPObjectFile.cpp b/apps/common/loaders/OSPObjectFile.cpp similarity index 95% rename from apps/volumeViewer/loaders/OSPObjectFile.cpp rename to apps/common/loaders/OSPObjectFile.cpp index 75445f528b..d0822a4774 100644 --- a/apps/volumeViewer/loaders/OSPObjectFile.cpp +++ b/apps/common/loaders/OSPObjectFile.cpp @@ -225,8 +225,6 @@ OSPVolume OSPObjectFile::importVolume(const tinyxml2::XMLNode *root) OSPVolume volume = NULL; if (dpFromEnv) { // Create the OSPRay object. - std::cout << "#osp.loader: found OSPRAY_DATA_PARALLEL env-var, " - << "#osp.loader: trying to use data _parallel_ mode..." << std::endl; osp::vec3i blockDims; int rc = sscanf(dpFromEnv,"%dx%dx%d",&blockDims.x,&blockDims.y,&blockDims.z); if (rc !=3) @@ -237,10 +235,6 @@ OSPVolume OSPObjectFile::importVolume(const tinyxml2::XMLNode *root) ospSetVec3i(volume,"num_dp_blocks",blockDims); } else { // Create the OSPRay object. - std::cout << "#osp.loader: no OSPRAY_DATA_PARALLEL dimensions set, " - << "#osp.loader: assuming data replicated mode is desired" << std::endl; - std::cout << "#osp.loader: to use data parallel mode, set OSPRAY_DATA_PARALLEL env-var to xx" << std::endl; - std::cout << "#osp.loader: where X, Y, and Z are the desired _number_ of data parallel blocks" << std::endl; volume = ospNewVolume("block_bricked_volume"); } if (volume == NULL) diff --git a/apps/volumeViewer/loaders/OSPObjectFile.h b/apps/common/loaders/OSPObjectFile.h similarity index 100% rename from apps/volumeViewer/loaders/OSPObjectFile.h rename to apps/common/loaders/OSPObjectFile.h diff --git a/apps/volumeViewer/loaders/ObjectFile.cpp b/apps/common/loaders/ObjectFile.cpp similarity index 100% rename from apps/volumeViewer/loaders/ObjectFile.cpp rename to apps/common/loaders/ObjectFile.cpp diff --git a/apps/volumeViewer/loaders/ObjectFile.h b/apps/common/loaders/ObjectFile.h similarity index 99% rename from apps/volumeViewer/loaders/ObjectFile.h rename to apps/common/loaders/ObjectFile.h index 5de368bfe6..126c87835b 100644 --- a/apps/volumeViewer/loaders/ObjectFile.h +++ b/apps/common/loaders/ObjectFile.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include #include "ospray/ospray.h" diff --git a/apps/volumeViewer/loaders/PLYTriangleMeshFile.cpp b/apps/common/loaders/PLYTriangleMeshFile.cpp similarity index 100% rename from apps/volumeViewer/loaders/PLYTriangleMeshFile.cpp rename to apps/common/loaders/PLYTriangleMeshFile.cpp diff --git a/apps/volumeViewer/loaders/PLYTriangleMeshFile.h b/apps/common/loaders/PLYTriangleMeshFile.h similarity index 100% rename from apps/volumeViewer/loaders/PLYTriangleMeshFile.h rename to apps/common/loaders/PLYTriangleMeshFile.h diff --git a/apps/volumeViewer/loaders/README.txt b/apps/common/loaders/README.txt similarity index 100% rename from apps/volumeViewer/loaders/README.txt rename to apps/common/loaders/README.txt diff --git a/apps/volumeViewer/loaders/RMVolumeFile.cpp b/apps/common/loaders/RMVolumeFile.cpp similarity index 97% rename from apps/volumeViewer/loaders/RMVolumeFile.cpp rename to apps/common/loaders/RMVolumeFile.cpp index 7b934de23d..5d2c06fb49 100644 --- a/apps/volumeViewer/loaders/RMVolumeFile.cpp +++ b/apps/common/loaders/RMVolumeFile.cpp @@ -69,9 +69,7 @@ struct RMLoaderThreads { void *result = NULL; for (int i=0;ivoxel[i]); -#endif ospcommon::vec3i region_lo(I*256,J*256,K*128); ospcommon::vec3i region_sz(256,256,128); ospSetRegion(volume,block->voxel,(osp::vec3i&)region_lo,(osp::vec3i&)region_sz); @@ -148,12 +144,10 @@ struct RMLoaderThreads { ospcommon::vec2f blockRange(block->voxel[0]); extendVoxelRange(blockRange,&block->voxel[0],256*256*128); -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP mutex.lock(); this->voxelRange.x = std::min(this->voxelRange.x,blockRange.x); this->voxelRange.y = std::max(this->voxelRange.y,blockRange.y); mutex.unlock(); -#endif } delete block; } diff --git a/apps/volumeViewer/loaders/RMVolumeFile.h b/apps/common/loaders/RMVolumeFile.h similarity index 100% rename from apps/volumeViewer/loaders/RMVolumeFile.h rename to apps/common/loaders/RMVolumeFile.h diff --git a/apps/volumeViewer/loaders/RawVolumeFile.cpp b/apps/common/loaders/RawVolumeFile.cpp similarity index 95% rename from apps/volumeViewer/loaders/RawVolumeFile.cpp rename to apps/common/loaders/RawVolumeFile.cpp index b17dd3a360..60556beb8a 100644 --- a/apps/volumeViewer/loaders/RawVolumeFile.cpp +++ b/apps/common/loaders/RawVolumeFile.cpp @@ -59,8 +59,6 @@ OSPVolume RawVolumeFile::importVolume(OSPVolume volume) if (strcmp(voxelType, "uchar") == 0) voxelSize = sizeof(unsigned char); - else if (strcmp(voxelType, "ushort") == 0) - voxelSize = sizeof(uint16_t); else if (strcmp(voxelType, "float") == 0) voxelSize = sizeof(float); else if (strcmp(voxelType, "double") == 0) @@ -122,10 +120,8 @@ OSPVolume RawVolumeFile::importVolume(OSPVolume volume) ospSetVec3i(volume, "dimensions", (osp::vec3i&)importVolumeDimensions); } -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP ospcommon::vec2f voxelRange(+std::numeric_limits::infinity(), -std::numeric_limits::infinity()); -#endif if (!useSubvolume) { @@ -154,17 +150,11 @@ ospcommon::vec2f voxelRange(+std::numeric_limits::infinity(), exitOnCondition(voxelsRead != slicesToRead*voxelCount, "end of volume file reached before read completed"); -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP if (strcmp(voxelType, "uchar") == 0) { extendVoxelRange(voxelRange, (unsigned char *)voxelData, volumeDimensions.x*volumeDimensions.y); } - else if (strcmp(voxelType, "ushort") == 0) { - extendVoxelRange(voxelRange, - (uint16_t *)voxelData, - volumeDimensions.x*volumeDimensions.y); - } else if (strcmp(voxelType, "float") == 0) { extendVoxelRange(voxelRange, (float *)voxelData, @@ -178,8 +168,6 @@ ospcommon::vec2f voxelRange(+std::numeric_limits::infinity(), else { exitOnCondition(true, "unsupported voxel type"); } -#endif - ospcommon::vec3i region_lo(0, 0, z); ospcommon::vec3i region_sz(volumeDimensions.x, @@ -201,10 +189,8 @@ ospcommon::vec2f voxelRange(+std::numeric_limits::infinity(), } else { -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP throw std::runtime_error("computation of voxel range not yet " "implemented for subvolumes"); -#endif // Allocate memory for a single row of voxel data. unsigned char *rowData = new unsigned char[volumeDimensions.x * voxelSize]; @@ -268,9 +254,7 @@ ospcommon::vec2f voxelRange(+std::numeric_limits::infinity(), fclose(file); // Return the volume. -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP VolumeFile::voxelRangeOf[volume] = voxelRange; -#endif return(volume); } diff --git a/apps/volumeViewer/loaders/RawVolumeFile.h b/apps/common/loaders/RawVolumeFile.h similarity index 100% rename from apps/volumeViewer/loaders/RawVolumeFile.h rename to apps/common/loaders/RawVolumeFile.h diff --git a/apps/volumeViewer/loaders/SymbolRegistry.cpp b/apps/common/loaders/SymbolRegistry.cpp similarity index 100% rename from apps/volumeViewer/loaders/SymbolRegistry.cpp rename to apps/common/loaders/SymbolRegistry.cpp diff --git a/apps/volumeViewer/loaders/TinyXML2.cpp b/apps/common/loaders/TinyXML2.cpp similarity index 100% rename from apps/volumeViewer/loaders/TinyXML2.cpp rename to apps/common/loaders/TinyXML2.cpp diff --git a/apps/common/loaders/TinyXML2.h b/apps/common/loaders/TinyXML2.h new file mode 100644 index 0000000000..8436e30432 --- /dev/null +++ b/apps/common/loaders/TinyXML2.h @@ -0,0 +1,2095 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +#endif + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) +# ifndef DEBUG +# define DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _WIN32 +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#else +# define TINYXML2_LIB +#endif + + +#if defined(DEBUG) +# if defined(_MSC_VER) +# define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +# else +# define TIXMLASSERT( x ) {} +#endif + + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) +// Microsoft visual studio, version 2005 and higher. +/*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... +);*/ +inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) +{ + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; +} +#define TIXML_SSCANF sscanf_s +#elif defined WINCE +#define TIXML_SNPRINTF _snprintf +#define TIXML_SSCANF sscanf +#else +// GCC version 3 and higher +//#warning( "Using sn* functions." ) +#define TIXML_SNPRINTF snprintf +#define TIXML_SSCANF sscanf +#endif + +/* Versioning, past 1.0.14: + http://semver.org/ +*/ +static const int TIXML2_MAJOR_VERSION = 2; +static const int TIXML2_MINOR_VERSION = 1; +static const int TIXML2_PATCH_VERSION = 0; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLText; +class XMLDeclaration; +class XMLUnknown; +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] +*/ +class StrPair +{ +public: + enum { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + COLLAPSE_WHITESPACE = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags ); + char* ParseName( char* in ); + +private: + void Reset(); + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + // After parsing, if *_end != 0, it can be set to zero. + int _flags; + char* _start; + char* _end; +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray< T, INIT >() { + _mem = _pool; + _allocated = INIT; + _size = 0; + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Clear() { + _size = 0; + } + + void Push( T t ) { + EnsureCapacity( _size+1 ); + _mem[_size++] = t; + } + + T* PushArr( int count ) { + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + return _mem[--_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& PeekTop() const { + TIXMLASSERT( _size > 0 ); + return _mem[ _size - 1]; + } + + int Size() const { + return _size; + } + + int Capacity() const { + return _allocated; + } + + const T* Mem() const { + return _mem; + } + + T* Mem() { + return _mem; + } + +private: + void EnsureCapacity( int cap ) { + if ( cap > _allocated ) { + int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INIT]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + // Delete the blocks. + for( int i=0; i<_blockPtrs.Size(); ++i ) { + delete _blockPtrs[i]; + } + } + + virtual int ItemSize() const { + return SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + for( int i=0; ichunk[i].next = &block->chunk[i+1]; + } + block->chunk[COUNT-1].next = 0; + _root = block->chunk; + } + void* result = _root; + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + _nAllocs++; + _nUntracked++; + return result; + } + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Chunk* chunk = (Chunk*)mem; +#ifdef DEBUG + memset( chunk, 0xfe, sizeof(Chunk) ); +#endif + chunk->next = _root; + _root = chunk; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + _nUntracked--; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private + +private: + union Chunk { + Chunk* next; + char mem[SIZE]; + }; + struct Block { + Chunk chunk[COUNT]; + }; + DynArray< Block*, 10 > _blockPtrs; + Chunk* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + + +/* + Utility functionality. +*/ +class XMLUtil +{ +public: + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static const char* SkipWhiteSpace( const char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static char* SkipWhiteSpace( char* p ) { + while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { + ++p; + } + return p; + } + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + return ( ( ch < 128 ) ? isalpha( ch ) : 1 ) + || ch == ':' + || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + int n = 0; + if ( p == q ) { + return true; + } + while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return const_cast(const_cast(this)->LastChild() ); + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* value=0 ) const; + + XMLElement* LastChildElement( const char* value=0 ) { + return const_cast(const_cast(this)->LastChildElement(value) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; + + XMLElement* PreviousSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* value=0 ) const; + + XMLElement* NextSiblingElement( const char* value=0 ) { + return const_cast(const_cast(this)->NextSiblingElement( value ) ); + } + + /** + Add a child node as the last (right) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the afterThis node + is not a child of this node, or if the node does not + belong to the same document. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + // internal + virtual char* ParseDeep( char*, StrPair* ); + +protected: + XMLNode( XMLDocument* ); + virtual ~XMLNode(); + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported + +private: + bool _isCData; +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported + +private: +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + char* ParseDeep( char*, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + +enum XMLError { + XML_NO_ERROR = 0, + XML_SUCCESS = 0, + + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_ELEMENT_MISMATCH, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_IDENTIFYING_TAG, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const; + + /// The value of the attribute. + const char* Value() const; + + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i=0; + QueryIntValue( &i ); + return i; + } + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_NO_ERROR on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities ); + + mutable StrPair _name; + mutable StrPair _value; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLBase; + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. 0 will be + returned if there is an error. For a method with error + checking, see QueryIntAttribute() + */ + int IntAttribute( const char* name ) const { + int i=0; + QueryIntAttribute( name, &i ); + return i; + } + /// See IntAttribute() + unsigned UnsignedAttribute( const char* name ) const { + unsigned i=0; + QueryUnsignedAttribute( name, &i ); + return i; + } + /// See IntAttribute() + bool BoolAttribute( const char* name ) const { + bool b=false; + QueryBoolAttribute( name, &b ); + return b; + } + /// See IntAttribute() + double DoubleAttribute( const char* name ) const { + double d=0; + QueryDoubleAttribute( name, &d ); + return d; + } + /// See IntAttribute() + float FloatAttribute( const char* name ) const { + float f=0; + QueryFloatAttribute( name, &f ); + return f; + } + + /** Given an attribute name, QueryIntAttribute() returns + XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + + /** Given an attribute name, QueryAttribute() returns + XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + int QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + int QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + int QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + int QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + int QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, float value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, SetText() is limited compared to creating an XMLText child + and mutating it directly. + + If the first child of 'this' is a XMLText, SetText() sets its value to + the given string, otherwise it will create a first child that is an XMLText. + + This is a convenient method for setting the text of simple contained text: + @verbatim + This is text + fooElement->SetText( "Hullaballoo!" ); + Hullaballoo! + @endverbatim + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then it will not change "This is text", but rather prefix it with a text element: + @verbatim + Hullaballoo!This is text + @endverbatim + + For this XML: + @verbatim + + @endverbatim + SetText() will generate + @verbatim + Hullaballoo! + @endverbatim + */ + void SetText( const char* inText ); + /// Convenience method for setting text inside and element. See SetText() for important limitations. + void SetText( int value ); + /// Convenience method for setting text inside and element. See SetText() for important limitations. + void SetText( unsigned value ); + /// Convenience method for setting text inside and element. See SetText() for important limitations. + void SetText( bool value ); + /// Convenience method for setting text inside and element. See SetText() for important limitations. + void SetText( double value ); + /// Convenience method for setting text inside and element. See SetText() for important limitations. + void SetText( float value ); + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + // internal: + enum { + OPEN, // + CLOSED, // + CLOSING // + }; + int ClosingType() const { + return _closingType; + } + char* ParseDeep( char* p, StrPair* endTag ); + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindAttribute( const char* name ); + XMLAttribute* FindOrCreateAttribute( const char* name ); + //void LinkAttribute( XMLAttribute* attrib ); + char* ParseAttributes( char* p ); + + enum { BUF_SIZE = 200 }; + int _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + return this; + } + virtual const XMLDocument* ToDocument() const { + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_NO_ERROR (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + + /** + Load an XML file from disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_NO_ERROR (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespace; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory for the object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ) { + node->_parent->DeleteChild( node ); + } + + void SetError( XMLError error, const char* str1, const char* str2 ); + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_NO_ERROR; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + /// Return a possibly helpful diagnostic location or string. + const char* GetErrorStr1() const { + return _errorStr1; + } + /// Return a possibly helpful secondary diagnostic location or string. + const char* GetErrorStr2() const { + return _errorStr2; + } + /// If there is an error, print it to stdout. + void PrintError() const; + + /// Clear the document, resetting it to the initial state. + void Clear(); + + // internal + char* Identify( char* p, XMLNode** node ); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespace; + const char* _errorStr1; + const char* _errorStr2; + char* _charBuffer; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; +}; + + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + XMLHandle( XMLNode* node ) { + _node = node; + } + /// Create a handle from a node. + XMLHandle( XMLNode& node ) { + _node = &node; + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) { + _node = ref._node; + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* value=0 ) { + return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* _value=0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + XMLConstHandle( const XMLNode* node ) { + _node = node; + } + XMLConstHandle( const XMLNode& node ) { + _node = &node; + } + XMLConstHandle( const XMLConstHandle& ref ) { + _node = ref._node; + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* value=0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + virtual ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name, bool compactMode=false ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + virtual void CloseElement( bool compactMode=false ); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + /** + If in print to memory mode, reset the buffer to the + beginning. + */ + void ClearBuffer() { + _buffer.Clear(); + _buffer.Push(0); + } + +protected: + virtual bool CompactMode( const XMLElement& ) { return _compactMode; } + + /** Prints out the space before an element. You may override to change + the space and tabs used. A PrintSpace() override should call Print(). + */ + virtual void PrintSpace( int depth ); + void Print( const char* format, ... ); + + void SealElement(); + bool _elementJustOpened; + DynArray< const char*, 10 > _stack; + +private: + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< char, 20 > _buffer; +#ifdef _MSC_VER + DynArray< char, 20 > _accumulator; +#endif +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED diff --git a/apps/volumeViewer/loaders/TriangleMeshFile.cpp b/apps/common/loaders/TriangleMeshFile.cpp similarity index 100% rename from apps/volumeViewer/loaders/TriangleMeshFile.cpp rename to apps/common/loaders/TriangleMeshFile.cpp diff --git a/apps/volumeViewer/loaders/TriangleMeshFile.h b/apps/common/loaders/TriangleMeshFile.h similarity index 98% rename from apps/volumeViewer/loaders/TriangleMeshFile.h rename to apps/common/loaders/TriangleMeshFile.h index e1b38178c3..331ec53d0f 100644 --- a/apps/volumeViewer/loaders/TriangleMeshFile.h +++ b/apps/common/loaders/TriangleMeshFile.h @@ -19,7 +19,7 @@ #include #include #include -#include "ospray/include/ospray/ospray.h" +#include "ospray/ospray.h" //! \brief Define a function to create an instance of the InternalClass //! associated with ExternalName. diff --git a/apps/volumeViewer/loaders/VolumeFile.cpp b/apps/common/loaders/VolumeFile.cpp similarity index 98% rename from apps/volumeViewer/loaders/VolumeFile.cpp rename to apps/common/loaders/VolumeFile.cpp index 80815e8307..e67ec1597f 100644 --- a/apps/volumeViewer/loaders/VolumeFile.cpp +++ b/apps/common/loaders/VolumeFile.cpp @@ -48,7 +48,5 @@ OSPVolume VolumeFile::importVolume(const std::string &filename, OSPVolume volume return(symbolRegistry[type] ? (*symbolRegistry[type])(fullfilename, volume) : NULL); } -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP std::map VolumeFile::voxelRangeOf; -#endif diff --git a/apps/volumeViewer/loaders/VolumeFile.h b/apps/common/loaders/VolumeFile.h similarity index 98% rename from apps/volumeViewer/loaders/VolumeFile.h rename to apps/common/loaders/VolumeFile.h index cf9b71cc27..a84b9be870 100644 --- a/apps/volumeViewer/loaders/VolumeFile.h +++ b/apps/common/loaders/VolumeFile.h @@ -20,7 +20,7 @@ #include "common/common.h" #include "common/vec.h" // ospray public -#include "ospray/include/ospray/ospray.h" +#include "ospray/ospray.h" // std #include #include @@ -80,9 +80,7 @@ class VolumeFile { //! A string description of this class. virtual std::string toString() const { return("ospray_module_loaders::VolumeFile"); } -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP static std::map voxelRangeOf; -#endif //! Print an error message. void emitMessage(const std::string &kind, const std::string &message) const diff --git a/apps/volumeViewer/loaders/CMakeLists.txt b/apps/common/miniSG/CMakeLists.txt similarity index 52% rename from apps/volumeViewer/loaders/CMakeLists.txt rename to apps/common/miniSG/CMakeLists.txt index 10545ab8c3..fa827ffd2f 100644 --- a/apps/volumeViewer/loaders/CMakeLists.txt +++ b/apps/common/miniSG/CMakeLists.txt @@ -14,27 +14,43 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - -IF(NOT THIS_IS_MIC) - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) +IF (APPLE) + # the magick libraries apparently have some issues on mac that requrie + # ospray to be built with very special compile flags; we can specify those, + # but then it no longer works with the mac-version of Qt, so we currently + # pick qt compatiblity over magick compatibility. + SET(USE_IMAGE_MAGICK OFF) +ELSE() + OPTION(USE_IMAGE_MAGICK "Use ImageMagick for ModelViewer's Texture loaders.") + MARK_AS_ADVANCED(USE_IMAGE_MAGICK) +ENDIF() - LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules/loaders) +IF (USE_IMAGE_MAGICK) + FIND_PACKAGE(ImageMagick COMPONENTS Magick++) + IF (IMAGEMAGICK_FOUND) + ADD_DEFINITIONS(-DUSE_IMAGEMAGICK) + INCLUDE_DIRECTORIES(${ImageMagick_Magick++_INCLUDE_DIR}) + SET(MAGICK_LIBRARIES ${ImageMagick_Magick++_LIBRARY}) + ELSE() + MESSAGE(STATUS "ImageMagick not found. Texture loaders other than PPM are disabled.") + ENDIF() +ENDIF() - OSPRAY_ADD_LIBRARY(ospray_module_loaders SHARED - ObjectFile.cpp - OSPObjectFile.cpp - PLYTriangleMeshFile.cpp - RawVolumeFile.cpp - RMVolumeFile.cpp - SymbolRegistry.cpp - TinyXML2.cpp - TriangleMeshFile.cpp - VolumeFile.cpp - ) +ADD_LIBRARY(ospray_minisg STATIC + miniSG.cpp + importer.cpp + importOBJ.cpp + importHBP.cpp + importSTL.cpp + importMSG.cpp + importTRI.cpp + importX3D.cpp + importRIVL.cpp +) - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray_module_loaders ospray) - OSPRAY_SET_LIBRARY_VERSION(ospray_module_loaders) - OSPRAY_INSTALL_LIBRARY(ospray_module_loaders) -ENDIF() +TARGET_LINK_LIBRARIES(ospray_minisg + ospray_xml + ospray_common + ospray + ${MAGICK_LIBRARIES} +) diff --git a/apps/modelViewer/miniSG/importHBP.cpp b/apps/common/miniSG/importHBP.cpp similarity index 100% rename from apps/modelViewer/miniSG/importHBP.cpp rename to apps/common/miniSG/importHBP.cpp diff --git a/apps/modelViewer/miniSG/importMSG.cpp b/apps/common/miniSG/importMSG.cpp similarity index 100% rename from apps/modelViewer/miniSG/importMSG.cpp rename to apps/common/miniSG/importMSG.cpp diff --git a/apps/modelViewer/miniSG/importOBJ.cpp b/apps/common/miniSG/importOBJ.cpp similarity index 90% rename from apps/modelViewer/miniSG/importOBJ.cpp rename to apps/common/miniSG/importOBJ.cpp index adf2f29b53..fcdf3b3776 100644 --- a/apps/modelViewer/miniSG/importOBJ.cpp +++ b/apps/common/miniSG/importOBJ.cpp @@ -28,7 +28,6 @@ following tools don't have to care about this.*/ #define BOING_HACK 1 - namespace ospray { namespace miniSG { using std::cout; @@ -37,9 +36,9 @@ namespace ospray { /*! Three-index vertex, indexing start at 0, -1 means invalid vertex. */ struct Vertex { int v, vt, vn; - Vertex() {}; - Vertex(int v) : v(v), vt(v), vn(v) {}; - Vertex(int v, int vt, int vn) : v(v), vt(vt), vn(vn) {}; + Vertex() {} + Vertex(int v) : v(v), vt(v), vn(v) {} + Vertex(int v, int vt, int vn) : v(v), vt(vt), vn(vn) {} }; static inline bool operator < ( const Vertex& a, const Vertex& b ) { @@ -54,7 +53,9 @@ namespace ospray { size_t len = strlen(token); if (len == 0) return token; char* pe = (char*)(token + len - 1); - while ((*pe == ' ' || *pe == '\t' || *pe == '\r') && pe >= token) *pe-- = 0; + while ((*pe == ' ' || *pe == '\t' || *pe == '\r') && pe >= token) { + *pe-- = 0; + } return token; } @@ -128,7 +129,6 @@ namespace ospray { /*! Material handling. */ Material *curMaterial; Material *defaultMaterial; - //std::map > material; /*! Internal methods. */ int fix_v (int index); @@ -136,13 +136,15 @@ namespace ospray { int fix_vn(int index); void flushFaceGroup(); Vertex getInt3(const char*& token); - uint32_t getVertex(std::map& vertexMap, Mesh *mesh, const Vertex& i); + uint32_t getVertex(std::map& vertexMap, + Mesh *mesh, + const Vertex& i); }; - OBJLoader::OBJLoader(Model &model, const ospcommon::FileName &fileName) - : model(model), - curMaterial(NULL), - path(fileName.path()) + OBJLoader::OBJLoader(Model &model, const ospcommon::FileName &fileName) : + model(model), + path(fileName.path()), + curMaterial(nullptr) { /* open file */ std::ifstream cin; @@ -153,10 +155,7 @@ namespace ospray { } // /* generate default material */ - // Handle defaultMaterial = g_device->rtNewMaterial("matte"); - // g_device->rtSetFloat3(defaultMaterial, "reflectance", 0.5f, 0.5f, 0.5f); - // g_device->rtCommit(defaultMaterial); - defaultMaterial = NULL; + defaultMaterial = nullptr; curMaterial = defaultMaterial; char line[1000000]; @@ -178,13 +177,16 @@ namespace ospray { if (token[0] == 0) continue; /*! parse position */ - if (token[0] == 'v' && isSep(token[1])) { v.push_back(getVec3f(token += 2)); continue; } + if (token[0] == 'v' && isSep(token[1])) + { v.push_back(getVec3f(token += 2)); continue; } /* parse normal */ - if (token[0] == 'v' && token[1] == 'n' && isSep(token[2])) { vn.push_back(getVec3f(token += 3)); continue; } + if (token[0] == 'v' && token[1] == 'n' && isSep(token[2])) + { vn.push_back(getVec3f(token += 3)); continue; } /* parse texcoord */ - if (token[0] == 'v' && token[1] == 't' && isSep(token[2])) { vt.push_back(getVec2f(token += 3)); continue; } + if (token[0] == 'v' && token[1] == 't' && isSep(token[2])) + { vt.push_back(getVec2f(token += 3)); continue; } /*! parse face */ if (token[0] == 'f' && isSep(token[1])) @@ -205,8 +207,10 @@ namespace ospray { { flushFaceGroup(); std::string name(parseSep(token += 6)); - if (material.find(name) == material.end()) curMaterial = defaultMaterial; - else curMaterial = material[name]; + if (material.find(name) == material.end()) + curMaterial = defaultMaterial; + else + curMaterial = material[name]; continue; } @@ -239,8 +243,7 @@ namespace ospray { char line[10000]; memset(line, 0, sizeof(line)); - // Handle cur = null; - Material *cur = NULL; + Material *cur = nullptr; while (cin.peek() != -1) { /* load next multiline */ @@ -384,11 +387,6 @@ namespace ospray { { if (curGroup.empty()) return; - // temporary data arrays - // std::vector &positions = ; - // std::vector &normals; - // std::vector &texcoords; - // std::vector &triangles; std::map vertexMap; Mesh *mesh = new Mesh; model.mesh.push_back(mesh); @@ -413,7 +411,7 @@ namespace ospray { tri.v0 = v0; tri.v1 = v1; tri.v2 = v2; - mesh->triangle.push_back(tri); //Vec3i(v0, v1, v2)); + mesh->triangle.push_back(tri); } } curGroup.clear(); @@ -422,9 +420,7 @@ namespace ospray { void importOBJ(Model &model, const ospcommon::FileName &fileName) { - std::cout << "ospray::miniSG::importOBJ: importing from " << fileName << endl; - OBJLoader loader(model,fileName); - std::cout << "ospray::miniSG::importOBJ: found " << model.numUniqueTriangles() << " in " << model.numMeshes() << std::endl; + OBJLoader(model,fileName); } } // ::ospray::minisg diff --git a/apps/modelViewer/miniSG/importRIVL.cpp b/apps/common/miniSG/importRIVL.cpp similarity index 76% rename from apps/modelViewer/miniSG/importRIVL.cpp rename to apps/common/miniSG/importRIVL.cpp index cefb0b30c7..ee5ca52e6e 100644 --- a/apps/modelViewer/miniSG/importRIVL.cpp +++ b/apps/common/miniSG/importRIVL.cpp @@ -28,8 +28,8 @@ // stl #include #include -// // libxml -#include "apps/common/xml/XML.h" +// libxml +#include "common/xml/XML.h" // stdlib, for mmap #include #include @@ -50,7 +50,7 @@ namespace ospray { using std::endl; //! base pointer to mmapped binary file (that all offsets are relative to) - unsigned char *binBasePtr = NULL; + unsigned char *binBasePtr = nullptr; /*! Base class for all scene graph node types */ struct Node : public ospcommon::RefCount @@ -60,12 +60,12 @@ namespace ospray { /*! \brief create a new instance of given type, and parse its content from given xml doc - doc may be NULL, in which case a new instnace of given node + doc may be nullptr, in which case a new instnace of given node type will be created with given default values for said type. if the given node type is not known or registered, this - function may return NULL. + function may return nullptr. */ std::string name; @@ -152,13 +152,13 @@ namespace ospray { std::vector > nodeList; TriangleMesh::TriangleMesh() - : triangle(NULL), + : triangle(nullptr), numTriangles(0), - vertex(NULL), + vertex(nullptr), numVertices(0), - normal(NULL), + normal(nullptr), numNormals(0), - texCoord(NULL), + texCoord(nullptr), numTexCoords(0) {} @@ -196,7 +196,7 @@ namespace ospray { throw std::runtime_error("emply RIVL model !?"); Ref lastNode; - for (int childID=0;childIDchild.size();childID++) {//xmlNode *node = root->children; node; node = node->next) { + for (size_t childID = 0; childID < root->child.size(); childID++) { xml::Node *node = root->child[childID]; std::string nodeName = node->name; if (nodeName == "text") { @@ -210,7 +210,7 @@ namespace ospray { int height = -1, width = -1, ofs = -1, channels = -1, depth = -1; std::string format; - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < node->prop.size(); pID++) { xml::Prop *prop = node->prop[pID]; if (prop->name == "ofs") { ofs = atol(prop->value.c_str()); @@ -226,12 +226,18 @@ namespace ospray { format = prop->value.c_str(); } } - assert(ofs != size_t(-1) && "Offset not properly parsed for Texture2D nodes"); - assert(width != size_t(-1) && "Width not properly parsed for Texture2D nodes"); - assert(height != size_t(-1) && "Height not properly parsed for Texture2D nodes"); - assert(channels != size_t(-1) && "Channel count not properly parsed for Texture2D nodes"); - assert(depth != size_t(-1) && "Depth not properly parsed for Texture2D nodes"); - assert( strcmp(format.c_str(), "") != 0 && "Format not properly parsed for Texture2D nodes"); + assert(ofs != -1 + && "Offset not properly parsed for Texture2D nodes"); + assert(width != -1 + && "Width not properly parsed for Texture2D nodes"); + assert(height != -1 + && "Height not properly parsed for Texture2D nodes"); + assert(channels != -1 + && "Channel count not properly parsed for Texture2D nodes"); + assert(depth != -1 + && "Depth not properly parsed for Texture2D nodes"); + assert(strcmp(format.c_str(), "") != 0 + && "Format not properly parsed for Texture2D nodes"); txt.ptr->texData->channels = channels; txt.ptr->texData->depth = depth; @@ -268,24 +274,19 @@ namespace ospray { std::string name; std::string type; - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < node->prop.size(); pID++) { xml::Prop *prop = node->prop[pID]; - // for (xmlAttr *attr = node->properties; attr; attr = attr->next) { if (prop->name == "name") { - // xmlChar *value = xmlNodeListGetString(node->doc, attr->children, 1); - name = prop->value;//(const char*)value; + name = prop->value; mat->setParam("name", name.c_str()); mat->name = name; - //xmlFree(value); } else if (prop->name == "type") { - // xmlChar *value = xmlNodeListGetString(node->doc, attr->children, 1); - type = prop->value; //(const char*)value; + type = prop->value; mat->setParam("type", type.c_str()); - //xmlFree(value); } } - for (int childID=0;childIDchild.size();childID++) {//xmlNode *child=node->children; child; child=child->next) { + for (size_t childID = 0; childID < node->child.size(); childID++) { xml::Node *child = node->child[childID]; std::string childNodeType = child->name; @@ -293,24 +294,18 @@ namespace ospray { std::string childName; std::string childType; - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr *attr = child->properties; attr; attr = attr->next) { if (prop->name == "name") { - // xmlChar *value = xmlNodeListGetString(node->doc, attr->children, 1); - childName = prop->value; //(const char*)value; - //xmlFree(value); + childName = prop->value; } else if (prop->name == "type") { - // xmlChar *value = xmlNodeListGetString(node->doc, attr->children, 1); - childType = prop->value; //(const char*)value; - //xmlFree(value); + childType = prop->value; } } //Get the data out of the node - // xmlChar *value = xmlNodeListGetString(node->doc, child->children, 1); char *value = strdup(child->content.c_str()); -#define NEXT_TOK strtok(NULL, " \t\n\r") +#define NEXT_TOK strtok(nullptr, " \t\n\r") char *s = strtok((char*)value, " \t\n\r"); //TODO: UGLY! Find a better way. if (!childType.compare("float")) { @@ -372,22 +367,16 @@ namespace ospray { free(value); } else if (!childNodeType.compare("textures")) { int num = -1; - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr *attr = child->properties; attr; attr = attr->next) { if (prop->name == "num") { num = atol(prop->value.c_str()); - //xmlFree(value); } } - // xmlChar *value = xmlNodaeListGetString(node->doc, child->children, 1); - if (child->content == "") { - // empty texture node .... } else { char *tokenBuffer = strdup(child->content.c_str()); - //xmlFree(value); char *s = strtok(tokenBuffer, " \t\n\r"); while (s) { @@ -398,9 +387,10 @@ namespace ospray { } free(tokenBuffer); } - if (mat->textures.size() != num) { - throw std::runtime_error("invalid number of textures in material " - "(found either more or less than the 'num' field specifies"); + if (mat->textures.size() != static_cast(num)) { + throw std::runtime_error("invalid number of textures in" + " material (found either more or less" + " than the 'num' field specifies"); } } } @@ -412,7 +402,7 @@ namespace ospray { nodeList.push_back(camera.ptr); // parse values - for (int pID=0;pIDchild.size();pID++) { + for (size_t pID = 0; pID < node->child.size(); pID++) { xml::Node *childNode = node->child[pID]; if (childNode->name == "from") { sscanf(childNode->content.c_str(),"%f %f %f", @@ -434,16 +424,13 @@ namespace ospray { nodeList.push_back(xfm.ptr); // find child ID - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0;pID < node->prop.size(); pID++) { xml::Prop *prop = node->prop[pID]; - // for (xmlAttr* attr = node->properties; attr; attr = attr->next) if (prop->name == "child") { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - size_t childID = atoi(prop->value.c_str());//(char*)value); + size_t childID = atoi(prop->value.c_str()); miniSG::Node *child = nodeList[childID].ptr; assert(child); xfm->child = child; - //xmlFree(value); } } @@ -462,9 +449,9 @@ namespace ospray { &xfm->xfm.p.x, &xfm->xfm.p.y, &xfm->xfm.p.z); - //xmlFree(value); if (numRead != 12) { - throw std::runtime_error("invalid number of elements in RIVL transform node"); + throw std::runtime_error("invalid number of elements in RIVL" + " transform node"); } // ------------------------------------------------------- @@ -473,25 +460,20 @@ namespace ospray { Ref mesh = new miniSG::TriangleMesh; nodeList.push_back(mesh.ptr); - for (int childID=0;childIDchild.size();childID++) {//xmlNode *child=node->children;child;child=child->next) { + for (size_t childID = 0; childID < node->child.size(); childID++) { xml::Node *child = node->child[childID]; std::string childType = child->name; if (childType == "text") { } else if (childType == "vertex") { size_t ofs = -1, num = -1; // scan parameters ... - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr* attr = child->properties; attr; attr = attr->next) if (prop->name == "ofs") { - //xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - ofs = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + ofs = atol(prop->value.c_str()); } else if (prop->name == "num") { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - num = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + num = atol(prop->value.c_str()); } } assert(ofs != size_t(-1)); @@ -501,18 +483,13 @@ namespace ospray { } else if (childType == "normal") { size_t ofs = -1, num = -1; // scan parameters ... - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr* attr = child->properties; attr; attr = attr->next) - if (prop->name == "ofs"){ //!strcmp((const char*)attr->name,"ofs")) { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - ofs = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + if (prop->name == "ofs"){ + ofs = atol(prop->value.c_str()); } - else if (prop->name == "num") {//!strcmp((const char*)attr->name,"num")) { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - num = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + else if (prop->name == "num") { + num = atol(prop->value.c_str()); } } assert(ofs != size_t(-1)); @@ -522,20 +499,13 @@ namespace ospray { } else if (childType == "texcoord") { size_t ofs = -1, num = -1; // scan parameters ... - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr* attr = child->properties; attr; attr = attr->next) - if (prop->name == "ofs") {//!strcmp((const char*)attr->name,"ofs")) { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - // ofs = atol((char*)value); - ofs = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + if (prop->name == "ofs") { + ofs = atol(prop->value.c_str()); } - else if (prop->name == "num") {//!strcmp((const char*)attr->name,"num")) { - // xmlChar* value = xmlNodeListGetString(node->doc, attr->children, 1); - // num = atol((char*)value); - num = atol(prop->value.c_str()); //(char*)value); - //xmlFree(value); + else if (prop->name == "num") { + num = atol(prop->value.c_str()); } } assert(ofs != size_t(-1)); @@ -545,14 +515,13 @@ namespace ospray { } else if (childType == "prim") { size_t ofs = -1, num = -1; // scan parameters ... - for (int pID=0;pIDprop.size();pID++) { + for (size_t pID = 0; pID < child->prop.size(); pID++) { xml::Prop *prop = child->prop[pID]; - // for (xmlAttr* attr = child->properties; attr; attr = attr->next) - if (prop->name == "ofs") {//!strcmp((const char*)attr->name,"ofs")) { - ofs = atol(prop->value.c_str()); //(char*)value); + if (prop->name == "ofs") { + ofs = atol(prop->value.c_str()); } - else if (prop->name == "num") {//!strcmp((const char*)attr->name,"num")) { - num = atol(prop->value.c_str()); //(char*)value); + else if (prop->name == "num") { + num = atol(prop->value.c_str()); } } assert(ofs != size_t(-1)); @@ -561,7 +530,9 @@ namespace ospray { mesh->triangle = (vec4i*)(binBasePtr+ofs); } else if (childType == "materiallist") { char* value = strdup(child->content.c_str()); - for(char *s=strtok((char*)value," \t\n\r");s;s=strtok(NULL," \t\n\r")) { + for(char *s=strtok((char*)value," \t\n\r"); + s; + s=strtok(nullptr," \t\n\r")) { size_t matID = atoi(s); Ref mat = nodeList[matID].cast(); mat.ptr->refInc(); @@ -585,10 +556,10 @@ namespace ospray { if (node->content == "") // empty group... ; - // std::cout << "warning: xmlNodeListGetString(...) returned NULL" << std::endl; + // std::cout << "warning: xmlNodeListGetString(...) returned nullptr" << std::endl; else { char *value = strdup(node->content.c_str()); - for(char *s=strtok((char*)value," \t\n\r");s;s=strtok(NULL," \t\n\r")) { + for(char *s=strtok((char*)value," \t\n\r");s;s=strtok(nullptr," \t\n\r")) { size_t childID = atoi(s); miniSG::Node *child = nodeList[childID].ptr; //assert(child); @@ -599,7 +570,7 @@ namespace ospray { } lastNode = group.ptr; } else { - nodeList.push_back(NULL); + nodeList.push_back(nullptr); //throw std::runtime_error("unknown node type '"+nodeName+"' in RIVL model"); } } @@ -624,11 +595,11 @@ namespace ospray { fclose(file); #ifdef _WIN32 - HANDLE fileHandle = CreateFile(binFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fileHandle == NULL) + HANDLE fileHandle = CreateFile(binFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (fileHandle == nullptr) fprintf(stderr, "could not open file '%s' (error %lu)\n", binFileName.c_str(), GetLastError()); - HANDLE fileMappingHandle = CreateFileMapping(fileHandle, NULL, PAGE_READONLY, 0, 0, NULL); - if (fileMappingHandle == NULL) + HANDLE fileMappingHandle = CreateFileMapping(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (fileMappingHandle == nullptr) fprintf(stderr, "could not create file mapping (error %lu)\n", GetLastError()); #else int fd = ::open(binFileName.c_str(), O_LARGEFILE | O_RDONLY); @@ -640,8 +611,7 @@ namespace ospray { #ifdef _WIN32 MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, fileSize); #else - mmap(NULL,fileSize,PROT_READ,MAP_SHARED,fd,0); - // mmap(NULL,fileSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); + mmap(nullptr,fileSize,PROT_READ,MAP_SHARED,fd,0); #endif xml::XMLDoc *doc = xml::readXML(fileName); @@ -656,7 +626,7 @@ namespace ospray { { Group *g = dynamic_cast(node.ptr); if (g) { - for (int i=0;ichild.size();i++) + for (size_t i = 0; i < g->child.size(); i++) traverseSG(model,g->child[i],xfm); return; } @@ -687,22 +657,20 @@ namespace ospray { mesh->triangle.resize(tm->numTriangles); mesh->triangleMaterialId.resize(tm->numTriangles); bool anyNotZero = false; - for (int i=0;inumTriangles;i++) { + for (size_t i = 0; i < tm->numTriangles; i++) { Triangle t; t.v0 = tm->triangle[i].x; t.v1 = tm->triangle[i].y; t.v2 = tm->triangle[i].z; - // if (std::max(std::max(t.v0,t.v1),t.v2) >= mesh->position.size()) - // t.v0 = t.v1 = t.v2 = 0; - // assert(t.v0 >= 0 && t.v0 < mesh->position.size()); - // assert(t.v1 >= 0 && t.v1 < mesh->position.size()); - // assert(t.v2 >= 0 && t.v2 < mesh->position.size()); mesh->triangle[i] = t; - assert(mesh->triangle[i].v0 >= 0 && mesh->triangle[i].v0 < mesh->position.size()); - assert(mesh->triangle[i].v1 >= 0 && mesh->triangle[i].v1 < mesh->position.size()); - assert(mesh->triangle[i].v2 >= 0 && mesh->triangle[i].v2 < mesh->position.size()); + assert(mesh->triangle[i].v0 >= 0 + && mesh->triangle[i].v0 < mesh->position.size()); + assert(mesh->triangle[i].v1 >= 0 + && mesh->triangle[i].v1 < mesh->position.size()); + assert(mesh->triangle[i].v2 >= 0 + && mesh->triangle[i].v2 < mesh->position.size()); mesh->triangleMaterialId[i] = tm->triangle[i].w >>16; if (mesh->triangleMaterialId[i]) anyNotZero = true; @@ -710,16 +678,15 @@ namespace ospray { if (!anyNotZero) mesh->triangleMaterialId.clear(); - for (int i=0;inumVertices;i++) { + for (size_t i = 0; i < tm->numVertices; i++) { mesh->position[i].x = tm->vertex[i].x; mesh->position[i].y = tm->vertex[i].y; mesh->position[i].z = tm->vertex[i].z; mesh->position[i].w = 0; } if (tm->numNormals > 0) { - // assert(tm->numNormals == tm->numVertices); mesh->normal.resize(tm->numVertices); - for (int i=0;inumNormals;i++) { + for (size_t i = 0; i < tm->numNormals; i++) { mesh->normal[i].x = tm->normal[i].x; mesh->normal[i].y = tm->normal[i].y; mesh->normal[i].z = tm->normal[i].z; @@ -727,9 +694,8 @@ namespace ospray { } } if (tm->numTexCoords > 0) { - // assert(tm->numTexCoords == tm->numVertices); mesh->texcoord.resize(tm->numVertices); - for (int i=0; inumTexCoords; i++) { + for (size_t i = 0; i < tm->numTexCoords; i++) { (vec2f&)mesh->texcoord[i] = (vec2f&)tm->texCoord[i]; } } @@ -748,16 +714,15 @@ namespace ospray { model.instance.push_back(Instance(meshID,xfm)); return; - // throw std::runtime_error("meshes not yet implemented in importRIVL"); } RIVLMaterial *mt = dynamic_cast(node.ptr); if (mt) { - // model.material.push_back(mt->general); return; } - throw std::runtime_error("unhandled node type '"+node->toString()+"' in traverseSG"); + throw std::runtime_error("unhandled node type '" + + node->toString() + "' in traverseSG"); } /*! import a wavefront OBJ file, and add it to the specified model */ diff --git a/apps/modelViewer/miniSG/importSTL.cpp b/apps/common/miniSG/importSTL.cpp similarity index 100% rename from apps/modelViewer/miniSG/importSTL.cpp rename to apps/common/miniSG/importSTL.cpp diff --git a/apps/modelViewer/miniSG/importTRI.cpp b/apps/common/miniSG/importTRI.cpp similarity index 90% rename from apps/modelViewer/miniSG/importTRI.cpp rename to apps/common/miniSG/importTRI.cpp index c3d6bdf6ee..36a7fa9bc1 100644 --- a/apps/modelViewer/miniSG/importTRI.cpp +++ b/apps/common/miniSG/importTRI.cpp @@ -29,7 +29,7 @@ namespace ospray { if (!file) error("could not open input file"); int32_t numVertices; - fread(&numVertices,1,sizeof(numVertices),file); + auto rc = fread(&numVertices,1,sizeof(numVertices),file); Mesh *mesh = new Mesh; model.mesh.push_back(mesh); @@ -37,8 +37,9 @@ namespace ospray { mesh->position.resize(numVertices); mesh->normal.resize(numVertices); mesh->triangle.resize(numVertices/3); - fread(&mesh->position[0],numVertices,4*sizeof(float),file); - fread(&mesh->normal[0],numVertices,4*sizeof(float),file); + rc = fread(&mesh->position[0],numVertices,4*sizeof(float),file); + rc = fread(&mesh->normal[0],numVertices,4*sizeof(float),file); + (void)rc; for (int i=0;itriangle[i].v0 = 3*i+0; mesh->triangle[i].v1 = 3*i+1; diff --git a/apps/modelViewer/miniSG/importX3D.cpp b/apps/common/miniSG/importX3D.cpp similarity index 93% rename from apps/modelViewer/miniSG/importX3D.cpp rename to apps/common/miniSG/importX3D.cpp index bb471eddfc..14a0967597 100644 --- a/apps/modelViewer/miniSG/importX3D.cpp +++ b/apps/common/miniSG/importX3D.cpp @@ -20,7 +20,7 @@ #include "miniSG.h" #include "importer.h" // xml lib -#include "apps/common/xml/XML.h" +#include "common/xml/XML.h" // std #include #include @@ -82,7 +82,7 @@ namespace ospray { while (tok) { long thisID = atol(tok); if (thisID == -1) { - for (int i=2;ichild.size();childID++) { + for (size_t childID = 0; childID < root->child.size(); childID++) { xml::Node *node = root->child[childID]; if (node->name == "Coordinate") { @@ -118,7 +118,8 @@ namespace ospray { continue; } - throw std::runtime_error("importX3D: unknown child type '"+node->name+"' to 'IndexedFaceSet' node"); + throw std::runtime_error("importX3D: unknown child type '" + + node->name + "' to 'IndexedFaceSet' node"); } model.mesh.push_back(mesh); @@ -126,7 +127,7 @@ namespace ospray { } void parseShape(Model &model, const affine3f &xfm, xml::Node *root) { - for (int childID=0;childIDchild.size();childID++) { + for (size_t childID = 0; childID < root->child.size(); childID++) { xml::Node *node = root->child[childID]; if (node->name == "Appearance") { @@ -153,7 +154,7 @@ namespace ospray { affine3f xfm = parentXFM; // TODO: parse actual xfm parmeters ... - for (int childID=0;childIDchild.size();childID++) { + for (size_t childID = 0; childID < root->child.size(); childID++) { xml::Node *node = root->child[childID]; if (node->name == "DirectionalLight") { @@ -170,7 +171,8 @@ namespace ospray { continue; } - throw std::runtime_error("importX3D: unknown 'transform' child type '"+node->name+"'"); + throw std::runtime_error("importX3D: unknown 'transform' child type '" + + node->name + "'"); } } void parseX3D(Model &model, xml::Node *root) @@ -179,7 +181,7 @@ namespace ospray { assert(root->child[0]->name == "head"); assert(root->child[1]->name == "Scene"); xml::Node *sceneNode = root->child[1]; - for (int childID=0;childIDchild.size();childID++) { + for (size_t childID = 0; childID < sceneNode->child.size(); childID++) { xml::Node *node = sceneNode->child[childID]; if (node->name == "Background") { /* ignore */ diff --git a/apps/modelViewer/miniSG/importer.cpp b/apps/common/miniSG/importer.cpp similarity index 83% rename from apps/modelViewer/miniSG/importer.cpp rename to apps/common/miniSG/importer.cpp index 0e5b54ce83..198523e342 100644 --- a/apps/modelViewer/miniSG/importer.cpp +++ b/apps/common/miniSG/importer.cpp @@ -33,23 +33,15 @@ namespace ospray { model->mesh.push_back(mesh); model->instance.push_back(Instance(meshID)); mesh = NULL; - known_positions.clear(); - known_normals.clear(); - known_texcoords.clear(); } /*! find given vertex and return its ID, or add if it doesn't yet exist */ uint32_t ImportHelper::addVertex(const vec3f &position) { Assert(mesh); - if (known_positions.find(position) == known_positions.end()) { - int newID = mesh->position.size(); - mesh->position.push_back(position); - mesh->bounds.extend(position); - known_positions[position] = newID; - return newID; - } else - return known_positions[position]; + mesh->bounds.extend(position); + mesh->position.push_back(position); + return mesh->position.size() - 1; } /*! add new triangle to the mesh. may discard the triangle if it is degenerated. */ diff --git a/apps/modelViewer/miniSG/importer.h b/apps/common/miniSG/importer.h similarity index 67% rename from apps/modelViewer/miniSG/importer.h rename to apps/common/miniSG/importer.h index 793bcb11bc..76e6c9b8f8 100644 --- a/apps/modelViewer/miniSG/importer.h +++ b/apps/common/miniSG/importer.h @@ -32,23 +32,10 @@ namespace ospray { Model *model; /*!< current model we're importing a new mesh into */ Mesh *mesh; /*!< current mesh we're importing */ - /*! to tell the ID of any known position in the mesh's list of vertex positions */ - std::map known_positions; - /*! to tell the ID of any known vtx normal in the mesh's list of vertex normals */ - std::map known_normals; - /*! to tell the ID of any known texcoord in the mesh's list of texcoords */ - std::map known_texcoords; - ImportHelper(Model &model, const std::string &name = ""); - /*! find given vertex and return its ID, or add if it doesn't yet exist */ + /*! add given vertex and return its index */ uint32_t addVertex(const vec3f &position); - /*! find given vertex and return its ID, or add if it doesn't yet exist */ - uint32_t addVertex(const vec3f &position, const vec2f &texcoord); - /*! find given vertex and return its ID, or add if it doesn't yet exist */ - uint32_t addVertex(const vec3f &position, const vec3f &normal, const vec2f &texcoord); - /*! find given vertex and return its ID, or add if it doesn't yet exist */ - uint32_t addVertex(const vec3f &position, const vec3f &normal); /*! add new triangle to the mesh. may discard the triangle if it is degenerated. */ void addTriangle(const miniSG::Triangle &triangle); diff --git a/apps/common/miniSG/miniSG.cpp b/apps/common/miniSG/miniSG.cpp new file mode 100644 index 0000000000..9a304402ab --- /dev/null +++ b/apps/common/miniSG/miniSG.cpp @@ -0,0 +1,503 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "miniSG.h" + +#ifdef USE_IMAGEMAGICK +//#define MAGICKCORE_QUANTUM_DEPTH 16 +//#define MAGICKCORE_HDRI_ENABLE 1 +# include +using namespace Magick; +# ifndef MaxRGB +# define MaxRGB QuantumRange +# endif +#endif + +namespace ospray { +namespace miniSG { + +Texture2D::Texture2D() + : channels(0) + , depth(0) + , width(0) + , height(0) + , data(nullptr) +{} + +Material::Material() +{ + // setParam( "Kd", vec3f(.7f) ); + // setParam( "Ks", vec3f(0.f) ); + // setParam( "Ka", vec3f(0.f) ); +} + +Texture2D *loadTexture(const std::string &_path, const std::string &fileNameBase, const bool prefereLinear) +{ + std::string path = _path; + FileName fileName = path+"/"+fileNameBase; + + if (fileNameBase.size() > 0) { + if (fileNameBase.substr(0,1) == "/") {// Absolute path. + fileName = fileNameBase; + path = ""; + } + } + + static std::map textureCache; + if (textureCache.find(fileName.str()) != textureCache.end()) + return textureCache[fileName.str()]; + + Texture2D *tex = nullptr; + const std::string ext = fileName.ext(); + if (ext == "ppm") { + try { + int rc, peekchar; + + // open file + FILE *file = fopen(fileName.str().c_str(),"rb"); + const int LINESZ=10000; + char lineBuf[LINESZ+1]; + + if (!file) + throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); + + // read format specifier: + int format=0; + rc = fscanf(file,"P%i\n",&format); + if (format != 6) + throw std::runtime_error("#osp:miniSG: can currently load only binary P6 subformats for PPM texture files. " + "Please report this bug at ospray.github.io."); + + // skip all comment lines + peekchar = getc(file); + while (peekchar == '#') { + auto tmp = fgets(lineBuf,LINESZ,file); + (void)tmp; + peekchar = getc(file); + } ungetc(peekchar,file); + + // read width and height from first non-comment line + int width=-1,height=-1; + rc = fscanf(file,"%i %i\n",&width,&height); + if (rc != 2) + throw std::runtime_error("#osp:miniSG: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + + // skip all comment lines + peekchar = getc(file); + while (peekchar == '#') { + auto tmp = fgets(lineBuf,LINESZ,file); + (void)tmp; + peekchar = getc(file); + } ungetc(peekchar,file); + + // read maxval + int maxVal=-1; + rc = fscanf(file,"%i",&maxVal); + peekchar = getc(file); + + if (rc != 1) + throw std::runtime_error("#osp:miniSG: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + if (maxVal != 255) + throw std::runtime_error("#osp:miniSG: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + + tex = new Texture2D; + tex->width = width; + tex->height = height; + tex->channels = 3; + tex->depth = 1; + tex->prefereLinear = prefereLinear; + tex->data = new unsigned char[width*height*3]; + rc = fread(tex->data,width*height*3,1,file); + // flip in y, because OSPRay's textures have the origin at the lower left corner + unsigned char *texels = (unsigned char *)tex->data; + for (int y = 0; y < height/2; y++) + for (int x = 0; x < width*3; x++) + std::swap(texels[y*width*3+x], texels[(height-1-y)*width*3+x]); + } catch(std::runtime_error e) { + std::cerr << e.what() << std::endl; + } + } else if (ext == "pfm") { + try { + // Note: the PFM file specification does not support comments thus we don't skip any + // http://netpbm.sourceforge.net/doc/pfm.html + int rc = 0; + FILE *file = fopen(fileName.str().c_str(), "rb"); + const int LINESZ = 10000; + char lineBuf[LINESZ + 1]; + if (!file) { + throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); + } + // read format specifier: + // PF: color floating point image + // Pf: grayscae floating point image + char format[2] = {0}; + fscanf(file, "%c%c\n", &format[0], &format[1]); + if (format[0] != 'P' || (format[1] != 'F' && format[1] != 'f')){ + throw std::runtime_error("#osp:miniSG: invalid pfm texture file, header is not PF or Pf"); + } + int numChannels = 3; + if (format[1] == 'f') { + numChannels = 1; + } + + // read width and height + int width = -1; + int height = -1; + rc = fscanf(file, "%i %i\n", &width, &height); + if (rc != 2) { + throw std::runtime_error("#osp:miniSG: could not parse width and height in PF PFM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + + // read scale factor/endiannes + float scaleEndian = 0.0; + rc = fscanf(file, "%f\n", &scaleEndian); + + if (rc != 1) { + throw std::runtime_error("#osp:miniSG: could not parse scale factor/endianness in PF PFM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + if (scaleEndian == 0.0) { + throw std::runtime_error("#osp:miniSG: scale factor/endianness in PF PFM file can not be 0"); + } + if (scaleEndian > 0.0) { + throw std::runtime_error("#osp:miniSG: could not parse PF PFM file '"+fileName.str()+"': currently supporting only little endian formats" + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + float scaleFactor = std::abs(scaleEndian); + + tex = new Texture2D; + tex->width = width; + tex->height = height; + tex->channels = numChannels; + tex->depth = sizeof(float); + tex->prefereLinear = prefereLinear; + tex->data = new unsigned char[width * height * numChannels * sizeof(float)]; + fread(tex->data, sizeof(float), width * height * numChannels, file); + // flip in y, because OSPRay's textures have the origin at the lower left corner + float *texels = (float *)tex->data; + for (size_t y = 0; y < height / 2; ++y) { + for (size_t x = 0; x < width * numChannels; ++x) { + // Scale the pixels by the scale factor + texels[y * width * numChannels + x] = texels[y * width * numChannels + x] * scaleFactor; + texels[(height - 1 - y) * width * numChannels + x] = texels[(height - 1 - y) * width * numChannels + x] * scaleFactor; + std::swap(texels[y * width * numChannels + x], texels[(height - 1 - y) * width * numChannels + x]); + } + } + } catch(std::runtime_error e) { + std::cerr << e.what() << std::endl; + } + } + else { +#ifdef USE_IMAGEMAGICK + Magick::Image image(fileName.str().c_str()); + tex = new Texture2D; + tex->width = image.columns(); + tex->height = image.rows(); + tex->channels = image.matte() ? 4 : 3; + tex->depth = 4; + tex->prefereLinear = prefereLinear; + float rcpMaxRGB = 1.0f/float(MaxRGB); + const Magick::PixelPacket* pixels = image.getConstPixels(0,0,tex->width,tex->height); + if (!pixels) { + std::cerr << "#osp:minisg: failed to load texture '"+fileName.str()+"'" << std::endl; + delete tex; + tex = nullptr; + } else { + tex->data = new float[tex->width*tex->height*tex->channels]; + // convert pixels and flip image (because OSPRay's textures have the origin at the lower left corner) + for (size_t y=0; yheight; y++) { + for (size_t x=0; xwidth; x++) { + const Magick::PixelPacket &pixel = pixels[y*tex->width+x]; + float *dst = &((float*)tex->data)[(x+(tex->height-1-y)*tex->width)*tex->channels]; + *dst++ = pixel.red * rcpMaxRGB; + *dst++ = pixel.green * rcpMaxRGB; + *dst++ = pixel.blue * rcpMaxRGB; + if (tex->channels == 4) + *dst++ = pixel.opacity * rcpMaxRGB; + } + } + } +#endif + } + textureCache[fileName.str()] = tex; + return tex; +} + + +float Material::getParam(const char *name, float defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::FLOAT && "Param type mismatch" ); + return it->second->f[0]; + } + + return defaultVal; +} + +vec2f Material::getParam(const char *name, vec2f defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::FLOAT_2 && "Param type mismatch" ); + return vec2f(it->second->f[0], it->second->f[1]); + } + + return defaultVal; +} + +vec3f Material::getParam(const char *name, vec3f defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::FLOAT_3 && "Param type mismatch" ); + return vec3f( it->second->f[0], it->second->f[1], it->second->f[2] ); + } + + return defaultVal; +} + +vec4f Material::getParam(const char *name, vec4f defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::FLOAT_4 && "Param type mismatch" ); + return vec4f( it->second->f[0], it->second->f[1], it->second->f[2], it->second->f[3] ); + } + + return defaultVal; +} + +int32_t Material::getParam(const char *name, int32_t defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::INT && "Param type mismatch" ); + return it->second->i[0]; + } + + return defaultVal; +} + +vec2i Material::getParam(const char *name, vec2i defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::INT_2 && "Param type mismatch" ); + return vec2i(it->second->i[0], it->second->i[1]); + } + + return defaultVal; +} + +vec3i Material::getParam(const char *name, vec3i defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::INT_3 && "Param type mismatch" ); + return vec3i(it->second->i[0], it->second->i[1], it->second->i[2]); + } + + return defaultVal; +} + +vec4i Material::getParam(const char *name, vec4i defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::INT_4 && "Param type mismatch" ); + return vec4i(it->second->i[0], it->second->i[1], it->second->i[2], it->second->i[3]); + } + + return defaultVal; +} + + +uint32_t Material::getParam(const char *name, uint32_t defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::UINT && "Param type mismatch" ); + return it->second->ui[0]; + } + + return defaultVal; +} + +vec2ui Material::getParam(const char *name, vec2ui defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::UINT_2 && "Param type mismatch" ); + return vec2ui(it->second->ui[0], it->second->ui[1]); + } + + return defaultVal; +} + +vec3ui Material::getParam(const char *name, vec3ui defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::UINT_3 && "Param type mismatch" ); + return vec3ui(it->second->ui[0], it->second->ui[1], it->second->ui[2]); + } + + return defaultVal; +} + +vec4ui Material::getParam(const char *name, vec4ui defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::UINT_4 && "Param type mismatch" ); + return vec4ui(it->second->ui[0], it->second->ui[1], it->second->ui[2], it->second->ui[3]); + } + + return defaultVal; +} + +const char *Material::getParam(const char *name, const char *defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::STRING && "Param type mismatch" ); + return it->second->s; + } + + return defaultVal; +} + +void *Material::getParam(const char *name, void *defaultVal) +{ + ParamMap::iterator it = params.find(name); + if (it != params.end()) { + assert( it->second->type == Param::TEXTURE /*|| other 'data' types*/&& "Param type mismatch" ); + return it->second->ptr; + } + + return defaultVal; +} + +void error(const std::string &err) +{ + throw std::runtime_error("ospray::miniSG fatal error : "+err); +} + +bool operator==(const Instance &a, const Instance &b) +{ return a.meshID == b.meshID && a.xfm == b.xfm; } + +bool operator!=(const Instance &a, const Instance &b) +{ return !(a==b); } + + +/*! computes and returns the world-space bounding box of given mesh */ +box3f Mesh::getBBox() +{ + if (bounds.empty()) { + for (size_t i = 0; i < position.size(); i++) + bounds.extend(position[i]); + } + return bounds; +} + +/*! computes and returns the world-space bounding box of the entire model */ +box3f Model::getBBox() +{ + // this does not yet properly support instancing with transforms! + box3f bBox = ospcommon::empty; + if (!instance.empty()) { + std::vector meshBounds; + for (size_t i = 0; i < mesh.size(); i++) + meshBounds.push_back(mesh[i]->getBBox()); + for (size_t i = 0;i < instance.size(); i++) { + box3f b_i = meshBounds[instance[i].meshID]; + vec3f corner; + for (int iz=0;iz<2;iz++) { + corner.z = iz?b_i.upper.z:b_i.lower.z; + for (int iy=0;iy<2;iy++) { + corner.y = iy?b_i.upper.y:b_i.lower.y; + for (int ix=0;ix<2;ix++) { + corner.x = ix?b_i.upper.x:b_i.lower.x; + bBox.extend(xfmPoint(instance[i].xfm,corner)); + } + } + } + } + } else { + for (size_t i = 0; i < mesh.size(); i++) + bBox.extend(mesh[i]->getBBox()); + } + return bBox; +} + +size_t Model::numUniqueTriangles() const +{ + size_t sum = 0; + for (size_t i = 0; i < mesh.size(); i++) + sum += mesh[i]->triangle.size(); + return sum; +} + +OSPTexture2D createTexture2D(Texture2D *msgTex) +{ + if(msgTex == NULL) { + static int numWarnings = 0; + if (++numWarnings < 10) + std::cout << "WARNING: material does not have Textures (only warning for the first 10 times)!" << std::endl; + return NULL; + } + + static std::map alreadyCreatedTextures; + if (alreadyCreatedTextures.find(msgTex) != alreadyCreatedTextures.end()) + return alreadyCreatedTextures[msgTex]; + + //TODO: We need to come up with a better way to handle different possible pixel layouts + OSPTextureFormat type = OSP_TEXTURE_R8; + + if (msgTex->depth == 1) { + if( msgTex->channels == 1 ) type = OSP_TEXTURE_R8; + if( msgTex->channels == 3 ) + type = msgTex->prefereLinear ? OSP_TEXTURE_RGB8 : OSP_TEXTURE_SRGB; + if( msgTex->channels == 4 ) + type = msgTex->prefereLinear ? OSP_TEXTURE_RGBA8 : OSP_TEXTURE_SRGBA; + } else if (msgTex->depth == 4) { + if( msgTex->channels == 1 ) type = OSP_TEXTURE_R32F; + if( msgTex->channels == 3 ) type = OSP_TEXTURE_RGB32F; + if( msgTex->channels == 4 ) type = OSP_TEXTURE_RGBA32F; + } + + vec2i texSize(msgTex->width, msgTex->height); + OSPTexture2D ospTex = ospNewTexture2D( (osp::vec2i&)texSize, + type, + msgTex->data, + 0); + + alreadyCreatedTextures[msgTex] = ospTex; + + ospCommit(ospTex); + //g_tex = ospTex; // remember last texture for debugging + + return ospTex; +} + +} // ::ospray::minisg +} // ::ospray + diff --git a/apps/modelViewer/miniSG/miniSG.h b/apps/common/miniSG/miniSG.h similarity index 94% rename from apps/modelViewer/miniSG/miniSG.h rename to apps/common/miniSG/miniSG.h index 1985f20a83..e82ce7679f 100644 --- a/apps/modelViewer/miniSG/miniSG.h +++ b/apps/common/miniSG/miniSG.h @@ -19,14 +19,12 @@ // ospray PUBLIC api #include "ospray/ospray.h" // ospcommon -#include "common/common.h" -#include "common/RefCount.h" -#include "common/box.h" -#include "common/AffineSpace.h" -#include "common/FileName.h" - -// // embree -// #include "common/sys/filename.h" +#include "ospcommon/common.h" +#include "ospcommon/RefCount.h" +#include "ospcommon/box.h" +#include "ospcommon/AffineSpace.h" +#include "ospcommon/FileName.h" + // stl #include #include @@ -74,18 +72,17 @@ namespace ospray { } DataType; void clear() { - Assert2(this != NULL, "Tried to clear a null parameter"); switch( type ) { case STRING: if (s) free((void*)s); - s = NULL; + s = nullptr; break; case TEXTURE: - ptr = NULL; + ptr = nullptr; break; default: type = TEXTURE; - ptr = NULL; + ptr = nullptr; }; } @@ -116,7 +113,7 @@ namespace ospray { void set(vec4ui v) { clear(); type = UINT_3; i[0] = v.x; i[1] = v.y; i[2] = v.z; i[3] = v.w; } Param() { - s = NULL; + s = nullptr; type = INT; } union { @@ -217,17 +214,14 @@ namespace ospray { }; struct Instance : public RefCount { - Instance() : meshID(0), xfm(ospcommon::one), ospGeometry(NULL) {} - Instance(int meshID, affine3f xfm=ospcommon::one) - : meshID(meshID), xfm(xfm), ospGeometry(NULL) - {}; + Instance() : meshID(0), xfm(ospcommon::one), ospGeometry(nullptr) {} + Instance(int meshID, affine3f xfm = ospcommon::one) + : meshID(meshID), xfm(xfm), ospGeometry(nullptr) {} Instance(const Instance &o) - : meshID(o.meshID), xfm(o.xfm), ospGeometry(o.ospGeometry) - {} + : meshID(o.meshID), xfm(o.xfm), ospGeometry(o.ospGeometry) {} - affine3f xfm; int meshID; - + affine3f xfm; OSPGeometry ospGeometry; }; @@ -276,5 +270,6 @@ namespace ospray { void error(const std::string &err); - } // ::ospray::minisg + OSPTexture2D createTexture2D(Texture2D *msgTex); + } // ::ospray::miniSG } // ::ospray diff --git a/apps/common/ospray_cpp/CMakeLists.txt b/apps/common/ospray_cpp/CMakeLists.txt new file mode 100644 index 0000000000..d8eb5dc797 --- /dev/null +++ b/apps/common/ospray_cpp/CMakeLists.txt @@ -0,0 +1,32 @@ +## ======================================================================== ## +## Copyright 2009-2016 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +OSPRAY_INSTALL_SDK_HEADERS( + Camera.h + Data.h + FrameBuffer.h + Geometry.h + Light.h + ManagedObject.h + Material.h + Model.h + PixelOp.h + Renderer.h + Texture2D.h + TransferFunction.h + Volume.h + DESTINATION ../ospray_cpp +) diff --git a/apps/common/ospray_cpp/Camera.h b/apps/common/ospray_cpp/Camera.h new file mode 100644 index 0000000000..9f1d384ee0 --- /dev/null +++ b/apps/common/ospray_cpp/Camera.h @@ -0,0 +1,56 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class Camera : public ManagedObject_T +{ +public: + + Camera(const std::string &type); + Camera(const Camera ©); + Camera(OSPCamera existing = nullptr); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Camera::Camera(const std::string &type) +{ + OSPCamera c = ospNewCamera(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPCamera!"); + } +} + +inline Camera::Camera(const Camera ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Camera::Camera(OSPCamera existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Data.h b/apps/common/ospray_cpp/Data.h new file mode 100644 index 0000000000..b7171a7004 --- /dev/null +++ b/apps/common/ospray_cpp/Data.h @@ -0,0 +1,53 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class Data : public ManagedObject_T +{ +public: + + Data(size_t numItems, OSPDataType format, + const void *init = nullptr, int flags = 0); + Data(const Data ©); + Data(OSPData existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Data::Data(size_t numItems, OSPDataType format, + const void *init, int flags) +{ + m_object = ospNewData(numItems, format, init, flags); +} + +inline Data::Data(const Data ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Data::Data(OSPData existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/FrameBuffer.h b/apps/common/ospray_cpp/FrameBuffer.h new file mode 100644 index 0000000000..e93c565a9d --- /dev/null +++ b/apps/common/ospray_cpp/FrameBuffer.h @@ -0,0 +1,136 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +namespace ospray { +namespace cpp { + +class FrameBuffer : public ManagedObject_T +{ +public: + + FrameBuffer() = default;//NOTE(jda) - this does *not* create the underlying + // OSP object + FrameBuffer(const osp::vec2i &size, + OSPFrameBufferFormat format = OSP_FB_SRGBA, + int channels = OSP_FB_COLOR); + FrameBuffer(const FrameBuffer ©); + FrameBuffer(FrameBuffer &&move); + FrameBuffer(OSPFrameBuffer existing); + + FrameBuffer& operator=(const FrameBuffer ©); + FrameBuffer& operator=( FrameBuffer &&move); + + ~FrameBuffer(); + + void setPixelOp(PixelOp &p); + void setPixelOp(OSPPixelOp p); + + const void *map(OSPFrameBufferChannel channel); + void unmap(void *ptr); + void clear(uint32_t channel); + +private: + + void free(); + + bool m_owner = true; +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline FrameBuffer::FrameBuffer(const osp::vec2i &size, + OSPFrameBufferFormat format, + int channels) +{ + m_object = ospNewFrameBuffer(size, format, channels); +} + +inline FrameBuffer::FrameBuffer(const FrameBuffer ©) : + ManagedObject_T(copy.handle()), + m_owner(false) +{ +} + +inline FrameBuffer::FrameBuffer(FrameBuffer &&move) : + ManagedObject_T(move.handle()) +{ + move.m_object = nullptr; +} + +inline FrameBuffer::FrameBuffer(OSPFrameBuffer existing) : + ManagedObject_T(existing) +{ +} + +inline FrameBuffer& FrameBuffer::operator=(const FrameBuffer ©) +{ + free(); + m_object = copy.m_object; + return *this; +} + +inline FrameBuffer& FrameBuffer::operator=(FrameBuffer &&move) +{ + free(); + m_object = move.m_object; + move.m_object = nullptr; + return *this; +} + +inline FrameBuffer::~FrameBuffer() +{ + free(); +} + +inline void FrameBuffer::setPixelOp(PixelOp &p) +{ + setPixelOp(p.handle()); +} + +inline void FrameBuffer::setPixelOp(OSPPixelOp p) +{ + ospSetPixelOp(handle(), p); +} + +inline const void *FrameBuffer::map(OSPFrameBufferChannel channel) +{ + return ospMapFrameBuffer(handle(), channel); +} + +inline void FrameBuffer::unmap(void *ptr) +{ + ospUnmapFrameBuffer(ptr, handle()); +} + +inline void FrameBuffer::clear(uint32_t channel) +{ + ospFrameBufferClear(handle(), channel); +} + +inline void FrameBuffer::free() +{ + if (m_owner && handle()) { + ospFreeFrameBuffer(handle()); + } +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Geometry.h b/apps/common/ospray_cpp/Geometry.h new file mode 100644 index 0000000000..fdd74f562a --- /dev/null +++ b/apps/common/ospray_cpp/Geometry.h @@ -0,0 +1,70 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +namespace ospray { +namespace cpp { + +class Geometry : public ManagedObject_T +{ +public: + + Geometry(const std::string &type); + Geometry(const Geometry ©); + Geometry(OSPGeometry existing); + + void setMaterial(Material &m); + void setMaterial(OSPMaterial m); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Geometry::Geometry(const std::string &type) +{ + OSPGeometry c = ospNewGeometry(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPGeometry!"); + } +} + +inline Geometry::Geometry(const Geometry ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Geometry::Geometry(OSPGeometry existing) : + ManagedObject_T(existing) +{ +} + +inline void Geometry::setMaterial(Material &m) +{ + setMaterial(m.handle()); +} + +inline void Geometry::setMaterial(OSPMaterial m) +{ + ospSetMaterial(handle(), m); +} + +}// namespace cpp +}// namespace ospray diff --git a/ospray/math/operator_vec3TW.ih b/apps/common/ospray_cpp/Light.h similarity index 67% rename from ospray/math/operator_vec3TW.ih rename to apps/common/ospray_cpp/Light.h index 02499c45d2..7dbf108bbb 100644 --- a/ospray/math/operator_vec3TW.ih +++ b/apps/common/ospray_cpp/Light.h @@ -14,33 +14,32 @@ // limitations under the License. // // ======================================================================== // -inline __RWIDTH __vecT __OPFCT(const __AWIDTH __vecT &a, const __BWIDTH __vecT &b) +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class Light : public ManagedObject_T { - __RWIDTH __vecT r; - r.x = a.x __OP b.x; - r.y = a.y __OP b.y; - r.z = a.z __OP b.z; - return r; -} +public: + + Light(const Light ©); + Light(OSPLight existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// -inline __RWIDTH __vecT __OPFCT(const __AWIDTH __vecT &a, const __BWIDTH __scaT &b) +inline Light::Light(const Light ©) : + ManagedObject_T(copy.handle()) { - __RWIDTH __vecT r; - r.x = a.x __OP b; - r.y = a.y __OP b; - r.z = a.z __OP b; - return r; } -inline __RWIDTH __vecT __OPFCT(const __AWIDTH __scaT &a, const __BWIDTH __vecT &b) +inline Light::Light(OSPLight existing) : + ManagedObject_T(existing) { - __RWIDTH __vecT r; - r.x = a __OP b.x; - r.y = a __OP b.y; - r.z = a __OP b.z; - return r; } -#undef __AWIDTH -#undef __BWIDTH -#undef __RWIDTH +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/ManagedObject.h b/apps/common/ospray_cpp/ManagedObject.h new file mode 100644 index 0000000000..cbc4312904 --- /dev/null +++ b/apps/common/ospray_cpp/ManagedObject.h @@ -0,0 +1,290 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +#include +#include "ospcommon/vec.h" + +namespace ospray { + namespace cpp { + + class ManagedObject + { + + public: + + // string + virtual void set(const std::string &name, const std::string &v) = 0; + + // int + virtual void set(const std::string &name, int v) = 0; + virtual void set(const std::string &name, int v1, int v2) = 0; + virtual void set(const std::string &name, int v1, int v2, int v3) = 0; + + // float + virtual void set(const std::string &name, float v) = 0; + virtual void set(const std::string &name, float v1, float v2) = 0; + virtual void set(const std::string &name, float v1, float v2, float v3) = 0; + + // double + virtual void set(const std::string &name, double v) = 0; + virtual void set(const std::string &name, double v1, double v2) = 0; + virtual void set(const std::string &name, double v1, double v2, double v3)= 0; + + // ospcommon::vec2 + virtual void set(const std::string &name, const ospcommon::vec2i &v) = 0; + virtual void set(const std::string &name, const ospcommon::vec2f &v) = 0; + + // ospcommon::vec3 + virtual void set(const std::string &name, const ospcommon::vec3i &v) = 0; + virtual void set(const std::string &name, const ospcommon::vec3f &v) = 0; + + // ospcommon::vec4 + virtual void set(const std::string &name, const ospcommon::vec4f &v) = 0; + + // void* + virtual void set(const std::string &name, void *v) = 0; + + // OSPObject* + virtual void set(const std::string &name, OSPObject v) = 0; + + //! Commit to ospray + virtual void commit() const = 0; + + //! Get the underlying generic OSPObject handle + virtual OSPObject object() const = 0; + + virtual ~ManagedObject() {} + }; + + //! \todo auto-commit mode + + template + class ManagedObject_T : public ManagedObject + { + public: + + ManagedObject_T(OSP_TYPE object = nullptr); + virtual ~ManagedObject_T(); + + void set(const std::string &name, const std::string &v) override; + + void set(const std::string &name, int v) override; + void set(const std::string &name, int v1, int v2) override; + void set(const std::string &name, int v1, int v2, int v3) override; + + void set(const std::string &name, float v) override; + void set(const std::string &name, float v1, float v2) override; + void set(const std::string &name, float v1, float v2, float v3) override; + + void set(const std::string &name, double v) override; + void set(const std::string &name, double v1, double v2) override; + void set(const std::string &name, double v1, double v2, double v3) override; + + void set(const std::string &name, const ospcommon::vec2i &v) override; + void set(const std::string &name, const ospcommon::vec2f &v) override; + + void set(const std::string &name, const ospcommon::vec3i &v) override; + void set(const std::string &name, const ospcommon::vec3f &v) override; + + void set(const std::string &name, const ospcommon::vec4f &v) override; + + void set(const std::string &name, void *v) override; + + void set(const std::string &name, OSPObject v) override; + + template + void set(const std::string &name, const ManagedObject_T &v); + + void commit() const override; + + OSPObject object() const override; + + //! Get the underlying specific OSP* handle + OSP_TYPE handle() const; + + protected: + + OSP_TYPE m_object; + }; + + // Inlined function definitions /////////////////////////////////////////////// + + template + inline ManagedObject_T::ManagedObject_T(OSP_TYPE object) : + m_object(object) + { + using OSPObject_T = typename std::remove_pointer::type; + using OtherOSP_T = typename std::remove_pointer::type; + static_assert(std::is_same::value || + std::is_base_of::value, + "ManagedObject_T can only be instantiated with " + "OSPObject (a.k.a. osp::ManagedObject*) or one of its" + "decendants (a.k.a. the OSP* family of types)."); + } + + template + inline ManagedObject_T::~ManagedObject_T() + { + } + + template + inline void ManagedObject_T::set(const std::string &name, + const std::string &v) + { + ospSetString(m_object, name.c_str(), v.c_str()); + } + + template + inline void ManagedObject_T::set(const std::string &name, int v) + { + ospSet1i(m_object, name.c_str(), v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + int v1, int v2) + { + ospSet2i(m_object, name.c_str(), v1, v2); + } + + template + inline void ManagedObject_T::set(const std::string &name, + int v1, int v2, int v3) + { + ospSet3i(m_object, name.c_str(), v1, v2, v3); + } + + template + inline void ManagedObject_T::set(const std::string &name, float v) + { + ospSet1f(m_object, name.c_str(), v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + float v1, float v2) + { + ospSet2f(m_object, name.c_str(), v1, v2); + } + + template + inline void ManagedObject_T::set(const std::string &name, + float v1, float v2, float v3) + { + ospSet3f(m_object, name.c_str(), v1, v2, v3); + } + + template + inline void ManagedObject_T::set(const std::string &name, double v) + { + ospSet1f(m_object, name.c_str(), v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + double v1, double v2) + { + ospSet2f(m_object, name.c_str(), v1, v2); + } + + template + inline void ManagedObject_T::set(const std::string &name, + double v1, double v2, double v3) + { + ospSet3f(m_object, name.c_str(), v1, v2, v3); + } + + template + inline void ManagedObject_T::set(const std::string &name, + const ospcommon::vec2i &v) + { + ospSetVec2i(m_object, name.c_str(), (const osp::vec2i&)v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + const ospcommon::vec2f &v) + { + ospSetVec2f(m_object, name.c_str(), (const osp::vec2f&)v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + const ospcommon::vec3i &v) + { + ospSetVec3i(m_object, name.c_str(), (const osp::vec3i&)v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + const ospcommon::vec3f &v) + { + ospSetVec3f(m_object, name.c_str(), (const osp::vec3f&)v); + } + + template + inline void ManagedObject_T::set(const std::string &name, + const ospcommon::vec4f &v) + { + ospSetVec4f(m_object, name.c_str(), (const osp::vec4f&)v); + } + + template + inline void ManagedObject_T::set(const std::string &name, void *v) + { + ospSetVoidPtr(m_object, name.c_str(), v); + } + + template + inline void ManagedObject_T::set(const std::string &name, OSPObject v) + { + ospSetObject(m_object, name.c_str(), v); + } + + template + template + inline void + ManagedObject_T::set(const std::string &name, + const ManagedObject_T &v) + { + ospSetObject(m_object, name.c_str(), v.handle()); + } + + template + inline void ManagedObject_T::commit() const + { + ospCommit(m_object); + } + + template + OSPObject ManagedObject_T::object() const + { + return (OSPObject)m_object; + } + + template + inline OSP_TYPE ManagedObject_T::handle() const + { + return m_object; + } + + }// namespace cpp +}// namespace ospray diff --git a/modules/seismic/SymbolRegistry.cpp b/apps/common/ospray_cpp/Material.h similarity index 64% rename from modules/seismic/SymbolRegistry.cpp rename to apps/common/ospray_cpp/Material.h index a8a7219f49..a21bda0490 100644 --- a/modules/seismic/SymbolRegistry.cpp +++ b/apps/common/ospray_cpp/Material.h @@ -14,21 +14,33 @@ // limitations under the License. // // ======================================================================== // -#include "apps/volumeViewer/loaders/OSPObjectFile.h" -#include "SeismicHorizonFile.h" -#include "SeismicVolumeFile.h" +#pragma once -// Loaders for seismic horizon files for supported self-describing formats. -OSP_REGISTER_TRIANGLEMESH_FILE(SeismicHorizonFile, dds); +#include -// Loader for seismic volume files for supported self-describing formats. -OSP_REGISTER_VOLUME_FILE(SeismicVolumeFile, dds); -OSP_REGISTER_VOLUME_FILE(SeismicVolumeFile, H); -OSP_REGISTER_VOLUME_FILE(SeismicVolumeFile, sgy); -OSP_REGISTER_VOLUME_FILE(SeismicVolumeFile, segy); +namespace ospray { +namespace cpp { -//! Module initialization function -extern "C" void ospray_init_module_seismic() +class Material : public ManagedObject_T +{ +public: + + Material() = default; + Material(const Material ©); + Material(OSPMaterial existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Material::Material(const Material ©) : + ManagedObject_T(copy.handle()) { - std::cout << "loaded module 'seismic'." << std::endl; } + +inline Material::Material(OSPMaterial existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Model.h b/apps/common/ospray_cpp/Model.h new file mode 100644 index 0000000000..0635611067 --- /dev/null +++ b/apps/common/ospray_cpp/Model.h @@ -0,0 +1,97 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +namespace ospray { +namespace cpp { + +class Model : public ManagedObject_T +{ +public: + + Model(); + Model(const Model ©); + Model(OSPModel existing); + + void addGeometry(Geometry &v); + void addGeometry(OSPGeometry v); + + void removeGeometry(Geometry &v); + void removeGeometry(OSPGeometry v); + + void addVolume(Volume &v); + void addVolume(OSPVolume v); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Model::Model() +{ + OSPModel c = ospNewModel(); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPModel!"); + } +} + +inline Model::Model(const Model ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Model::Model(OSPModel existing) : + ManagedObject_T(existing) +{ +} + +inline void Model::addGeometry(Geometry &v) +{ + addGeometry(v.handle()); +} + +inline void Model::addGeometry(OSPGeometry v) +{ + ospAddGeometry(handle(), v); +} + +inline void Model::removeGeometry(Geometry &v) +{ + removeGeometry(v.handle()); +} + +inline void Model::removeGeometry(OSPGeometry v) +{ + ospRemoveGeometry(handle(), v); +} + +inline void Model::addVolume(Volume &v) +{ + addVolume(v.handle()); +} + +inline void Model::addVolume(OSPVolume v) +{ + ospAddVolume(handle(), v); +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/PixelOp.h b/apps/common/ospray_cpp/PixelOp.h new file mode 100644 index 0000000000..64e66defae --- /dev/null +++ b/apps/common/ospray_cpp/PixelOp.h @@ -0,0 +1,58 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class PixelOp : public ManagedObject_T +{ +public: + + PixelOp() = default; + PixelOp(const std::string &type); + PixelOp(const PixelOp ©); + PixelOp(OSPPixelOp existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline PixelOp::PixelOp(const std::string &type) +{ + OSPPixelOp c = ospNewPixelOp(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPPixelOp!"); + } +} + +inline PixelOp::PixelOp(const PixelOp ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline PixelOp::PixelOp(OSPPixelOp existing) : + ManagedObject_T(existing) +{ +} + + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Renderer.h b/apps/common/ospray_cpp/Renderer.h new file mode 100644 index 0000000000..9ca3240aa8 --- /dev/null +++ b/apps/common/ospray_cpp/Renderer.h @@ -0,0 +1,86 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include +#include + +namespace ospray { +namespace cpp { + +class Renderer : public ManagedObject_T +{ +public: + + Renderer(const std::string &type); + Renderer(const Renderer ©); + Renderer(OSPRenderer existing = nullptr); + + Material newMaterial(const std::string &type); + Light newLight(const std::string &type); + + float renderFrame(const FrameBuffer &fb, uint32_t channels); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Renderer::Renderer(const std::string &type) +{ + OSPRenderer c = ospNewRenderer(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPRenderer!"); + } +} + +inline Renderer::Renderer(const Renderer ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Renderer::Renderer(OSPRenderer existing) : + ManagedObject_T(existing) +{ +} + +inline Material Renderer::newMaterial(const std::string &type) +{ + auto mat = Material(ospNewMaterial(handle(), type.c_str())); + + if (!mat.handle()) { + throw std::runtime_error("Failed to create OSPMaterial!"); + } + + return mat; +} + +inline Light Renderer::newLight(const std::string &type) +{ + return Light(ospNewLight(handle(), type.c_str())); +} + +inline float Renderer::renderFrame(const FrameBuffer &fb, uint32_t channels) +{ + return ospRenderFrame(fb.handle(), handle(), channels); +} + + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Texture2D.h b/apps/common/ospray_cpp/Texture2D.h new file mode 100644 index 0000000000..11257c0803 --- /dev/null +++ b/apps/common/ospray_cpp/Texture2D.h @@ -0,0 +1,45 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class Texture2D : public ManagedObject_T +{ +public: + + Texture2D(const Texture2D ©); + Texture2D(OSPTexture2D existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Texture2D::Texture2D(const Texture2D ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Texture2D::Texture2D(OSPTexture2D existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/modules/tachyon/TachyonRenderer.h b/apps/common/ospray_cpp/TransferFunction.h similarity index 56% rename from modules/tachyon/TachyonRenderer.h rename to apps/common/ospray_cpp/TransferFunction.h index 458937eb62..ddc5837294 100644 --- a/modules/tachyon/TachyonRenderer.h +++ b/apps/common/ospray_cpp/TransferFunction.h @@ -16,47 +16,41 @@ #pragma once -/*! \defgroup ospray_render_tachyon OSPRay's Tachyon-style renderer - - \ingroup ospray_supported_renderers - - \brief Implements the data, materials, and lighting models of John - Stone's "Tachyon" renderer as used by VMD. - */ - -// ospray -#include "ospray/render/Renderer.h" +#include namespace ospray { - - struct Camera; - struct Model; - - enum { - RC_EYELIGHT=0, - RC_PRIMID, - RC_GEOMID, - RC_INSTID, - RC_GNORMAL, - RC_TESTSHADOW, - } RC_SHADEMODE; - - /*! \brief Implements the family of simple ray cast renderers */ - struct TachyonRenderer : public Renderer { - TachyonRenderer(); - virtual void commit(); - virtual std::string toString() const { return "ospray::TachyonRenderer"; } - - Model *model; - Camera *camera; - Data *textureData; - Data *pointLightData; - void *pointLightArray; - uint32 numPointLights; - Data *dirLightData; - void *dirLightArray; - uint32 numDirLights; - bool doShadows; - }; - -} // ::ospray +namespace cpp { + +class TransferFunction : public ManagedObject_T +{ +public: + + TransferFunction(const std::string &type); + TransferFunction(const TransferFunction ©); + TransferFunction(OSPTransferFunction existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline TransferFunction::TransferFunction(const std::string &type) +{ + OSPTransferFunction c = ospNewTransferFunction(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPTransferFunction!"); + } +} + +inline TransferFunction::TransferFunction(const TransferFunction ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline TransferFunction::TransferFunction(OSPTransferFunction existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/apps/common/ospray_cpp/Volume.h b/apps/common/ospray_cpp/Volume.h new file mode 100644 index 0000000000..41305906da --- /dev/null +++ b/apps/common/ospray_cpp/Volume.h @@ -0,0 +1,56 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospray { +namespace cpp { + +class Volume : public ManagedObject_T +{ +public: + + Volume(const std::string &type); + Volume(const Volume ©); + Volume(OSPVolume existing); +}; + +// Inlined function definitions /////////////////////////////////////////////// + +inline Volume::Volume(const std::string &type) +{ + OSPVolume c = ospNewVolume(type.c_str()); + if (c) { + m_object = c; + } else { + throw std::runtime_error("Failed to create OSPVolume!"); + } +} + +inline Volume::Volume(const Volume ©) : + ManagedObject_T(copy.handle()) +{ +} + +inline Volume::Volume(OSPVolume existing) : + ManagedObject_T(existing) +{ +} + +}// namespace cpp +}// namespace ospray diff --git a/scripts/build_gitlab/win.sh b/apps/common/script/CMakeLists.txt old mode 100755 new mode 100644 similarity index 72% rename from scripts/build_gitlab/win.sh rename to apps/common/script/CMakeLists.txt index 9b5781bd18..cf572fc655 --- a/scripts/build_gitlab/win.sh +++ b/apps/common/script/CMakeLists.txt @@ -1,5 +1,5 @@ ## ======================================================================== ## -## Copyright 2015-2016 Intel Corporation ## +## Copyright 2009-2015 Intel Corporation ## ## ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## ## you may not use this file except in compliance with the License. ## @@ -14,19 +14,14 @@ ## limitations under the License. ## ## ======================================================================== ## -#!/bin/sh +ADD_LIBRARY(ospray_script + OSPRayScriptHandler.cpp + OSPRayScriptHandler.h +) -mkdir build -cd build - -cmake -L \ --G "Visual Studio 12 2013 Win64" \ --T "Intel C++ Compiler 16.0" \ --D OSPRAY_BUILD_ISA=ALL \ --D OSPRAY_BUILD_MIC_SUPPORT=OFF \ --D OSPRAY_USE_EXTERNAL_EMBREE=ON \ --D embree_DIR=../../embree/lib/cmake/embree-2.9.0 \ --D USE_IMAGE_MAGICK=OFF \ -.. - -cmake --build . --config Release --target ALL_BUILD -- -m -nologo +TARGET_LINK_LIBRARIES(ospray_script + ospray_common + ospray + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS} +) diff --git a/apps/common/script/OSPRayScriptHandler.cpp b/apps/common/script/OSPRayScriptHandler.cpp new file mode 100644 index 0000000000..e01001d693 --- /dev/null +++ b/apps/common/script/OSPRayScriptHandler.cpp @@ -0,0 +1,527 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "OSPRayScriptHandler.h" + +#include "chaiscript/chaiscript_stdlib.hpp" +#include "chaiscript/utility/utility.hpp" + +using std::runtime_error; + +using std::cerr; +using std::cin; +using std::cout; +using std::endl; + +#include +using std::string; +using std::stringstream; + +#include + +#ifdef USE_SYSTEM_READLINE +# include +# include +#else + +static char *mystrdup (const char *s) { + size_t len = strlen(s); // Space for length plus nul + char *d = static_cast(malloc (len+1)); + if (d == nullptr) return nullptr; // No memory +#if 0//MSVC + strcpy_s(d, len+1, s); // Copy the characters +#else + strncpy(d,s,len); // Copy the characters +#endif + d[len] = '\0'; + return d; // Return the new string +} + +static char* readline(const char* p) +{ + string retval; + cout << p ; + getline(std::cin, retval); + return cin.eof() ? nullptr : mystrdup(retval.c_str()); +} + + +static void add_history(const char*){} +static void using_history(){} +#endif + +// Static helper functions //////////////////////////////////////////////////// + +static std::string get_next_command() { + std::string retval("quit"); + if ( ! std::cin.eof() ) { + char *input_raw = readline("% "); + if ( input_raw ) { + add_history(input_raw); + + std::string val(input_raw); + size_t pos = val.find_first_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(0, pos); + } + pos = val.find_last_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(pos+1, std::string::npos); + } + + retval = val; + + ::free(input_raw); + } + } + return retval; +} + +// Scripting callback functions /////////////////////////////////////////////// + +namespace chaiospray { + +void ospLoadModule(const string &name) +{ + ::ospLoadModule(name.c_str()); +} + +void ospSetString(OSPObject _object, const string &id, const string &s) +{ + ::ospSetString(_object, id.c_str(), s.c_str()); +} + +void ospSetObject(OSPObject _object, const string &id, OSPObject object) +{ + ::ospSetObject(_object, id.c_str(), object); +} + +void ospSet1f(OSPObject _object, const string &id, float x) +{ + ::ospSet1f(_object, id.c_str(), x); +} + +void ospSet1i(OSPObject _object, const string &id, int x) +{ + ::ospSet1i(_object, id.c_str(), x); +} + +void ospSet2f(OSPObject _object, const string &id, float x, float y) +{ + ::ospSet2f(_object, id.c_str(), x, y); +} + +void ospSet2i(OSPObject _object, const string &id, int x, int y) +{ + ::ospSet2i(_object, id.c_str(), x, y); +} + +void ospSet3f(OSPObject _object, const string &id, float x, float y, float z) +{ + ::ospSet3f(_object, id.c_str(), x, y, z); +} + +void ospSet3i(OSPObject _object, const string &id, int x, int y, int z) +{ + ::ospSet3i(_object, id.c_str(), x, y, z); +} + +void ospSetVoidPtr(OSPObject _object, const string &id, void *v) +{ + ::ospSetVoidPtr(_object, id.c_str(), v); +} + +void ospCommit(OSPObject object) +{ + ::ospCommit(object); +} + +} + +// OSPRayScriptHandler definitions //////////////////////////////////////////// + +namespace ospray { + +OSPRayScriptHandler::OSPRayScriptHandler(OSPModel model, + OSPRenderer renderer, + OSPCamera camera) : + m_model(model), + m_renderer(renderer), + m_camera(camera), + m_chai(chaiscript::Std_Lib::library()), + m_running(false) +{ + registerScriptTypes(); + registerScriptFunctions(); + + std::stringstream ss; + ss << "Commands available:" << endl << endl; + ss << "exit --> exit command mode" << endl; + ss << "done --> synonomous with 'exit'" << endl; + ss << "quit --> synonomous with 'exit'" << endl; + ss << "run [file] --> execute a script file" << endl << endl; + + ss << "OSPRay viewer objects available:" << endl << endl; + ss << "c --> Camera" << endl; + ss << "m --> Model" << endl; + ss << "r --> Renderer" << endl << endl; + + ss << "OSPRay API functions available:" << endl << endl; + ss << "ospLoadModule(module_name)" << endl; + ss << "ospSetString(object, id, string)" << endl; + ss << "ospSetObject(object, id, object)" << endl; + ss << "ospSet1f(object, id, float)" << endl; + ss << "ospSet2f(object, id, float, float)" << endl; + ss << "ospSet3f(object, id, float, float, float)" << endl; + ss << "ospSet1i(object, id, int)" << endl; + ss << "ospSet2i(object, id, int, int)" << endl; + ss << "ospSet3i(object, id, int, int, int)" << endl; + ss << "ospSetVoidPtr(object, id, ptr)" << endl; + ss << "ospCommit(object)" << endl; + ss << endl; + + m_helpText = ss.str(); +} + +OSPRayScriptHandler::~OSPRayScriptHandler() +{ + stop(); +} + +void OSPRayScriptHandler::runScriptFromFile(const std::string &file) +{ + if (m_running) { + throw runtime_error("Cannot execute a script file when" + " running interactively!"); + } + + registerScriptObjects(); + + try { + m_chai.eval_file(file); + } catch (const chaiscript::exception::eval_error &e) { + cerr << "ERROR: script '" << file << "' executed with error(s):" << endl; + cerr << " " << e.what() << endl; + } +} + +void OSPRayScriptHandler::consoleLoop() +{ + registerScriptObjects(); + + using_history(); + + do { + std::string input = get_next_command(); + + if (input == "done" || input == "exit" || input == "quit") { + break; + } else if (input == "help") { + cout << m_helpText << endl; + continue; + } else { + stringstream ss(input); + string command, arg; + ss >> command >> arg; + if (command == "run") { + runChaiFile(arg); + continue; + } + } + + runChaiLine(input); + + } while (m_running); + + cout << "**** EXIT COMMAND MODE *****" << endl; +} + +void OSPRayScriptHandler::runChaiLine(const std::string &line) +{ + try { + m_chai.eval(line); + } catch (const chaiscript::exception::eval_error &e) { + cerr << e.what() << endl; + } +} + +void OSPRayScriptHandler::runChaiFile(const std::string &file) +{ + try { + m_chai.eval_file(file); + } catch (const std::runtime_error &e) { + cerr << e.what() << endl; + } +} + +void OSPRayScriptHandler::start() +{ + stop(); + cout << "**** START COMMAND MODE ****" << endl << endl; + cout << "Type 'help' to see available objects and functions." << endl; + cout << endl; + m_running = true; + m_thread = std::thread(&OSPRayScriptHandler::consoleLoop, this); +} + +void OSPRayScriptHandler::stop() +{ + m_running = false; + if (m_thread.joinable()) + m_thread.join(); +} + +bool OSPRayScriptHandler::running() +{ + return m_running; +} + +chaiscript::ChaiScript &OSPRayScriptHandler::scriptEngine() +{ + if (m_running) + throw runtime_error("Cannot modify the script env while running!"); + + return m_chai; +} + +void OSPRayScriptHandler::registerScriptObjects() +{ + m_chai.add(chaiscript::var(m_model), "m"); + m_chai.add(chaiscript::var(m_renderer), "r"); + m_chai.add(chaiscript::var(m_camera), "c"); +} + +void OSPRayScriptHandler::registerScriptTypes() +{ + chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); + + // Class types // + + chaiscript::utility::add_class>(*m, "ManagedObject", + { + chaiscript::constructor()>(), + chaiscript::constructor(OSPObject)>() + }, + { + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + //{chaiscript::fun(static_cast(&ospray::cpp::ManagedObject::set)), "set"}, + {chaiscript::fun(&ospray::cpp::ManagedObject::commit), "commit"}, + {chaiscript::fun(&ospray::cpp::ManagedObject::object), "handle"} + } + ); + + chaiscript::utility::add_class(*m, "Camera", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Data", + { + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "FrameBuffer", + { + chaiscript::constructor(), + chaiscript::constructor() + }, + { + {chaiscript::fun(static_cast(&ospray::cpp::FrameBuffer::setPixelOp)), "setPixelOp"}, + {chaiscript::fun(static_cast(&ospray::cpp::FrameBuffer::setPixelOp)), "setPixelOp"} + } + ); + + chaiscript::utility::add_class(*m, "Geometry", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + {chaiscript::fun(static_cast(&ospray::cpp::Geometry::setMaterial)), "setMaterial"}, + {chaiscript::fun(static_cast(&ospray::cpp::Geometry::setMaterial)), "setMaterial"} + } + ); + + chaiscript::utility::add_class(*m, "Light", + { + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Material", + { + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Model", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + {chaiscript::fun(static_cast(&ospray::cpp::Model::addGeometry)), "addGeometry"}, + {chaiscript::fun(static_cast(&ospray::cpp::Model::addGeometry)), "addGeometry"}, + {chaiscript::fun(static_cast(&ospray::cpp::Model::removeGeometry)), "removeGeometry"}, + {chaiscript::fun(static_cast(&ospray::cpp::Model::removeGeometry)), "removeGeometry"}, + {chaiscript::fun(static_cast(&ospray::cpp::Model::addVolume)), "addVolume"}, + {chaiscript::fun(static_cast(&ospray::cpp::Model::addVolume)), "addVolume"} + } + ); + + chaiscript::utility::add_class(*m, "PixelOp", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Renderer", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + {chaiscript::fun(&ospray::cpp::Renderer::newMaterial), "newMaterial"}, + {chaiscript::fun(&ospray::cpp::Renderer::newLight), "newLight"} + } + ); + + chaiscript::utility::add_class(*m, "TransferFunction", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Texture2D", + { + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + chaiscript::utility::add_class(*m, "Volume", + { + chaiscript::constructor(), + chaiscript::constructor(), + chaiscript::constructor() + }, + { + } + ); + + m_chai.add(m); + + // Inheritance relationships // + + // osp objects + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + + // ospray::cpp objects + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); + m_chai.add(chaiscript::base_class()); +} + +void OSPRayScriptHandler::registerScriptFunctions() +{ + m_chai.add(chaiscript::fun(&chaiospray::ospLoadModule), "ospLoadModule"); + m_chai.add(chaiscript::fun(&chaiospray::ospSetString), "ospSetString" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSetObject), "ospSetObject" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet1f), "ospSet1f" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet1i), "ospSet1i" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet2f), "ospSet2f" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet2i), "ospSet2i" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet3f), "ospSet3f" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSet3i), "ospSet3i" ); + m_chai.add(chaiscript::fun(&chaiospray::ospSetVoidPtr), "ospSetVoidPtr"); + m_chai.add(chaiscript::fun(&chaiospray::ospCommit), "ospCommit" ); +} + +}// namespace ospray diff --git a/apps/common/script/OSPRayScriptHandler.h b/apps/common/script/OSPRayScriptHandler.h new file mode 100644 index 0000000000..e40e474dfd --- /dev/null +++ b/apps/common/script/OSPRayScriptHandler.h @@ -0,0 +1,91 @@ +// ======================================================================== // +// Copyright 2009-2015 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ChaiScript +#include "chaiscript/chaiscript.hpp" + +namespace ospray { + +class OSPRayScriptHandler +{ +public: + + OSPRayScriptHandler(OSPModel model, OSPRenderer renderer, OSPCamera camera); + ~OSPRayScriptHandler(); + + void runScriptFromFile(const std::string &file); + + void start(); + void stop(); + + bool running(); + +protected: + + //! \brief ChaiScript engine, only accessable if the interactive thread isn't + //! running. + //! + //! \note Allow anyone who extends (inherits from) OSPRayScriptHandler to + //! have access to the engine to let them add custom functions or types. + chaiscript::ChaiScript &scriptEngine(); + + //! \note Child classes should append this string with any additional help + //! text that is desired when 'help' is invoked in the script engine. + std::string m_helpText; + +private: + + void consoleLoop(); + + void runChaiLine(const std::string &line); + void runChaiFile(const std::string &file); + + void registerScriptObjects(); + void registerScriptTypes(); + void registerScriptFunctions(); + + // Data // + + cpp::Model m_model; + cpp::Renderer m_renderer; + cpp::Camera m_camera; + + chaiscript::ChaiScript m_chai; + + bool m_running; + + //! \brief background thread to handle the scripting commands from the console + std::thread m_thread; +}; + +}// namespace ospray diff --git a/apps/common/script/chaiscript/chaiscript.hpp b/apps/common/script/chaiscript/chaiscript.hpp new file mode 100644 index 0000000000..79ef8cf41a --- /dev/null +++ b/apps/common/script/chaiscript/chaiscript.hpp @@ -0,0 +1,832 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_HPP_ +#define CHAISCRIPT_HPP_ + + + +/// @mainpage +/// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides +/// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions. +/// +/// The parts of the ChaiScript API that the average user will be concerned with are contained in the +/// chaiscript namespace and the chaiscript::ChaiScript class. +/// +/// The end user parts of the API are extremely simple both in size and ease of use. +/// +/// Currently, all source control and project management aspects of ChaiScript occur on [github](http://www.github.com/ChaiScript/ChaiScript"). +/// +/// ------------------------------------------------------------ +/// +/// @sa chaiscript +/// @sa chaiscript::ChaiScript +/// @sa ChaiScript_Language for Built in Functions +/// @sa @ref LangGettingStarted +/// @sa @ref LangKeywordRef +/// @sa @ref LangInPlaceRef +/// @sa @ref LangObjectSystemRef +/// @sa http://www.chaiscript.com +/// @sa http://www.github.com/ChaiScript/ChaiScript +/// +/// ----------------------------------------------------------- +/// +/// @section gettingstarted API Getting Started +/// +/// - @ref basics +/// - @ref compiling +/// - @ref eval +/// - @ref adding_items +/// - @ref operatoroverloading +/// - @ref add_class +/// - @ref pointer_conversions +/// - @ref baseclasses +/// - @ref functionobjects +/// - @ref threading +/// - @ref exceptions +/// +/// +/// @subsection basics Basics +/// +/// Basic simple example: +/// +/// ~~~~~~~{.cpp} +/// //main.cpp +/// #include +/// +/// double function(int i, double j) +/// { +/// return i * j; +/// } +/// +/// int main() +/// { +/// chaiscript::ChaiScript chai; +/// chai.add(&function, "function"); +/// +/// double d = chai.eval("function(3, 4.75);"); +/// } +/// ~~~~~~~ +/// +/// ------------------------------------------------------ +/// +/// @subsection compiling Compiling ChaiScript Applications +/// +/// ChaiScript is a header only library with only one dependency: The +/// operating system provided dynamic library loader, which has to be specified on some platforms. +/// +/// @subsubsection compilinggcc Compiling with GCC +/// +/// To compile the above application on a Unix like operating system (MacOS, Linux) with GCC you need to link +/// the dynamic loader. For example: +/// +/// ~~~~~~~~ +/// gcc main.cpp -I/path/to/chaiscript/headers -ldl +/// ~~~~~~~~ +/// +/// Alternatively, you may compile without threading support. +/// +/// ~~~~~~~~ +/// gcc main.cpp -I/path/to/chaiscript/headers -ldl -DCHAISCRIPT_NO_THREADS +/// ~~~~~~~~ +/// +/// ------------------------------------------ +/// +/// @subsection eval Evaluating Scripts +/// +/// Scripts can be evaluated with the () operator, eval method or eval_file method. +/// +/// @subsubsection parenoperator () Operator +/// +/// operator() can be used as a handy shortcut for evaluating ChaiScript snippets. +/// +/// ~~~~~~~~{.cpp} +/// chaiscript::ChaiScript chai; +/// chai("print(@"hello world@")"); +/// ~~~~~~~~ +/// +/// @sa chaiscript::ChaiScript::operator()(const std::string &) +/// +/// @subsubsection evalmethod Method 'eval' +/// +/// The eval method is somewhat more verbose and can be used to get type safely return values +/// from the script. +/// +/// ~~~~~~~~{.cpp} +/// chaiscript::ChaiScript chai; +/// chai.eval("callsomefunc()"); +/// int result = chai.eval("1 + 3"); +/// // result now equals 4 +/// ~~~~~~~~ +/// +/// @sa chaiscript::ChaiScript::eval +/// +/// @subsubsection evalfilemethod Method 'eval_file' +/// +/// The 'eval_file' method loads a file from disk and executes the script in it +/// +/// ~~~~~~~~{.cpp} +/// chaiscript::ChaiScript chai; +/// chai.eval_file("myfile.chai"); +/// std::string result = chai.eval_file("myfile.chai") // extract the last value returned from the file +/// ~~~~~~~~ +/// +/// @sa chaiscript::ChaiScript::eval_file +/// +/// -------------------------------------------------- +/// +/// @subsection adding_items Adding Items to ChaiScript +/// +/// ChaiScript supports 4 basic things that can be added: objects, functions, type infos and Modules +/// +/// @subsubsection adding_objects Adding Objects +/// +/// Named objects can be created with the chaiscript::var function. Note: adding a object +/// adds it to the current thread scope, not to a global scope. If you have multiple +/// threads that need to access the same variables you will need to add them +/// separately for each thread, from the thread itself. +/// +/// ~~~~~~~~~{.cpp} +/// using namespace chaiscript; +/// ChaiScript chai; +/// int i = 5; +/// chai.add(var(i), "i"); +/// chai("print(i)"); +/// ~~~~~~~~~ +/// +/// Immutable objects can be created with the chaiscript::const_var function. +/// +/// ~~~~~~~~~{.cpp} +/// chai.add(const_var(i), "i"); +/// chai("i = 5"); // exception throw, cannot assign const var +/// ~~~~~~~~~ +/// +/// Named variables can only be accessed from the context they are created in. +/// If you want a global variable, it must be const, and created with the +/// chaiscript::ChaiScript::add_global_const function. +/// +/// ~~~~~~~~~{.cpp} +/// chai.add_global_const(const_var(i), "i"); +/// chai("def somefun() { print(i); }; somefun();"); +/// ~~~~~~~~~ +/// +/// @subsubsection adding_functions Adding Functions +/// +/// Functions, methods and members are all added using the same function: chaiscript::fun. +/// +/// ~~~~~~~~~{.cpp} +/// using namespace chaiscript; +/// +/// class MyClass { +/// public: +/// int memberdata; +/// void method(); +/// void method2(int); +/// static void staticmethod(); +/// void overloadedmethod(); +/// void overloadedmethod(const std::string &); +/// }; +/// +/// ChaiScript chai; +/// chai.add(fun(&MyClass::memberdata), "memberdata"); +/// chai.add(fun(&MyClass::method), "method"); +/// chai.add(fun(&MyClass::staticmethod), "staticmethod"); +/// ~~~~~~~~~ +/// +/// Overloaded methods will need some help, to hint the compiler as to which overload you want: +/// +/// ~~~~~~~~~{.cpp} +/// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); +/// chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); +/// ~~~~~~~~~ +/// +/// There are also shortcuts built into chaiscript::fun for binding up to the first two parameters of the function. +/// +/// ~~~~~~~~~{.cpp} +/// MyClass obj; +/// chai.add(fun(&MyClass::method, &obj), "method"); +/// chai("method()"); // equiv to obj.method() +/// chai.add(fun(&MyClass::method2, &obj, 3), "method2"); +/// chai("method2()"); // equiv to obj.method2(3) +/// ~~~~~~~~~ +/// +/// @subsubsection addingtypeinfo Adding Type Info +/// +/// ChaiScript will automatically support any type implicitly provided to it in the form +/// of objects and function parameters / return types. However, it can be nice to let ChaiScript +/// know more details about the types you are giving it. For instance, the "clone" functionality +/// cannot work unless there is a copy constructor registered and the name of the type is known +/// (so that ChaiScript can look up the copy constructor). +/// +/// Continuing with the example "MyClass" from above: +/// +/// ~~~~~~~~{.cpp} +/// chai.add(user_type(), "MyClass"); +/// ~~~~~~~~ +/// +/// @subsubsection adding_modules Adding Modules +/// +/// Modules are holders for collections of ChaiScript registrations. +/// +/// ~~~~~~~~{.cpp} +/// ModulePtr module = get_sum_module(); +/// chai.add(module); +/// ~~~~~~~~ +/// +/// @sa chaiscript::Module +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection operatoroverloading Operator Overloading +/// +/// Operators are just like any other function in ChaiScript, to overload an operator, simply register it. +/// +/// ~~~~~~~~{.cpp} +/// class MyClass { +/// MyClass operator+(const MyClass &) const; +/// }; +/// +/// chai.add(fun(&MyClass::operator+), "+"); +/// +/// std::string append_string_int(const std::string &t_lhs, int t_rhs) +/// { +/// std::stringstream ss; +/// ss << t_lhs << t_rhs; +/// return ss.str(); +/// } +/// +/// chai.add(fun(append_string_int), "+"); +/// ~~~~~~~~ +/// +/// @sa @ref adding_functions +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection add_class Class Helper Utility +/// +/// Much of the work of adding new classes to ChaiScript can be reduced with the help +/// of the add_class helper utility. +/// +/// ~~~~~~~~{.cpp} +/// class Test +/// { +/// public: +/// void function() {} +/// std::string function2() { return "Function2"; } +/// void function3() {} +/// std::string functionOverload(double) { return "double"; } +/// std::string functionOverload(int) { return "int"; } +/// }; +/// +/// int main() +/// { +/// chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); +/// +/// chaiscript::utility::add_class(*m, +/// "Test", +/// { constructor(), +/// constructor() }, +/// { {fun(&Test::function), "function"}, +/// {fun(&Test::function2), "function2"}, +/// {fun(&Test::function2), "function3"} +/// {fun(static_cast(&Test::functionOverload)), "functionOverload"} +/// {fun(static_cast(&Test::functionOverload)), "functionOverload"} } +/// ); +/// +/// +/// chaiscript::ChaiScript chai; +/// chai.add(m); +/// } +/// ~~~~~~~~ +/// +/// @sa @ref adding_modules +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection pointer_conversions Pointer / Object Conversions +/// +/// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr, +/// std::shared_ptr, std::reference_wrapper, std::reference_wrapper and value types automatically. +/// +/// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference counting). +/// Const may be added, but never removed. +/// +/// The take away is that you can pretty much expect function calls to Just Work when you need them to. +/// +/// ~~~~~~~~{.cpp} +/// void fun1(const int *); +/// void fun2(int *); +/// void fun3(int); +/// void fun4(int &); +/// void fun5(const int &); +/// void fun5(std::shared_ptr); +/// void fun6(std::shared_ptr); +/// void fun7(const std::shared_ptr &); +/// void fun8(const std::shared_ptr &); +/// void fun9(std::reference_wrapper); +/// void fun10(std::reference_wrapper); +/// +/// int main() +/// { +/// using namespace chaiscript +/// chaiscript::ChaiScript chai; +/// chai.add(fun(fun1), "fun1"); +/// chai.add(fun(fun2), "fun2"); +/// chai.add(fun(fun3), "fun3"); +/// chai.add(fun(fun4), "fun4"); +/// chai.add(fun(fun5), "fun5"); +/// chai.add(fun(fun6), "fun6"); +/// chai.add(fun(fun7), "fun7"); +/// chai.add(fun(fun8), "fun8"); +/// chai.add(fun(fun9), "fun9"); +/// chai.add(fun(fun10), "fun10"); +/// +/// chai("var i = 10;"); +/// chai("fun1(i)"); +/// chai("fun2(i)"); +/// chai("fun3(i)"); +/// chai("fun4(i)"); +/// chai("fun5(i)"); +/// chai("fun6(i)"); +/// chai("fun7(i)"); +/// chai("fun8(i)"); +/// chai("fun9(i)"); +/// chai("fun10(i)"); +/// } +/// ~~~~~~~~ +/// +/// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of the automatic casts that +/// available and tested. +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection baseclasses Base Classes +/// +/// ChaiScript supports handling of passing a derived class object to a function expecting a base class object. +/// For the process to work, the base/derived relationship must be registered with the engine. +/// +/// ~~~~~~~~{.cpp} +/// class Base {}; +/// class Derived : public Base {}; +/// void myfunction(Base *b); +/// +/// int main() +/// { +/// chaiscript::ChaiScript chai; +/// chai.add(chaiscript::base_class()); +/// Derived d; +/// chai.add(chaiscript::var(&d), "d"); +/// chai.add(chaiscript::fun(&myfunction), "myfunction"); +/// chai("myfunction(d)"); +/// } +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection functionobjects Function Objects +/// +/// Functions are first class objects in ChaiScript and ChaiScript supports automatic conversion +/// between ChaiScript functions and std::function objects. +/// +/// ~~~~~~~~{.cpp} +/// void callafunc(const std::function &t_func) +/// { +/// t_func("bob"); +/// } +/// +/// int main() +/// { +/// chaiscript::ChaiScript chai; +/// chai.add(chaiscript::fun(&callafunc), "callafunc"); +/// chai("callafunc(fun(x) { print(x); })"); // pass a lambda function to the registered function +/// // which expects a typed std::function +/// +/// std::function f = chai.eval >("dump_system"); +/// f(); // call the ChaiScript function dump_system, from C++ +/// } +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection threading Threading +/// +/// Thread safety is automatically handled within the ChaiScript system. Objects can be added +/// and scripts executed from multiple threads. For each thread that executes scripts, a new +/// context is created and managed by the engine. +/// +/// Thread safety can be disabled by defining CHAISCRIPT_NO_THREADS when using the library. +/// +/// Disabling thread safety increases performance in many cases. +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection exceptions Exception Handling +/// +/// @subsubsection exceptionsbasics Exception Handling Basics +/// +/// Exceptions can be thrown in ChaiScript and caught in C++ or thrown in C++ and caught in +/// ChaiScript. +/// +/// ~~~~~~~~{.cpp} +/// void throwexception() +/// { +/// throw std::runtime_error("err"); +/// } +/// +/// int main() +/// { +/// // Throw in C++, catch in ChaiScript +/// chaiscript::ChaiScript chai; +/// chai.add(chaiscript::fun(&throwexception), "throwexception"); +/// chai("try { throwexception(); } catch (e) { print(e.what()); }"); // prints "err" +/// +/// // Throw in ChaiScript, catch in C++ +/// try { +/// chai("throw(1)"); +/// } catch (chaiscript::Boxed_Value bv) { +/// int i = chaiscript::boxed_cast(bv); +/// // i == 1 +/// } +/// } +/// ~~~~~~~~ +/// +/// @subsubsection exceptionsautomatic Exception Handling Automatic Unboxing +/// +/// As an alternative to the manual unboxing of exceptions shown above, exception specifications allow the user to tell +/// ChaiScript what possible exceptions are expected from the script being executed. +/// +/// Example: +/// ~~~~~~~~{.cpp} +/// chaiscript::ChaiScript chai; +/// +/// try { +/// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification()); +/// } catch (const double e) { +/// } catch (int) { +/// } catch (float) { +/// } catch (const std::string &) { +/// } catch (const std::exception &e) { +/// // This is the one what will be called in the specific throw() above +/// } +/// ~~~~~~~~ +/// +/// @sa chaiscript::Exception_Handler for details on automatic exception unboxing +/// @sa chaiscript::exception_specification + + + +/// @page LangObjectSystemRef ChaiScript Language Object Model Reference +/// +/// +/// ChaiScript has an object system built in, for types defined within the ChaiScript system. +/// +/// ~~~~~~~~~ +/// attr Rectangle::height +/// attr Rectangle::width +/// def Rectangle::Rectangle() { this.height = 10; this.width = 20 } +/// def Rectangle::area() { this.height * this.width } +/// +/// var rect = Rectangle() +/// rect.height = 30 +/// print(rect.area()) +/// ~~~~~~~~~ +/// +/// Since ChaiScript 5.4.0 it has been possible to use the "class" keyword to simplify this code. +/// +/// ~~~~~~~~~ +/// class Rectangle { +/// attr height +/// attr width +/// def Rectangle() { this.height = 10; this.width = 20 } +/// def area() { this.height * this.width } +/// } +/// +/// var rect = Rectangle() +/// rect.height = 30 +/// print(rect.area()) +/// ~~~~~~~~~ +/// +/// @sa @ref keywordattr +/// @sa @ref keyworddef + +/// @page LangInPlaceRef ChaiScript Language In-Place Creation Reference +/// @section inplacevector Vector +/// +/// ~~~~~~~~~ +/// In-place Vector ::= "[" [expression ("," expression)*] "]" +/// ~~~~~~~~~ +/// +/// @section inplacerangedvector Ranged Vector +/// +/// ~~~~~~~~~ +/// In-place Ranged Vector ::= "[" value ".." value "]" +/// ~~~~~~~~~ +/// +/// Creates a vector over a range (eg. 1..10) +/// +/// @section inplacemap Map +/// +/// ~~~~~~~~ +/// In-place Map ::= "[" (string ":" expression)+ "]" +/// ~~~~~~~~ + +/// @page LangGettingStarted ChaiScript Language Getting Started +/// +/// ChaiScript is a simple language that should feel familiar to anyone who knows +/// C++ or ECMAScript (JavaScript). +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptloops Loops +/// +/// Common looping constructs exist in ChaiScript +/// +/// ~~~~~~~~ +/// var i = 0; +/// while (i < 10) +/// { +/// // do something +/// ++i; +/// } +/// ~~~~~~~~ +/// +/// ~~~~~~~~ +/// for (var i = 0; i < 10; ++i) +/// { +/// // do something +/// } +/// ~~~~~~~~ +/// +/// @sa @ref keywordfor +/// @sa @ref keywordwhile +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptifs Conditionals +/// +/// If statements work as expected +/// +/// ~~~~~~~~ +/// var b = true; +/// +/// if (b) { +/// // do something +/// } else if (c < 10) { +/// // do something else +/// } else { +/// // or do this +/// } +/// ~~~~~~~~ +/// +/// @sa @ref keywordif +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptfunctions Functions +/// +/// Functions are defined with the def keyword +/// +/// ~~~~~~~~ +/// def myfun(x) { print(x); } +/// +/// myfun(10); +/// ~~~~~~~~ +/// +/// Functions may have "guards" which determine if which is called. +/// +/// ~~~~~~~~ +/// eval> def myfun2(x) : x < 10 { print("less than 10"); } +/// eval> def myfun2(x) : x >= 10 { print("10 or greater"); } +/// eval> myfun2(5) +/// less than 10 +/// eval> myfun2(12) +/// 10 or greater +/// ~~~~~~~~ +/// +/// @sa @ref keyworddef +/// @sa @ref keywordattr +/// @sa @ref LangObjectSystemRef +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptfunctionobjects Function Objects +/// +/// Functions are first class types in ChaiScript and can be used as variables. +/// +/// ~~~~~~~~ +/// eval> var p = print; +/// eval> p(1); +/// 1 +/// ~~~~~~~~ +/// +/// They can also be passed to functions. +/// +/// ~~~~~~~~ +/// eval> def callfunc(f, lhs, rhs) { return f(lhs, rhs); } +/// eval> def do_something(lhs, rhs) { print("lhs: ${lhs}, rhs: ${rhs}"); } +/// eval> callfunc(do_something, 1, 2); +/// lhs: 1, rhs: 2 +/// ~~~~~~~~ +/// +/// Operators can also be treated as functions by using the back tick operator. Building on the above example: +/// +/// ~~~~~~~~ +/// eval> callfunc(`+`, 1, 4); +/// 5 +/// eval> callfunc(`*`, 3, 2); +/// 6 +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// @sa @ref LangKeywordRef +/// @sa ChaiScript_Language for Built in Functions + + +/// @page LangKeywordRef ChaiScript Language Keyword Reference +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordattr attr +/// Defines a ChaiScript object attribute +/// +/// ~~~~~~~~ +/// Attribute Definition ::= "attr" class_name "::" attribute_name +/// ~~~~~~~~ +/// +/// @sa @ref LangObjectSystemRef +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordauto auto +/// +/// Defines a variable +/// +/// ~~~~~~~~ +/// Variable ::= "auto" identifier +/// ~~~~~~~~ +/// +/// Synonym for @ref keywordvar +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordbreak break +/// Stops execution of a looping block. +/// +/// ~~~~~~~~ +/// Break Statement ::= "break" +/// ~~~~~~~~ +/// +/// @sa @ref keywordfor +/// @sa @ref keywordwhile +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keyworddef def +/// Begins a function or method definition +/// +/// ~~~~~~~~ +/// Function Definition ::= [annotation + CR/LF] "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block +/// Method Definition ::= [annotation + CR/LF] "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block +/// ~~~~~~~~ +/// +/// annotation: meta-annotation on function, currently used as documentation. Optional. +/// identifier: name of function. Required. +/// args: comma-delimited list of parameter names with optional type specifiers. Optional. +/// guards: guarding statement that act as a prerequisite for the function. Optional. +/// { }: scoped block as function body. Required. +/// +/// Functions return values in one of two ways: +/// +/// By using an explicit return call, optionally passing the value to be returned. +/// By implicitly returning the value of the last expression (if it is not a while or for loop). +/// +/// Method definitions for known types extend those types with new methods. This includes C++ and ChaiScript defined types. +/// Method definitions for unknown types implicitly define the named type. +/// +/// @sa @ref LangObjectSystemRef +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordelse else +/// @sa @ref keywordif +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordfor for +/// ~~~~~~~~ +/// For Block ::= "for" "(" [initial] ";" stop_condition ";" loop_expression ")" block +/// ~~~~~~~~ +/// This loop can be broken using the @ref keywordbreak command. +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordfun fun +/// Begins an anonymous function declaration (sometimes called a lambda). +/// +/// ~~~~~~~~ +/// Lambda ::= "fun" "(" [variable] ("," variable)* ")" block +/// ~~~~~~~~ +/// +/// _Example_ +/// +/// ~~~~~~~~ +/// // Generate an anonymous function object that adds 2 to its parameter +/// var f = fun(x) { x + 2; } +/// ~~~~~~~~ +/// +/// @sa @ref keyworddef for more details on ChaiScript functions +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordif if +/// Begins a conditional block of code that only executes if the condition evaluates as true. +/// ~~~~~~~~ +/// If Block ::= "if" "(" condition ")" block +/// Else If Block ::= "else if" "(" condition ")" block +/// Else Block ::= "else" block +/// ~~~~~~~~ +/// +/// _Example_ +/// +/// ~~~~~~~~ +/// if (true) { +/// // do something +/// } else if (false) { +/// // do something else +/// } else { +/// // otherwise do this +/// } +/// ~~~~~~~~ +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordtry try +/// ~~~~~~~~ +/// Try Block ::= "try" block +/// ("catch" ["(" [type] variable ")"] [":" guards] block)+ +/// ["finally" block] +/// ~~~~~~~~ +/// +/// @sa ChaiScript_Language::throw +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordwhile while +/// +/// Begins a conditional block of code that loops 0 or more times, as long as the condition is true +/// +/// ~~~~~~~~ +/// While Block ::= "while" "(" condition ")" block +/// ~~~~~~~~ +/// +/// This loop can be broken using the @ref keywordbreak command. +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordvar var +/// +/// Defines a variable +/// +/// ~~~~~~~~ +/// Variable ::= "var" identifier +/// ~~~~~~~~ +/// +/// Synonym for @ref keywordauto + + +/// @namespace chaiscript +/// @brief Namespace chaiscript contains every API call that the average user will be concerned with. + +/// @namespace chaiscript::detail +/// @brief Classes and functions reserved for internal use. Items in this namespace are not supported. + +#include "chaiscript_defines.hpp" + +#include "dispatchkit/dispatchkit.hpp" +#include "dispatchkit/function_call.hpp" +#include "dispatchkit/dynamic_object.hpp" +#include "dispatchkit/boxed_number.hpp" + +#include "language/chaiscript_eval.hpp" +#include "language/chaiscript_engine.hpp" + + + +#endif /* CHAISCRIPT_HPP_ */ diff --git a/apps/common/script/chaiscript/chaiscript_defines.hpp b/apps/common/script/chaiscript/chaiscript_defines.hpp new file mode 100644 index 0000000000..8d250ebfd3 --- /dev/null +++ b/apps/common/script/chaiscript/chaiscript_defines.hpp @@ -0,0 +1,118 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_DEFINES_HPP_ +#define CHAISCRIPT_DEFINES_HPP_ + +#ifdef _MSC_VER +#define CHAISCRIPT_STRINGIZE(x) "" #x +#define CHAISCRIPT_COMPILER_VERSION CHAISCRIPT_STRINGIZE(_MSC_FULL_VER) +#define CHAISCRIPT_MSVC _MSC_VER +#define CHAISCRIPT_HAS_DECLSPEC +#if _MSC_VER <= 1800 +#define CHAISCRIPT_MSVC_12 +#endif +#else +#define CHAISCRIPT_COMPILER_VERSION __VERSION__ +#endif + +#ifndef CHAISCRIPT_MSVC_12 +#define CHAISCRIPT_HAS_MAGIC_STATICS +#endif + +#include + +#if defined( _LIBCPP_VERSION ) +#define CHAISCRIPT_LIBCPP +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#define CHAISCRIPT_WINDOWS +#endif + +#if defined(_WIN32) +#if defined(__llvm__) +#define CHAISCRIPT_COMPILER_NAME "clang(windows)" +#elif defined(__GNUC__) +#define CHAISCRIPT_COMPILER_NAME "gcc(mingw)" +#else +#define CHAISCRIPT_COMPILER_NAME "msvc" +#endif +#else +#if defined(__llvm__) +#define CHAISCRIPT_COMPILER_NAME "clang" +#elif defined(__GNUC__) +#define CHAISCRIPT_COMPILER_NAME "gcc" +#else +#define CHAISCRIPT_COMPILER_NAME "unknown" +#endif +#endif + +#if (defined(CHAISCRIPT_MSVC) && !defined(CHAISCRIPT_MSVC_12)) || (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || (defined(__llvm__) && !defined(CHAISCRIPT_LIBCPP)) +/// Currently only g++>=4.8 supports this natively +/// \todo Make this support other compilers when possible +#define CHAISCRIPT_HAS_THREAD_LOCAL +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6) +#define CHAISCRIPT_GCC_4_6 +#endif + +#if defined(__llvm__) +#define CHAISCRIPT_CLANG +#endif + +#if (defined(__GNUC__) && __GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) || defined(CHAISCRIPT_MSVC) || defined(CHAISCRIPT_CLANG) +#define CHAISCRIPT_OVERRIDE override +#else +#define CHAISCRIPT_OVERRIDE +#endif + + +#ifdef CHAISCRIPT_HAS_DECLSPEC +#define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport) +#else +#define CHAISCRIPT_MODULE_EXPORT extern "C" +#endif + +#ifdef CHAISCRIPT_MSVC_12 +#define CHAISCRIPT_NOEXCEPT throw() +#define CHAISCRIPT_CONSTEXPR +#else +#define CHAISCRIPT_NOEXCEPT noexcept +#define CHAISCRIPT_CONSTEXPR constexpr +#endif + +#ifdef _DEBUG +#define CHAISCRIPT_DEBUG true +#else +#define CHAISCRIPT_DEBUG false +#endif + +#include + +namespace chaiscript { + static const int version_major = 5; + static const int version_minor = 8; + static const int version_patch = 0; + + static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION; + static const char *compiler_name = CHAISCRIPT_COMPILER_NAME; + static const bool debug_build = CHAISCRIPT_DEBUG; + + template + inline std::shared_ptr make_shared(Arg && ... arg) + { +#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED + return std::make_shared(std::forward(arg)...); +#else + return std::shared_ptr(static_cast(new D(std::forward(arg)...))); +#endif + } + +} +#endif + diff --git a/apps/common/script/chaiscript/chaiscript_stdlib.hpp b/apps/common/script/chaiscript/chaiscript_stdlib.hpp new file mode 100644 index 0000000000..4b38f2cd21 --- /dev/null +++ b/apps/common/script/chaiscript/chaiscript_stdlib.hpp @@ -0,0 +1,66 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// and Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_STDLIB_HPP_ +#define CHAISCRIPT_STDLIB_HPP_ + +#include +#include +#include +#include +#include + +#include "chaiscript_defines.hpp" +#include "dispatchkit/dispatchkit.hpp" +#include "dispatchkit/bootstrap.hpp" +#include "dispatchkit/bootstrap_stl.hpp" +#include "dispatchkit/boxed_value.hpp" +#include "language/chaiscript_prelude.chai" +#include "utility/json_wrap.hpp" + +#ifndef CHAISCRIPT_NO_THREADS +#include +#endif + + +/// @file +/// +/// This file generates the standard library that normal ChaiScript usage requires. + +namespace chaiscript +{ + class Std_Lib + { + public: + + static ModulePtr library() + { + using namespace bootstrap; + + ModulePtr lib = Bootstrap::bootstrap(); + + lib->add(standard_library::vector_type >("Vector")); + lib->add(standard_library::string_type("string")); + lib->add(standard_library::map_type >("Map")); + lib->add(standard_library::pair_type >("Pair")); + +#ifndef CHAISCRIPT_NO_THREADS + lib->add(standard_library::future_type>("future")); + lib->add(chaiscript::fun([](const std::function &t_func){ return std::async(std::launch::async, t_func);}), "async"); +#endif + + lib->add(json_wrap::library()); + + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); + + return lib; + } + + }; +} + +#endif + diff --git a/apps/common/script/chaiscript/chaiscript_threading.hpp b/apps/common/script/chaiscript/chaiscript_threading.hpp new file mode 100644 index 0000000000..2a0e63c3e6 --- /dev/null +++ b/apps/common/script/chaiscript/chaiscript_threading.hpp @@ -0,0 +1,242 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_THREADING_HPP_ +#define CHAISCRIPT_THREADING_HPP_ + + +#include + +#ifndef CHAISCRIPT_NO_THREADS +#include +#include +#else +#ifndef CHAISCRIPT_NO_THREADS_WARNING +#pragma message ("ChaiScript is compiling without thread safety.") +#endif +#endif + +#include "chaiscript_defines.hpp" + +/// \file +/// +/// This file contains code necessary for thread support in ChaiScript. +/// If the compiler definition CHAISCRIPT_NO_THREADS is defined then thread safety +/// is disabled in ChaiScript. This has the result that some code is faster, because mutex locks are not required. +/// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than +/// one thread simultaneously. + +namespace chaiscript +{ + namespace detail + { + /// If threading is enabled, then this namespace contains std thread classes. + /// If threading is not enabled, then stubbed in wrappers that do nothing are provided. + /// This allows us to avoid \#ifdef code in the sections that need thread safety. + namespace threading + { + +#ifndef CHAISCRIPT_NO_THREADS + + template + class unique_lock : public std::unique_lock + { + public: + explicit unique_lock(T &t) : std::unique_lock(t) {} + }; + + template + class shared_lock : public std::unique_lock + { + public: + explicit shared_lock(T &t) : std::unique_lock(t) {} + void unlock() {} + }; + + template + class lock_guard : public std::lock_guard + { + public: + explicit lock_guard(T &t) : std::lock_guard(t) {} + }; + + class shared_mutex : public std::mutex { }; + + using std::mutex; + + using std::recursive_mutex; + +#ifdef CHAISCRIPT_HAS_THREAD_LOCAL + /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If + /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. + template + class Thread_Storage + { + public: + + explicit Thread_Storage(void *t_key) + : m_key(t_key) + { + } + + ~Thread_Storage() + { + t().erase(m_key); + } + + inline const T *operator->() const + { + return &(t()[m_key]); + } + + inline const T &operator*() const + { + return t()[m_key]; + } + + inline T *operator->() + { + return &(t()[m_key]); + } + + inline T &operator*() + { + return t()[m_key]; + } + + + void *m_key; + + private: + static std::unordered_map &t() + { + thread_local static std::unordered_map my_t; + return my_t; + } + }; + +#else + + /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If + /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. + /// + /// This version is used if the compiler does not support thread_local + template + class Thread_Storage + { + public: + + explicit Thread_Storage(void *) + { + } + + inline const T *operator->() const + { + return get_tls().get(); + } + + inline const T &operator*() const + { + return *get_tls(); + } + + inline T *operator->() + { + return get_tls().get(); + } + + inline T &operator*() + { + return *get_tls(); + } + + + private: + /// \todo this leaks thread instances. It needs to be culled from time to time + std::shared_ptr get_tls() const + { + unique_lock lock(m_mutex); + + const auto id = std::this_thread::get_id(); + auto itr = m_instances.find(id); + + if (itr != m_instances.end()) { return itr->second; } + + std::shared_ptr new_instance(std::make_shared()); + + m_instances.insert(std::make_pair(id, new_instance)); + + return new_instance; + } + + + mutable mutex m_mutex; + mutable std::unordered_map > m_instances; + }; +#endif // threading enabled but no tls + +#else // threading disabled + template + class unique_lock + { + public: + explicit unique_lock(T &) {} + void lock() {} + void unlock() {} + }; + + template + class shared_lock + { + public: + explicit shared_lock(T &) {} + void lock() {} + void unlock() {} + }; + + template + class lock_guard + { + public: + explicit lock_guard(T &) {} + }; + + class shared_mutex { }; + + class recursive_mutex {}; + + + template + class Thread_Storage + { + public: + explicit Thread_Storage(void *) + { + } + + inline T *operator->() const + { + return &obj; + } + + inline T &operator*() const + { + return obj; + } + + private: + mutable T obj; + }; + +#endif + } + } +} + + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/any.hpp b/apps/common/script/chaiscript/dispatchkit/any.hpp new file mode 100644 index 0000000000..e86ba0ed07 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/any.hpp @@ -0,0 +1,175 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// and Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_ANY_HPP_ +#define CHAISCRIPT_ANY_HPP_ + +#include + +namespace chaiscript { + namespace detail { + namespace exception + { + /// \brief Thrown in the event that an Any cannot be cast to the desired type + /// + /// It is used internally during function dispatch. + /// + /// \sa chaiscript::detail::Any + class bad_any_cast : public std::bad_cast + { + public: + bad_any_cast() CHAISCRIPT_NOEXCEPT + : m_what("bad any cast") + { + } + + bad_any_cast(const bad_any_cast &) = default; + + virtual ~bad_any_cast() CHAISCRIPT_NOEXCEPT {} + + /// \brief Description of what error occurred + virtual const char * what() const CHAISCRIPT_NOEXCEPT CHAISCRIPT_OVERRIDE + { + return m_what.c_str(); + } + + private: + std::string m_what; + }; + } + + + class Any { + private: + struct Data + { + explicit Data(const std::type_info &t_type) + : m_type(t_type) + { + } + + Data &operator=(const Data &) = delete; + + virtual ~Data() {} + + virtual void *data() = 0; + const std::type_info &type() const + { + return m_type; + } + + virtual std::unique_ptr clone() const = 0; + const std::type_info &m_type; + }; + + template + struct Data_Impl : Data + { + explicit Data_Impl(T t_type) + : Data(typeid(T)), + m_data(std::move(t_type)) + { + } + + virtual ~Data_Impl() {} + + virtual void *data() CHAISCRIPT_OVERRIDE + { + return &m_data; + } + + std::unique_ptr clone() const CHAISCRIPT_OVERRIDE + { + return std::unique_ptr(new Data_Impl(m_data)); + } + + Data_Impl &operator=(const Data_Impl&) = delete; + + T m_data; + }; + + std::unique_ptr m_data; + + public: + // construct/copy/destruct + Any() = default; + + Any(const Any &t_any) + { + if (!t_any.empty()) + { + m_data = t_any.m_data->clone(); + } else { + m_data.reset(); + } + } + +#if !defined(_MSC_VER) || _MSC_VER != 1800 + Any(Any &&) = default; + Any &operator=(Any &&t_any) = default; +#endif + + template::type>::value>::type> + explicit Any(ValueType &&t_value) + : m_data(std::unique_ptr(new Data_Impl::type>(std::forward(t_value)))) + { + } + + + Any & operator=(const Any &t_any) + { + Any copy(t_any); + swap(copy); + return *this; + } + + template + ToType &cast() const + { + if (m_data && typeid(ToType) == m_data->type()) + { + return *static_cast(m_data->data()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + + ~Any() + { + } + + // modifiers + Any & swap(Any &t_other) + { + std::swap(t_other.m_data, m_data); + return *this; + } + + // queries + bool empty() const + { + return !bool(m_data); + } + + const std::type_info & type() const + { + if (m_data) + { + return m_data->type(); + } else { + return typeid(void); + } + } + }; + + } +} + +#endif + + diff --git a/apps/common/script/chaiscript/dispatchkit/bad_boxed_cast.hpp b/apps/common/script/chaiscript/dispatchkit/bad_boxed_cast.hpp new file mode 100644 index 0000000000..0716749d97 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/bad_boxed_cast.hpp @@ -0,0 +1,69 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_ +#define CHAISCRIPT_BAD_BOXED_CAST_HPP_ + +#include +#include + +#include "../chaiscript_defines.hpp" +#include "type_info.hpp" + +namespace chaiscript { +class Type_Info; +} // namespace chaiscript + +namespace chaiscript +{ + namespace exception + { + /// \brief Thrown in the event that a Boxed_Value cannot be cast to the desired type + /// + /// It is used internally during function dispatch and may be used by the end user. + /// + /// \sa chaiscript::boxed_cast + class bad_boxed_cast : public std::bad_cast + { + public: + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, + std::string t_what) CHAISCRIPT_NOEXCEPT + : from(std::move(t_from)), to(&t_to), m_what(std::move(t_what)) + { + } + + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to) + : from(std::move(t_from)), to(&t_to), m_what("Cannot perform boxed_cast: " + t_from.name() + " to: " + t_to.name()) + { + } + + explicit bad_boxed_cast(std::string t_what) CHAISCRIPT_NOEXCEPT + : to(nullptr), m_what(std::move(t_what)) + { + } + + bad_boxed_cast(const bad_boxed_cast &) = default; + virtual ~bad_boxed_cast() CHAISCRIPT_NOEXCEPT {} + + /// \brief Description of what error occurred + virtual const char * what() const CHAISCRIPT_NOEXCEPT CHAISCRIPT_OVERRIDE + { + return m_what.c_str(); + } + + Type_Info from; ///< Type_Info contained in the Boxed_Value + const std::type_info *to; ///< std::type_info of the desired (but failed) result type + + private: + std::string m_what; + }; + } +} + + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/bind_first.hpp b/apps/common/script/chaiscript/dispatchkit/bind_first.hpp new file mode 100644 index 0000000000..fcba5348c8 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/bind_first.hpp @@ -0,0 +1,74 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BIND_FIRST_HPP_ +#define CHAISCRIPT_BIND_FIRST_HPP_ + +#include + +namespace chaiscript +{ + namespace detail + { + + template + T* get_pointer(T *t) + { + return t; + } + + template + T* get_pointer(const std::reference_wrapper &t) + { + return &t.get(); + } + + template + std::function bind_first(Ret (*f)(P1, Param...), O&& o) + { + return std::function( + [f, o](Param...param) -> Ret { + return f(std::forward(o), std::forward(param)...); + } + ); + } + + template + std::function bind_first(Ret (Class::*f)(Param...), O&& o) + { + return std::function( + [f, o](Param...param) -> Ret { + return (get_pointer(o)->*f)(std::forward(param)...); + } + ); + } + + template + std::function bind_first(Ret (Class::*f)(Param...) const, O&& o) + { + return std::function( + [f, o](Param...param) -> Ret { + return (get_pointer(o)->*f)(std::forward(param)...); + } + ); + + } + + template + std::function bind_first(const std::function &f, O&& o) + { + return std::function( + [f, o](Param...param) -> Ret { + return f(o, std::forward(param)...); + }); + } + + + } +} + + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/bootstrap.hpp b/apps/common/script/chaiscript/dispatchkit/bootstrap.hpp new file mode 100644 index 0000000000..ef9f032cb3 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/bootstrap.hpp @@ -0,0 +1,630 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BOOTSTRAP_HPP_ +#define CHAISCRIPT_BOOTSTRAP_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bad_boxed_cast.hpp" +#include "boxed_cast.hpp" +#include "boxed_number.hpp" +#include "boxed_value.hpp" +#include "dispatchkit.hpp" +#include "type_conversions.hpp" +#include "dynamic_object.hpp" +#include "operators.hpp" +#include "proxy_constructors.hpp" +#include "proxy_functions.hpp" +#include "proxy_functions_detail.hpp" +#include "register_function.hpp" +#include "type_info.hpp" +#include "../utility/utility.hpp" +#include "../language/chaiscript_common.hpp" + +namespace chaiscript +{ + /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types + namespace bootstrap + { + namespace detail + { + /// \brief Constructs a new POD value object from a Boxed_Number + /// \param[in] v Boxed_Number to copy into the new object + /// \returns The newly created object. + template + std::shared_ptr construct_pod(const Boxed_Number &v) + { + return std::make_shared(v.get_as()); + } + } + + template::value>::type > + ModulePtr array(const std::string &type, ModulePtr m = std::make_shared()) + { + typedef typename std::remove_extent::type ReturnType; + const auto extent = std::extent::value; + m->add(user_type(), type); + m->add(fun( + [extent](T& t, size_t index)->ReturnType &{ + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); + } else { + return t[index]; + } + } + ), "[]" + ); + + m->add(fun( + [extent](const T &t, size_t index)->const ReturnType &{ + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); + } else { + return t[index]; + } + } + ), "[]" + ); + + m->add(fun( + [extent](const T &) { + return extent; + }), "size"); + + + return m; + } + + /// \brief Adds a copy constructor for the given type to the given Model + /// \param[in] type The name of the type. The copy constructor will be named "type". + /// \param[in,out] m The Module to add the copy constructor to + /// \tparam T The type to add a copy constructor for + /// \returns The passed in ModulePtr, or the newly constructed one if the default param is used + template + ModulePtr copy_constructor(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(constructor(), type); + return m; + } + + /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users. + /// \tparam T Type to create comparison operators for + /// \param[in,out] m module to add comparison operators to + /// \returns the passed in ModulePtr or the newly constructed one if the default params are used. + template + ModulePtr opers_comparison(ModulePtr m = std::make_shared()) + { + operators::equal(m); + operators::greater_than(m); + operators::greater_than_equal(m); + operators::less_than(m); + operators::less_than_equal(m); + operators::not_equal(m); + return m; + } + + + + /// \brief Adds default and copy constructors for the given type + /// \param[in] type The name of the type to add the constructors for. + /// \param[in,out] m The Module to add the basic constructors to + /// \tparam T Type to generate basic constructors for + /// \returns The passed in ModulePtr, or the newly constructed one if the default param is used + /// \sa copy_constructor + /// \sa constructor + template + ModulePtr basic_constructors(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(constructor(), type); + copy_constructor(type, m); + return m; + } + + /// \brief Adds a constructor for a POD type + /// \tparam T The type to add the constructor for + /// \param[in] type The name of the type + /// \param[in,out] m The Module to add the constructor to + template + ModulePtr construct_pod(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(fun(&detail::construct_pod), type); + return m; + } + + + /// to_string function for internal use. Uses ostream operator<< + template + std::string to_string(Input i) + { + std::stringstream ss; + ss << i; + return ss.str(); + } + + /// Internal function for converting from a string to a value + /// uses ostream operator >> to perform the conversion + template + auto parse_string(const std::string &i) + -> typename std::enable_if< + !std::is_same::value + && !std::is_same::value + && !std::is_same::value, + Input>::type + { + std::stringstream ss(i); + Input t; + ss >> t; + return t; + } + + template + auto parse_string(const std::string &) + -> typename std::enable_if< + std::is_same::value + || std::is_same::value + || std::is_same::value, + Input>::type + { + throw std::runtime_error("Parsing of wide characters is not yet supported"); + } + + + /// Add all common functions for a POD type. All operators, and + /// common conversions + template + ModulePtr bootstrap_pod_type(const std::string &name, ModulePtr m = std::make_shared()) + { + m->add(user_type(), name); + m->add(constructor(), name); + construct_pod(name, m); + + m->add(fun(&parse_string), "to_" + name); + return m; + } + + + /// "clone" function for a shared_ptr type. This is used in the case + /// where you do not want to make a deep copy of an object during cloning + /// but want to instead maintain the shared_ptr. It is needed internally + /// for handling of Proxy_Function object (that is, + /// function variables. + template + std::shared_ptr shared_ptr_clone(const std::shared_ptr &p) + { + return p; + } + + /// Specific version of shared_ptr_clone just for Proxy_Functions + template + std::shared_ptr::type> + shared_ptr_unconst_clone(const std::shared_ptr::type> &p) + { + return std::const_pointer_cast::type>(p); + } + + + + /// Assignment function for shared_ptr objects, does not perform a copy of the + /// object pointed to, instead maintains the shared_ptr concept. + /// Similar to shared_ptr_clone. Used for Proxy_Function. + template + Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr &rhs) + { + if (lhs.is_undef() + || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info::get()))) + { + lhs.assign(Boxed_Value(rhs)); + return lhs; + } else { + throw exception::bad_boxed_cast("type mismatch in pointer assignment"); + } + } + + /// Class consisting of only static functions. All default bootstrapping occurs + /// from this class. + class Bootstrap + { + private: + /// Function allowing for assignment of an unknown type to any other value + static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) + { + if (lhs.is_undef()) + { + return (lhs.assign(rhs)); + } else { + throw exception::bad_boxed_cast("boxed_value has a set type already"); + } + } + + static void print(const std::string &s) + { + fwrite(s.c_str(), 1, s.size(), stdout); + } + + static void println(const std::string &s) + { + puts(s.c_str()); + } + + + /// Add all arithmetic operators for PODs + static void opers_arithmetic_pod(ModulePtr m = std::make_shared()) + { + m->add(fun(&Boxed_Number::equals), "=="); + m->add(fun(&Boxed_Number::less_than), "<"); + m->add(fun(&Boxed_Number::greater_than), ">"); + m->add(fun(&Boxed_Number::greater_than_equal), ">="); + m->add(fun(&Boxed_Number::less_than_equal), "<="); + m->add(fun(&Boxed_Number::not_equal), "!="); + + m->add(fun(&Boxed_Number::pre_decrement), "--"); + m->add(fun(&Boxed_Number::pre_increment), "++"); + m->add(fun(&Boxed_Number::sum), "+"); + m->add(fun(&Boxed_Number::unary_plus), "+"); + m->add(fun(&Boxed_Number::unary_minus), "-"); + m->add(fun(&Boxed_Number::difference), "-"); + m->add(fun(&Boxed_Number::assign_bitwise_and), "&="); + m->add(fun(&Boxed_Number::assign), "="); + m->add(fun(&Boxed_Number::assign_bitwise_or), "|="); + m->add(fun(&Boxed_Number::assign_bitwise_xor), "^="); + m->add(fun(&Boxed_Number::assign_remainder), "%="); + m->add(fun(&Boxed_Number::assign_shift_left), "<<="); + m->add(fun(&Boxed_Number::assign_shift_right), ">>="); + m->add(fun(&Boxed_Number::bitwise_and), "&"); + m->add(fun(&Boxed_Number::bitwise_complement), "~"); + m->add(fun(&Boxed_Number::bitwise_xor), "^"); + m->add(fun(&Boxed_Number::bitwise_or), "|"); + m->add(fun(&Boxed_Number::assign_product), "*="); + m->add(fun(&Boxed_Number::assign_quotient), "/="); + m->add(fun(&Boxed_Number::assign_sum), "+="); + m->add(fun(&Boxed_Number::assign_difference), "-="); + m->add(fun(&Boxed_Number::quotient), "/"); + m->add(fun(&Boxed_Number::shift_left), "<<"); + m->add(fun(&Boxed_Number::product), "*"); + m->add(fun(&Boxed_Number::remainder), "%"); + m->add(fun(&Boxed_Number::shift_right), ">>"); + + + } + + /// Create a bound function object. The first param is the function to bind + /// the remaining parameters are the args to bind into the result + static Boxed_Value bind_function(const std::vector ¶ms) + { + if (params.empty()) { + throw exception::arity_error(0, 1); + } + + Const_Proxy_Function f = boxed_cast(params[0]); + + if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) + { + throw exception::arity_error(static_cast(params.size()), f->get_arity()); + } + + return Boxed_Value(Const_Proxy_Function(std::make_shared(std::move(f), + std::vector(params.begin() + 1, params.end())))); + } + + + static bool has_guard(const Const_Proxy_Function &t_pf) + { + auto pf = std::dynamic_pointer_cast(t_pf); + return pf && pf->get_guard(); + } + + static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) + { + const auto pf = std::dynamic_pointer_cast(t_pf); + if (pf && pf->get_guard()) + { + return pf->get_guard(); + } else { + throw std::runtime_error("Function does not have a guard"); + } + } + + static void throw_exception(const Boxed_Value &bv) { + throw bv; + } + + static std::string what(const std::exception &e) + { + return e.what(); + } + + /// Boolean specialization of internal to_string function + static std::string bool_to_string(bool b) + { + if (b) + { + return "true"; + } else { + return "false"; + } + } + + template + static std::vector do_return_boxed_value_vector(FunctionType f, + const dispatch::Proxy_Function_Base *b) + { + auto v = (b->*f)(); + + std::vector vbv; + + for (const auto &o: v) + { + vbv.push_back(const_var(o)); + } + + return vbv; + } + + + static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) + { + const auto pf = std::dynamic_pointer_cast(t_pf); + return pf && pf->get_parse_tree(); + } + + static chaiscript::AST_NodePtr get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) + { + const auto pf = std::dynamic_pointer_cast(t_pf); + if (pf && pf->get_parse_tree()) + { + return pf->get_parse_tree(); + } else { + throw std::runtime_error("Function does not have a parse tree"); + } + } + + template + static std::function (const dispatch::Proxy_Function_Base*)> return_boxed_value_vector(const Function &f) + { + return [f](const dispatch::Proxy_Function_Base *b) { + return do_return_boxed_value_vector(f, b); + }; + } + + + public: + /// \brief perform all common bootstrap functions for std::string, void and POD types + /// \param[in,out] m Module to add bootstrapped functions to + /// \returns passed in ModulePtr, or newly created one if default argument is used + static ModulePtr bootstrap(ModulePtr m = std::make_shared()) + { + m->add(user_type(), "void"); + m->add(user_type(), "bool"); + m->add(user_type(), "Object"); + m->add(user_type(), "Number"); + m->add(user_type(), "Function"); + m->add(user_type(), "Assignable_Function"); + m->add(user_type(), "exception"); + + m->add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); + m->add(fun(&dispatch::Proxy_Function_Base::annotation), "get_annotation"); + m->add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); + + + m->add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types"); + m->add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions"); + + + m->add(user_type(), "out_of_range"); + m->add(user_type(), "logic_error"); + m->add(chaiscript::base_class()); + m->add(chaiscript::base_class()); + m->add(chaiscript::base_class()); + + m->add(user_type(), "runtime_error"); + m->add(chaiscript::base_class()); + + m->add(constructor(), "runtime_error"); + m->add(fun(std::function(&what)), "what"); + + m->add(user_type(), "Dynamic_Object"); + m->add(constructor(), "Dynamic_Object"); + m->add(constructor(), "Dynamic_Object"); + m->add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); + m->add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); + m->add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); + m->add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); + + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); + m->add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); + + m->eval(R""( + def Dynamic_Object::clone() { + auto &new_o = Dynamic_Object(this.get_type_name()); + for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); + new_o; + } + )""); + + m->add(fun(&has_guard), "has_guard"); + m->add(fun(&get_guard), "get_guard"); + + m->add(fun(&Boxed_Value::is_undef), "is_var_undef"); + m->add(fun(&Boxed_Value::is_null), "is_var_null"); + m->add(fun(&Boxed_Value::is_const), "is_var_const"); + m->add(fun(&Boxed_Value::is_ref), "is_var_reference"); + m->add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); + m->add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); + m->add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); + m->add(fun(&Boxed_Value::is_type), "is_type"); + m->add(fun(&Boxed_Value::get_attr), "get_var_attr"); + m->add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); + m->add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); + + m->add(fun(&Boxed_Value::get_type_info), "get_type_info"); + m->add(user_type(), "Type_Info"); + m->add(constructor(), "Type_Info"); + + + operators::equal(m); + + m->add(fun(&Type_Info::is_const), "is_type_const"); + m->add(fun(&Type_Info::is_reference), "is_type_reference"); + m->add(fun(&Type_Info::is_void), "is_type_void"); + m->add(fun(&Type_Info::is_undef), "is_type_undef"); + m->add(fun(&Type_Info::is_pointer), "is_type_pointer"); + m->add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); + m->add(fun(&Type_Info::name), "cpp_name"); + m->add(fun(&Type_Info::bare_name), "cpp_bare_name"); + m->add(fun(&Type_Info::bare_equal), "bare_equal"); + + + basic_constructors("bool", m); + operators::assign(m); + operators::equal(m); + operators::not_equal(m); + + m->add(fun([](const std::string &s) -> std::string { return s; }), "to_string"); + m->add(fun(&Bootstrap::bool_to_string), "to_string"); + m->add(fun(&unknown_assign), "="); + m->add(fun(&throw_exception), "throw"); + m->add(fun(&what), "what"); + + m->add(fun(&to_string), "to_string"); + m->add(fun(&Boxed_Number::to_string), "to_string"); + + bootstrap_pod_type("double", m); + bootstrap_pod_type("long_double", m); + bootstrap_pod_type("float", m); + bootstrap_pod_type("int", m); + bootstrap_pod_type("long", m); + bootstrap_pod_type("unsigned_int", m); + bootstrap_pod_type("unsigned_long", m); + bootstrap_pod_type("long_long", m); + bootstrap_pod_type("unsigned_long_long", m); + bootstrap_pod_type("size_t", m); + bootstrap_pod_type("char", m); + bootstrap_pod_type("wchar_t", m); + bootstrap_pod_type("char16_t", m); + bootstrap_pod_type("char32_t", m); + bootstrap_pod_type("int8_t", m); + bootstrap_pod_type("int16_t", m); + bootstrap_pod_type("int32_t", m); + bootstrap_pod_type("int64_t", m); + bootstrap_pod_type("uint8_t", m); + bootstrap_pod_type("uint16_t", m); + bootstrap_pod_type("uint32_t", m); + bootstrap_pod_type("uint64_t", m); + + operators::logical_compliment(m); + + opers_arithmetic_pod(m); + + + m->add(fun(&print), "print_string"); + m->add(fun(&println), "println_string"); + + m->add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); + + m->add(fun(&shared_ptr_unconst_clone), "clone"); + m->add(fun(&ptr_assign::type>), "="); + m->add(fun(&ptr_assign::type>), "="); + m->add(chaiscript::base_class()); + m->add(fun( + [](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr &t_rhs) { + t_lhs.assign(t_rhs); + } + ), "=" + ); + + m->add(fun(&Boxed_Value::type_match), "type_match"); + + + m->add(chaiscript::fun(&has_parse_tree), "has_parse_tree"); + m->add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); + + m->add(chaiscript::base_class()); + + m->add(chaiscript::user_type(), "arithmetic_error"); + m->add(chaiscript::base_class()); + + +// chaiscript::bootstrap::standard_library::vector_type > >("AST_NodeVector", m); + + + chaiscript::utility::add_class(*m, + "eval_error", + { }, + { {fun(&chaiscript::exception::eval_error::reason), "reason"}, + {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, + {fun(std::function (const chaiscript::exception::eval_error &t_eval_error)>([](const chaiscript::exception::eval_error &t_eval_error) -> std::vector { + std::vector retval; + std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), + std::back_inserter(retval), + &chaiscript::var &>); + return retval; + })), "call_stack"} } + ); + + + chaiscript::utility::add_class(*m, + "File_Position", + { constructor(), + constructor() }, + { {fun(&File_Position::line), "line"}, + {fun(&File_Position::column), "column"} } + ); + + + chaiscript::utility::add_class(*m, + "AST_Node", + { }, + { {fun(&AST_Node::text), "text"}, + {fun(&AST_Node::identifier), "identifier"}, + {fun(&AST_Node::filename), "filename"}, + {fun(&AST_Node::start), "start"}, + {fun(&AST_Node::end), "end"}, + {fun(&AST_Node::to_string), "to_string"}, + {fun(std::function (const chaiscript::AST_Node &t_node)>([](const chaiscript::AST_Node &t_node) -> std::vector { + std::vector retval; + std::transform(t_node.children.begin(), t_node.children.end(), + std::back_inserter(retval), + &chaiscript::var &>); + return retval; + })), "children"}, + {fun(&AST_Node::replace_child), "replace_child"} + } + ); + + + chaiscript::utility::add_class(*m, + "ChaiScript_Parser", + { constructor() }, + { {fun(&parser::ChaiScript_Parser::parse), "parse"}, + {fun(&parser::ChaiScript_Parser::ast), "ast"} } + ); + + + + return m; + } + }; + } +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/bootstrap_stl.hpp b/apps/common/script/chaiscript/dispatchkit/bootstrap_stl.hpp new file mode 100644 index 0000000000..8c225c3beb --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -0,0 +1,643 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +/// \file +/// This file contains utility functions for registration of STL container +/// classes. The methodology used is based on the SGI STL concepts. +/// http://www.sgi.com/tech/stl/table_of_contents.html + + +#ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_ +#define CHAISCRIPT_BOOTSTRAP_STL_HPP_ + +#include +#include +#include +#include +#include + +#include "bootstrap.hpp" +#include "boxed_value.hpp" +#include "dispatchkit.hpp" +#include "operators.hpp" +#include "proxy_constructors.hpp" +#include "register_function.hpp" +#include "type_info.hpp" + +namespace chaiscript +{ + namespace bootstrap + { + namespace standard_library + { + + /// Bidir_Range, based on the D concept of ranges. + /// \todo Update the Range code to base its capabilities on + /// the user_typetraits of the iterator passed in + template + struct Bidir_Range + { + typedef Container container_type; + typedef typename std::iterator_traits::reference reference_type; + + Bidir_Range(Container &c) + : m_begin(c.begin()), m_end(c.end()) + { + } + + bool empty() const + { + return m_begin == m_end; + } + + void pop_front() + { + if (empty()) + { + throw std::range_error("Range empty"); + } + ++m_begin; + } + + void pop_back() + { + if (empty()) + { + throw std::range_error("Range empty"); + } + --m_end; + } + + reference_type front() const + { + if (empty()) + { + throw std::range_error("Range empty"); + } + return *m_begin; + } + + reference_type back() const + { + if (empty()) + { + throw std::range_error("Range empty"); + } + typename Container::iterator pos = m_end; + --pos; + return *(pos); + } + + typename Container::iterator m_begin; + typename Container::iterator m_end; + }; + + template + struct Const_Bidir_Range + { + typedef const Container container_type; + typedef typename std::iterator_traits::reference const_reference_type; + + Const_Bidir_Range(const Container &c) + : m_begin(c.begin()), m_end(c.end()) + { + } + + bool empty() const + { + return m_begin == m_end; + } + + void pop_front() + { + if (empty()) + { + throw std::range_error("Range empty"); + } + ++m_begin; + } + + void pop_back() + { + if (empty()) + { + throw std::range_error("Range empty"); + } + --m_end; + } + + const_reference_type front() const + { + if (empty()) + { + throw std::range_error("Range empty"); + } + return *m_begin; + } + + const_reference_type back() const + { + if (empty()) + { + throw std::range_error("Range empty"); + } + typename Container::const_iterator pos = m_end; + --pos; + return *(pos); + } + + typename Container::const_iterator m_begin; + typename Container::const_iterator m_end; + }; + + namespace detail { + + template + size_t count(const T &t_target, const typename T::key_type &t_key) + { + return t_target.count(t_key); + } + + template + void insert(T &t_target, const T &t_other) + { + t_target.insert(t_other.begin(), t_other.end()); + } + + template + void insert_ref(T &t_target, const typename T::value_type &t_val) + { + t_target.insert(t_val); + } + + + + /// Add Bidir_Range support for the given ContainerType + template + ModulePtr input_range_type_impl(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type + "_Range"); + + copy_constructor(type + "_Range", m); + + m->add(constructor(), "range_internal"); + + m->add(fun(&Bidir_Type::empty), "empty"); + m->add(fun(&Bidir_Type::pop_front), "pop_front"); + m->add(fun(&Bidir_Type::front), "front"); + m->add(fun(&Bidir_Type::pop_back), "pop_back"); + m->add(fun(&Bidir_Type::back), "back"); + + return m; + } + + + /// Algorithm for inserting at a specific position into a container + template + void insert_at(Type &container, int pos, const typename Type::value_type &v) + { + auto itr = container.begin(); + auto end = container.end(); + + if (pos < 0 || std::distance(itr, end) < pos) + { + throw std::range_error("Cannot insert past end of range"); + } + + std::advance(itr, pos); + container.insert(itr, v); + } + + + /// Algorithm for erasing a specific position from a container + template + void erase_at(Type &container, int pos) + { + auto itr = container.begin(); + auto end = container.end(); + + if (pos < 0 || std::distance(itr, end) < (pos-1)) + { + throw std::range_error("Cannot erase past end of range"); + } + + std::advance(itr, pos); + container.erase(itr); + } + } + + template + ModulePtr input_range_type(const std::string &type, ModulePtr m = std::make_shared()) + { + detail::input_range_type_impl >(type,m); + detail::input_range_type_impl >("Const_" + type, m); + return m; + } + + + /// Add random_access_container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/RandomAccessContainer.html + template + ModulePtr random_access_container_type(const std::string &/*type*/, ModulePtr m = std::make_shared()) + { + //In the interest of runtime safety for the m, we prefer the at() method for [] access, + //to throw an exception in an out of bounds condition. + m->add( + fun( + [](ContainerType &c, int index) -> typename ContainerType::reference { + return c.at(index); + }), "[]"); + + m->add( + fun( + [](const ContainerType &c, int index) -> typename ContainerType::const_reference { + return c.at(index); + }), "[]"); + + return m; + } + + + /// Add assignable concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Assignable.html + template + ModulePtr assignable_type(const std::string &type, ModulePtr m = std::make_shared()) + { + copy_constructor(type, m); + operators::assign(m); + return m; + } + + + /// Add container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Container.html + template + ModulePtr container_type(const std::string &/*type*/, ModulePtr m = std::make_shared()) + { + m->add(fun([](const ContainerType *a) { return a->size(); } ), "size"); + m->add(fun([](const ContainerType *a) { return a->empty(); } ), "empty"); + m->add(fun([](ContainerType *a) { a->clear(); } ), "clear"); + return m; + } + + + /// Add default constructable concept to the given Type + /// http://www.sgi.com/tech/stl/DefaultConstructible.html + template + ModulePtr default_constructible_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(constructor(), type); + return m; + } + + + + + /// Add sequence concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Sequence.html + template + ModulePtr sequence_type(const std::string &/*type*/, ModulePtr m = std::make_shared()) + { + m->add(fun(&detail::insert_at), + []()->std::string{ + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + return "insert_ref_at"; + } else { + return "insert_at"; + } + }()); + + m->add(fun(&detail::erase_at), "erase_at"); + + return m; + } + + + /// Add back insertion sequence concept to the given ContainerType + /// http://www.sgi.com/tech/stl/BackInsertionSequence.html + template + ModulePtr back_insertion_sequence_type(const std::string &type, ModulePtr m = std::make_shared()) + { + typedef typename ContainerType::reference (ContainerType::*backptr)(); + + m->add(fun(static_cast(&ContainerType::back)), "back"); + + + typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &); + m->add(fun(static_cast(&ContainerType::push_back)), + [&]()->std::string{ + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m->eval( + "# Pushes the second value onto the container while making a clone of the value\n" + "def push_back(" + type + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_back_ref(x) \n" + " } else { \n" + " container.push_back_ref(clone(x)); \n" + " }\n" + "} \n" + ); + + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + + m->add(fun(&ContainerType::pop_back), "pop_back"); + return m; + } + + + + /// Front insertion sequence + /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html + template + ModulePtr front_insertion_sequence_type(const std::string &type, ModulePtr m = std::make_shared()) + { + typedef typename ContainerType::reference (ContainerType::*front_ptr)(); + typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const; + typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference); + typedef void (ContainerType::*pop_ptr)(); + + m->add(fun(static_cast(&ContainerType::front)), "front"); + m->add(fun(static_cast(&ContainerType::front)), "front"); + + m->add(fun(static_cast(&ContainerType::push_front)), + [&]()->std::string{ + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m->eval( + "# Pushes the second value onto the front of container while making a clone of the value\n" + "def push_front(" + type + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_front_ref(x) \n" + " } else { \n" + " container.push_front_ref(clone(x)); \n" + " }\n" + "} \n" + ); + return "push_front_ref"; + } else { + return "push_front"; + } + }()); + + m->add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); + return m; + } + + + /// bootstrap a given PairType + /// http://www.sgi.com/tech/stl/pair.html + template + ModulePtr pair_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + + + typename PairType::first_type PairType::* f = &PairType::first; + typename PairType::second_type PairType::* s = &PairType::second; + + m->add(fun(f), "first"); + m->add(fun(s), "second"); + + basic_constructors(type, m); + m->add(constructor(), type); + + return m; + } + + + + /// Add pair associative container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html + + template + ModulePtr pair_associative_container_type(const std::string &type, ModulePtr m = std::make_shared()) + { + pair_type(type + "_Pair", m); + + return m; + } + + + /// Add unique associative container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html + template + ModulePtr unique_associative_container_type(const std::string &/*type*/, ModulePtr m = std::make_shared()) + { + m->add(fun(detail::count), "count"); + + typedef size_t (ContainerType::*erase_ptr)(const typename ContainerType::key_type &); + + m->add(fun(static_cast(&ContainerType::erase)), "erase"); + + m->add(fun(&detail::insert), "insert"); + + m->add(fun(&detail::insert_ref), + []()->std::string{ + if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { + return "insert_ref"; + } else { + return "insert"; + } + }()); + + + return m; + } + + + /// Add a MapType container + /// http://www.sgi.com/tech/stl/Map.html + template + ModulePtr map_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + + typedef typename MapType::mapped_type &(MapType::*elem_access)(const typename MapType::key_type &); + typedef const typename MapType::mapped_type &(MapType::*const_elem_access)(const typename MapType::key_type &) const; + + m->add(fun(static_cast(&MapType::operator[])), "[]"); + + m->add(fun(static_cast(&MapType::at)), "at"); + m->add(fun(static_cast(&MapType::at)), "at"); + + if (typeid(MapType) == typeid(std::map)) + { + m->eval(R"( + def Map::`==`(Map rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second)) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )" + ); + } + + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + unique_associative_container_type(type, m); + pair_associative_container_type(type, m); + input_range_type(type, m); + + return m; + } + + + /// hopefully working List type + /// http://www.sgi.com/tech/stl/List.html + template + ModulePtr list_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + + front_insertion_sequence_type(type, m); + back_insertion_sequence_type(type, m); + sequence_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + return m; + } + + + /// Create a vector type with associated concepts + /// http://www.sgi.com/tech/stl/Vector.html + template + ModulePtr vector_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + + typedef typename VectorType::reference (VectorType::*frontptr)(); + typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const; + + m->add(fun(static_cast(&VectorType::front)), "front"); + m->add(fun(static_cast(&VectorType::front)), "front"); + + + back_insertion_sequence_type(type, m); + sequence_type(type, m); + random_access_container_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + if (typeid(VectorType) == typeid(std::vector)) + { + m->eval(R"( + def Vector::`==`(Vector rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front(), r2.front())) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )" + ); + } + + return m; + } + + /// Add a String container + /// http://www.sgi.com/tech/stl/basic_string.html + template + ModulePtr string_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + operators::addition(m); + operators::assign_sum(m); + opers_comparison(m); + random_access_container_type(type, m); + sequence_type(type, m); + default_constructible_type(type, m); + // container_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + //Special case: add push_back to string (which doesn't support other back_insertion operations + m->add(fun(&String::push_back), + []()->std::string{ + if (typeid(typename String::value_type) == typeid(Boxed_Value)) { + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + + + m->add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); } ), "find"); + m->add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); } ), "rfind"); + m->add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); } ), "find_first_of"); + m->add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of"); + m->add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of"); + m->add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of"); + + m->add(fun([](String *s) { s->clear(); } ), "clear"); + m->add(fun([](const String *s) { return s->empty(); } ), "empty"); + m->add(fun([](const String *s) { return s->size(); } ), "size"); + + m->add(fun([](const String *s) { return s->c_str(); } ), "c_str"); + m->add(fun([](const String *s) { return s->data(); } ), "data"); + m->add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); } ), "substr"); + + return m; + } + + + + /// Add a MapType container + /// http://www.sgi.com/tech/stl/Map.html + template + ModulePtr future_type(const std::string &type, ModulePtr m = std::make_shared()) + { + m->add(user_type(), type); + + m->add(fun([](const FutureType &t) { return t.valid(); }), "valid"); + m->add(fun(&FutureType::get), "get"); + m->add(fun(&FutureType::wait), "wait"); + + return m; + } + } + } +} + + +#endif + + diff --git a/apps/common/script/chaiscript/dispatchkit/boxed_cast.hpp b/apps/common/script/chaiscript/dispatchkit/boxed_cast.hpp new file mode 100644 index 0000000000..44450a0f86 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/boxed_cast.hpp @@ -0,0 +1,111 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BOXED_CAST_HPP_ +#define CHAISCRIPT_BOXED_CAST_HPP_ + +#include "../chaiscript_defines.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "type_conversions.hpp" +#include "type_info.hpp" + +namespace chaiscript { +class Type_Conversions; +namespace detail { +namespace exception { +class bad_any_cast; +} // namespace exception +} // namespace detail +} // namespace chaiscript + +namespace chaiscript +{ + + /// \brief Function for extracting a value stored in a Boxed_Value object + /// \tparam Type The type to extract from the Boxed_Value + /// \param[in] bv The Boxed_Value to extract a typed value from + /// \returns Type equivalent to the requested type + /// \throws exception::bad_boxed_cast If the requested conversion is not possible + /// + /// boxed_cast will attempt to make conversions between value, &, *, std::shared_ptr, std::reference_wrapper, + /// and std::function (const and non-const) where possible. boxed_cast is used internally during function + /// dispatch. This means that all of these conversions will be attempted automatically for you during + /// ChaiScript function calls. + /// + /// \li non-const values can be extracted as const or non-const + /// \li const values can be extracted only as const + /// \li Boxed_Value constructed from pointer or std::reference_wrapper can be extracted as reference, + /// pointer or value types + /// \li Boxed_Value constructed from std::shared_ptr or value types can be extracted as reference, + /// pointer, value, or std::shared_ptr types + /// + /// Conversions to std::function objects are attempted as well + /// + /// Example: + /// \code + /// // All of the following should succeed + /// chaiscript::Boxed_Value bv(1); + /// std::shared_ptr spi = chaiscript::boxed_cast >(bv); + /// int i = chaiscript::boxed_cast(bv); + /// int *ip = chaiscript::boxed_cast(bv); + /// int &ir = chaiscript::boxed_cast(bv); + /// std::shared_ptr cspi = chaiscript::boxed_cast >(bv); + /// const int ci = chaiscript::boxed_cast(bv); + /// const int *cip = chaiscript::boxed_cast(bv); + /// const int &cir = chaiscript::boxed_cast(bv); + /// \endcode + /// + /// std::function conversion example + /// \code + /// chaiscript::ChaiScript chai; + /// Boxed_Value bv = chai.eval("`+`"); // Get the functor for the + operator which is built in + /// std::function f = chaiscript::boxed_cast >(bv); + /// int i = f(2,3); + /// assert(i == 5); + /// \endcode + template + typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) + { + if (!t_conversions || bv.get_type_info().bare_equal(user_type()) || (t_conversions && !(*t_conversions)->convertable_type())) { + try { + return detail::Cast_Helper::cast(bv, t_conversions); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + } + } + + + if (t_conversions && (*t_conversions)->convertable_type()) + { + try { + // std::cout << "trying an up conversion " << typeid(Type).name() << '\n'; + // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it + // either way, we are not responsible if it doesn't work + return detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions); + } catch (...) { + try { + // std::cout << "trying a down conversion " << typeid(Type).name() << '\n'; + // try going the other way - down the inheritance graph + return detail::Cast_Helper::cast((*t_conversions)->boxed_type_down_conversion(t_conversions->saves(), bv), t_conversions); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } + } + } else { + // If it's not polymorphic, just throw the error, don't waste the time on the + // attempted dynamic_cast + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } + + } + +} + + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/boxed_cast_helper.hpp b/apps/common/script/chaiscript/dispatchkit/boxed_cast_helper.hpp new file mode 100644 index 0000000000..505b1e1f0d --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -0,0 +1,271 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_ +#define CHAISCRIPT_BOXED_CAST_HELPER_HPP_ + +#include +#include + +#include "boxed_value.hpp" +#include "type_info.hpp" + + +namespace chaiscript +{ + class Type_Conversions_State; + + namespace detail + { + // Cast_Helper_Inner helper classes + + template + T* throw_if_null(T *t) + { + if (t) return t; + throw std::runtime_error("Attempted to dereference null Boxed_Value"); + } + + /// Generic Cast_Helper_Inner, for casting to any type + template + struct Cast_Helper_Inner + { + typedef typename std::add_const::type Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (ob.get_type_info().bare_equal_type_info(typeid(Result))) + { + auto p = throw_if_null(ob.get_const_ptr()); + return *static_cast(p); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + template + struct Cast_Helper_Inner : Cast_Helper_Inner + { + }; + + + /// Cast_Helper_Inner for casting to a const * type + template + struct Cast_Helper_Inner + { + typedef const Result * Result_Type; + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (ob.get_type_info().bare_equal_type_info(typeid(Result))) + { + return static_cast(ob.get_const_ptr()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + /// Cast_Helper_Inner for casting to a * type + template + struct Cast_Helper_Inner + { + typedef Result * Result_Type; + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (!ob.get_type_info().is_const() && ob.get_type_info() == typeid(Result)) + { + return static_cast(ob.get_ptr()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner : public Cast_Helper_Inner + { + }; + + + /// Cast_Helper_Inner for casting to a & type + template + struct Cast_Helper_Inner + { + typedef const Result& Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (ob.get_type_info().bare_equal_type_info(typeid(Result))) + { + auto p = throw_if_null(ob.get_const_ptr()); + return *static_cast(p); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + + + /// Cast_Helper_Inner for casting to a & type + template + struct Cast_Helper_Inner + { + typedef Result& Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (!ob.get_type_info().is_const() && ob.get_type_info().bare_equal_type_info(typeid(Result))) + { + return *(static_cast(throw_if_null(ob.get_ptr()))); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + }; + + /// Cast_Helper_Inner for casting to a std::shared_ptr<> type + template + struct Cast_Helper_Inner > + { + typedef std::shared_ptr Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + return ob.get().cast >(); + } + }; + + /// Cast_Helper_Inner for casting to a std::shared_ptr type + template + struct Cast_Helper_Inner > + { + typedef std::shared_ptr Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + if (!ob.get_type_info().is_const()) + { + return std::const_pointer_cast(ob.get().cast >()); + } else { + return ob.get().cast >(); + } + } + }; + + /// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type + template + struct Cast_Helper_Inner > : Cast_Helper_Inner > + { + }; + + template + struct Cast_Helper_Inner &> : Cast_Helper_Inner > + { + }; + + /// Cast_Helper_Inner for casting to a const std::shared_ptr & type + template + struct Cast_Helper_Inner > : Cast_Helper_Inner > + { + }; + + template + struct Cast_Helper_Inner &> : Cast_Helper_Inner > + { + }; + + + /// Cast_Helper_Inner for casting to a Boxed_Value type + template<> + struct Cast_Helper_Inner + { + typedef Boxed_Value Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + return ob; + } + }; + + /// Cast_Helper_Inner for casting to a Boxed_Value & type + template<> + struct Cast_Helper_Inner + { + typedef std::reference_wrapper Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + return std::ref(const_cast(ob)); + } + }; + + + /// Cast_Helper_Inner for casting to a const Boxed_Value & type + template<> + struct Cast_Helper_Inner : Cast_Helper_Inner + { + }; + + template<> + struct Cast_Helper_Inner : Cast_Helper_Inner + { + }; + + + /// Cast_Helper_Inner for casting to a std::reference_wrapper type + template + struct Cast_Helper_Inner > : Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner > : Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner &> : Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner > : Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner > : Cast_Helper_Inner + { + }; + + template + struct Cast_Helper_Inner & > : Cast_Helper_Inner + { + }; + + /// The exposed Cast_Helper object that by default just calls the Cast_Helper_Inner + template + struct Cast_Helper + { + typedef typename Cast_Helper_Inner::Result_Type Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + { + return Cast_Helper_Inner::cast(ob, t_conversions); + } + }; + } + +} + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/boxed_number.hpp b/apps/common/script/chaiscript/dispatchkit/boxed_number.hpp new file mode 100644 index 0000000000..604d0d03c7 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/boxed_number.hpp @@ -0,0 +1,1046 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BOXED_NUMERIC_HPP_ +#define CHAISCRIPT_BOXED_NUMERIC_HPP_ + +#include +#include +#include + +#include "../language/chaiscript_algebraic.hpp" +#include "any.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "type_info.hpp" + +namespace chaiscript { +class Type_Conversions; +} // namespace chaiscript + +namespace chaiscript +{ + namespace exception + { + struct arithmetic_error : std::runtime_error + { + arithmetic_error(const std::string& reason) : std::runtime_error("Arithmetic error: " + reason) {} + arithmetic_error(const arithmetic_error &) = default; + virtual ~arithmetic_error() CHAISCRIPT_NOEXCEPT {} + }; + } +} + +namespace chaiscript +{ + +// Due to the nature of generating every possible arithmetic operation, there +// are going to be warnings generated on every platform regarding size and sign, +// this is OK, so we're disabling size/and sign type warnings +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4244 4018 4389 4146 4365 4267) +#endif + + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wfloat-equal" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + + /// \brief Represents any numeric type, generically. Used internally for generic operations between POD values + class Boxed_Number + { + private: + enum class Common_Types { + t_int32, + t_double, + t_uint8, + t_int8, + t_uint16, + t_int16, + t_uint32, + t_uint64, + t_int64, + t_float, + t_long_double + }; + + template + static inline void check_divide_by_zero(T t, typename std::enable_if::value>::type* = nullptr) + { +#ifndef CHAISCRIPT_NO_PROTECT_DIVIDEBYZERO + if (t == 0) { + throw chaiscript::exception::arithmetic_error("divide by zero"); + } +#endif + } + + template + static inline void check_divide_by_zero(T, typename std::enable_if::value>::type* = nullptr) + { + } + + static CHAISCRIPT_CONSTEXPR Common_Types get_common_type(size_t t_size, bool t_signed) + { + return (t_size == 1 && t_signed)?(Common_Types::t_int8) + :(t_size == 1)?(Common_Types::t_uint8) + :(t_size == 2 && t_signed)?(Common_Types::t_int16) + :(t_size == 2)?(Common_Types::t_uint16) + :(t_size == 4 && t_signed)?(Common_Types::t_int32) + :(t_size == 4)?(Common_Types::t_uint32) + :(t_size == 8 && t_signed)?(Common_Types::t_int64) + :(Common_Types::t_uint64); + } + + + static Common_Types get_common_type(const Boxed_Value &t_bv) + { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == typeid(int)) { + return get_common_type(sizeof(int), true); + } else if (inp_ == typeid(double)) { + return Common_Types::t_double; + } else if (inp_ == typeid(long double)) { + return Common_Types::t_long_double; + } else if (inp_ == typeid(float)) { + return Common_Types::t_float; + } else if (inp_ == typeid(char)) { + return get_common_type(sizeof(char), std::is_signed::value); + } else if (inp_ == typeid(unsigned char)) { + return get_common_type(sizeof(unsigned char), false); + } else if (inp_ == typeid(unsigned int)) { + return get_common_type(sizeof(unsigned int), false); + } else if (inp_ == typeid(long)) { + return get_common_type(sizeof(long), true); + } else if (inp_ == typeid(long long)) { + return get_common_type(sizeof(long long), true); + } else if (inp_ == typeid(unsigned long)) { + return get_common_type(sizeof(unsigned long), false); + } else if (inp_ == typeid(unsigned long long)) { + return get_common_type(sizeof(unsigned long long), false); + } else if (inp_ == typeid(std::int8_t)) { + return Common_Types::t_int8; + } else if (inp_ == typeid(std::int16_t)) { + return Common_Types::t_int16; + } else if (inp_ == typeid(std::int32_t)) { + return Common_Types::t_int32; + } else if (inp_ == typeid(std::int64_t)) { + return Common_Types::t_int64; + } else if (inp_ == typeid(std::uint8_t)) { + return Common_Types::t_uint8; + } else if (inp_ == typeid(std::uint16_t)) { + return Common_Types::t_uint16; + } else if (inp_ == typeid(std::uint32_t)) { + return Common_Types::t_uint32; + } else if (inp_ == typeid(std::uint64_t)) { + return Common_Types::t_uint64; + } else if (inp_ == typeid(wchar_t)) { + return get_common_type(sizeof(wchar_t), std::is_signed::value); + } else if (inp_ == typeid(char16_t)) { + return get_common_type(sizeof(char16_t), std::is_signed::value); + } else if (inp_ == typeid(char32_t)) { + return get_common_type(sizeof(char32_t), std::is_signed::value); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static Boxed_Value boolean_go(Operators::Opers t_oper, const T &t, const T &u) + { + switch (t_oper) + { + case Operators::equals: + return const_var(t == u); + case Operators::less_than: + return const_var(t < u); + case Operators::greater_than: + return const_var(t > u); + case Operators::less_than_equal: + return const_var(t <= u); + case Operators::greater_than_equal: + return const_var(t >= u); + case Operators::not_equal: + return const_var(t != u); + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static Boxed_Value unary_go(Operators::Opers t_oper, T &t, const Boxed_Value &t_lhs) + { + switch (t_oper) + { + case Operators::pre_increment: + ++t; + break; + case Operators::pre_decrement: + --t; + break; + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + + return t_lhs; + } + + template + static Boxed_Value binary_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) + { + switch (t_oper) + { + case Operators::assign: + t = u; + break; + case Operators::assign_product: + t *= u; + break; + case Operators::assign_sum: + t += u; + break; + case Operators::assign_quotient: + check_divide_by_zero(u); + t /= u; + break; + case Operators::assign_difference: + t -= u; + break; + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + + return t_lhs; + } + + template + static Boxed_Value binary_int_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) + { + switch (t_oper) + { + case Operators::assign_bitwise_and: + t &= u; + break; + case Operators::assign_bitwise_or: + t |= u; + break; + case Operators::assign_shift_left: + t <<= u; + break; + case Operators::assign_shift_right: + t >>= u; + break; + case Operators::assign_remainder: + check_divide_by_zero(u); + t %= u; + break; + case Operators::assign_bitwise_xor: + t ^= u; + break; + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + return t_lhs; + } + + template + static Boxed_Value const_unary_int_go(Operators::Opers t_oper, const T &t) + { + switch (t_oper) + { + case Operators::bitwise_complement: + return const_var(~t); + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static Boxed_Value const_binary_int_go(Operators::Opers t_oper, const T &t, const T &u) + { + switch (t_oper) + { + case Operators::shift_left: + return const_var(t << u); + case Operators::shift_right: + return const_var(t >> u); + case Operators::remainder: + check_divide_by_zero(u); + return const_var(t % u); + case Operators::bitwise_and: + return const_var(t & u); + case Operators::bitwise_or: + return const_var(t | u); + case Operators::bitwise_xor: + return const_var(t ^ u); + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static Boxed_Value const_unary_go(Operators::Opers t_oper, const T &t) + { + switch (t_oper) + { + case Operators::unary_minus: + return const_var(-t); + case Operators::unary_plus: + return const_var(+t); + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static Boxed_Value const_binary_go(Operators::Opers t_oper, const T &t, const T &u) + { + switch (t_oper) + { + case Operators::sum: + return const_var(t + u); + case Operators::quotient: + check_divide_by_zero(u); + return const_var(t / u); + case Operators::product: + return const_var(t * u); + case Operators::difference: + return const_var(t - u); + default: + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) + -> typename std::enable_if::value && !std::is_floating_point::value, Boxed_Value>::type + { + typedef typename std::common_type::type common_type; + if (t_oper > Operators::boolean_flag && t_oper < Operators::non_const_flag) + { + return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); + } else if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); + } else if (t_oper > Operators::non_const_int_flag && t_oper < Operators::const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + return binary_int_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); + } else if (t_oper > Operators::const_int_flag && t_oper < Operators::const_flag) { + return const_binary_int_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); + } else if (t_oper > Operators::const_flag) { + return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) + -> typename std::enable_if::value || std::is_floating_point::value, Boxed_Value>::type + { + typedef typename std::common_type::type common_type; + if (t_oper > Operators::boolean_flag && t_oper < Operators::non_const_flag) + { + return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); + } else if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); + } else if (t_oper > Operators::const_flag) { + return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + // Unary + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) + -> typename std::enable_if::value, Boxed_Value>::type + { + if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); + } else if (t_oper > Operators::const_int_flag && t_oper < Operators::const_flag) { + return const_unary_int_go(t_oper, *static_cast(t_lhs.get_const_ptr())); + } else if (t_oper > Operators::const_flag) { + return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) + -> typename std::enable_if::value, Boxed_Value>::type + { + if (t_oper > Operators::non_const_flag && t_oper < Operators::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { + return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); + } else if (t_oper > Operators::const_flag) { + return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + inline static Boxed_Value oper_rhs(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) + { + switch (get_common_type(t_rhs)) { + case Common_Types::t_int32: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint8: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_int8: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint16: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_int16: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint32: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint64: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_int64: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_double: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_float: + return go(t_oper, t_lhs, t_rhs); + case Common_Types::t_long_double: + return go(t_oper, t_lhs, t_rhs); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) + { + switch (get_common_type(t_lhs)) { + case Common_Types::t_int32: + return go(t_oper, t_lhs); + case Common_Types::t_uint8: + return go(t_oper, t_lhs); + case Common_Types::t_int8: + return go(t_oper, t_lhs); + case Common_Types::t_uint16: + return go(t_oper, t_lhs); + case Common_Types::t_int16: + return go(t_oper, t_lhs); + case Common_Types::t_uint32: + return go(t_oper, t_lhs); + case Common_Types::t_uint64: + return go(t_oper, t_lhs); + case Common_Types::t_int64: + return go(t_oper, t_lhs); + case Common_Types::t_double: + return go(t_oper, t_lhs); + case Common_Types::t_float: + return go(t_oper, t_lhs); + case Common_Types::t_long_double: + return go(t_oper, t_lhs); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + + inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) + { + switch (get_common_type(t_lhs)) { + case Common_Types::t_int32: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint8: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_int8: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint16: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_int16: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint32: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_uint64: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_int64: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_double: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_float: + return oper_rhs(t_oper, t_lhs, t_rhs); + case Common_Types::t_long_double: + return oper_rhs(t_oper, t_lhs, t_rhs); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + template + static inline Target get_as_aux(const Boxed_Value &t_bv) + { + return static_cast(*static_cast(t_bv.get_const_ptr())); + } + + template + static std::string to_string_aux(const Boxed_Value &v) + { + std::ostringstream oss; + oss << *static_cast(v.get_const_ptr()); + return oss.str(); + } + + public: + Boxed_Number() + : bv(Boxed_Value(0)) + { + } + + Boxed_Number(Boxed_Value v) + : bv(std::move(v)) + { + validate_boxed_number(bv); + } + + Boxed_Number(const Boxed_Number &) = default; + +#if !defined(_MSC_VER) || _MSC_VER != 1800 + Boxed_Number(Boxed_Number &&) = default; + Boxed_Number& operator=(Boxed_Number &&) = default; +#endif + + template explicit Boxed_Number(T t) + : bv(Boxed_Value(t)) + { + validate_boxed_number(bv); + } + + static bool is_floating_point(const Boxed_Value &t_bv) + { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == typeid(double)) { + return true; + } else if (inp_ == typeid(long double)) { + return true; + } else if (inp_ == typeid(float)) { + return true; + } else { + return false; + } + } + + Boxed_Number get_as(const Type_Info &inp_) const + { + if (inp_.bare_equal_type_info(typeid(int))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(double))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(float))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(long double))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(char))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned char))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(wchar_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(char16_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(char32_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned int))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(long))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(long long))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned long))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(unsigned long long))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(int8_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(int16_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(int32_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(int64_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(uint8_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(uint16_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(uint32_t))) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal_type_info(typeid(uint64_t))) { + return Boxed_Number(get_as()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + + } + + template Target get_as() const + { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return get_as_aux(bv); + case Common_Types::t_uint8: + return get_as_aux(bv); + case Common_Types::t_int8: + return get_as_aux(bv); + case Common_Types::t_uint16: + return get_as_aux(bv); + case Common_Types::t_int16: + return get_as_aux(bv); + case Common_Types::t_uint32: + return get_as_aux(bv); + case Common_Types::t_uint64: + return get_as_aux(bv); + case Common_Types::t_int64: + return get_as_aux(bv); + case Common_Types::t_double: + return get_as_aux(bv); + case Common_Types::t_float: + return get_as_aux(bv); + case Common_Types::t_long_double: + return get_as_aux(bv); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + std::string to_string() const + { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return std::to_string(get_as()); + case Common_Types::t_uint8: + return std::to_string(get_as()); + case Common_Types::t_int8: + return std::to_string(get_as()); + case Common_Types::t_uint16: + return std::to_string(get_as()); + case Common_Types::t_int16: + return std::to_string(get_as()); + case Common_Types::t_uint32: + return std::to_string(get_as()); + case Common_Types::t_uint64: + return std::to_string(get_as()); + case Common_Types::t_int64: + return std::to_string(get_as()); + case Common_Types::t_double: + return to_string_aux(bv); + case Common_Types::t_float: + return to_string_aux(bv); + case Common_Types::t_long_double: + return to_string_aux(bv); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + bool operator==(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::equals, this->bv, t_rhs.bv)); + } + + bool operator<(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::less_than, this->bv, t_rhs.bv)); + } + + bool operator>(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::greater_than, this->bv, t_rhs.bv)); + } + + bool operator>=(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::greater_than_equal, this->bv, t_rhs.bv)); + } + + bool operator<=(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::less_than_equal, this->bv, t_rhs.bv)); + } + + bool operator!=(const Boxed_Number &t_rhs) const + { + return boxed_cast(oper(Operators::not_equal, this->bv, t_rhs.bv)); + } + + Boxed_Number operator--() + { + return oper(Operators::pre_decrement, this->bv); + } + + Boxed_Number operator++() + { + return oper(Operators::pre_increment, this->bv); + } + + Boxed_Number operator+(const Boxed_Number &t_rhs) const + { + return oper(Operators::sum, this->bv, t_rhs.bv); + } + + Boxed_Number operator+() const + { + return oper(Operators::unary_plus, this->bv); + } + + Boxed_Number operator-() const + { + return oper(Operators::unary_minus, this->bv); + } + + Boxed_Number operator-(const Boxed_Number &t_rhs) const + { + return oper(Operators::difference, this->bv, t_rhs.bv); + } + + Boxed_Number operator&=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_and, this->bv, t_rhs.bv); + } + + static void validate_boxed_number(const Boxed_Value &v) + { + const Type_Info &inp_ = v.get_type_info(); + if (inp_ == typeid(bool)) + { + throw chaiscript::detail::exception::bad_any_cast(); + } + + if (!inp_.is_arithmetic()) + { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + // cppcheck-suppress operatorEq + Boxed_Number operator=(const Boxed_Value &v) + { + validate_boxed_number(v); + bv = v; + return *this; + } + + // cppcheck-suppress operatorEq + Boxed_Number operator=(const Boxed_Number &t_rhs) const + { + return oper(Operators::assign, this->bv, t_rhs.bv); + } + + Boxed_Number operator|=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_or, this->bv, t_rhs.bv); + } + + Boxed_Number operator^=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_xor, this->bv, t_rhs.bv); + } + + Boxed_Number operator%=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_remainder, this->bv, t_rhs.bv); + } + + Boxed_Number operator<<=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_shift_left, this->bv, t_rhs.bv); + } + + Boxed_Number operator>>=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_shift_right, this->bv, t_rhs.bv); + } + + Boxed_Number operator&(const Boxed_Number &t_rhs) const + { + return oper(Operators::bitwise_and, this->bv, t_rhs.bv); + } + + Boxed_Number operator~() const + { + return oper(Operators::bitwise_complement, this->bv); + } + + Boxed_Number operator^(const Boxed_Number &t_rhs) const + { + return oper(Operators::bitwise_xor, this->bv, t_rhs.bv); + } + + Boxed_Number operator|(const Boxed_Number &t_rhs) const + { + return oper(Operators::bitwise_or, this->bv, t_rhs.bv); + } + + Boxed_Number operator*=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_product, this->bv, t_rhs.bv); + } + Boxed_Number operator/=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_quotient, this->bv, t_rhs.bv); + } + Boxed_Number operator+=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_sum, this->bv, t_rhs.bv); + } + Boxed_Number operator-=(const Boxed_Number &t_rhs) + { + return oper(Operators::assign_difference, this->bv, t_rhs.bv); + } + + Boxed_Number operator/(const Boxed_Number &t_rhs) const + { + return oper(Operators::quotient, this->bv, t_rhs.bv); + } + + Boxed_Number operator<<(const Boxed_Number &t_rhs) const + { + return oper(Operators::shift_left, this->bv, t_rhs.bv); + } + + Boxed_Number operator*(const Boxed_Number &t_rhs) const + { + return oper(Operators::product, this->bv, t_rhs.bv); + } + + Boxed_Number operator%(const Boxed_Number &t_rhs) const + { + return oper(Operators::remainder, this->bv, t_rhs.bv); + } + + Boxed_Number operator>>(const Boxed_Number &t_rhs) const + { + return oper(Operators::shift_right, this->bv, t_rhs.bv); + } + + + + static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::equals, t_lhs.bv, t_rhs.bv)); + } + + static bool less_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::less_than, t_lhs.bv, t_rhs.bv)); + } + + static bool greater_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::greater_than, t_lhs.bv, t_rhs.bv)); + } + + static bool greater_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::greater_than_equal, t_lhs.bv, t_rhs.bv)); + } + + static bool less_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::less_than_equal, t_lhs.bv, t_rhs.bv)); + } + + static bool not_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return boxed_cast(oper(Operators::not_equal, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number pre_decrement(Boxed_Number t_lhs) + { + return oper(Operators::pre_decrement, t_lhs.bv); + } + + static Boxed_Number pre_increment(Boxed_Number t_lhs) + { + return oper(Operators::pre_increment, t_lhs.bv); + } + + static const Boxed_Number sum(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::sum, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) + { + return oper(Operators::unary_plus, t_lhs.bv); + } + + static const Boxed_Number unary_minus(const Boxed_Number &t_lhs) + { + return oper(Operators::unary_minus, t_lhs.bv); + } + + static const Boxed_Number difference(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::difference, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_and, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_or, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_bitwise_xor, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_remainder(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_remainder, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_shift_left(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_shift_left, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_shift_right(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_shift_right, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::bitwise_and, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs) + { + return oper(Operators::bitwise_complement, t_lhs.bv, Boxed_Value(0)); + } + + static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::bitwise_xor, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::bitwise_or, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_product(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_product, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_quotient(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_quotient, t_lhs.bv, t_rhs.bv); + } + + static Boxed_Number assign_sum(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_sum, t_lhs.bv, t_rhs.bv); + } + static Boxed_Number assign_difference(Boxed_Number t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::assign_difference, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number quotient(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::quotient, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number shift_left(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::shift_left, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number product(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::product, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number remainder(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::remainder, t_lhs.bv, t_rhs.bv); + } + + static const Boxed_Number shift_right(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) + { + return oper(Operators::shift_right, t_lhs.bv, t_rhs.bv); + } + + + + static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) + { + return oper(t_oper, t_lhs, t_rhs); + } + + static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) + { + return oper(t_oper, t_lhs); + } + + + + Boxed_Value bv; + }; + + namespace detail + { + /// Cast_Helper for converting from Boxed_Value to Boxed_Number + template<> + struct Cast_Helper + { + typedef Boxed_Number Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *) + { + return Boxed_Number(ob); + } + }; + + /// Cast_Helper for converting from Boxed_Value to Boxed_Number + template<> + struct Cast_Helper : Cast_Helper + { + }; + + /// Cast_Helper for converting from Boxed_Value to Boxed_Number + template<> + struct Cast_Helper : Cast_Helper + { + }; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + +} + + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/boxed_value.hpp b/apps/common/script/chaiscript/dispatchkit/boxed_value.hpp new file mode 100644 index 0000000000..5a49119fd5 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/boxed_value.hpp @@ -0,0 +1,435 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_BOXED_VALUE_HPP_ +#define CHAISCRIPT_BOXED_VALUE_HPP_ + +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "any.hpp" +#include "type_info.hpp" + +namespace chaiscript +{ + + /// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects + /// \sa chaiscript::boxed_cast + class Boxed_Value + { + public: + /// used for explicitly creating a "void" object + struct Void_Type + { + }; + + private: + /// structure which holds the internal state of a Boxed_Value + /// \todo Get rid of Any and merge it with this, reducing an allocation in the process + struct Data + { + Data(const Type_Info &ti, + chaiscript::detail::Any to, + bool tr, + const void *t_void_ptr, + bool t_return_value) + : m_type_info(ti), m_obj(std::move(to)), m_data_ptr(ti.is_const()?nullptr:const_cast(t_void_ptr)), m_const_data_ptr(t_void_ptr), + m_is_ref(tr), m_return_value(t_return_value) + { + } + + Data &operator=(const Data &rhs) + { + m_type_info = rhs.m_type_info; + m_obj = rhs.m_obj; + m_is_ref = rhs.m_is_ref; + m_data_ptr = rhs.m_data_ptr; + m_const_data_ptr = rhs.m_const_data_ptr; + m_return_value = rhs.m_return_value; + + if (rhs.m_attrs) + { + m_attrs = std::unique_ptr>>(new std::map>(*rhs.m_attrs)); + } + + return *this; + } + + Data(const Data &) = delete; + +#if !defined(__APPLE__) && (!defined(_MSC_VER) || _MSC_VER != 1800) + Data(Data &&) = default; + Data &operator=(Data &&rhs) = default; +#endif + + + Type_Info m_type_info; + chaiscript::detail::Any m_obj; + void *m_data_ptr; + const void *m_const_data_ptr; + std::unique_ptr>> m_attrs; + bool m_is_ref; + bool m_return_value; + }; + + struct Object_Data + { + static std::shared_ptr get(Boxed_Value::Void_Type, bool t_return_value) + { + return std::make_shared( + detail::Get_Type_Info::get(), + chaiscript::detail::Any(), + false, + nullptr, + t_return_value) + ; + } + + template + static std::shared_ptr get(const std::shared_ptr *obj, bool t_return_value) + { + return get(*obj, t_return_value); + } + + template + static std::shared_ptr get(const std::shared_ptr &obj, bool t_return_value) + { + return std::make_shared( + detail::Get_Type_Info::get(), + chaiscript::detail::Any(obj), + false, + obj.get(), + t_return_value + ); + } + + template + static std::shared_ptr get(std::shared_ptr &&obj, bool t_return_value) + { + auto ptr = obj.get(); + return std::make_shared( + detail::Get_Type_Info::get(), + chaiscript::detail::Any(std::move(obj)), + false, + ptr, + t_return_value + ); + } + + template + static std::shared_ptr get(T *t, bool t_return_value) + { + return get(std::ref(*t), t_return_value); + } + + template + static std::shared_ptr get(const T *t, bool t_return_value) + { + return get(std::cref(*t), t_return_value); + } + + + template + static std::shared_ptr get(std::reference_wrapper obj, bool t_return_value) + { + auto p = &obj.get(); + return std::make_shared( + detail::Get_Type_Info::get(), + chaiscript::detail::Any(std::move(obj)), + true, + p, + t_return_value + ); + } + + template + static std::shared_ptr get(T t, bool t_return_value) + { + auto p = std::make_shared(std::move(t)); + auto ptr = p.get(); + return std::make_shared( + detail::Get_Type_Info::get(), + chaiscript::detail::Any(std::move(p)), + false, + ptr, + t_return_value + ); + } + + static std::shared_ptr get() + { + return std::make_shared( + Type_Info(), + chaiscript::detail::Any(), + false, + nullptr, + false + ); + } + + }; + + public: + /// Basic Boxed_Value constructor + template::type>::value>::type> + explicit Boxed_Value(T &&t, bool t_return_value = false) + : m_data(Object_Data::get(std::forward(t), t_return_value)) + { + } + + /// Unknown-type constructor + Boxed_Value() + : m_data(Object_Data::get()) + { + } + +#if !defined(_MSC_VER) || _MSC_VER != 1800 + Boxed_Value(Boxed_Value&&) = default; + Boxed_Value& operator=(Boxed_Value&&) = default; +#endif + + Boxed_Value(const Boxed_Value&) = default; + Boxed_Value& operator=(const Boxed_Value&) = default; + + void swap(Boxed_Value &rhs) + { + std::swap(m_data, rhs.m_data); + } + + /// Copy the values stored in rhs.m_data to m_data. + /// m_data pointers are not shared in this case + Boxed_Value assign(const Boxed_Value &rhs) + { + (*m_data) = (*rhs.m_data); + return *this; + } + + const Type_Info &get_type_info() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_type_info; + } + + /// return true if the object is uninitialized + bool is_undef() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_type_info.is_undef(); + } + + bool is_const() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_type_info.is_const(); + } + + bool is_type(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT + { + return m_data->m_type_info.bare_equal(ti); + } + + bool is_null() const CHAISCRIPT_NOEXCEPT + { + return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr); + } + + const chaiscript::detail::Any & get() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_obj; + } + + bool is_ref() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_is_ref; + } + + bool is_return_value() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_return_value; + } + + void reset_return_value() const CHAISCRIPT_NOEXCEPT + { + m_data->m_return_value = false; + } + + bool is_pointer() const CHAISCRIPT_NOEXCEPT + { + return !is_ref(); + } + + void *get_ptr() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_data_ptr; + } + + const void *get_const_ptr() const CHAISCRIPT_NOEXCEPT + { + return m_data->m_const_data_ptr; + } + + Boxed_Value get_attr(const std::string &t_name) + { + if (!m_data->m_attrs) + { + m_data->m_attrs = std::unique_ptr>>(new std::map>()); + } + + auto &attr = (*m_data->m_attrs)[t_name]; + if (attr) { + return Boxed_Value(attr, Internal_Construction()); + } else { + Boxed_Value bv; //default construct a new one + attr = bv.m_data; + return bv; + } + } + + Boxed_Value ©_attrs(const Boxed_Value &t_obj) + { + if (t_obj.m_data->m_attrs) + { + m_data->m_attrs = std::unique_ptr>>(new std::map>(*t_obj.m_data->m_attrs)); + } + return *this; + } + + Boxed_Value &clone_attrs(const Boxed_Value &t_obj) + { + copy_attrs(t_obj); + reset_return_value(); + return *this; + } + + + /// \returns true if the two Boxed_Values share the same internal type + static bool type_match(const Boxed_Value &l, const Boxed_Value &r) CHAISCRIPT_NOEXCEPT + { + return l.get_type_info() == r.get_type_info(); + } + + private: + // necessary to avoid hitting the templated && constructor of Boxed_Value + struct Internal_Construction{}; + + Boxed_Value(const std::shared_ptr &t_data, Internal_Construction) + : m_data(t_data) { + } + + std::shared_ptr m_data; + }; + + /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or std::reference_type + /// a copy is not made. + /// @param t The value to box + /// + /// Example: + /// + /// ~~~{.cpp} + /// int i; + /// chaiscript::ChaiScript chai; + /// chai.add(chaiscript::var(i), "i"); + /// chai.add(chaiscript::var(&i), "ip"); + /// ~~~ + /// + /// @sa @ref adding_objects + template + Boxed_Value var(T &&t) + { + return Boxed_Value(std::forward(t)); + } + + namespace detail { + /// \brief Takes a value, copies it and returns a Boxed_Value object that is immutable + /// \param[in] t Value to copy and make const + /// \returns Immutable Boxed_Value + /// \sa Boxed_Value::is_const + template + Boxed_Value const_var_impl(const T &t) + { + return Boxed_Value(std::make_shared::type >(t)); + } + + /// \brief Takes a pointer to a value, adds const to the pointed to type and returns an immutable Boxed_Value. + /// Does not copy the pointed to value. + /// \param[in] t Pointer to make immutable + /// \returns Immutable Boxed_Value + /// \sa Boxed_Value::is_const + template + Boxed_Value const_var_impl(T *t) + { + return Boxed_Value( const_cast::type *>(t) ); + } + + /// \brief Takes a std::shared_ptr to a value, adds const to the pointed to type and returns an immutable Boxed_Value. + /// Does not copy the pointed to value. + /// \param[in] t Pointer to make immutable + /// \returns Immutable Boxed_Value + /// \sa Boxed_Value::is_const + template + Boxed_Value const_var_impl(const std::shared_ptr &t) + { + return Boxed_Value( std::const_pointer_cast::type>(t) ); + } + + /// \brief Takes a std::reference_wrapper value, adds const to the referenced type and returns an immutable Boxed_Value. + /// Does not copy the referenced value. + /// \param[in] t Reference object to make immutable + /// \returns Immutable Boxed_Value + /// \sa Boxed_Value::is_const + template + Boxed_Value const_var_impl(const std::reference_wrapper &t) + { + return Boxed_Value( std::cref(t.get()) ); + } + } + + /// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type + /// the value is not copied. If it is an object type, it is copied. + /// \param[in] t Object to make immutable + /// \returns Immutable Boxed_Value + /// \sa chaiscript::Boxed_Value::is_const + /// \sa chaiscript::var + /// + /// Example: + /// \code + /// enum Colors + /// { + /// Blue, + /// Green, + /// Red + /// }; + /// chaiscript::ChaiScript chai + /// chai.add(chaiscript::const_var(Blue), "Blue"); // add immutable constant + /// chai.add(chaiscript::const_var(Red), "Red"); + /// chai.add(chaiscript::const_var(Green), "Green"); + /// \endcode + /// + /// \todo support C++11 strongly typed enums + /// \sa \ref adding_objects + template + Boxed_Value const_var(const T &t) + { + return detail::const_var_impl(t); + } + +#ifdef CHAISCRIPT_HAS_MAGIC_STATICS + inline Boxed_Value const_var(bool b) { + static auto t = detail::const_var_impl(true); + static auto f = detail::const_var_impl(false); + + if (b) { + return t; + } else { + return f; + } + } +#endif + +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/callable_traits.hpp b/apps/common/script/chaiscript/dispatchkit/callable_traits.hpp new file mode 100644 index 0000000000..765e6d673d --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/callable_traits.hpp @@ -0,0 +1,107 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_CALLABLE_TRAITS_HPP_ +#define CHAISCRIPT_CALLABLE_TRAITS_HPP_ + +#include + +namespace chaiscript { + namespace dispatch { + namespace detail { + + template + struct Constructor + { + template + std::shared_ptr operator()(Inner&& ... inner) const { + return std::make_shared(std::forward(inner)...); + } + }; + + template + struct Const_Caller + { + Const_Caller(Ret (Class::*t_func)(Param...) const) : m_func(t_func) {} + + template + Ret operator()(const Class &o, Inner&& ... inner) const { + return (o.*m_func)(std::forward(inner)...); + } + + Ret (Class::*m_func)(Param...) const; + }; + + template + struct Fun_Caller + { + Fun_Caller(Ret( * t_func)(Param...) ) : m_func(t_func) {} + + template + Ret operator()(Inner&& ... inner) const { + return (m_func)(std::forward(inner)...); + } + + Ret(*m_func)(Param...); + }; + + template + struct Caller + { + Caller(Ret (Class::*t_func)(Param...)) : m_func(t_func) {} + + template + Ret operator()(Class &o, Inner&& ... inner) const { + return (o.*m_func)(std::forward(inner)...); + } + + Ret (Class::*m_func)(Param...); + }; + + template + struct Arity + { + }; + + template + struct Arity + { + static const size_t arity = sizeof...(Params); + }; + + + template + struct Function_Signature + { + }; + + template + struct Function_Signature + { + typedef Ret Return_Type; + typedef Ret (Signature)(Params...); + }; + + template + struct Function_Signature + { + typedef Ret Return_Type; + typedef Ret (Signature)(Params...); + }; + + + template + struct Callable_Traits + { + typedef typename Function_Signature::Signature Signature; + typedef typename Function_Signature::Return_Type Return_Type; + }; + } + } +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/dispatchkit.hpp b/apps/common/script/chaiscript/dispatchkit/dispatchkit.hpp new file mode 100644 index 0000000000..23dc7ee85d --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/dispatchkit.hpp @@ -0,0 +1,1555 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_DISPATCHKIT_HPP_ +#define CHAISCRIPT_DISPATCHKIT_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "../chaiscript_threading.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "type_conversions.hpp" +#include "dynamic_object.hpp" +#include "proxy_constructors.hpp" +#include "proxy_functions.hpp" +#include "type_info.hpp" + +namespace chaiscript { +class Boxed_Number; +} // namespace chaiscript + +namespace chaiscript { +namespace dispatch { +class Dynamic_Proxy_Function; +class Proxy_Function_Base; +struct Placeholder_Object; +} // namespace dispatch +} // namespace chaiscript + + +/// \namespace chaiscript::dispatch +/// \brief Classes and functions specific to the runtime dispatch side of ChaiScript. Some items may be of use to the end user. + +namespace chaiscript +{ + namespace exception + { + /// Exception thrown in the case that an object name is invalid because it is a reserved word + class reserved_word_error : public std::runtime_error + { + public: + reserved_word_error(const std::string &t_word) CHAISCRIPT_NOEXCEPT + : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word) + { + } + + reserved_word_error(const reserved_word_error &) = default; + + virtual ~reserved_word_error() CHAISCRIPT_NOEXCEPT {} + + std::string word() const + { + return m_word; + } + + private: + std::string m_word; + }; + + /// Exception thrown in the case that an object name is invalid because it contains illegal characters + class illegal_name_error : public std::runtime_error + { + public: + illegal_name_error(const std::string &t_name) CHAISCRIPT_NOEXCEPT + : std::runtime_error("Reserved name not allowed in object name: " + t_name), m_name(t_name) + { + } + + illegal_name_error(const illegal_name_error &) = default; + + virtual ~illegal_name_error() CHAISCRIPT_NOEXCEPT {} + + std::string name() const + { + return m_name; + } + + private: + std::string m_name; + }; + + + /// Exception thrown in the case that an object name is invalid because it already exists in current context + class name_conflict_error : public std::runtime_error + { + public: + name_conflict_error(const std::string &t_name) CHAISCRIPT_NOEXCEPT + : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name) + { + } + + name_conflict_error(const name_conflict_error &) = default; + + virtual ~name_conflict_error() CHAISCRIPT_NOEXCEPT {} + + std::string name() const + { + return m_name; + } + + private: + std::string m_name; + + }; + + + /// Exception thrown in the case that a non-const object was added as a shared object + class global_non_const : public std::runtime_error + { + public: + global_non_const() CHAISCRIPT_NOEXCEPT + : std::runtime_error("a global object must be const") + { + } + + global_non_const(const global_non_const &) = default; + virtual ~global_non_const() CHAISCRIPT_NOEXCEPT {} + }; + } + + + /// \brief Holds a collection of ChaiScript settings which can be applied to the ChaiScript runtime. + /// Used to implement loadable module support. + class Module + { + public: + Module &add(Type_Info ti, std::string name) + { + m_typeinfos.emplace_back(std::move(ti), std::move(name)); + return *this; + } + + Module &add(Type_Conversion d) + { + m_conversions.push_back(std::move(d)); + return *this; + } + + Module &add(Proxy_Function f, std::string name) + { + m_funcs.emplace_back(std::move(f), std::move(name)); + return *this; + } + + Module &add_global_const(Boxed_Value t_bv, std::string t_name) + { + if (!t_bv.is_const()) + { + throw chaiscript::exception::global_non_const(); + } + + m_globals.emplace_back(std::move(t_bv), std::move(t_name)); + return *this; + } + + + //Add a bit of ChaiScript to eval during module implementation + Module &eval(const std::string &str) + { + m_evals.push_back(str); + return *this; + } + + Module &add(const std::shared_ptr &m) + { + m->apply(*this, *this); + return *m; + } + + template + void apply(Eval &t_eval, Engine &t_engine) const + { + apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine); + apply(m_funcs.begin(), m_funcs.end(), t_engine); + apply_eval(m_evals.begin(), m_evals.end(), t_eval); + apply_single(m_conversions.begin(), m_conversions.end(), t_engine); + apply_globals(m_globals.begin(), m_globals.end(), t_engine); + } + + ~Module() + { + } + + bool has_function(const Proxy_Function &new_f, const std::string &name) + { + return std::any_of(m_funcs.begin(), m_funcs.end(), [&](const std::pair &existing_f) { + return existing_f.second == name && *(existing_f.first) == *(new_f); + }); + } + + + private: + std::vector > m_typeinfos; + std::vector > m_funcs; + std::vector > m_globals; + std::vector m_evals; + std::vector m_conversions; + + template + static void apply(InItr begin, const InItr end, T &t) + { + for_each(begin, end, [&t](typename std::iterator_traits::reference obj) + { + try { + t.add(obj.first, obj.second); + } catch (const chaiscript::exception::name_conflict_error &) { + /// \todo Should we throw an error if there's a name conflict + /// while applying a module? + } + } + ); + } + + template + static void apply_globals(InItr begin, InItr end, T &t) + { + while (begin != end) + { + t.add_global_const(begin->first, begin->second); + ++begin; + } + } + + template + static void apply_single(InItr begin, InItr end, T &t) + { + while (begin != end) + { + t.add(*begin); + ++begin; + } + } + + template + static void apply_eval(InItr begin, InItr end, T &t) + { + while (begin != end) + { + t.eval(*begin); + ++begin; + } + } + }; + + /// Convenience typedef for Module objects to be added to the ChaiScript runtime + typedef std::shared_ptr ModulePtr; + + namespace detail + { + /// A Proxy_Function implementation that is able to take + /// a vector of Proxy_Functions and perform a dispatch on them. It is + /// used specifically in the case of dealing with Function object variables + class Dispatch_Function : public dispatch::Proxy_Function_Base + { + public: + Dispatch_Function(std::vector t_funcs) + : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)), + m_funcs(std::move(t_funcs)) + { + } + + virtual bool operator==(const dispatch::Proxy_Function_Base &rhs) const CHAISCRIPT_OVERRIDE + { + try { + const auto &dispatch_fun = dynamic_cast(rhs); + return m_funcs == dispatch_fun.m_funcs; + } catch (const std::bad_cast &) { + return false; + } + } + + virtual ~Dispatch_Function() {} + + virtual std::vector get_contained_functions() const CHAISCRIPT_OVERRIDE + { + return std::vector(m_funcs.begin(), m_funcs.end()); + } + + + static int calculate_arity(const std::vector &t_funcs) + { + if (t_funcs.empty()) { + return -1; + } + + const auto arity = t_funcs.front()->get_arity(); + + for (const auto &func : t_funcs) + { + if (arity != func->get_arity()) + { + // The arities in the list do not match, so it's unspecified + return -1; + } + } + + return arity; + } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return std::any_of(m_funcs.cbegin(), m_funcs.cend(), + [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); }); + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return "Multiple method dispatch function wrapper."; + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return dispatch::dispatch(m_funcs, params, t_conversions); + } + + private: + std::vector m_funcs; + + static std::vector build_type_infos(const std::vector &t_funcs) + { + auto begin = t_funcs.cbegin(); + const auto &end = t_funcs.cend(); + + if (begin != end) + { + std::vector type_infos = (*begin)->get_param_types(); + + ++begin; + + bool size_mismatch = false; + + while (begin != end) + { + std::vector param_types = (*begin)->get_param_types(); + + if (param_types.size() != type_infos.size()) + { + size_mismatch = true; + } + + for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i) + { + if (!(type_infos[i] == param_types[i])) + { + type_infos[i] = detail::Get_Type_Info::get(); + } + } + + ++begin; + } + + assert(type_infos.size() > 0 && " type_info vector size is < 0, this is only possible if something else is broken"); + + if (size_mismatch) + { + type_infos.resize(1); + } + + return type_infos; + } + + return std::vector(); + } + }; + } + + + namespace detail + { + struct Stack_Holder + { + typedef std::vector> Scope; + typedef std::vector StackData; + + Stack_Holder() + : call_depth(0) + { + stacks.reserve(2); + stacks.emplace_back(1); + call_params.emplace_back(); + call_params.back().reserve(2); + } + + std::vector stacks; + + std::vector> call_params; + int call_depth; + }; + + /// Main class for the dispatchkit. Handles management + /// of the object stack, functions and registered types. + class Dispatch_Engine + { + + public: + typedef std::map Type_Name_Map; + typedef std::vector> Scope; + typedef std::vector StackData; + + struct State + { + std::vector>>> m_functions; + std::vector> m_function_objects; + std::vector> m_boxed_functions; + std::map m_global_objects; + Type_Name_Map m_types; + std::set m_reserved_words; + + State &operator=(const State &) = default; + State() = default; + State(const State &) = default; + }; + + Dispatch_Engine() + : m_stack_holder(this) + { + } + + ~Dispatch_Engine() + { + } + + /// \brief casts an object while applying any Dynamic_Conversion available + template + typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv) const + { + Type_Conversions_State state(m_conversions, m_conversions.conversion_saves()); + return chaiscript::boxed_cast(bv, &state); + } + + /// Add a new conversion for upcasting to a base class + void add(const Type_Conversion &d) + { + m_conversions.add_conversion(d); + } + + /// Add a new named Proxy_Function to the system + void add(const Proxy_Function &f, const std::string &name) + { + validate_object_name(name); + add_function(f, name); + } + + /// Set the value of an object, by name. If the object + /// is not available in the current scope it is created + void add(Boxed_Value obj, const std::string &name) + { + validate_object_name(name); + auto &stack = get_stack_data(); + + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) + { + auto itr = std::find_if(stack_elem->begin(), stack_elem->end(), + [&](const std::pair &o) { + return o.first == name; + }); + + if (itr != stack_elem->end()) + { + itr->second = std::move(obj); + return; + } + } + + add_object(name, std::move(obj)); + } + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder) + { + auto &stack_elem = get_stack_data(t_holder).back(); + + if (std::any_of(stack_elem.begin(), stack_elem.end(), + [&](const std::pair &o) { + return o.first == t_name; + })) + { + throw chaiscript::exception::name_conflict_error(t_name); + } + + get_stack_data(t_holder).back().emplace_back(t_name, std::move(obj)); + } + + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(const std::string &name, Boxed_Value obj) + { + add_object(name, std::move(obj), get_stack_holder()); + } + + /// Adds a new global shared object, between all the threads + void add_global_const(const Boxed_Value &obj, const std::string &name) + { + validate_object_name(name); + if (!obj.is_const()) + { + throw chaiscript::exception::global_non_const(); + } + + chaiscript::detail::threading::unique_lock l(m_mutex); + + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) + { + throw chaiscript::exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } + } + + /// Adds a new global (non-const) shared object, between all the threads + Boxed_Value add_global_no_throw(const Boxed_Value &obj, const std::string &name) + { + validate_object_name(name); + + chaiscript::detail::threading::unique_lock l(m_mutex); + + const auto itr = m_state.m_global_objects.find(name); + if (itr == m_state.m_global_objects.end()) + { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + return obj; + } else { + return itr->second; + } + } + + + /// Adds a new global (non-const) shared object, between all the threads + void add_global(const Boxed_Value &obj, const std::string &name) + { + validate_object_name(name); + + chaiscript::detail::threading::unique_lock l(m_mutex); + + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) + { + throw chaiscript::exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } + } + + /// Updates an existing global shared object or adds a new global shared object if not found + void set_global(const Boxed_Value &obj, const std::string &name) + { + validate_object_name(name); + + chaiscript::detail::threading::unique_lock l(m_mutex); + + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) + { + itr->second.assign(obj); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } + } + + /// Adds a new scope to the stack + void new_scope() + { + new_scope(*m_stack_holder); + } + + /// Pops the current scope from the stack + void pop_scope() + { + pop_scope(*m_stack_holder); + } + + /// Adds a new scope to the stack + void new_scope(Stack_Holder &t_holder) + { + get_stack_data(t_holder).emplace_back(); + t_holder.call_params.emplace_back(); + } + + /// Pops the current scope from the stack + void pop_scope(Stack_Holder &t_holder) + { + t_holder.call_params.pop_back(); + StackData &stack = get_stack_data(t_holder); + if (stack.size() > 1) + { + stack.pop_back(); + } else { + throw std::range_error("Unable to pop global stack"); + } + } + + + /// Pushes a new stack on to the list of stacks + static void new_stack(Stack_Holder &t_holder) + { + // add a new Stack with 1 element + t_holder.stacks.emplace_back(1); + } + + static void pop_stack(Stack_Holder &t_holder) + { + t_holder.stacks.pop_back(); + } + + /// Searches the current stack for an object of the given name + /// includes a special overload for the _ place holder object to + /// ensure that it is always in scope. + Boxed_Value get_object(const std::string &name, std::atomic_uint_fast32_t &t_loc) const + { + enum class Loc : uint_fast32_t { + located = 0x80000000, + is_local = 0x40000000, + stack_mask = 0x0FFF0000, + loc_mask = 0x0000FFFF + }; + + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + + if (loc == 0) + { + auto &stack = get_stack_data(); + + // Is it in the stack? + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) + { + for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s ) + { + if (s->first == name) { + t_loc.store( static_cast(std::distance(stack.rbegin(), stack_elem) << 16) + | static_cast(std::distance(stack_elem->begin(), s)) + | static_cast(Loc::located) + | static_cast(Loc::is_local), + std::memory_order_relaxed); + return s->second; + } + } + } + + t_loc.store( static_cast(Loc::located), std::memory_order_relaxed); + } else if (loc & static_cast(Loc::is_local)) { + auto &stack = get_stack_data(); + + return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)][loc & static_cast(Loc::loc_mask)].second; + } + + // Is the value we are looking for a global or function? + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) + { + return itr->second; + } + + // no? is it a function object? + auto obj = get_function_object_int(name, loc); + if (obj.first != loc) t_loc.store(uint_fast32_t(obj.first), std::memory_order_relaxed); + return obj.second; + + + } + + /// Registers a new named type + void add(const Type_Info &ti, const std::string &name) + { + add_global_const(const_var(ti), name + "_type"); + + chaiscript::detail::threading::unique_lock l(m_mutex); + + m_state.m_types.insert(std::make_pair(name, ti)); + } + + /// Returns the type info for a named type + Type_Info get_type(const std::string &name, bool t_throw = true) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto itr = m_state.m_types.find(name); + + if (itr != m_state.m_types.end()) + { + return itr->second; + } + + if (t_throw) { + throw std::range_error("Type Not Known"); + } else { + return Type_Info(); + } + } + + /// Returns the registered name of a known type_info object + /// compares the "bare_type_info" for the broadest possible + /// match + std::string get_type_name(const Type_Info &ti) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + for (const auto & elem : m_state.m_types) + { + if (elem.second.bare_equal(ti)) + { + return elem.first; + } + } + + return ti.bare_name(); + } + + /// Return all registered types + std::vector > get_types() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return std::vector >(m_state.m_types.begin(), m_state.m_types.end()); + } + + std::shared_ptr> get_method_missing_functions() const + { + uint_fast32_t method_missing_loc = m_method_missing_loc.load(std::memory_order_relaxed); + auto method_missing_funs = get_function("method_missing", method_missing_loc); + if (method_missing_funs.first != method_missing_loc) m_method_missing_loc.store(uint_fast32_t(method_missing_funs.first), std::memory_order_relaxed); + return std::move(method_missing_funs.second); + } + + + /// Return a function by name + std::pair>> get_function(const std::string &t_name, const size_t t_hint) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto &funs = get_functions_int(); + + auto itr = find_keyed_value(funs, t_name, t_hint); + + if (itr != funs.end()) + { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + return std::make_pair(size_t(0), std::make_shared>()); + } + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + Boxed_Value get_function_object(const std::string &t_name) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return get_function_object_int(t_name, 0).second; + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + /// \warn does not obtain a mutex lock. \sa get_function_object for public version + std::pair get_function_object_int(const std::string &t_name, const size_t t_hint) const + { + const auto &funs = get_boxed_functions_int(); + + auto itr = find_keyed_value(funs, t_name, t_hint); + + if (itr != funs.end()) + { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + throw std::range_error("Object not found: " + t_name); + } + } + + + /// Return true if a function exists + bool function_exists(const std::string &name) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto &functions = get_functions_int(); + return find_keyed_value(functions, name) != functions.end(); + } + + /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, + /// the current scope. + std::map get_parent_locals() const + { + auto &stack = get_stack_data(); + if (stack.size() > 1) + { + return std::map(stack[1].begin(), stack[1].end()); + } else { + return std::map(stack[0].begin(), stack[0].end()); + } + } + + /// \returns All values in the local thread state, added through the add() function + std::map get_locals() const + { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + return std::map(scope.begin(), scope.end()); + } + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals) + { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + scope = std::vector>(t_locals.begin(), t_locals.end()); + } + + + + /// + /// Get a map of all objects that can be seen from the current scope in a scripting context + /// + std::map get_scripting_objects() const + { + const Stack_Holder &s = *m_stack_holder; + + // We don't want the current context, but one up if it exists + const StackData &stack = (s.stacks.size()==1)?(s.stacks.back()):(s.stacks[s.stacks.size()-2]); + + std::map retval; + + // note: map insert doesn't overwrite existing values, which is why this works + for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr) + { + retval.insert(itr->begin(), itr->end()); + } + + // add the global values + chaiscript::detail::threading::shared_lock l(m_mutex); + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); + + return retval; + } + + + /// + /// Get a map of all functions that can be seen from a scripting context + /// + std::map get_function_objects() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto &funs = get_function_objects_int(); + + std::map objs; + + for (const auto & fun : funs) + { + objs.insert(std::make_pair(fun.first, const_var(fun.second))); + } + + return objs; + } + + + /// Get a vector of all registered functions + std::vector > get_functions() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + std::vector > rets; + + const auto &functions = get_functions_int(); + + for (const auto & function : functions) + { + for (const auto & internal_func : *function.second) + { + rets.emplace_back(function.first, internal_func); + } + } + + return rets; + } + + void add_reserved_word(const std::string &name) + { + chaiscript::detail::threading::unique_lock l(m_mutex); + + m_state.m_reserved_words.insert(name); + } + + const Type_Conversions &conversions() const + { + return m_conversions; + } + + bool is_attribute_call(const std::vector &t_funs, const std::vector &t_params, + bool t_has_params, const Type_Conversions_State &t_conversions) const + { + if (!t_has_params || t_params.empty()) { + return false; + } + + for (const auto &fun : t_funs) { + if (fun->is_attribute_function()) { + if (fun->compare_first_type(t_params[0], t_conversions)) { + return true; + } + } + } + + return false; + } + +#ifdef CHAISCRIPT_MSVC +// MSVC is unable to recognize that "rethrow_exception" causes the function to return +// so we must disable it here. +#pragma warning(push) +#pragma warning(disable : 4715) +#endif + Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, bool t_has_params, + const Type_Conversions_State &t_conversions) + { + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); + + const auto do_attribute_call = + [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value + { + std::vector attr_params{l_params.begin(), l_params.begin() + l_num_params}; + Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); + if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type())) { + struct This_Foist { + This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) { + m_e.get().new_scope(); + m_e.get().add_object("__this", t_bv); + } + + ~This_Foist() { + m_e.get().pop_scope(); + } + + std::reference_wrapper m_e; + }; + + This_Foist fi(*this, l_params.front()); + + auto func = boxed_cast>(bv); + try { + return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions); + } catch (const chaiscript::exception::bad_boxed_cast &) { + } catch (const chaiscript::exception::arity_error &) { + } catch (const chaiscript::exception::guard_error &) { + } + throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, std::vector{func}); + } else { + return bv; + } + }; + + if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) { + return do_attribute_call(1, params, *funs.second, t_conversions); + } else { + std::exception_ptr except; + + if (!funs.second->empty()) { + try { + return dispatch::dispatch(*funs.second, params, t_conversions); + } catch(chaiscript::exception::dispatch_error&) { + except = std::current_exception(); + } + } + + // If we get here we know that either there was no method with that name, + // or there was no matching method + + const auto functions = [&]()->std::vector { + std::vector fs; + + const auto method_missing_funs = get_method_missing_functions(); + + for (const auto &f : *method_missing_funs) + { + if(f->compare_first_type(params[0], t_conversions)) { + fs.push_back(f); + } + } + + return fs; + }(); + + + + const bool is_no_param = [&]()->bool{ + for (const auto &f : functions) { + if (f->get_arity() != 2) { + return false; + } + } + return true; + }(); + + if (!functions.empty()) { + try { + if (is_no_param) { + std::vector tmp_params(params); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + return do_attribute_call(2, tmp_params, functions, t_conversions); + } else { + return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, t_conversions); + } + } catch (const dispatch::option_explicit_set &e) { + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end()), + e.what()); + } + } + + // If we get all the way down here we know there was no "method_missing" + // method at all. + if (except) { + std::rethrow_exception(except); + } else { + throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end())); + } + } + } +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + + + + Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, + const Type_Conversions_State &t_conversions) const + { + uint_fast32_t loc = t_loc.load(std::memory_order_relaxed); + const auto funs = get_function(t_name, loc); + if (funs.first != loc) t_loc.store(uint_fast32_t(funs.first), std::memory_order_relaxed); + return dispatch::dispatch(*funs.second, params, t_conversions); + } + + + /// Dump object info to stdout + void dump_object(const Boxed_Value &o) const + { + std::cout << (o.is_const()?"const ":"") << type_name(o) << '\n'; + } + + /// Dump type info to stdout + void dump_type(const Type_Info &type) const + { + std::cout << (type.is_const()?"const ":"") << get_type_name(type); + } + + /// Dump function to stdout + void dump_function(const std::pair &f) const + { + std::vector params = f.second->get_param_types(); + std::string annotation = f.second->annotation(); + + if (annotation.size() > 0) { + std::cout << annotation; + } + dump_type(params.front()); + std::cout << " " << f.first << "("; + + for (std::vector::const_iterator itr = params.begin() + 1; + itr != params.end(); + ) + { + dump_type(*itr); + ++itr; + + if (itr != params.end()) + { + std::cout << ", "; + } + } + + std::cout << ") \n"; + } + + /// Returns true if a call can be made that consists of the first parameter + /// (the function) with the remaining parameters as its arguments. + Boxed_Value call_exists(const std::vector ¶ms) const + { + if (params.empty()) + { + throw chaiscript::exception::arity_error(static_cast(params.size()), 1); + } + + const Const_Proxy_Function &f = this->boxed_cast(params[0]); + const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves()); + + return Boxed_Value(f->call_match(std::vector(params.begin() + 1, params.end()), convs)); + } + + /// Dump all system info to stdout + void dump_system() const + { + std::cout << "Registered Types: \n"; + std::vector > types = get_types(); + for (std::vector >::const_iterator itr = types.begin(); + itr != types.end(); + ++itr) + { + std::cout << itr->first << ": "; + std::cout << itr->second.bare_name(); + std::cout << '\n'; + } + + std::cout << '\n'; + std::vector > funcs = get_functions(); + + std::cout << "Functions: \n"; + for (std::vector >::const_iterator itr = funcs.begin(); + itr != funcs.end(); + ++itr) + { + dump_function(*itr); + } + std::cout << '\n'; + } + + /// return true if the Boxed_Value matches the registered type by name + bool is_type(const Boxed_Value &r, const std::string &user_typename) const + { + try { + if (get_type(user_typename).bare_equal(r.get_type_info())) + { + return true; + } + } catch (const std::range_error &) { + } + + try { + const dispatch::Dynamic_Object &d = boxed_cast(r); + return d.get_type_name() == user_typename; + } catch (const std::bad_cast &) { + } + + return false; + } + + std::string type_name(const Boxed_Value &obj) const + { + return get_type_name(obj.get_type_info()); + } + + State get_state() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return m_state; + } + + void set_state(const State &t_state) + { + chaiscript::detail::threading::unique_lock l(m_mutex); + + m_state = t_state; + } + + static void save_function_params(Stack_Holder &t_s, std::initializer_list t_params) + { + t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(t_params)); + } + + static void save_function_params(Stack_Holder &t_s, std::vector &&t_params) + { + for (auto &¶m : t_params) + { + t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(param)); + } + } + + static void save_function_params(Stack_Holder &t_s, const std::vector &t_params) + { + t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end()); + } + + void save_function_params(std::initializer_list t_params) + { + save_function_params(*m_stack_holder, std::move(t_params)); + } + + void save_function_params(std::vector &&t_params) + { + save_function_params(*m_stack_holder, std::move(t_params)); + } + + void save_function_params(const std::vector &t_params) + { + save_function_params(*m_stack_holder, t_params); + } + + void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) + { + if (t_s.call_depth == 0) + { + m_conversions.enable_conversion_saves(t_saves, true); + } + + ++t_s.call_depth; + + save_function_params(m_conversions.take_saves(t_saves)); + } + + void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) + { + --t_s.call_depth; + + assert(t_s.call_depth >= 0); + + if (t_s.call_depth == 0) + { + t_s.call_params.back().clear(); + m_conversions.enable_conversion_saves(t_saves, false); + } + } + + void new_function_call() + { + new_function_call(*m_stack_holder, m_conversions.conversion_saves()); + } + + void pop_function_call() + { + pop_function_call(*m_stack_holder, m_conversions.conversion_saves()); + } + + Stack_Holder &get_stack_holder() + { + return *m_stack_holder; + } + + /// Returns the current stack + /// make const/non const versions + const StackData &get_stack_data() const + { + return m_stack_holder->stacks.back(); + } + + static StackData &get_stack_data(Stack_Holder &t_holder) + { + return t_holder.stacks.back(); + } + + StackData &get_stack_data() + { + return m_stack_holder->stacks.back(); + } + + private: + + const std::vector> &get_boxed_functions_int() const + { + return m_state.m_boxed_functions; + } + + std::vector> &get_boxed_functions_int() + { + return m_state.m_boxed_functions; + } + + const std::vector> &get_function_objects_int() const + { + return m_state.m_function_objects; + } + + std::vector> &get_function_objects_int() + { + return m_state.m_function_objects; + } + + const std::vector>>> &get_functions_int() const + { + return m_state.m_functions; + } + + std::vector>>> &get_functions_int() + { + return m_state.m_functions; + } + + static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs) + { + + auto dynamic_lhs(std::dynamic_pointer_cast(lhs)); + auto dynamic_rhs(std::dynamic_pointer_cast(rhs)); + + if (dynamic_lhs && dynamic_rhs) + { + if (dynamic_lhs->get_guard()) + { + return dynamic_rhs->get_guard() ? false : true; + } else { + return false; + } + } + + if (dynamic_lhs && !dynamic_rhs) + { + return false; + } + + if (!dynamic_lhs && dynamic_rhs) + { + return true; + } + + const auto &lhsparamtypes = lhs->get_param_types(); + const auto &rhsparamtypes = rhs->get_param_types(); + + const auto lhssize = lhsparamtypes.size(); + const auto rhssize = rhsparamtypes.size(); + +#ifdef CHAISCRIPT_HAS_MAGIC_STATICS + static auto boxed_type = user_type(); + static auto boxed_pod_type = user_type(); +#else + auto boxed_type = user_type(); + auto boxed_pod_type = user_type(); +#endif + + for (size_t i = 1; i < lhssize && i < rhssize; ++i) + { + const Type_Info < = lhsparamtypes[i]; + const Type_Info &rt = rhsparamtypes[i]; + + if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) + { + continue; // The first two types are essentially the same, next iteration + } + + // const is after non-const for the same type + if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) + { + return false; + } + + if (lt.bare_equal(rt) && !lt.is_const()) + { + return true; + } + + // boxed_values are sorted last + if (lt.bare_equal(boxed_type)) + { + return false; + } + + if (rt.bare_equal(boxed_type)) + { + if (lt.bare_equal(boxed_pod_type)) + { + return true; + } + return true; + } + + if (lt.bare_equal(boxed_pod_type)) + { + return false; + } + + if (rt.bare_equal(boxed_pod_type)) + { + return true; + } + + // otherwise, we want to sort by typeid + return lt < rt; + } + + return false; + } + + + /// Throw a reserved_word exception if the name is not allowed + void validate_object_name(const std::string &name) const + { + if (name.find("::") != std::string::npos) { + throw chaiscript::exception::illegal_name_error(name); + } + + chaiscript::detail::threading::shared_lock l(m_mutex); + + if (m_state.m_reserved_words.find(name) != m_state.m_reserved_words.end()) + { + throw chaiscript::exception::reserved_word_error(name); + } + } + + template + static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) + { + auto itr = find_keyed_value(t_c, t_key); + + if (itr == t_c.end()) { + t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here + t_c.emplace_back(t_key, std::forward(t_value)); + } else { + typedef typename Container::value_type value_type; + *itr = value_type(t_key, std::forward(t_value)); + } + } + + template + static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key) + { + return std::find_if(t_c.begin(), t_c.end(), + [&t_key](const typename Container::value_type &o) { + return o.first == t_key; + }); + } + + template + static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) + { + if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { + return t_c.begin() + t_hint; + } else { + return find_keyed_value(t_c, t_key); + } + } + + + /// Implementation detail for adding a function. + /// \throws exception::name_conflict_error if there's a function matching the given one being added + void add_function(const Proxy_Function &t_f, const std::string &t_name) + { + chaiscript::detail::threading::unique_lock l(m_mutex); + + auto &funcs = get_functions_int(); + + auto itr = find_keyed_value(funcs, t_name); + + Proxy_Function new_func = + [&]() -> Proxy_Function { + if (itr != funcs.end()) + { + auto vec = *itr->second; + for (const auto &func : vec) + { + if ((*t_f) == *(func)) + { + throw chaiscript::exception::name_conflict_error(t_name); + } + } + + vec.reserve(vec.size() + 1); // tightly control vec growth + vec.push_back(t_f); + std::stable_sort(vec.begin(), vec.end(), &function_less_than); + itr->second = std::make_shared>(vec); + return std::make_shared(std::move(vec)); + } else if (t_f->has_arithmetic_param()) { + // if the function is the only function but it also contains + // arithmetic operators, we must wrap it in a dispatch function + // to allow for automatic arithmetic type conversions + std::vector vec({t_f}); + funcs.emplace_back(t_name, std::make_shared>(vec)); + return std::make_shared(std::move(vec)); + } else { + funcs.emplace_back(t_name, std::make_shared>(std::initializer_list({t_f}))); + return t_f; + } + }(); + + add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func)); + add_keyed_value(get_function_objects_int(), t_name, std::move(new_func)); + } + + mutable chaiscript::detail::threading::shared_mutex m_mutex; + + + Type_Conversions m_conversions; + chaiscript::detail::threading::Thread_Storage m_stack_holder; + + mutable std::atomic_uint_fast32_t m_method_missing_loc; + + State m_state; + }; + + class Dispatch_State + { + public: + Dispatch_State(Dispatch_Engine &t_engine) + : m_engine(t_engine), + m_stack_holder(t_engine.get_stack_holder()), + m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves()) + { + } + + Dispatch_Engine *operator->() const { + return &m_engine.get(); + } + + Dispatch_Engine &operator*() const { + return m_engine.get(); + } + + Stack_Holder &stack_holder() const { + return m_stack_holder.get(); + } + + const Type_Conversions_State &conversions() const { + return m_conversions; + } + + Type_Conversions::Conversion_Saves &conversion_saves() const { + return m_conversions.saves(); + } + + void add_object(const std::string &t_name, Boxed_Value obj) const { + m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); + } + + private: + std::reference_wrapper m_engine; + std::reference_wrapper m_stack_holder; + Type_Conversions_State m_conversions; + }; + } +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/dynamic_object.hpp b/apps/common/script/chaiscript/dispatchkit/dynamic_object.hpp new file mode 100644 index 0000000000..05fc069707 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/dynamic_object.hpp @@ -0,0 +1,126 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_ +#define CHAISCRIPT_DYNAMIC_OBJECT_HPP_ + +#include +#include +#include + +#include "boxed_value.hpp" + +namespace chaiscript { +class Type_Conversions; +namespace dispatch { +class Proxy_Function_Base; +} // namespace dispatch +} // namespace chaiscript + +namespace chaiscript +{ + namespace dispatch + { + struct option_explicit_set : std::runtime_error { + option_explicit_set(const std::string &t_param_name) + : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") + { + + } + + option_explicit_set(const option_explicit_set &) = default; + + virtual ~option_explicit_set() CHAISCRIPT_NOEXCEPT {} + }; + + class Dynamic_Object + { + public: + Dynamic_Object(std::string t_type_name) + : m_type_name(std::move(t_type_name)), m_option_explicit(false) + { + } + + Dynamic_Object() : m_type_name(""), m_option_explicit(false) + { + } + + bool is_explicit() const + { + return m_option_explicit; + } + + void set_explicit(const bool t_explicit) + { + m_option_explicit = t_explicit; + } + + std::string get_type_name() const + { + return m_type_name; + } + + const Boxed_Value &operator[](const std::string &t_attr_name) const + { + return get_attr(t_attr_name); + } + + Boxed_Value &operator[](const std::string &t_attr_name) + { + return get_attr(t_attr_name); + } + + const Boxed_Value &get_attr(const std::string &t_attr_name) const + { + auto a = m_attrs.find(t_attr_name); + + if (a != m_attrs.end()) { + return a->second; + } else { + throw std::range_error("Attr not found '" + t_attr_name + "' and cannot be added to const obj"); + } + } + + Boxed_Value &get_attr(const std::string &t_attr_name) + { + return m_attrs[t_attr_name]; + } + + Boxed_Value &method_missing(const std::string &t_method_name) + { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + + return get_attr(t_method_name); + } + + const Boxed_Value &method_missing(const std::string &t_method_name) const + { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + + return get_attr(t_method_name); + } + + + std::map get_attrs() const + { + return m_attrs; + } + + private: + std::string m_type_name; + bool m_option_explicit; + + std::map m_attrs; + }; + + } +} +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/dynamic_object_detail.hpp b/apps/common/script/chaiscript/dispatchkit/dynamic_object_detail.hpp new file mode 100644 index 0000000000..8c9b8f662e --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -0,0 +1,253 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_ +#define CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "proxy_functions.hpp" +#include "type_info.hpp" +#include "dynamic_object.hpp" + +namespace chaiscript { +class Type_Conversions; +namespace dispatch { +class Proxy_Function_Base; +} // namespace dispatch +} // namespace chaiscript + +namespace chaiscript +{ + namespace dispatch + { + namespace detail + { + /// A Proxy_Function implementation designed for calling a function + /// that is automatically guarded based on the first param based on the + /// param's type name + class Dynamic_Object_Function : public Proxy_Function_Base + { + public: + Dynamic_Object_Function( + std::string t_type_name, + const Proxy_Function &t_func, + bool t_is_attribute = false) + : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()), + m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type()), + m_is_attribute(t_is_attribute) + { + assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + Dynamic_Object_Function( + std::string t_type_name, + const Proxy_Function &t_func, + const Type_Info &t_ti, + bool t_is_attribute = false) + : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()), + m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type()), + m_is_attribute(t_is_attribute) + { + assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + virtual ~Dynamic_Object_Function() {} + + Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; + Dynamic_Object_Function(Dynamic_Object_Function &) = delete; + + virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE + { + if (const auto *df = dynamic_cast(&f)) + { + return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); + } else { + return false; + } + } + + virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return m_is_attribute; } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) + { + return m_func->call_match(vals, t_conversions); + } else { + return false; + } + } + + virtual std::vector get_contained_functions() const CHAISCRIPT_OVERRIDE + { + return {m_func}; + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return m_func->annotation(); + } + + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) + { + return (*m_func)(params, t_conversions); + } else { + throw exception::guard_error(); + } + } + + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); + } + + private: + static std::vector build_param_types( + const std::vector &t_inner_types, const Type_Info& t_objectti) + { + std::vector types(t_inner_types); + + assert(types.size() > 1); + //assert(types[1].bare_equal(user_type())); + types[1] = t_objectti; + return types; + } + + bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name, + const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const + { + if (bv.get_type_info().bare_equal(m_doti)) + { + try { + const Dynamic_Object &d = boxed_cast(bv, &t_conversions); + return name == "Dynamic_Object" || d.get_type_name() == name; + } catch (const std::bad_cast &) { + return false; + } + } else { + if (ti) + { + return bv.get_type_info().bare_equal(*ti); + } else { + return false; + } + } + + } + + bool dynamic_object_typename_match(const std::vector &bvs, const std::string &name, + const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const + { + if (bvs.size() > 0) + { + return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); + } else { + return false; + } + } + + std::string m_type_name; + Proxy_Function m_func; + std::unique_ptr m_ti; + const Type_Info m_doti; + bool m_is_attribute; + + + }; + + + /** + * A Proxy_Function implementation designed for creating a new + * Dynamic_Object + * that is automatically guarded based on the first param based on the + * param's type name + */ + class Dynamic_Object_Constructor : public Proxy_Function_Base + { + public: + Dynamic_Object_Constructor( + std::string t_type_name, + const Proxy_Function &t_func) + : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1), + m_type_name(std::move(t_type_name)), m_func(t_func) + { + assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + static std::vector build_type_list(const std::vector &tl) + { + auto begin = tl.begin(); + auto end = tl.end(); + + if (begin != end) + { + ++begin; + } + + return std::vector(begin, end); + } + + virtual ~Dynamic_Object_Constructor() {} + + virtual bool operator==(const Proxy_Function_Base &f) const CHAISCRIPT_OVERRIDE + { + const Dynamic_Object_Constructor *dc = dynamic_cast(&f); + return dc && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); + } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; + new_vals.insert(new_vals.end(), vals.begin(), vals.end()); + + return m_func->call_match(new_vals, t_conversions); + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return m_func->annotation(); + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); + std::vector new_params{bv}; + new_params.insert(new_params.end(), params.begin(), params.end()); + + (*m_func)(new_params, t_conversions); + + return bv; + } + + private: + std::string m_type_name; + Proxy_Function m_func; + + }; + } + } +} +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/exception_specification.hpp b/apps/common/script/chaiscript/dispatchkit/exception_specification.hpp new file mode 100644 index 0000000000..55398f7dc1 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/exception_specification.hpp @@ -0,0 +1,199 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ +#define CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ + +#include + +#include "../chaiscript_defines.hpp" +#include "boxed_cast.hpp" + +namespace chaiscript { +class Boxed_Value; +namespace exception { +class bad_boxed_cast; +} // namespace exception +} // namespace chaiscript + +namespace chaiscript +{ + namespace detail + { + /// \todo make this a variadic template + struct Exception_Handler_Base + { + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) = 0; + + virtual ~Exception_Handler_Base() {} + + protected: + template + static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) + { + try { T t = t_engine.boxed_cast(bv); throw t; } catch (const chaiscript::exception::bad_boxed_cast &) {} + } + }; + + template + struct Exception_Handler_Impl1 : Exception_Handler_Base + { + virtual ~Exception_Handler_Impl1() {} + + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE + { + throw_type(bv, t_engine); + } + }; + template + struct Exception_Handler_Impl2 : Exception_Handler_Base + { + virtual ~Exception_Handler_Impl2() {} + + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE + { + throw_type(bv, t_engine); + throw_type(bv, t_engine); + } + }; + + template + struct Exception_Handler_Impl3 : Exception_Handler_Base + { + virtual ~Exception_Handler_Impl3() {} + + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE + { + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + } + }; + template + struct Exception_Handler_Impl4 : Exception_Handler_Base + { + virtual ~Exception_Handler_Impl4() {} + + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE + { + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + } + }; + template + struct Exception_Handler_Impl5 : Exception_Handler_Base + { + virtual ~Exception_Handler_Impl5() {} + + virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) CHAISCRIPT_OVERRIDE + { + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + throw_type(bv, t_engine); + } + }; + } + + /// \brief Used in the automatic unboxing of exceptions thrown during script evaluation + /// + /// Exception specifications allow the user to tell ChaiScript what possible exceptions are expected from the script + /// being executed. Exception_Handler objects are created with the chaiscript::exception_specification() function. + /// + /// Example: + /// \code + /// chaiscript::ChaiScript chai; + /// + /// try { + /// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); + /// } catch (const double e) { + /// } catch (int) { + /// } catch (float) { + /// } catch (const std::string &) { + /// } catch (const std::exception &e) { + /// // This is the one what will be called in the specific throw() above + /// } + /// \endcode + /// + /// It is recommended that if catching the generic \c std::exception& type that you specifically catch + /// the chaiscript::exception::eval_error type, so that there is no confusion. + /// + /// \code + /// try { + /// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); + /// } catch (const chaiscript::exception::eval_error &) { + /// // Error in script parsing / execution + /// } catch (const std::exception &e) { + /// // Error explicitly thrown from script + /// } + /// \endcode + /// + /// Similarly, if you are using the ChaiScript::eval form that unboxes the return value, then chaiscript::exception::bad_boxed_cast + /// should be handled as well. + /// + /// \code + /// try { + /// chai.eval("1.0", chaiscript::exception_specification()); + /// } catch (const chaiscript::exception::eval_error &) { + /// // Error in script parsing / execution + /// } catch (const chaiscript::exception::bad_boxed_cast &) { + /// // Error unboxing return value + /// } catch (const std::exception &e) { + /// // Error explicitly thrown from script + /// } + /// \endcode + /// + /// \sa chaiscript::exception_specification for creation of chaiscript::Exception_Handler objects + /// \sa \ref exceptions + typedef std::shared_ptr Exception_Handler; + + /// \brief creates a chaiscript::Exception_Handler which handles one type of exception unboxing + /// \sa \ref exceptions + template + Exception_Handler exception_specification() + { + return Exception_Handler(new detail::Exception_Handler_Impl1()); + } + + /// \brief creates a chaiscript::Exception_Handler which handles two types of exception unboxing + /// \sa \ref exceptions + template + Exception_Handler exception_specification() + { + return Exception_Handler(new detail::Exception_Handler_Impl2()); + } + + /// \brief creates a chaiscript::Exception_Handler which handles three types of exception unboxing + /// \sa \ref exceptions + template + Exception_Handler exception_specification() + { + return Exception_Handler(new detail::Exception_Handler_Impl3()); + } + + /// \brief creates a chaiscript::Exception_Handler which handles four types of exception unboxing + /// \sa \ref exceptions + template + Exception_Handler exception_specification() + { + return Exception_Handler(new detail::Exception_Handler_Impl4()); + } + + /// \brief creates a chaiscript::Exception_Handler which handles five types of exception unboxing + /// \sa \ref exceptions + template + Exception_Handler exception_specification() + { + return Exception_Handler(new detail::Exception_Handler_Impl5()); + } +} + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/function_call.hpp b/apps/common/script/chaiscript/dispatchkit/function_call.hpp new file mode 100644 index 0000000000..ea0a61e2ea --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/function_call.hpp @@ -0,0 +1,137 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_FUNCTION_CALL_HPP_ +#define CHAISCRIPT_FUNCTION_CALL_HPP_ + +#include +#include +#include + +#include "boxed_cast.hpp" +#include "function_call_detail.hpp" +#include "proxy_functions.hpp" +#include "callable_traits.hpp" + +namespace chaiscript { +class Boxed_Value; +class Type_Conversions_State; +namespace detail { +template struct Cast_Helper; +} // namespace detail +} // namespace chaiscript + +namespace chaiscript +{ + namespace dispatch + { + /// Build a function caller that knows how to dispatch on a set of functions + /// example: + /// std::function f = + /// build_function_caller(dispatchkit.get_function("print")); + /// \returns A std::function object for dispatching + /// \param[in] funcs the set of functions to dispatch on. + template + std::function + functor(const std::vector &funcs, const Type_Conversions_State *t_conversions) + { + const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), + [](const Const_Proxy_Function &f) { + return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity::arity; + }); + + if (!has_arity_match) { + throw exception::bad_boxed_cast(user_type(), typeid(std::function)); + } + + FunctionType *p=nullptr; + return detail::build_function_caller_helper(p, funcs, t_conversions); + } + + /// Build a function caller for a particular Proxy_Function object + /// useful in the case that a function is being pass out from scripting back + /// into code + /// example: + /// void my_function(Proxy_Function f) + /// { + /// std::function local_f = + /// build_function_caller(f); + /// } + /// \returns A std::function object for dispatching + /// \param[in] func A function to execute. + template + std::function + functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions) + { + return functor(std::vector({std::move(func)}), t_conversions); + } + + /// Helper for automatically unboxing a Boxed_Value that contains a function object + /// and creating a typesafe C++ function caller from it. + template + std::function + functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions) + { + return functor(boxed_cast(bv, t_conversions), t_conversions); + } + } + + namespace detail{ + /// Cast helper to handle automatic casting to const std::function & + template + struct Cast_Helper &> + { + typedef std::function Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + { + if (ob.get_type_info().bare_equal(user_type())) + { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner &>::cast(ob, t_conversions); + } + } + }; + + /// Cast helper to handle automatic casting to std::function + template + struct Cast_Helper > + { + typedef std::function Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + { + if (ob.get_type_info().bare_equal(user_type())) + { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner >::cast(ob, t_conversions); + } + } + }; + + /// Cast helper to handle automatic casting to const std::function + template + struct Cast_Helper > + { + typedef std::function Result_Type; + + static Result_Type cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) + { + if (ob.get_type_info().bare_equal(user_type())) + { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner >::cast(ob, t_conversions); + } + } + }; + } +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/function_call_detail.hpp b/apps/common/script/chaiscript/dispatchkit/function_call_detail.hpp new file mode 100644 index 0000000000..8ed6115c20 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/function_call_detail.hpp @@ -0,0 +1,167 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ +#define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ + +#include +#include +#include +#include +#include + +#include "boxed_cast.hpp" +#include "boxed_number.hpp" +#include "boxed_value.hpp" +#include "type_conversions.hpp" +#include "proxy_functions.hpp" + +namespace chaiscript +{ + namespace dispatch + { + namespace detail + { + /// Internal helper class for handling the return + /// value of a build_function_caller + template + struct Function_Caller_Ret + { + static Ret call(const std::vector &t_funcs, + const std::vector ¶ms, const Type_Conversions_State *t_conversions) + { + if (t_conversions) { + return boxed_cast(dispatch::dispatch(t_funcs, params, *t_conversions), t_conversions); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return boxed_cast(dispatch::dispatch(t_funcs, params, state), t_conversions); + } + } + }; + + /** + * Specialization for arithmetic return types + */ + template + struct Function_Caller_Ret + { + static Ret call(const std::vector &t_funcs, + const std::vector ¶ms, const Type_Conversions_State *t_conversions) + { + if (t_conversions) { + return Boxed_Number(dispatch::dispatch(t_funcs, params, *t_conversions)).get_as(); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return Boxed_Number(dispatch::dispatch(t_funcs, params, state)).get_as(); + } + } + }; + + + /** + * Specialization for void return types + */ + template<> + struct Function_Caller_Ret + { + static void call(const std::vector &t_funcs, + const std::vector ¶ms, const Type_Conversions_State *t_conversions) + { + if (t_conversions) { + dispatch::dispatch(t_funcs, params, *t_conversions); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + dispatch::dispatch(t_funcs, params, state); + } + } + }; + + /** + * used internally for unwrapping a function call's types + */ + template + struct Build_Function_Caller_Helper + { + Build_Function_Caller_Helper(std::vector t_funcs, const Type_Conversions *t_conversions) + : m_funcs(std::move(t_funcs)), + m_conversions(t_conversions) + { + } + + template + Ret operator()(P&& ... param) + { + if (m_conversions) { + Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves()); + return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { + box

(std::forward

(param))... + }, &state + ); + } else { + return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { + box

(std::forward

(param))... + }, nullptr + ); + } + + } + + template + static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type + { + return Boxed_Value(std::ref(std::forward(q))); + } + + template + static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type + { + return Boxed_Value(std::forward(q)); + } + + template + static Boxed_Value box(Boxed_Value bv) + { + return bv; + } + + + std::vector m_funcs; + const Type_Conversions *m_conversions; + }; + + + + /// \todo what happens if t_conversions is deleted out from under us?! + template + std::function build_function_caller_helper(Ret (Params...), const std::vector &funcs, const Type_Conversions_State *t_conversions) + { + /* + if (funcs.size() == 1) + { + std::shared_ptr> pfi = + std::dynamic_pointer_cast > + (funcs[0]); + + if (pfi) + { + return pfi->internal_function(); + } + // looks like this either wasn't a Proxy_Function_Impl or the types didn't match + // we cannot make any other guesses or assumptions really, so continuing + } +*/ + + return std::function(Build_Function_Caller_Helper(funcs, t_conversions?t_conversions->get():nullptr)); + } + } + } +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/handle_return.hpp b/apps/common/script/chaiscript/dispatchkit/handle_return.hpp new file mode 100644 index 0000000000..2650e3b7cb --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/handle_return.hpp @@ -0,0 +1,278 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_HANDLE_RETURN_HPP_ +#define CHAISCRIPT_HANDLE_RETURN_HPP_ + +#include +#include +#include + +#include "boxed_number.hpp" +#include "boxed_value.hpp" + +namespace chaiscript { +class Boxed_Number; +} // namespace chaiscript + +namespace chaiscript +{ + namespace dispatch + { + template class Proxy_Function_Callable_Impl; + template class Assignable_Proxy_Function_Impl; + + namespace detail + { + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template + struct Handle_Return + { + template::type>::value>::type> + static Boxed_Value handle(T r) + { + return Boxed_Value(std::move(r), true); + } + + template::type>::value>::type> + static Boxed_Value handle(T &&r) + { + return Boxed_Value(std::make_shared(std::forward(r)), true); + } + }; + + template + struct Handle_Return &> + { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + chaiscript::make_shared>>(f) + ); + } + }; + + template + struct Handle_Return> + { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + chaiscript::make_shared>>(f) + ); + } + }; + + template + struct Handle_Return>> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + chaiscript::make_shared>(std::ref(*f),f) + ); + } + }; + + template + struct Handle_Return> &> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + chaiscript::make_shared>(std::ref(*f),f) + ); + } + }; + + template + struct Handle_Return>> + { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + chaiscript::make_shared>(std::ref(*f),f) + ); + } + }; + + template + struct Handle_Return &> + { + static Boxed_Value handle(std::function &f) { + return Boxed_Value( + chaiscript::make_shared>(std::ref(f), + std::shared_ptr>()) + ); + } + + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + chaiscript::make_shared>>(f) + ); + } + }; + + template + struct Handle_Return + { + static Boxed_Value handle(Ret *p) + { + return Boxed_Value(p, true); + } + }; + + template + struct Handle_Return + { + static Boxed_Value handle(const Ret *p) + { + return Boxed_Value(p, true); + } + }; + + template + struct Handle_Return &> + { + static Boxed_Value handle(const std::shared_ptr &r) + { + return Boxed_Value(r, true); + } + }; + + template + struct Handle_Return > + { + static Boxed_Value handle(const std::shared_ptr &r) + { + return Boxed_Value(r, true); + } + }; + + template + struct Handle_Return &> + { + static Boxed_Value handle(const std::shared_ptr &r) + { + return Boxed_Value(r, true); + } + }; + + template + struct Handle_Return + { + static Boxed_Value handle(const Ret &r) + { + return Boxed_Value(std::cref(r), true); + } + }; + + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template + struct Handle_Return + { + static Boxed_Value handle(Ret &r) + { + return Boxed_Value(std::ref(r)); + } + + static Boxed_Value handle(const Ret &r) + { + return Boxed_Value(std::cref(r)); + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Value &r) + { + return r; + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Value &r) + { + return r; + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Value &r) + { + return r; + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Value &r) + { + return r; + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Number &r) + { + return r.bv; + } + }; + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle(const Boxed_Number &r) + { + return r.bv; + } + }; + + + /** + * Used internally for handling a return value from a Proxy_Function call + */ + template<> + struct Handle_Return + { + static Boxed_Value handle() + { + return Boxed_Value(Boxed_Value::Void_Type()); + } + }; + } + } +} + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/operators.hpp b/apps/common/script/chaiscript/dispatchkit/operators.hpp new file mode 100644 index 0000000000..2d4401ee85 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/operators.hpp @@ -0,0 +1,465 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_OPERATORS_HPP_ +#define CHAISCRIPT_OPERATORS_HPP_ + +#include "../chaiscript_defines.hpp" +#include "register_function.hpp" + +namespace chaiscript +{ + namespace bootstrap + { + namespace operators + { + namespace detail + { + /// \todo make this return a decltype once we drop gcc 4.6 + template + auto assign(L l, R r) -> L& + { + return (l = r); + } + + template + auto assign_bitwise_and(L l, R r) -> decltype((l &= r)) + { + return (l &= r); + } + + template + auto assign_xor(L l, R r) -> decltype((l^=r)) + { + return (l ^= r); + } + + template + auto assign_bitwise_or(L l, R r) -> decltype((l |= r)) + { + return (l |= r); + } + + template + auto assign_difference(L l, R r) -> decltype(( l -= r)) + { + return (l -= r); + } + + template + auto assign_left_shift(L l, R r) -> decltype(( l <<= r)) + { + return (l <<= r); + } + + template + auto assign_product(L l, R r) -> decltype(( l *= r )) + { + return (l *= r); + } + + template + auto assign_quotient(L l, R r) -> decltype(( l /= r )) + { + return (l /= r); + } + + template + auto assign_remainder(L l, R r) -> decltype(( l %= r )) + { + return (l %= r); + } + + template + auto assign_right_shift(L l, R r) -> decltype(( l >>= r)) + { + return (l >>= r); + } + + /// \todo make this return a decltype once we drop gcc 4.6 + template + auto assign_sum(L l, R r) -> L& + { + return (l += r); + } + + template + auto prefix_decrement(L l) -> decltype(( --l )) + { + return (--l); + } + + template + auto prefix_increment(L l) -> decltype(( ++l )) + { + return (++l); + } + + template + auto equal(L l, R r) -> decltype(( l == r )) + { + return (l == r); + } + + template + auto greater_than(L l, R r) -> decltype(( l > r )) + { + return (l > r); + } + + template + auto greater_than_equal(L l, R r) -> decltype(( l >= r )) + { + return (l >= r); + } + + template + auto less_than(L l, R r) -> decltype(( l < r )) + { + return (l < r); + } + + template + auto less_than_equal(L l, R r) -> decltype(( l <= r )) + { + return (l <= r); + } + + template + auto logical_compliment(L l) -> decltype(( !l )) + { + return (!l); + } + + template + auto not_equal(L l, R r) -> decltype(( l != r )) + { + return (l != r); + } + + template + auto addition(L l, R r) -> decltype(( l + r )) + { + return (l + r); + } + + template + auto unary_plus(L l) -> decltype(( +l )) + { + return (+l); + } + + template + auto subtraction(L l, R r) -> decltype(( l - r )) + { + return (l - r); + } + + template + auto unary_minus(L l) -> decltype(( -l )) + { +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4146) + return (-l); +#pragma warning(pop) +#else + return (-l); +#endif + } + + template + auto bitwise_and(L l, R r) -> decltype(( l & r )) + { + return (l & r); + } + + template + auto bitwise_compliment(L l) -> decltype(( ~l )) + { + return (~l); + } + + template + auto bitwise_xor(L l, R r) -> decltype(( l ^ r )) + { + return (l ^ r); + } + + template + auto bitwise_or(L l, R r) -> decltype(( l | r )) + { + return (l | r); + } + + template + auto division(L l, R r) -> decltype(( l / r )) + { + return (l / r); + } + + template + auto left_shift(L l, R r) -> decltype(( l << r )) + { + return l << r; + } + + template + auto multiplication(L l, R r) -> decltype(( l * r )) + { + return l * r; + } + + template + auto remainder(L l, R r) -> decltype(( l % r )) + { + return (l % r); + } + + template + auto right_shift(L l, R r) -> decltype(( l >> r )) + { + return (l >> r); + } + } + + + + template + ModulePtr assign(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign), "="); + return m; + } + + template + ModulePtr assign_bitwise_and(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_bitwise_and), "&="); + return m; + } + + template + ModulePtr assign_xor(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_xor), "^="); + return m; + } + + template + ModulePtr assign_bitwise_or(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_bitwise_or), "|="); + return m; + } + + template + ModulePtr assign_difference(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_difference), "-="); + return m; + } + + template + ModulePtr assign_left_shift(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_left_shift), "<<="); + return m; + } + + template + ModulePtr assign_product(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_product), "*="); + return m; + } + + template + ModulePtr assign_quotient(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_quotient), "/="); + return m; + } + + template + ModulePtr assign_remainder(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_remainder), "%="); + return m; + } + + template + ModulePtr assign_right_shift(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_right_shift), ">>="); + return m; + } + + template + ModulePtr assign_sum(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::assign_sum), "+="); + return m; + } + + template + ModulePtr prefix_decrement(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::prefix_decrement), "--"); + return m; + } + + template + ModulePtr prefix_increment(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::prefix_increment), "++"); + return m; + } + + template + ModulePtr equal(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::equal), "=="); + return m; + } + + template + ModulePtr greater_than(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::greater_than), ">"); + return m; + } + + template + ModulePtr greater_than_equal(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::greater_than_equal), ">="); + return m; + } + + template + ModulePtr less_than(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::less_than), "<"); + return m; + } + + template + ModulePtr less_than_equal(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::less_than_equal), "<="); + return m; + } + + template + ModulePtr logical_compliment(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::logical_compliment), "!"); + return m; + } + + template + ModulePtr not_equal(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::not_equal), "!="); + return m; + } + + template + ModulePtr addition(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::addition), "+"); + return m; + } + + template + ModulePtr unary_plus(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::unary_plus), "+"); + return m; + } + + template + ModulePtr subtraction(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::subtraction), "-"); + return m; + } + + template + ModulePtr unary_minus(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::unary_minus), "-"); + return m; + } + + template + ModulePtr bitwise_and(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::bitwise_and), "&"); + return m; + } + + template + ModulePtr bitwise_compliment(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::bitwise_compliment), "~"); + return m; + } + + template + ModulePtr bitwise_xor(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::bitwise_xor), "^"); + return m; + } + + template + ModulePtr bitwise_or(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::bitwise_or), "|"); + return m; + } + + template + ModulePtr division(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::division), "/"); + return m; + } + + template + ModulePtr left_shift(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::left_shift), "<<"); + return m; + } + + template + ModulePtr multiplication(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::multiplication), "*"); + return m; + } + + template + ModulePtr remainder(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::remainder), "%"); + return m; + } + + template + ModulePtr right_shift(ModulePtr m = std::make_shared()) + { + m->add(chaiscript::fun(&detail::right_shift), ">>"); + return m; + } + } + } +} + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/proxy_constructors.hpp b/apps/common/script/chaiscript/dispatchkit/proxy_constructors.hpp new file mode 100644 index 0000000000..a5ad41ec83 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/proxy_constructors.hpp @@ -0,0 +1,53 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + + +#ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ +#define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ + +#include "proxy_functions.hpp" + +namespace chaiscript +{ + namespace dispatch + { + namespace detail + { + + template + Proxy_Function build_constructor_(Class (*)(Params...)) + { + auto call = dispatch::detail::Constructor(); + + return Proxy_Function( + chaiscript::make_shared (Params...), decltype(call)>>(call)); + } + } + } + + + /// \brief Generates a constructor function for use with ChaiScript + /// + /// \tparam T The signature of the constructor to generate. In the form of: ClassType (ParamType1, ParamType2, ...) + /// + /// Example: + /// \code + /// chaiscript::ChaiScript chai; + /// // Create a new function that creates a MyClass object using the (int, float) constructor + /// // and call that function "MyClass" so that it appears as a normal constructor to the user. + /// chai.add(constructor(), "MyClass"); + /// \endcode + template + Proxy_Function constructor() + { + T *f = nullptr; + return (dispatch::detail::build_constructor_(f)); + } + +} + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/proxy_functions.hpp b/apps/common/script/chaiscript/dispatchkit/proxy_functions.hpp new file mode 100644 index 0000000000..6128b33bfc --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/proxy_functions.hpp @@ -0,0 +1,933 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + + +#ifndef CHAISCRIPT_PROXY_FUNCTIONS_HPP_ +#define CHAISCRIPT_PROXY_FUNCTIONS_HPP_ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "boxed_cast.hpp" +#include "boxed_value.hpp" +#include "proxy_functions_detail.hpp" +#include "type_info.hpp" +#include "dynamic_object.hpp" + +namespace chaiscript { +class Type_Conversions; +namespace exception { +class bad_boxed_cast; +struct arity_error; +} // namespace exception +} // namespace chaiscript + +namespace chaiscript +{ + class Boxed_Number; + struct AST_Node; + + typedef std::shared_ptr AST_NodePtr; + + namespace dispatch + { + template + std::function functor(std::shared_ptr func, const Type_Conversions_State *t_conversions); + + class Param_Types + { + public: + Param_Types() + : m_has_types(false), + m_doti(user_type()) + {} + + Param_Types(std::vector> t_types) + : m_types(std::move(t_types)), + m_has_types(false), + m_doti(user_type()) + { + update_has_types(); + } + + void push_front(std::string t_name, Type_Info t_ti) + { + m_types.emplace(m_types.begin(), std::move(t_name), std::move(t_ti)); + update_has_types(); + } + + bool operator==(const Param_Types &t_rhs) const + { + return m_types == t_rhs.m_types; + } + + bool match(const std::vector &vals, const Type_Conversions_State &t_conversions) const + { + if (!m_has_types) return true; + if (vals.size() != m_types.size()) return false; + + for (size_t i = 0; i < vals.size(); ++i) + { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; + + if (bv.get_type_info().bare_equal(m_doti)) + { + try { + const Dynamic_Object &d = boxed_cast(bv, &t_conversions); + return name == "Dynamic_Object" || d.get_type_name() == name; + } catch (const std::bad_cast &) { + return false; + } + } else { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) + { + if (!bv.get_type_info().bare_equal(ti)) { + return false; + } + } else { + return false; + } + } + } + } + + return true; + } + + const std::vector> &types() const + { + return m_types; + } + + private: + void update_has_types() + { + for (const auto &type : m_types) + { + if (!type.first.empty()) + { + m_has_types = true; + return; + } + } + + m_has_types = false; + } + + std::vector> m_types; + bool m_has_types; + Type_Info m_doti; + + }; + + /** + * Pure virtual base class for all Proxy_Function implementations + * Proxy_Functions are a type erasure of type safe C++ + * function calls. At runtime parameter types are expected to be + * tested against passed in types. + * Dispatch_Engine only knows how to work with Proxy_Function, no other + * function classes. + */ + class Proxy_Function_Base + { + public: + virtual ~Proxy_Function_Base() {} + + Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const + { + if (m_arity < 0 || size_t(m_arity) == params.size()) { + return do_call(params, t_conversions); + } else { + throw exception::arity_error(static_cast(params.size()), m_arity); + } + } + + /// Returns a vector containing all of the types of the parameters the function returns/takes + /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned + /// value contains exactly 1 Type_Info object: the return type + /// \returns the types of all parameters. + const std::vector &get_param_types() const { return m_types; } + + virtual bool operator==(const Proxy_Function_Base &) const = 0; + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; + + virtual bool is_attribute_function() const { return false; } + + bool has_arithmetic_param() const + { + return m_has_arithmetic_param; + } + + virtual std::vector > get_contained_functions() const + { + return std::vector >(); + } + + //! Return true if the function is a possible match + //! to the passed in values + bool filter(const std::vector &vals, const Type_Conversions_State &t_conversions) const + { + if (m_arity < 0) + { + return true; + } else if (static_cast(m_arity) == vals.size()) { + if (m_arity == 0) + { + return true; + } else if (m_arity > 1) { + return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); + } else { + return compare_type_to_param(m_types[1], vals[0], t_conversions); + } + } else { + return false; + } + } + + /// \returns the number of arguments the function takes or -1 if it is variadic + int get_arity() const + { + return m_arity; + } + + virtual std::string annotation() const = 0; + + static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) + { + if (ti.is_undef() + || ti.bare_equal(user_type()) + || (!bv.get_type_info().is_undef() + && ( (ti.bare_equal(user_type()) && bv.get_type_info().is_arithmetic()) + || ti.bare_equal(bv.get_type_info()) + || bv.get_type_info().bare_equal(user_type >()) + || t_conversions->converts(ti, bv.get_type_info()) + ) + ) + ) + { + return true; + } else { + return false; + } + } + + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const + { + return compare_type_to_param(m_types[1], bv, t_conversions); + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const = 0; + + Proxy_Function_Base(std::vector t_types, int t_arity) + : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false) + { + for (size_t i = 1; i < m_types.size(); ++i) + { + if (m_types[i].is_arithmetic()) + { + m_has_arithmetic_param = true; + return; + } + } + + } + + + static bool compare_types(const std::vector &tis, const std::vector &bvs, const Type_Conversions_State &t_conversions) + { + if (tis.size() - 1 != bvs.size()) + { + return false; + } else { + const size_t size = bvs.size(); + for (size_t i = 0; i < size; ++i) + { + if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { return false; } + } + } + return true; + } + + std::vector m_types; + int m_arity; + bool m_has_arithmetic_param; + }; + } + + /// \brief Common typedef used for passing of any registered function in ChaiScript + typedef std::shared_ptr Proxy_Function; + + /// \brief Const version of Proxy_Function. Points to a const Proxy_Function. This is how most registered functions + /// are handled internally. + typedef std::shared_ptr Const_Proxy_Function; + + namespace exception + { + /// \brief Exception thrown if a function's guard fails + class guard_error : public std::runtime_error + { + public: + guard_error() CHAISCRIPT_NOEXCEPT + : std::runtime_error("Guard evaluation failed") + { } + + guard_error(const guard_error &) = default; + + virtual ~guard_error() CHAISCRIPT_NOEXCEPT + { } + }; + } + + namespace dispatch + { + /** + * A Proxy_Function implementation that is not type safe, the called function + * is expecting a vector that it works with how it chooses. + */ + class Dynamic_Proxy_Function : public Proxy_Function_Base + { + public: + Dynamic_Proxy_Function( + int t_arity=-1, + AST_NodePtr t_parsenode = AST_NodePtr(), + Param_Types t_param_types = Param_Types(), + std::string t_description = "", + Proxy_Function t_guard = Proxy_Function()) + : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), + m_param_types(std::move(t_param_types)), + m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)), m_description(std::move(t_description)) + { + } + + virtual ~Dynamic_Proxy_Function() {} + + virtual bool operator==(const Proxy_Function_Base &rhs) const CHAISCRIPT_OVERRIDE + { + const Dynamic_Proxy_Function *prhs = dynamic_cast(&rhs); + + return this == &rhs + || (prhs + && this->m_arity == prhs->m_arity + && !this->m_guard && !prhs->m_guard + && this->m_param_types == prhs->m_param_types); + } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return (m_arity < 0 || (vals.size() == size_t(m_arity) && m_param_types.match(vals, t_conversions))) + && test_guard(vals, t_conversions); + } + + + Proxy_Function get_guard() const + { + return m_guard; + } + + AST_NodePtr get_parse_tree() const + { + return m_parsenode; + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return m_description; + } + + + protected: + bool test_guard(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const + { + if (m_guard) + { + try { + return boxed_cast((*m_guard)(params, t_conversions)); + } catch (const exception::arity_error &) { + return false; + } catch (const exception::bad_boxed_cast &) { + return false; + } + } else { + return true; + } + } + + private: + static std::vector build_param_type_list(const Param_Types &t_types) + { + // For the return type + std::vector types{chaiscript::detail::Get_Type_Info::get()}; + + for (const auto &t : t_types.types()) + { + if (t.second.is_undef()) { + types.push_back(chaiscript::detail::Get_Type_Info::get()); + } else { + types.push_back(t.second); + } + } + + return types; + } + + Param_Types m_param_types; + Proxy_Function m_guard; + AST_NodePtr m_parsenode; + std::string m_description; + }; + + + + template + class Dynamic_Proxy_Function_Impl : public Dynamic_Proxy_Function + { + public: + Dynamic_Proxy_Function_Impl( + Callable t_f, + int t_arity=-1, + AST_NodePtr t_parsenode = AST_NodePtr(), + Param_Types t_param_types = Param_Types(), + std::string t_description = "", + Proxy_Function t_guard = Proxy_Function()) + : Dynamic_Proxy_Function( + t_arity, + std::move(t_parsenode), + std::move(t_param_types), + std::move(t_description), + std::move(t_guard) + ), + m_f(std::move(t_f)) + { + } + + virtual ~Dynamic_Proxy_Function_Impl() {} + + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + if (call_match(params, t_conversions) && test_guard(params, t_conversions)) + { + return m_f(params); + } else { + throw exception::guard_error(); + } + } + + private: + + Callable m_f; + }; + + template + Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg&& ... a) + { + return chaiscript::make_shared>( + std::forward(c), std::forward(a)...); + } + + /// An object used by Bound_Function to represent "_" parameters + /// of a binding. This allows for unbound parameters during bind. + struct Placeholder_Object + { + }; + + /// An implementation of Proxy_Function that takes a Proxy_Function + /// and substitutes bound parameters into the parameter list + /// at runtime, when call() is executed. + /// it is used for bind(function, param1, _, param2) style calls + class Bound_Function : public Proxy_Function_Base + { + public: + Bound_Function(const Const_Proxy_Function &t_f, + const std::vector &t_args) + : Proxy_Function_Base(build_param_type_info(t_f, t_args), (t_f->get_arity()<0?-1:static_cast(build_param_type_info(t_f, t_args).size())-1)), + m_f(t_f), m_args(t_args) + { + assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast(m_args.size())); + } + + virtual bool operator==(const Proxy_Function_Base &t_f) const CHAISCRIPT_OVERRIDE + { + return &t_f == this; + } + + virtual ~Bound_Function() {} + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return m_f->call_match(build_param_list(vals), t_conversions); + } + + virtual std::vector get_contained_functions() const CHAISCRIPT_OVERRIDE + { + return std::vector{m_f}; + } + + + std::vector build_param_list(const std::vector ¶ms) const + { + auto parg = params.begin(); + auto barg = m_args.begin(); + + std::vector args; + + while (!(parg == params.end() && barg == m_args.end())) + { + while (barg != m_args.end() + && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info::get())) + { + args.push_back(*barg); + ++barg; + } + + if (parg != params.end()) + { + args.push_back(*parg); + ++parg; + } + + if (barg != m_args.end() + && barg->get_type_info() == chaiscript::detail::Get_Type_Info::get()) + { + ++barg; + } + } + return args; + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return "Bound: " + m_f->annotation(); + } + + protected: + static std::vector build_param_type_info(const Const_Proxy_Function &t_f, + const std::vector &t_args) + { + assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast(t_args.size())); + + if (t_f->get_arity() < 0) { return std::vector(); } + + std::vector types = t_f->get_param_types(); + assert(types.size() == t_args.size() + 1); + +#ifdef CHAISCRIPT_MSVC_12 +#pragma warning(push) +#pragma warning(disable : 6011) +#endif + // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14 + std::vector retval{types[0]}; +#ifdef CHAISCRIPT_MSVC_12 +#pragma warning(pop) +#endif + + for (size_t i = 0; i < types.size() - 1; ++i) + { + if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info::get()) + { + retval.push_back(types[i+1]); + } + } + + return retval; + } + + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return (*m_f)(build_param_list(params), t_conversions); + } + + private: + Const_Proxy_Function m_f; + std::vector m_args; + }; + + class Proxy_Function_Impl_Base : public Proxy_Function_Base + { + public: + Proxy_Function_Impl_Base(const std::vector &t_types) + : Proxy_Function_Base(t_types, static_cast(t_types.size()) - 1) + { + } + + virtual ~Proxy_Function_Impl_Base() {} + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return ""; + } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return static_cast(vals.size()) == get_arity() && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions)); + } + + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; + }; + + + + /// For any callable object + template + class Proxy_Function_Callable_Impl : public Proxy_Function_Impl_Base + { + public: + Proxy_Function_Callable_Impl(Callable f) + : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast(nullptr))), + m_f(std::move(f)) + { + } + + virtual ~Proxy_Function_Callable_Impl() {} + + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); + } + + virtual bool operator==(const Proxy_Function_Base &t_func) const CHAISCRIPT_OVERRIDE + { + return dynamic_cast *>(&t_func) != nullptr; + } + + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + typedef typename detail::Function_Signature::Return_Type Return_Type; + return detail::Do_Call::template go(m_f, params, t_conversions); + } + + private: + Callable m_f; + }; + + + class Assignable_Proxy_Function : public Proxy_Function_Impl_Base + { + public: + Assignable_Proxy_Function(const std::vector &t_types) + : Proxy_Function_Impl_Base(t_types) + { + } + + virtual ~Assignable_Proxy_Function() {} + + + virtual void assign(const std::shared_ptr &t_rhs) = 0; + + + }; + + template + class Assignable_Proxy_Function_Impl : public Assignable_Proxy_Function + { + public: + Assignable_Proxy_Function_Impl(std::reference_wrapper> t_f, std::shared_ptr> t_ptr) + : Assignable_Proxy_Function(detail::build_param_type_list(static_cast(nullptr))), + m_f(std::move(t_f)), m_shared_ptr_holder(std::move(t_ptr)) + { + assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); + } + + virtual ~Assignable_Proxy_Function_Impl() {} + + virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); + } + + virtual bool operator==(const Proxy_Function_Base &t_func) const CHAISCRIPT_OVERRIDE + { + return dynamic_cast *>(&t_func) != nullptr; + } + + std::function internal_function() const + { + return m_f.get(); + } + + virtual void assign(const std::shared_ptr &t_rhs) CHAISCRIPT_OVERRIDE { + m_f.get() = dispatch::functor(t_rhs, nullptr); + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + return detail::Do_Call::result_type>::template go(m_f.get(), params, t_conversions); + } + + + private: + std::reference_wrapper> m_f; + std::shared_ptr> m_shared_ptr_holder; + }; + /// Attribute getter Proxy_Function implementation + template + class Attribute_Access : public Proxy_Function_Base + { + public: + Attribute_Access(T Class::* t_attr) + : Proxy_Function_Base(param_types(), 1), + m_attr(t_attr) + { + } + + virtual ~Attribute_Access() {} + + virtual bool is_attribute_function() const CHAISCRIPT_OVERRIDE { return true; } + + virtual bool operator==(const Proxy_Function_Base &t_func) const CHAISCRIPT_OVERRIDE + { + const Attribute_Access * aa + = dynamic_cast *>(&t_func); + + if (aa) { + return m_attr == aa->m_attr; + } else { + return false; + } + } + + virtual bool call_match(const std::vector &vals, const Type_Conversions_State &) const CHAISCRIPT_OVERRIDE + { + if (vals.size() != 1) + { + return false; + } + + return vals[0].get_type_info().bare_equal(user_type()); + } + + virtual std::string annotation() const CHAISCRIPT_OVERRIDE + { + return ""; + } + + protected: + virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const CHAISCRIPT_OVERRIDE + { + const Boxed_Value &bv = params[0]; + if (bv.is_const()) + { + const Class *o = boxed_cast(bv, &t_conversions); + return detail::Handle_Return::type>::handle(o->*m_attr); + } else { + Class *o = boxed_cast(bv, &t_conversions); + return detail::Handle_Return::type>::handle(o->*m_attr); + } + } + + private: + static std::vector param_types() + { + return {user_type(), user_type()}; + } + + T Class::* m_attr; + }; + } + + namespace exception + { + /// \brief Exception thrown in the case that a method dispatch fails + /// because no matching function was found + /// + /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast + /// exception + class dispatch_error : public std::runtime_error + { + public: + dispatch_error(std::vector t_parameters, + std::vector t_functions) + : std::runtime_error("Error with function dispatch"), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + { + } + + dispatch_error(std::vector t_parameters, + std::vector t_functions, + const std::string &t_desc) + : std::runtime_error(t_desc), parameters(std::move(t_parameters)), functions(std::move(t_functions)) + { + } + + + dispatch_error(const dispatch_error &) = default; + virtual ~dispatch_error() CHAISCRIPT_NOEXCEPT {} + + std::vector parameters; + std::vector functions; + }; + } + + namespace dispatch + { + namespace detail + { + template + bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector &plist, + const Type_Conversions_State &t_conversions) + { + const std::vector &types = t_func->get_param_types(); + + if (t_func->get_arity() == -1) return false; + + assert(plist.size() == types.size() - 1); + + return std::mismatch(plist.begin(), plist.end(), + types.begin()+1, + [&](const Boxed_Value &bv, const Type_Info &ti) { + return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions) + || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic()); + } + ) == std::make_pair(plist.end(), types.end()); + } + + template + Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector &plist, + const Type_Conversions_State &t_conversions, const Funcs &t_funcs) + { + InItr matching_func(end); + + while (begin != end) + { + if (types_match_except_for_arithmetic(begin->second, plist, t_conversions)) + { + if (matching_func == end) + { + matching_func = begin; + } else { + // handle const members vs non-const member, which is not really ambiguous + const auto &mat_fun_param_types = matching_func->second->get_param_types(); + const auto &next_fun_param_types = begin->second->get_param_types(); + + if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { + matching_func = begin; // keep the new one, the const/non-const matchup is correct + } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { + // keep the old one, it has a better const/non-const matchup + } else { + // ambiguous function call + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); + } + } + } + + ++begin; + } + + if (matching_func == end) + { + // no appropriate function to attempt arithmetic type conversion on + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); + } + + + std::vector newplist; + newplist.reserve(plist.size()); + + const std::vector &tis = matching_func->second->get_param_types(); + std::transform(tis.begin() + 1, tis.end(), + plist.begin(), + std::back_inserter(newplist), + [](const Type_Info &ti, const Boxed_Value ¶m) -> Boxed_Value { + if (ti.is_arithmetic() && param.get_type_info().is_arithmetic()) { + return Boxed_Number(param).get_as(ti).bv; + } else { + return param; + } + } + ); + + + + try { + return (*(matching_func->second))(newplist, t_conversions); + } catch (const exception::bad_boxed_cast &) { + //parameter failed to cast + } catch (const exception::arity_error &) { + //invalid num params + } catch (const exception::guard_error &) { + //guard failed to allow the function to execute + } + + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); + + } + } + + /** + * Take a vector of functions and a vector of parameters. Attempt to execute + * each function against the set of parameters, in order, until a matching + * function is found or throw dispatch_error if no matching function is found + */ + template + Boxed_Value dispatch(const Funcs &funcs, + const std::vector &plist, const Type_Conversions_State &t_conversions) + { + std::vector> ordered_funcs; + ordered_funcs.reserve(funcs.size()); + + for (const auto &func : funcs) + { + const auto arity = func->get_arity(); + + if (arity == -1) + { + ordered_funcs.emplace_back(plist.size(), func.get()); + } else if (arity == static_cast(plist.size())) { + size_t numdiffs = 0; + for (size_t i = 0; i < plist.size(); ++i) + { + if (!func->get_param_types()[i+1].bare_equal(plist[i].get_type_info())) + { + ++numdiffs; + } + } + ordered_funcs.emplace_back(numdiffs, func.get()); + } + } + + + for (size_t i = 0; i <= plist.size(); ++i) + { + for (const auto &func : ordered_funcs ) + { + try { + if (func.first == i && func.second->filter(plist, t_conversions)) + { + return (*(func.second))(plist, t_conversions); + } + } catch (const exception::bad_boxed_cast &) { + //parameter failed to cast, try again + } catch (const exception::arity_error &) { + //invalid num params, try again + } catch (const exception::guard_error &) { + //guard failed to allow the function to execute, + //try again + } + } + } + + return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs); + } + } +} + + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/proxy_functions_detail.hpp b/apps/common/script/chaiscript/dispatchkit/proxy_functions_detail.hpp new file mode 100644 index 0000000000..452a999b8f --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -0,0 +1,275 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ +#define CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ + +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "boxed_cast.hpp" +#include "boxed_value.hpp" +#include "handle_return.hpp" +#include "type_info.hpp" +#include "callable_traits.hpp" + +namespace chaiscript { +class Type_Conversions_State; +namespace exception { +class bad_boxed_cast; +} // namespace exception +} // namespace chaiscript + +namespace chaiscript +{ + namespace exception + { + /** + * Exception thrown when there is a mismatch in number of + * parameters during Proxy_Function execution + */ + struct arity_error : std::range_error + { + arity_error(int t_got, int t_expected) + : std::range_error("Function dispatch arity mismatch"), + got(t_got), expected(t_expected) + { + } + + arity_error(const arity_error &) = default; + + virtual ~arity_error() CHAISCRIPT_NOEXCEPT {} + + int got; + int expected; + }; + } + + namespace dispatch + { + namespace detail + { + /** + * Used by Proxy_Function_Impl to return a list of all param types + * it contains. + */ + template + std::vector build_param_type_list(Ret (*)(Params...)) + { + /// \note somehow this is responsible for a large part of the code generation + return { user_type(), user_type()... }; + } + + +#ifdef CHAISCRIPT_GCC_4_6 + /// \todo REMOVE THIS WHEN WE DROP G++4.6 + + + // Forward declaration + template + struct Try_Cast; + + template + struct Try_Cast + { + static void do_try(const std::vector ¶ms, size_t generation, const Type_Conversions_State &t_conversions) + { + boxed_cast(params[generation], &t_conversions); + Try_Cast::do_try(params, generation+1, t_conversions); + } + }; + + // 0th case + template<> + struct Try_Cast<> + { + static void do_try(const std::vector &, size_t, const Type_Conversions_State &) + { + } + }; + + + /** + * Used by Proxy_Function_Impl to determine if it is equivalent to another + * Proxy_Function_Impl object. This function is primarily used to prevent + * registration of two functions with the exact same signatures + */ + template + bool compare_types_cast(Ret (*)(Params...), + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + try { + Try_Cast::do_try(params, 0, t_conversions); + } catch (const exception::bad_boxed_cast &) { + return false; + } + + return true; + } + + template + struct Call_Func + { + + template + static Ret do_call(const Callable &f, + const std::vector ¶ms, const Type_Conversions_State &t_conversions, InnerParams &&... innerparams) + { + return Call_Func::do_call(f, params, t_conversions, std::forward(innerparams)..., params[sizeof...(Params) - count]); + } + }; + + template + struct Call_Func + { +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4100) /// Disable unreferenced formal parameter warning, which only shows up in MSVC I don't think there's any way around it \todo evaluate this +#endif + template + static Ret do_call(const Callable &f, + const std::vector &, const Type_Conversions_State &t_conversions, InnerParams &&... innerparams) + { + return f(boxed_cast(std::forward(innerparams), &t_conversions)...); + } +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + }; + + /** + * Used by Proxy_Function_Impl to perform typesafe execution of a function. + * The function attempts to unbox each parameter to the expected type. + * if any unboxing fails the execution of the function fails and + * the bad_boxed_cast is passed up to the caller. + */ + template + Ret call_func(const chaiscript::dispatch::detail::Function_Signature &, const Callable &f, + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + if (params.size() == sizeof...(Params)) + { + return Call_Func::do_call(f, params, t_conversions); + } + + throw exception::arity_error(static_cast(params.size()), sizeof...(Params)); + } + + + +#else + + template + struct Indexes + { + }; + + template + struct Make_Indexes + { + typedef typename Make_Indexes::indexes indexes; + }; + + template + struct Make_Indexes<0, I...> + { + typedef Indexes indexes; + }; + + + + /** + * Used by Proxy_Function_Impl to determine if it is equivalent to another + * Proxy_Function_Impl object. This function is primarily used to prevent + * registration of two functions with the exact same signatures + */ + template + bool compare_types_cast(Indexes, Ret (*)(Params...), + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + try { + (void)params; (void)t_conversions; + (void)std::initializer_list{(boxed_cast(params[I], &t_conversions), 0)...}; + return true; + } catch (const exception::bad_boxed_cast &) { + return false; + } + + } + + template + bool compare_types_cast(Ret (*f)(Params...), + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + typedef typename Make_Indexes::indexes indexes; + return compare_types_cast(indexes(), f, params, t_conversions); + } + + + template + Ret call_func(const chaiscript::dispatch::detail::Function_Signature &, Indexes, const Callable &f, + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + (void)params; (void)t_conversions; + return f(boxed_cast(params[I], &t_conversions)...); + } + + + /** + * Used by Proxy_Function_Impl to perform typesafe execution of a function. + * The function attempts to unbox each parameter to the expected type. + * if any unboxing fails the execution of the function fails and + * the bad_boxed_cast is passed up to the caller. + */ + template + Ret call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, + const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + typedef typename Make_Indexes::indexes indexes; + return call_func(sig, indexes(), f, params, t_conversions); + } + +#endif + + } + } + +} + + +namespace chaiscript +{ + namespace dispatch + { + namespace detail + { + template + struct Do_Call + { + template + static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + return Handle_Return::handle(call_func(Function_Signature(), fun, params, t_conversions)); + } + }; + + template<> + struct Do_Call + { + template + static Boxed_Value go(const Callable &fun, const std::vector ¶ms, const Type_Conversions_State &t_conversions) + { + call_func(Function_Signature(), fun, params, t_conversions); + return Handle_Return::handle(); + } + }; + } + } +} + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/register_function.hpp b/apps/common/script/chaiscript/dispatchkit/register_function.hpp new file mode 100644 index 0000000000..8334b7b659 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/register_function.hpp @@ -0,0 +1,140 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_ +#define CHAISCRIPT_REGISTER_FUNCTION_HPP_ + +#include + +#include "bind_first.hpp" +#include "proxy_functions.hpp" + +namespace chaiscript +{ + + /// \brief Creates a new Proxy_Function object from a free function, member function or data member + /// \param[in] t Function / member to expose + /// + /// \b Example: + /// \code + /// int myfunction(const std::string &); + /// class MyClass + /// { + /// public: + /// void memberfunction(); + /// int memberdata; + /// }; + /// + /// chaiscript::ChaiScript chai; + /// chai.add(fun(&myfunction), "myfunction"); + /// chai.add(fun(&MyClass::memberfunction), "memberfunction"); + /// chai.add(fun(&MyClass::memberdata), "memberdata"); + /// \endcode + /// + /// \sa \ref adding_functions + template + Proxy_Function fun(const T &t) + { + typedef typename dispatch::detail::Callable_Traits::Signature Signature; + + return Proxy_Function( + chaiscript::make_shared>(t)); + } + + template + Proxy_Function fun(Ret (*func)(Param...)) + { + auto fun_call = dispatch::detail::Fun_Caller(func); + + return Proxy_Function( + chaiscript::make_shared>(fun_call)); + + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...) const) + { + auto call = dispatch::detail::Const_Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...)) + { + auto call = dispatch::detail::Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + + } + + + template::value>::type*/> + Proxy_Function fun(T Class::* m /*, typename std::enable_if::value>::type* = 0*/ ) + { + return Proxy_Function(chaiscript::make_shared>(m)); + } + + + + + /// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it + /// \param[in] t Function / member to expose + /// \param[in] q Value to bind to first parameter + /// + /// \b Example: + /// \code + /// struct MyClass + /// { + /// void memberfunction(int); + /// }; + /// + /// MyClass obj; + /// chaiscript::ChaiScript chai; + /// // Add function taking only one argument, an int, and permanently bound to "obj" + /// chai.add(fun(&MyClass::memberfunction, std::ref(obj)), "memberfunction"); + /// \endcode + /// + /// \sa \ref adding_functions + template + Proxy_Function fun(T &&t, const Q &q) + { + return fun(detail::bind_first(std::forward(t), q)); + } + + /// \brief Creates a new Proxy_Function object from a free function or member function and binds the first and second parameters of it + /// \param[in] t Function / member to expose + /// \param[in] q Value to bind to first parameter + /// \param[in] r Value to bind to second parameter + /// + /// \b Example: + /// \code + /// struct MyClass + /// { + /// void memberfunction(int); + /// }; + /// + /// MyClass obj; + /// chaiscript::ChaiScript chai; + /// // Add function taking only no arguments, and permanently bound to "obj" and "1" + /// // memberfunction() will be equivalent to obj.memberfunction(1) + /// chai.add(fun(&MyClass::memberfunction, std::ref(obj), 1), "memberfunction"); + /// \endcode + /// + /// \sa \ref adding_functions + template + Proxy_Function fun(T &&t, Q &&q, R &&r) + { + return fun(detail::bind_first(detail::bind_first(std::forward(t), std::forward(q)), std::forward(r))); + } + +} + + +#endif + diff --git a/apps/common/script/chaiscript/dispatchkit/type_conversions.hpp b/apps/common/script/chaiscript/dispatchkit/type_conversions.hpp new file mode 100644 index 0000000000..3a4d2392b4 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/type_conversions.hpp @@ -0,0 +1,644 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ +#define CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ + +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_threading.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "type_info.hpp" + +namespace chaiscript +{ + namespace exception + { + class bad_boxed_dynamic_cast : public bad_boxed_cast + { + public: + bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, + const std::string &t_what) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(t_from, t_to, t_what) + { + } + + bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(t_from, t_to) + { + } + + bad_boxed_dynamic_cast(const std::string &w) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(w) + { + } + + bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default; + + virtual ~bad_boxed_dynamic_cast() CHAISCRIPT_NOEXCEPT {} + }; + + class bad_boxed_type_cast : public bad_boxed_cast + { + public: + bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to, + const std::string &t_what) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(t_from, t_to, t_what) + { + } + + bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(t_from, t_to) + { + } + + bad_boxed_type_cast(const std::string &w) CHAISCRIPT_NOEXCEPT + : bad_boxed_cast(w) + { + } + + bad_boxed_type_cast(const bad_boxed_type_cast &) = default; + + virtual ~bad_boxed_type_cast() CHAISCRIPT_NOEXCEPT {} + }; + } + + + namespace detail + { + class Type_Conversion_Base + { + public: + virtual Boxed_Value convert(const Boxed_Value &from) const = 0; + virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0; + + const Type_Info &to() const + { + return m_to; + } + const Type_Info &from() const + { + return m_from; + } + + virtual bool bidir() const + { + return true; + } + + virtual ~Type_Conversion_Base() {} + + protected: + Type_Conversion_Base(const Type_Info &t_to, const Type_Info &t_from) + : m_to(t_to), m_from(t_from) + { + } + + + private: + Type_Info m_to; + Type_Info m_from; + + }; + + template + class Static_Caster + { + public: + static Boxed_Value cast(const Boxed_Value &t_from) + { + if (t_from.get_type_info().bare_equal(chaiscript::user_type())) + { + if (t_from.is_pointer()) + { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + return Boxed_Value( + [&]()->std::shared_ptr{ + if (auto data = std::static_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) + { + return data; + } else { + throw std::bad_cast(); + } + }() + ); + } else { + return Boxed_Value( + [&]()->std::shared_ptr{ + if (auto data = std::static_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) + { + return data; + } else { + throw std::bad_cast(); + } + }() + ); + } + } else { + // Pull the reference out of the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + const From &d = detail::Cast_Helper::cast(t_from, nullptr); + const To &data = static_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = detail::Cast_Helper::cast(t_from, nullptr); + To &data = static_cast(d); + return Boxed_Value(std::ref(data)); + } + } + } else { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + } + } + + }; + + + template + class Dynamic_Caster + { + public: + static Boxed_Value cast(const Boxed_Value &t_from) + { + if (t_from.get_type_info().bare_equal(chaiscript::user_type())) + { + if (t_from.is_pointer()) + { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + return Boxed_Value( + [&]()->std::shared_ptr{ + if (auto data = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) + { + return data; + } else { + throw std::bad_cast(); + } + }() + ); + } else { + return Boxed_Value( + [&]()->std::shared_ptr{ + if (auto data = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) + { + return data; + } else { +#ifdef CHAISCRIPT_LIBCPP + /// \todo fix this someday after libc++ is fixed. + if (std::string(typeid(To).name()).find("Assignable_Proxy_Function") != std::string::npos) { + auto from = detail::Cast_Helper >::cast(t_from, nullptr); + if (std::string(typeid(*from).name()).find("Assignable_Proxy_Function_Impl") != std::string::npos) { + return std::static_pointer_cast(from); + } + } +#endif + throw std::bad_cast(); + } + }() + ); + } + } else { + // Pull the reference out of the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + const From &d = detail::Cast_Helper::cast(t_from, nullptr); + const To &data = dynamic_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = detail::Cast_Helper::cast(t_from, nullptr); + To &data = dynamic_cast(d); + return Boxed_Value(std::ref(data)); + } + } + } else { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + } + } + + }; + + + template + class Dynamic_Conversion_Impl : public Type_Conversion_Base + { + public: + Dynamic_Conversion_Impl() + : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) + { + } + + virtual Boxed_Value convert_down(const Boxed_Value &t_base) const CHAISCRIPT_OVERRIDE + { + return Dynamic_Caster::cast(t_base); + } + + virtual Boxed_Value convert(const Boxed_Value &t_derived) const CHAISCRIPT_OVERRIDE + { + return Static_Caster::cast(t_derived); + } + }; + + template + class Static_Conversion_Impl : public Type_Conversion_Base + { + public: + Static_Conversion_Impl() + : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) + { + } + + virtual Boxed_Value convert_down(const Boxed_Value &t_base) const CHAISCRIPT_OVERRIDE + { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), typeid(Derived), "Unable to cast down inheritance hierarchy with non-polymorphic types"); + } + + virtual bool bidir() const CHAISCRIPT_OVERRIDE + { + return false; + } + + virtual Boxed_Value convert(const Boxed_Value &t_derived) const CHAISCRIPT_OVERRIDE + { + return Static_Caster::cast(t_derived); + } + }; + + + + template + class Type_Conversion_Impl : public Type_Conversion_Base + { + public: + Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func) + : Type_Conversion_Base(std::move(t_to), std::move(t_from)), + m_func(std::move(t_func)) + { + } + + virtual Boxed_Value convert_down(const Boxed_Value &) const CHAISCRIPT_OVERRIDE + { + throw chaiscript::exception::bad_boxed_type_cast("No conversion exists"); + } + + virtual Boxed_Value convert(const Boxed_Value &t_from) const CHAISCRIPT_OVERRIDE + { + /// \todo better handling of errors from the conversion function + return m_func(t_from); + } + + virtual bool bidir() const CHAISCRIPT_OVERRIDE + { + return false; + } + + + private: + Callable m_func; + }; + } + + class Type_Conversions + { + public: + struct Conversion_Saves + { + Conversion_Saves() + : enabled(false) + {} + + bool enabled; + std::vector saves; + }; + + struct Less_Than + { + bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const + { + return *t_lhs != *t_rhs && t_lhs->before(*t_rhs); + } + }; + + Type_Conversions() + : m_mutex(), + m_conversions(), + m_convertableTypes(), + m_num_types(0), + m_thread_cache(this), + m_conversion_saves(this) + { + } + + Type_Conversions(const Type_Conversions &t_other) + : m_mutex(), + m_conversions(t_other.get_conversions()), + m_convertableTypes(t_other.m_convertableTypes), + m_num_types(m_conversions.size()), + m_thread_cache(this), + m_conversion_saves(this) + + { + } + + const std::set &thread_cache() const + { + auto &cache = *m_thread_cache; + if (cache.size() != m_num_types) + { + chaiscript::detail::threading::shared_lock l(m_mutex); + cache = m_convertableTypes; + } + + return cache; + } + + void add_conversion(const std::shared_ptr &conversion) + { + chaiscript::detail::threading::unique_lock l(m_mutex); + /// \todo error if a conversion already exists + m_conversions.insert(conversion); + m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()}); + m_num_types = m_convertableTypes.size(); + } + + template + bool convertable_type() const + { + return thread_cache().count(user_type().bare_type_info()) != 0; + } + + template + bool converts() const + { + return converts(user_type(), user_type()); + } + + bool converts(const Type_Info &to, const Type_Info &from) const + { + const auto &types = thread_cache(); + if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0) + { + return has_conversion(to, from); + } else { + return false; + } + } + + template + Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const + { + try { + Boxed_Value ret = get_conversion(user_type(), from.get_type_info())->convert(from); + if (t_saves.enabled) t_saves.saves.push_back(ret); + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), typeid(To), "Unable to perform dynamic_cast operation"); + } + } + + template + Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const + { + try { + Boxed_Value ret = get_conversion(to.get_type_info(), user_type())->convert_down(to); + if (t_saves.enabled) t_saves.saves.push_back(ret); + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), typeid(From), "Unable to perform dynamic_cast operation"); + } + } + + static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) + { + t_saves.enabled = t_val; + } + + std::vector take_saves(Conversion_Saves &t_saves) + { + std::vector ret; + std::swap(ret, t_saves.saves); + return ret; + } + + bool has_conversion(const Type_Info &to, const Type_Info &from) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + return find_bidir(to, from) != m_conversions.end(); + } + + std::shared_ptr get_conversion(const Type_Info &to, const Type_Info &from) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + auto itr = find(to, from); + + if (itr != m_conversions.end()) + { + return *itr; + } else { + throw std::out_of_range("No such conversion exists from " + from.bare_name() + " to " + to.bare_name()); + } + } + + Conversion_Saves &conversion_saves() const { + return *m_conversion_saves; + } + + private: + std::set >::const_iterator find_bidir( + const Type_Info &to, const Type_Info &from) const + { + return std::find_if(m_conversions.begin(), m_conversions.end(), + [&to, &from](const std::shared_ptr &conversion) -> bool + { + return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from)) + || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from)); + } + ); + } + + std::set >::const_iterator find( + const Type_Info &to, const Type_Info &from) const + { + return std::find_if(m_conversions.begin(), m_conversions.end(), + [&to, &from](const std::shared_ptr &conversion) + { + return conversion->to().bare_equal(to) && conversion->from().bare_equal(from); + } + ); + } + + std::set> get_conversions() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return m_conversions; + } + + + + mutable chaiscript::detail::threading::shared_mutex m_mutex; + std::set> m_conversions; + std::set m_convertableTypes; + std::atomic_size_t m_num_types; + mutable chaiscript::detail::threading::Thread_Storage> m_thread_cache; + mutable chaiscript::detail::threading::Thread_Storage m_conversion_saves; + }; + + class Type_Conversions_State + { + public: + Type_Conversions_State(const Type_Conversions &t_conversions, + Type_Conversions::Conversion_Saves &t_saves) + : m_conversions(t_conversions), + m_saves(t_saves) + { + } + + const Type_Conversions *operator->() const { + return &m_conversions.get(); + } + + const Type_Conversions *get() const { + return &m_conversions.get(); + } + + Type_Conversions::Conversion_Saves &saves() const { + return m_saves; + } + + private: + std::reference_wrapper m_conversions; + std::reference_wrapper m_saves; + }; + + typedef std::shared_ptr Type_Conversion; + + /// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you + /// want automatic conversions up your inheritance hierarchy. + /// + /// Create a new to class registration for applying to a module or to the ChaiScript engine + /// Currently, due to limitations in module loading on Windows, and for the sake of portability, + /// if you have a type that is introduced in a loadable module and is used by multiple modules + /// (through a tertiary dll that is shared between the modules, static linking the new type + /// into both loadable modules would not be portable), you need to register the type + /// relationship in all modules that use the newly added type in a polymorphic way. + /// + /// Example: + /// \code + /// class Base + /// {}; + /// class Derived : public Base + /// {}; + /// + /// chaiscript::ChaiScript chai; + /// chai.add(chaiscript::to_class()); + /// \endcode + /// + template + Type_Conversion base_class(typename std::enable_if::value && std::is_polymorphic::value>::type* = nullptr) + { + //Can only be used with related polymorphic types + //may be expanded some day to support conversions other than child -> parent + static_assert(std::is_base_of::value, "Classes are not related by inheritance"); + + return chaiscript::make_shared>(); + } + + template + Type_Conversion base_class(typename std::enable_if::value || !std::is_polymorphic::value>::type* = nullptr) + { + //Can only be used with related polymorphic types + //may be expanded some day to support conversions other than child -> parent + static_assert(std::is_base_of::value, "Classes are not related by inheritance"); + + return chaiscript::make_shared>(); + } + + + template + Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, + const Callable &t_func) + { + return chaiscript::make_shared>(t_from, t_to, t_func); + } + + template + Type_Conversion type_conversion(const Callable &t_function) + { + auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in some call recursion + return chaiscript::Boxed_Value(t_function(detail::Cast_Helper::cast(t_bv, nullptr))); + }; + + return chaiscript::make_shared>(user_type(), user_type(), func); + } + + template + Type_Conversion type_conversion() + { + static_assert(std::is_convertible::value, "Types are not automatically convertible"); + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in some call recursion + return chaiscript::Boxed_Value(To(detail::Cast_Helper::cast(t_bv, nullptr))); + }; + + return chaiscript::make_shared>(user_type(), user_type(), func); + } + + template + Type_Conversion vector_conversion() + { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::vector &from_vec = detail::Cast_Helper &>::cast(t_bv, nullptr); + + To vec; + vec.reserve(from_vec.size()); + for (const Boxed_Value &bv : from_vec) { + vec.push_back(detail::Cast_Helper::cast(bv, nullptr)); + } + + return Boxed_Value(std::move(vec)); + }; + + return chaiscript::make_shared>(user_type>(), user_type(), func); + } + + template + Type_Conversion map_conversion() + { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::map &from_map = detail::Cast_Helper &>::cast(t_bv, nullptr); + + To map; + for (const std::pair &p : from_map) { + map.insert(std::make_pair(p.first, detail::Cast_Helper::cast(p.second, nullptr))); + } + + return Boxed_Value(std::move(map)); + }; + + return chaiscript::make_shared>(user_type>(), user_type(), func); + } +} + + +#endif diff --git a/apps/common/script/chaiscript/dispatchkit/type_info.hpp b/apps/common/script/chaiscript/dispatchkit/type_info.hpp new file mode 100644 index 0000000000..1426085794 --- /dev/null +++ b/apps/common/script/chaiscript/dispatchkit/type_info.hpp @@ -0,0 +1,245 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_TYPE_INFO_HPP_ +#define CHAISCRIPT_TYPE_INFO_HPP_ + +#include +#include +#include +#include + +namespace chaiscript +{ + + namespace detail + { + template + struct Bare_Type + { + typedef typename std::remove_cv::type>::type>::type type; + }; + } + + + /// \brief Compile time deduced information about a type + class Type_Info + { + public: + CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, + bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) + : m_type_info(t_ti), m_bare_type_info(t_bare_ti), + m_flags((t_is_const << is_const_flag) + + (t_is_reference << is_reference_flag) + + (t_is_pointer << is_pointer_flag) + + (t_is_void << is_void_flag) + + (t_is_arithmetic << is_arithmetic_flag)) + { + } + + CHAISCRIPT_CONSTEXPR Type_Info() + : m_type_info(nullptr), m_bare_type_info(nullptr), + m_flags(1 << is_undef_flag) + { + } + +#if !defined(_MSC_VER) || _MSC_VER != 1800 + Type_Info(Type_Info&&) = default; + Type_Info& operator=(Type_Info&&) = default; +#endif + + Type_Info(const Type_Info&) = default; + Type_Info& operator=(const Type_Info&) = default; + + + CHAISCRIPT_CONSTEXPR bool operator<(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT + { + return m_type_info < ti.m_type_info; + } + + CHAISCRIPT_CONSTEXPR bool operator==(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT + { + return ti.m_type_info == m_type_info + || (ti.m_type_info && m_type_info && *ti.m_type_info == *m_type_info); + } + + CHAISCRIPT_CONSTEXPR bool operator==(const std::type_info &ti) const CHAISCRIPT_NOEXCEPT + { + return m_type_info != nullptr && (*m_type_info) == ti; + } + + CHAISCRIPT_CONSTEXPR bool bare_equal(const Type_Info &ti) const CHAISCRIPT_NOEXCEPT + { + return ti.m_bare_type_info == m_bare_type_info + || (ti.m_bare_type_info && m_bare_type_info && *ti.m_bare_type_info == *m_bare_type_info); + } + + CHAISCRIPT_CONSTEXPR bool bare_equal_type_info(const std::type_info &ti) const CHAISCRIPT_NOEXCEPT + { + return m_bare_type_info != nullptr + && (*m_bare_type_info) == ti; + } + + CHAISCRIPT_CONSTEXPR bool is_const() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_const_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_reference() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_reference_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_void() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_void_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_arithmetic() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_arithmetic_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_undef() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_undef_flag)) != 0; } + CHAISCRIPT_CONSTEXPR bool is_pointer() const CHAISCRIPT_NOEXCEPT { return (m_flags & (1 << is_pointer_flag)) != 0; } + + std::string name() const + { + if (m_type_info) + { + return m_type_info->name(); + } else { + return ""; + } + } + + std::string bare_name() const + { + if (m_bare_type_info) + { + return m_bare_type_info->name(); + } else { + return ""; + } + } + + CHAISCRIPT_CONSTEXPR const std::type_info *bare_type_info() const + { + return m_bare_type_info; + } + + private: + const std::type_info *m_type_info; + const std::type_info *m_bare_type_info; + unsigned int m_flags; + static const int is_const_flag = 0; + static const int is_reference_flag = 1; + static const int is_pointer_flag = 2; + static const int is_void_flag = 3; + static const int is_arithmetic_flag = 4; + static const int is_undef_flag = 5; + }; + + namespace detail + { + /// Helper used to create a Type_Info object + template + struct Get_Type_Info + { + typedef T type; + + static Type_Info get() + { + return Type_Info(std::is_const::type>::type>::value, + std::is_reference::value, std::is_pointer::value, + std::is_void::value, + (std::is_arithmetic::value || std::is_arithmetic::type>::value) + && !std::is_same::type>::type, bool>::value, + &typeid(T), + &typeid(typename Bare_Type::type)); + } + }; + + template + struct Get_Type_Info > + { + typedef T type; + + static Type_Info get() + { + return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, + &typeid(std::shared_ptr ), + &typeid(typename Bare_Type::type)); + } + }; + + template + struct Get_Type_Info &> + { + typedef T type; + + static Type_Info get() + { + return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, + &typeid(const std::shared_ptr &), + &typeid(typename Bare_Type::type)); + } + }; + + template + struct Get_Type_Info > + { + typedef T type; + + static Type_Info get() + { + return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, + &typeid(std::reference_wrapper ), + &typeid(typename Bare_Type::type)); + } + }; + + template + struct Get_Type_Info &> + { + typedef T type; + + static Type_Info get() + { + return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, + &typeid(const std::reference_wrapper &), + &typeid(typename Bare_Type::type)); + } + }; + + } + + /// \brief Creates a Type_Info object representing the type passed in + /// \tparam T Type of object to get a Type_Info for, derived from the passed in parameter + /// \return Type_Info for T + /// + /// \b Example: + /// \code + /// int i; + /// chaiscript::Type_Info ti = chaiscript::user_type(i); + /// \endcode + template + Type_Info user_type(const T &/*t*/) + { + return detail::Get_Type_Info::get(); + } + + + /// \brief Creates a Type_Info object representing the templated type + /// \tparam T Type of object to get a Type_Info for + /// \return Type_Info for T + /// + /// \b Example: + /// \code + /// chaiscript::Type_Info ti = chaiscript::user_type(); + /// \endcode + template + Type_Info user_type() + { + return detail::Get_Type_Info::get(); + } + +} + +#endif + diff --git a/apps/common/script/chaiscript/language/chaiscript_algebraic.hpp b/apps/common/script/chaiscript/language/chaiscript_algebraic.hpp new file mode 100644 index 0000000000..52441e8f03 --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_algebraic.hpp @@ -0,0 +1,130 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_ALGEBRAIC_HPP_ +#define CHAISCRIPT_ALGEBRAIC_HPP_ + +#include + +namespace chaiscript +{ + + struct Operators { + enum Opers + { + boolean_flag, + equals, less_than, greater_than, less_than_equal, greater_than_equal, not_equal, + non_const_flag, + assign, pre_increment, pre_decrement, assign_product, assign_sum, + assign_quotient, assign_difference, + non_const_int_flag, + assign_bitwise_and, assign_bitwise_or, assign_shift_left, assign_shift_right, + assign_remainder, assign_bitwise_xor, + const_int_flag, + shift_left, shift_right, remainder, bitwise_and, bitwise_or, bitwise_xor, bitwise_complement, + const_flag, + sum, quotient, product, difference, unary_plus, unary_minus, + invalid + }; + + static const char *to_string(Opers t_oper) { + const char *opers[] = { + "", + "==", "<", ">", "<=", ">=", "!=", + "", + "=", "++", "--", "*=", "+=", + "/=", "-=", + "", + "&=", "|=", "<<=", ">>=", + "%=", "^=", + "", + "<<", ">>", "%", "&", "|", "^", "~", + "", + "+", "/", "*", "-", "+", "-", + "" + }; + return opers[t_oper]; + } + + static Opers to_operator(const std::string &t_str, bool t_is_unary = false) + { + if (t_str == "==") + { + return equals; + } else if (t_str == "<") { + return less_than; + } else if (t_str == ">") { + return greater_than; + } else if (t_str == "<=") { + return less_than_equal; + } else if (t_str == ">=") { + return greater_than_equal; + } else if (t_str == "!=") { + return not_equal; + } else if (t_str == "=") { + return assign; + } else if (t_str == "++") { + return pre_increment; + } else if (t_str == "--") { + return pre_decrement; + } else if (t_str == "*=") { + return assign_product; + } else if (t_str == "+=") { + return assign_sum; + } else if (t_str == "-=") { + return assign_difference; + } else if (t_str == "&=") { + return assign_bitwise_and; + } else if (t_str == "|=") { + return assign_bitwise_or; + } else if (t_str == "<<=") { + return assign_shift_left; + } else if (t_str == ">>=") { + return assign_shift_right; + } else if (t_str == "%=") { + return assign_remainder; + } else if (t_str == "^=") { + return assign_bitwise_xor; + } else if (t_str == "<<") { + return shift_left; + } else if (t_str == ">>") { + return shift_right; + } else if (t_str == "%") { + return remainder; + } else if (t_str == "&") { + return bitwise_and; + } else if (t_str == "|") { + return bitwise_or; + } else if (t_str == "^") { + return bitwise_xor; + } else if (t_str == "~") { + return bitwise_complement; + } else if (t_str == "+") { + if (t_is_unary) { + return unary_plus; + } else { + return sum; + } + } else if (t_str == "-") { + if (t_is_unary) { + return unary_minus; + } else { + return difference; + } + } else if (t_str == "/") { + return quotient; + } else if (t_str == "*") { + return product; + } else { + return invalid; + } + } + + }; +} + +#endif /* _CHAISCRIPT_ALGEBRAIC_HPP */ + diff --git a/apps/common/script/chaiscript/language/chaiscript_common.hpp b/apps/common/script/chaiscript/language/chaiscript_common.hpp new file mode 100644 index 0000000000..b44a5a3bf9 --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_common.hpp @@ -0,0 +1,634 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_COMMON_HPP_ +#define CHAISCRIPT_COMMON_HPP_ + +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "../dispatchkit/boxed_value.hpp" +#include "../dispatchkit/dispatchkit.hpp" +#include "../dispatchkit/proxy_functions.hpp" +#include "../dispatchkit/type_info.hpp" + +namespace chaiscript { +struct AST_Node; +} // namespace chaiscript + +namespace chaiscript +{ + + /// Signature of module entry point that all binary loadable modules must implement. + typedef ModulePtr (*Create_Module_Func)(); + + + /// Types of AST nodes available to the parser and eval + class AST_Node_Type { + public: + enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Arg_List, Variable, Equation, Var_Decl, + Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, + Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, + Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, + Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg, Global_Decl + }; + }; + + namespace + { + + /// Helper lookup to get the name of each node type + const char *ast_node_type_to_string(int ast_node_type) { + const char *ast_node_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", + "Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", + "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", + "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", + "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg"}; + + return ast_node_types[ast_node_type]; + } + } + + /// \brief Convenience type for file positions + struct File_Position { + int line; + int column; + + File_Position(int t_file_line, int t_file_column) + : line(t_file_line), column(t_file_column) { } + + File_Position() : line(0), column(0) { } + }; + + struct Parse_Location { + Parse_Location(std::string t_fname="", const int t_start_line=0, const int t_start_col=0, + const int t_end_line=0, const int t_end_col=0) + : start(t_start_line, t_start_col), + end(t_end_line, t_end_col), + filename(std::make_shared(std::move(t_fname))) + { + } + + Parse_Location(std::shared_ptr t_fname, const int t_start_line=0, const int t_start_col=0, + const int t_end_line=0, const int t_end_col=0) + : start(t_start_line, t_start_col), + end(t_end_line, t_end_col), + filename(std::move(t_fname)) + { + } + + + + File_Position start; + File_Position end; + std::shared_ptr filename; + }; + + + /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree + typedef std::shared_ptr AST_NodePtr; + typedef std::shared_ptr AST_NodePtr_Const; + + + /// \brief Classes which may be thrown during error cases when ChaiScript is executing. + namespace exception + { + + /// Errors generated during parsing or evaluation + struct eval_error : std::runtime_error { + std::string reason; + File_Position start_position; + std::string filename; + std::string detail; + std::vector call_stack; + + eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, + const std::vector &t_parameters, const std::vector &t_functions, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_NOEXCEPT : + std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)), + reason(t_why), start_position(t_where), filename(t_fname), detail(format_detail(t_functions, t_dot_notation, t_ss)) + {} + + eval_error(const std::string &t_why, + const std::vector &t_parameters, const std::vector &t_functions, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_NOEXCEPT : + std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)), + reason(t_why), detail(format_detail(t_functions, t_dot_notation, t_ss)) + {} + + + eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) CHAISCRIPT_NOEXCEPT : + std::runtime_error(format(t_why, t_where, t_fname)), + reason(t_why), start_position(t_where), filename(t_fname) + {} + + eval_error(const std::string &t_why) CHAISCRIPT_NOEXCEPT + : std::runtime_error("Error: \"" + t_why + "\" "), + reason(t_why) + {} + + eval_error(const eval_error &) = default; + + std::string pretty_print() const + { + std::ostringstream ss; + + ss << what(); + if (call_stack.size() > 0) { + ss << "during evaluation at (" << fname(call_stack[0]) << " " << startpos(call_stack[0]) << ")\n"; + ss << '\n' << detail << '\n'; + ss << " " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) << ") '" << pretty(call_stack[0]) << "'"; + for (size_t j = 1; j < call_stack.size(); ++j) { + if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block + && id(call_stack[j]) != chaiscript::AST_Node_Type::File) + { + ss << '\n'; + ss << " from " << fname(call_stack[j]) << " (" << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) << "'"; + } + } + } + ss << '\n'; + return ss.str(); + } + + virtual ~eval_error() CHAISCRIPT_NOEXCEPT {} + + private: + + template + static int id(const T& t) + { + return t->identifier; + } + + template + static std::string pretty(const T& t) + { + return t->pretty_print(); + } + + template + static const std::string &fname(const T& t) + { + return t->filename(); + } + + template + static std::string startpos(const T& t) + { + std::ostringstream oss; + oss << t->start().line << ", " << t->start().column; + return oss.str(); + } + + static std::string format_why(const std::string &t_why) + { + return "Error: \"" + t_why + "\""; + } + + static std::string format_types(const Const_Proxy_Function &t_func, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) + { + int arity = t_func->get_arity(); + std::vector types = t_func->get_param_types(); + + std::string retval; + if (arity == -1) + { + retval = "(...)"; + if (t_dot_notation) + { + retval = "(Object)." + retval; + } + } else if (types.size() <= 1) { + retval = "()"; + } else { + std::stringstream ss; + ss << "("; + + std::string paramstr; + + for (size_t index = 1; + index != types.size(); + ++index) + { + paramstr += (types[index].is_const()?"const ":""); + paramstr += t_ss.get_type_name(types[index]); + + if (index == 1 && t_dot_notation) + { + paramstr += ").("; + if (types.size() == 2) + { + paramstr += ", "; + } + } else { + paramstr += ", "; + } + } + + ss << paramstr.substr(0, paramstr.size() - 2); + + ss << ")"; + retval = ss.str(); + } + + + std::shared_ptr dynfun + = std::dynamic_pointer_cast(t_func); + + if (dynfun) + { + Proxy_Function f = dynfun->get_guard(); + + if (f) + { + auto dynfunguard = std::dynamic_pointer_cast(f); + if (dynfunguard) + { + retval += " : " + format_guard(dynfunguard->get_parse_tree()); + } + } + + retval += "\n Defined at " + format_location(dynfun->get_parse_tree()); + } + + return retval; + } + + template + static std::string format_guard(const T &t) + { + return t->pretty_print(); + } + + template + static std::string format_location(const T &t) + { + if (t) { + std::ostringstream oss; + oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")"; + return oss.str(); + } else { + return "(internal)"; + } + + } + + static std::string format_detail(const std::vector &t_functions, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) + { + std::stringstream ss; + if (t_functions.size() == 1) + { + ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; + } else { + ss << " " << t_functions.size() << " overloads available:\n"; + + for (const auto & t_function : t_functions) + { + ss << " " << format_types((t_function), t_dot_notation, t_ss) << '\n'; + } + + } + + return ss.str(); + + } + + static std::string format_parameters(const std::vector &t_parameters, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) + { + std::stringstream ss; + ss << "("; + + if (!t_parameters.empty()) + { + std::string paramstr; + + for (auto itr = t_parameters.begin(); + itr != t_parameters.end(); + ++itr) + { + paramstr += (itr->is_const()?"const ":""); + paramstr += t_ss.type_name(*itr); + + if (itr == t_parameters.begin() && t_dot_notation) + { + paramstr += ").("; + if (t_parameters.size() == 1) + { + paramstr += ", "; + } + } else { + paramstr += ", "; + } + } + + ss << paramstr.substr(0, paramstr.size() - 2); + } + ss << ")"; + + return ss.str(); + } + + static std::string format_filename(const std::string &t_fname) + { + std::stringstream ss; + + if (t_fname != "__EVAL__") + { + ss << "in '" << t_fname << "' "; + } else { + ss << "during evaluation "; + } + + return ss.str(); + } + + static std::string format_location(const File_Position &t_where) + { + std::stringstream ss; + ss << "at (" << t_where.line << ", " << t_where.column << ")"; + return ss.str(); + } + + static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, + const std::vector &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) + { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << "With parameters: " << format_parameters(t_parameters, t_dot_notation, t_ss); + ss << " "; + + ss << format_filename(t_fname); + ss << " "; + + ss << format_location(t_where); + + return ss.str(); + } + + static std::string format(const std::string &t_why, + const std::vector &t_parameters, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) + { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << "With parameters: " << format_parameters(t_parameters, t_dot_notation, t_ss); + ss << " "; + + return ss.str(); + } + + static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) + { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << format_filename(t_fname); + ss << " "; + + ss << format_location(t_where); + + return ss.str(); + } + }; + + + /// Errors generated when loading a file + struct file_not_found_error : std::runtime_error { + file_not_found_error(const std::string &t_filename) CHAISCRIPT_NOEXCEPT + : std::runtime_error("File Not Found: " + t_filename) + { } + + file_not_found_error(const file_not_found_error &) = default; + virtual ~file_not_found_error() CHAISCRIPT_NOEXCEPT {} + }; + + } + + + /// \brief Struct that doubles as both a parser ast_node and an AST node. + struct AST_Node : std::enable_shared_from_this { + public: + const int identifier; //< \todo shouldn't this be a strongly typed enum value? + const std::string text; + Parse_Location location; + std::vector children; + AST_NodePtr annotation; + + const std::string &filename() const { + return *location.filename; + } + + const File_Position &start() const { + return location.start; + } + + const File_Position &end() const { + return location.end; + } + + virtual std::string pretty_print() const + { + std::ostringstream oss; + + oss << text; + + for (auto & elem : this->children) { + oss << elem->pretty_print(); + } + + return oss.str(); + } + + + /// Prints the contents of an AST node, including its children, recursively + std::string to_string(const std::string &t_prepend = "") const { + std::ostringstream oss; + + oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " + << this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n'; + + for (auto & elem : this->children) { + oss << elem->to_string(t_prepend + " "); + } + return oss.str(); + } + + Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const + { + try { + return eval_internal(t_e); + } catch (exception::eval_error &ee) { + ee.call_stack.push_back(shared_from_this()); + throw; + } + } + + static bool get_bool_condition(const Boxed_Value &t_bv) { + try { + return boxed_cast(t_bv); + } + catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Condition not boolean"); + } + } + + + void replace_child(const AST_NodePtr &t_child, const AST_NodePtr &t_new_child) + { + std::replace(children.begin(), children.end(), t_child, t_new_child); + } + + virtual ~AST_Node() {} + + protected: + AST_Node(std::string t_ast_node_text, int t_id, Parse_Location t_loc, + std::vector t_children = std::vector()) : + identifier(t_id), text(std::move(t_ast_node_text)), + location(std::move(t_loc)), + children(std::move(t_children)) + { + } + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const + { + throw std::runtime_error("Undispatched ast_node (internal error)"); + } + + private: + // Copy and assignment explicitly unimplemented + AST_Node(const AST_Node &) = delete; + AST_Node& operator=(const AST_Node &) = delete; + }; + + + namespace eval + { + namespace detail + { + /// Special type for returned values + struct Return_Value { + Boxed_Value retval; + + Return_Value(Boxed_Value t_return_value) : retval(std::move(t_return_value)) { } + }; + + + /// Special type indicating a call to 'break' + struct Break_Loop { + Break_Loop() { } + }; + + + /// Special type indicating a call to 'continue' + struct Continue_Loop { + Continue_Loop() { } + }; + + + /// Creates a new scope then pops it on destruction + struct Scope_Push_Pop + { + Scope_Push_Pop(const Scope_Push_Pop &) = delete; + Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete; + + Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) + : m_ds(t_ds) + { + m_ds.get()->new_scope(m_ds.get().stack_holder()); + } + + ~Scope_Push_Pop() + { + m_ds.get()->pop_scope(m_ds.get().stack_holder()); + } + + + private: + std::reference_wrapper m_ds; + }; + + /// Creates a new function call and pops it on destruction + struct Function_Push_Pop + { + Function_Push_Pop(const Function_Push_Pop &) = delete; + Function_Push_Pop& operator=(const Function_Push_Pop &) = delete; + + Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) + : m_ds(t_ds) + { + m_ds.get()->new_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); + } + + ~Function_Push_Pop() + { + m_ds.get()->pop_function_call(m_ds.get().stack_holder(), m_ds.get().conversion_saves()); + } + + void save_params(const std::vector &t_params) + { + m_ds.get()->save_function_params(t_params); + } + + void save_params(std::initializer_list t_params) + { + m_ds.get()->save_function_params(std::move(t_params)); + } + + + private: + std::reference_wrapper m_ds; + }; + + /// Creates a new scope then pops it on destruction + struct Stack_Push_Pop + { + Stack_Push_Pop(const Stack_Push_Pop &) = delete; + Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete; + + Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) + : m_ds(t_ds) + { + m_ds.get()->new_stack(m_ds.get().stack_holder()); + } + + ~Stack_Push_Pop() + { + m_ds.get()->pop_stack(m_ds.get().stack_holder()); + } + + + private: + std::reference_wrapper m_ds; + }; + } + } +} + +#endif /* _CHAISCRIPT_COMMON_HPP */ + diff --git a/apps/common/script/chaiscript/language/chaiscript_engine.hpp b/apps/common/script/chaiscript/language/chaiscript_engine.hpp new file mode 100644 index 0000000000..f3aa0693ca --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_engine.hpp @@ -0,0 +1,994 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_ENGINE_HPP_ +#define CHAISCRIPT_ENGINE_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "../chaiscript_threading.hpp" +#include "../dispatchkit/boxed_cast_helper.hpp" +#include "../dispatchkit/boxed_value.hpp" +#include "../dispatchkit/dispatchkit.hpp" +#include "../dispatchkit/type_conversions.hpp" +#include "../dispatchkit/proxy_functions.hpp" +#include "chaiscript_common.hpp" + +#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__) +#include +#endif + +#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) +#include +#else +#ifdef CHAISCRIPT_WINDOWS +#define VC_EXTRA_LEAN +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif +#endif + + +#include "../dispatchkit/exception_specification.hpp" +#include "chaiscript_parser.hpp" + +namespace chaiscript +{ + namespace exception + { + /// \brief Thrown if an error occurs while attempting to load a binary module + struct load_module_error : std::runtime_error + { + load_module_error(const std::string &t_reason) CHAISCRIPT_NOEXCEPT + : std::runtime_error(t_reason) + { + } + + load_module_error(const load_module_error &) = default; + virtual ~load_module_error() CHAISCRIPT_NOEXCEPT {} + }; + } + + namespace detail + { +#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) + struct Loadable_Module + { + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) + { + if (!m_data) + { + throw chaiscript::exception::load_module_error(dlerror()); + } + } + + DLModule(const DLModule &); // Explicitly unimplemented copy constructor + DLModule &operator=(const DLModule &); // Explicitly unimplemented assignment operator + + ~DLModule() + { + dlclose(m_data); + } + + void *m_data; + }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw chaiscript::exception::load_module_error(dlerror()); + } + } + + static T cast_symbol(void *p) + { + union cast_union + { + T func_ptr; + void *in_ptr; + }; + + cast_union c; + c.in_ptr = p; + return c.func_ptr; + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; +#else + +#ifdef WIN32 + + + struct Loadable_Module + { + template + static std::wstring to_wstring(const T &t_str) + { + return std::wstring(t_str.begin(), t_str.end()); + } + + template + static std::string to_string(const T &t_str) + { + return std::string(t_str.begin(), t_str.end()); + } + +#if defined(_UNICODE) || defined(UNICODE) + template + static std::wstring to_proper_string(const T &t_str) + { + return to_wstring(t_str); + } +#else + template + static std::string to_proper_string(const T &t_str) + { + return to_string(t_str); + } +#endif + + static std::string get_error_message(DWORD t_err) + { + typedef LPTSTR StringType; + +#if defined(_UNICODE) || defined(UNICODE) + std::wstring retval = L"Unknown Error"; +#else + std::string retval = "Unknown Error"; +#endif + StringType lpMsgBuf = nullptr; + + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + t_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&lpMsgBuf), + 0, nullptr ) != 0 && lpMsgBuf) + { + retval = lpMsgBuf; + LocalFree(lpMsgBuf); + } + + return to_string(retval); + } + + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) + { + if (!m_data) + { + throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); + } + } + + ~DLModule() + { + FreeLibrary(m_data); + } + + HMODULE m_data; + }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; + +#else + struct Loadable_Module + { + Loadable_Module(const std::string &, const std::string &) + { + throw chaiscript::exception::load_module_error("Loadable module support not available for your platform"); + } + + ModulePtr m_moduleptr; + }; +#endif +#endif + + typedef std::shared_ptr Loadable_Module_Ptr; + } + + + /// \brief The main object that the ChaiScript user will use. + class ChaiScript { + + mutable chaiscript::detail::threading::shared_mutex m_mutex; + mutable chaiscript::detail::threading::recursive_mutex m_use_mutex; + + std::set m_used_files; + std::map m_loaded_modules; + std::set m_active_loaded_modules; + + std::vector m_module_paths; + std::vector m_use_paths; + + chaiscript::detail::Dispatch_Engine m_engine; + + /// Evaluates the given string in by parsing it and running the results through the evaluator + Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) + { + try { + parser::ChaiScript_Parser parser; + if (parser.parse(t_input, t_filename)) { + //parser.show_match_stack(); + return parser.optimized_ast()->eval(m_engine); + } else { + return Boxed_Value(); + } + } + catch (chaiscript::eval::detail::Return_Value &rv) { + return rv.retval; + } + } + + + + + + /// Evaluates the given file and looks in the 'use' paths + const Boxed_Value internal_eval_file(const std::string &t_filename) { + for (const auto &path : m_use_paths) + { + try { + const auto appendedpath = path + t_filename; + return do_eval(load_file(appendedpath), appendedpath, true); + } catch (const exception::file_not_found_error &) { + // failed to load, try the next path + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } + } + + // failed to load by any name + throw exception::file_not_found_error(t_filename); + + } + + + + /// Evaluates the given string, used during eval() inside of a script + const Boxed_Value internal_eval(const std::string &t_e) { + try { + return do_eval(t_e, "__EVAL__", true); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } + } + + /// Returns the current evaluation m_engine + chaiscript::detail::Dispatch_Engine &get_eval_engine() { + return m_engine; + } + + /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. + void build_eval_system(const ModulePtr &t_lib) { + m_engine.add_reserved_word("def"); + m_engine.add_reserved_word("fun"); + m_engine.add_reserved_word("while"); + m_engine.add_reserved_word("for"); + m_engine.add_reserved_word("if"); + m_engine.add_reserved_word("else"); + m_engine.add_reserved_word("&&"); + m_engine.add_reserved_word("||"); + m_engine.add_reserved_word(","); + m_engine.add_reserved_word("auto"); + m_engine.add_reserved_word("return"); + m_engine.add_reserved_word("break"); + m_engine.add_reserved_word("true"); + m_engine.add_reserved_word("false"); + m_engine.add_reserved_word("class"); + m_engine.add_reserved_word("attr"); + m_engine.add_reserved_word("var"); + m_engine.add_reserved_word("global"); + m_engine.add_reserved_word("GLOBAL"); + m_engine.add_reserved_word("_"); + + if (t_lib) + { + add(t_lib); + } + + m_engine.add(fun([this](){ m_engine.dump_system(); }), "dump_system"); + m_engine.add(fun([this](const Boxed_Value &t_bv){ m_engine.dump_object(t_bv); }), "dump_object"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type){ return m_engine.is_type(t_bv, t_type); }), "is_type"); + m_engine.add(fun([this](const Boxed_Value &t_bv){ return m_engine.type_name(t_bv); }), "type_name"); + m_engine.add(fun([this](const std::string &t_f){ return m_engine.function_exists(t_f); }), "function_exists"); + m_engine.add(fun([this](){ return m_engine.get_function_objects(); }), "get_functions"); + m_engine.add(fun([this](){ return m_engine.get_scripting_objects(); }), "get_objects"); + + m_engine.add( + dispatch::make_dynamic_proxy_function( + [this](const std::vector &t_params) { + return m_engine.call_exists(t_params); + }) + , "call_exists"); + +// m_engine.add(fun &)>(std::bind(&chaiscript::dispatch::Proxy_Function_Base::operator(), std::placeholders::_1, std::placeholders::_2, std::ref(m_engine.conversions()))), "call"); +// +// + + m_engine.add(fun( + [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) -> Boxed_Value { + Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves()); + return t_fun(t_params, s); + }), "call"); + + + m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name"); + + m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type"); + m_engine.add(fun([this](const std::string &t_type_name){ return m_engine.get_type(t_type_name, true); }), "type"); + + m_engine.add(fun( + [=](const Type_Info &t_from, const Type_Info &t_to, const std::function &t_func) { + m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func)); + } + ), "add_type_conversion"); + + + + m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module"); + m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module"); + + m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use"); + m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file"); + m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval"); + m_engine.add(fun([this](const AST_NodePtr &t_ast){ return eval(t_ast); }), "eval"); + m_engine.add(fun(&parse), "parse"); + + m_engine.add(fun(&ChaiScript::version_major), "version_major"); + m_engine.add(fun(&ChaiScript::version_minor), "version_minor"); + m_engine.add(fun(&ChaiScript::version_patch), "version_patch"); + m_engine.add(fun(&ChaiScript::version), "version"); + m_engine.add(fun(&ChaiScript::compiler_version), "compiler_version"); + m_engine.add(fun(&ChaiScript::compiler_name), "compiler_name"); + m_engine.add(fun(&ChaiScript::compiler_id), "compiler_id"); + m_engine.add(fun(&ChaiScript::debug_build), "debug_build"); + + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); + } + + + /// Helper function for loading a file + static std::string load_file(const std::string &t_filename) { + std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary ); + + if (!infile.is_open()) { + throw chaiscript::exception::file_not_found_error(t_filename); + } + + const auto size = infile.tellg(); + infile.seekg(0, std::ios::beg); + + assert(size >= 0); + + if (size == std::streampos(0)) + { + return std::string(); + } else { + std::vector v(static_cast(size)); + infile.read(&v[0], size); + return std::string(v.begin(), v.end()); + } + } + + public: + /// \brief Constructor for ChaiScript + /// \param[in] t_lib Standard library to apply to this ChaiScript instance + /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module + /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file + ChaiScript(const ModulePtr &t_lib, + std::vector t_modulepaths = std::vector(), + std::vector t_usepaths = std::vector()) + : m_module_paths(std::move(t_modulepaths)), m_use_paths(std::move(t_usepaths)) + { + if (m_module_paths.empty()) + { + m_module_paths.push_back(""); + } + + if (m_use_paths.empty()) + { + m_use_paths.push_back(""); + } + + build_eval_system(t_lib); + } + + /// \brief Constructor for ChaiScript. + /// + /// This version of the ChaiScript constructor attempts to find the stdlib module to load + /// at runtime generates an error if it cannot be found. + /// + /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module + /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file + ChaiScript( std::vector t_modulepaths = std::vector(), + std::vector t_usepaths = std::vector()) + : m_module_paths(std::move(t_modulepaths)), m_use_paths(std::move(t_usepaths)) + { + if (m_module_paths.empty()) + { + m_module_paths.push_back(""); + } + + if (m_use_paths.empty()) + { + m_use_paths.push_back(""); + } + +#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) + // If on Unix, add the path of the current executable to the module search path + // as windows would do + + union cast_union + { + Boxed_Value (ChaiScript::*in_ptr)(const std::string&); + void *out_ptr; + }; + + Dl_info rInfo; + memset( &rInfo, 0, sizeof(rInfo) ); + cast_union u; + u.in_ptr = &ChaiScript::use; + if ( dladdr(static_cast(u.out_ptr), &rInfo) && rInfo.dli_fname ) { + std::string dllpath(rInfo.dli_fname); + const size_t lastslash = dllpath.rfind('/'); + if (lastslash != std::string::npos) + { + dllpath.erase(lastslash); + } + + // Let's see if this is a link that we should expand + std::vector buf(2048); + const size_t pathlen = readlink(dllpath.c_str(), &buf.front(), buf.size()); + if (pathlen > 0 && pathlen < buf.size()) + { + dllpath = std::string(&buf.front(), pathlen); + } + + m_module_paths.insert(m_module_paths.begin(), dllpath+"/"); + } +#endif + + + // attempt to load the stdlib + load_module("chaiscript_stdlib-" + version()); + + build_eval_system(ModulePtr()); + } + + + const Boxed_Value eval(const AST_NodePtr &t_ast) + { + try { + return t_ast->eval(m_engine); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } + } + + static AST_NodePtr parse(const std::string &t_input) + { + parser::ChaiScript_Parser parser; + if (parser.parse(t_input, "PARSE")) { + //parser.show_match_stack(); + return parser.optimized_ast(); + } else { + throw chaiscript::exception::eval_error("Unknown error while parsing"); + } + } + + + static int version_major() + { + return chaiscript::version_major; + } + + static int version_minor() + { + return chaiscript::version_minor; + } + + static int version_patch() + { + return chaiscript::version_patch; + } + + static std::string version() + { + return std::to_string(version_major()) + '.' + std::to_string(version_minor()) + '.' + std::to_string(version_patch()); + } + + static std::string compiler_id() + { + return compiler_name() + '-' + compiler_version(); + } + + static std::string build_id() + { + return compiler_id() + (debug_build()?"-Debug":"-Release"); + } + + static std::string compiler_version() + { + return chaiscript::compiler_version; + } + + static std::string compiler_name() + { + return chaiscript::compiler_name; + } + + static bool debug_build() + { + return chaiscript::debug_build; + } + + + + std::string get_type_name(const Type_Info &ti) const + { + return m_engine.get_type_name(ti); + } + + template + std::string get_type_name() const + { + return get_type_name(user_type()); + } + + + /// \brief Loads and parses a file. If the file is already, it is not reloaded + /// The use paths specified at ChaiScript construction time are searched for the + /// requested file. + /// + /// \param[in] t_filename Filename to load and evaluate + Boxed_Value use(const std::string &t_filename) + { + for (const auto &path : m_use_paths) + { + try { + const auto appendedpath = path + t_filename; + + chaiscript::detail::threading::unique_lock l(m_use_mutex); + chaiscript::detail::threading::unique_lock l2(m_mutex); + + Boxed_Value retval; + + if (m_used_files.count(appendedpath) == 0) + { + l2.unlock(); + retval = eval_file(appendedpath); + l2.lock(); + m_used_files.insert(appendedpath); + } + + return retval; // return, we loaded it, or it was already loaded + } catch (const exception::file_not_found_error &) { + // failed to load, try the next path + } + } + + // failed to load by any name + throw exception::file_not_found_error(t_filename); + } + + /// \brief Adds a constant object that is available in all contexts and to all threads + /// \param[in] t_bv Boxed_Value to add as a global + /// \param[in] t_name Name of the value to add + /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object + /// \sa Boxed_Value::is_const + ChaiScript &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) + { + m_engine.add_global_const(t_bv, t_name); + return *this; + } + + /// \brief Adds a mutable object that is available in all contexts and to all threads + /// \param[in] t_bv Boxed_Value to add as a global + /// \param[in] t_name Name of the value to add + /// \warning The user is responsible for making sure the object is thread-safe if necessary + /// ChaiScript is thread-safe but provides no threading locking mechanism to the script + ChaiScript &add_global(const Boxed_Value &t_bv, const std::string &t_name) + { + m_engine.add_global(t_bv, t_name); + return *this; + } + + ChaiScript &set_global(const Boxed_Value &t_bv, const std::string &t_name) + { + m_engine.set_global(t_bv, t_name); + return *this; + } + + /// \brief Represents the current state of the ChaiScript system. State and be saved and restored + /// \warning State object does not contain the user defined type conversions of the engine. They + /// are left out due to performance considerations involved in tracking the state + /// \sa ChaiScript::get_state + /// \sa ChaiScript::set_state + struct State + { + std::set used_files; + chaiscript::detail::Dispatch_Engine::State engine_state; + std::set active_loaded_modules; + }; + + /// \brief Returns a state object that represents the current state of the global system + /// + /// The global system includes the reserved words, global const objects, functions and types. + /// local variables are thread specific and not included. + /// + /// \return Current state of the global system + /// + /// \b Example: + /// + /// \code + /// chaiscript::ChaiScript chai; + /// chaiscript::ChaiScript::State s = chai.get_state(); // represents bootstrapped initial state + /// \endcode + State get_state() const + { + chaiscript::detail::threading::lock_guard l(m_use_mutex); + chaiscript::detail::threading::shared_lock l2(m_mutex); + + State s; + s.used_files = m_used_files; + s.engine_state = m_engine.get_state(); + s.active_loaded_modules = m_active_loaded_modules; + return s; + } + + /// \brief Sets the state of the system + /// + /// The global system includes the reserved words, global objects, functions and types. + /// local variables are thread specific and not included. + /// + /// \param[in] t_state New state to set + /// + /// \b Example: + /// \code + /// chaiscript::ChaiScript chai; + /// chaiscript::ChaiScript::State s = chai.get_state(); // get initial state + /// chai.add(chaiscript::fun(&somefunction), "somefunction"); + /// chai.set_state(s); // restore initial state, which does not have the recently added "somefunction" + /// \endcode + void set_state(const State &t_state) + { + chaiscript::detail::threading::lock_guard l(m_use_mutex); + chaiscript::detail::threading::shared_lock l2(m_mutex); + + m_used_files = t_state.used_files; + m_active_loaded_modules = t_state.active_loaded_modules; + m_engine.set_state(t_state.engine_state); + } + + /// \returns All values in the local thread state, added through the add() function + std::map get_locals() const + { + return m_engine.get_locals(); + } + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals) + { + m_engine.set_locals(t_locals); + } + + /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state. + /// \param[in] t_t Item to add + /// \param[in] t_name Name of item to add + /// \returns Reference to current ChaiScript object + /// + /// \b Examples: + /// \code + /// chaiscript::ChaiScript chai; + /// chai.add(chaiscript::user_type(), "MyClass"); // Add explicit type info (not strictly necessary) + /// chai.add(chaiscript::fun(&MyClass::function), "function"); // Add a class method + /// MyClass obj; + /// chai.add(chaiscript::var(&obj), "obj"); // Add a pointer to a locally defined object + /// \endcode + /// + /// \sa \ref adding_items + template + ChaiScript &add(const T &t_t, const std::string &t_name) + { + m_engine.add(t_t, t_name); + return *this; + } + + /// \brief Add a new conversion for upcasting to a base class + /// \sa chaiscript::base_class + /// \param[in] d Base class / parent class + /// + /// \b Example: + /// \code + /// chaiscript::ChaiScript chai; + /// chai.add(chaiscript::base_class()); + /// \endcode + ChaiScript &add(const Type_Conversion &d) + { + m_engine.add(d); + return *this; + } + + /// \brief Adds all elements of a module to ChaiScript runtime + /// \param[in] t_p The module to add. + /// \sa chaiscript::Module + ChaiScript &add(const ModulePtr &t_p) + { + t_p->apply(*this, this->get_eval_engine()); + return *this; + } + + /// \brief Load a binary module from a dynamic library. Works on platforms that support + /// dynamic libraries. + /// \param[in] t_module_name Name of the module to load + /// + /// The module is searched for in the registered module path folders (chaiscript::ChaiScript::ChaiScript) + /// and with standard prefixes and postfixes: ("lib"|"")\(".dll"|".so"|".bundle"|""). + /// + /// Once the file is located, the system looks for the symbol "create_chaiscript_module_\". + /// If no file can be found matching the search criteria and containing the appropriate entry point + /// (the symbol mentioned above), an exception is thrown. + /// + /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found. + std::string load_module(const std::string &t_module_name) + { + std::vector errors; + std::string version_stripped_name = t_module_name; + size_t version_pos = version_stripped_name.find("-"+version()); + if (version_pos != std::string::npos) + { + version_stripped_name.erase(version_pos); + } + + std::vector prefixes{"lib", "cyg", ""}; + + std::vector postfixes{".dll", ".so", ".bundle", ""}; + + for (auto & elem : m_module_paths) + { + for (auto & prefix : prefixes) + { + for (auto & postfix : postfixes) + { + try { + const auto name = elem + prefix + t_module_name + postfix; + // std::cerr << "trying location: " << name << '\n'; + load_module(version_stripped_name, name); + return name; + } catch (const chaiscript::exception::load_module_error &e) { + // std::cerr << "error: " << e.what() << '\n'; + errors.push_back(e); + // Try next set + } + } + } + } + + std::string errstring; + + for (std::vector::const_iterator itr = errors.begin(); + itr != errors.end(); + ++itr) + { + if (!errstring.empty()) + { + errstring += "; "; + } + + errstring += itr->what(); + } + + throw chaiscript::exception::load_module_error("Unable to find module: " + t_module_name + " Errors: " + errstring); + } + + /// \brief Load a binary module from a dynamic library. Works on platforms that support + /// dynamic libraries. + /// + /// \param[in] t_module_name Module name to load + /// \param[in] t_filename Ignore normal filename search process and use specific filename + /// + /// \sa ChaiScript::load_module(const std::string &t_module_name) + void load_module(const std::string &t_module_name, const std::string &t_filename) + { + chaiscript::detail::threading::lock_guard l(m_use_mutex); + + if (m_loaded_modules.count(t_module_name) == 0) + { + detail::Loadable_Module_Ptr lm(new detail::Loadable_Module(t_module_name, t_filename)); + m_loaded_modules[t_module_name] = lm; + m_active_loaded_modules.insert(t_module_name); + add(lm->m_moduleptr); + } else if (m_active_loaded_modules.count(t_module_name) == 0) { + m_active_loaded_modules.insert(t_module_name); + add(m_loaded_modules[t_module_name]->m_moduleptr); + } + } + + + /// \brief Evaluates a string. Equivalent to ChaiScript::eval. + /// + /// \param[in] t_script Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions + /// + /// \return result of the script execution + /// + /// \throw chaiscript::exception::eval_error In the case that evaluation fails. + Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler()) + { + try { + return do_eval(t_script); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } + } + + /// \brief Evaluates a string and returns a typesafe result. + /// + /// \tparam T Type to extract from the result value of the script execution + /// \param[in] t_input Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions + /// \param[in] t_filename Optional filename to report to the user for where the error occured. Useful + /// in special cases where you are loading a file internally instead of using eval_file + /// + /// \return result of the script execution + /// + /// \throw chaiscript::exception::eval_error In the case that evaluation fails. + /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted + /// to the requested type. + template + T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__") + { + try { + return m_engine.boxed_cast(do_eval(t_input, t_filename)); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } + } + + /// \brief casts an object while applying any Dynamic_Conversion available + template + typename detail::Cast_Helper::Result_Type boxed_cast(const Boxed_Value &bv) const + { + return m_engine.boxed_cast(bv); + } + + + /// \brief Evaluates a string. + /// + /// \param[in] t_input Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions + /// \param[in] t_filename Optional filename to report to the user for where the error occurred. Useful + /// in special cases where you are loading a file internally instead of using eval_file + /// + /// \return result of the script execution + /// + /// \throw exception::eval_error In the case that evaluation fails. + Boxed_Value eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__") + { + try { + return do_eval(t_input, t_filename); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } + } + + /// \brief Loads the file specified by filename, evaluates it, and returns the result. + /// \param[in] t_filename File to load and parse. + /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions + /// \return result of the script execution + /// \throw chaiscript::exception::eval_error In the case that evaluation fails. + Boxed_Value eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) { + try { + return do_eval(load_file(t_filename), t_filename); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } + } + + /// \brief Loads the file specified by filename, evaluates it, and returns the type safe result. + /// \tparam T Type to extract from the result value of the script execution + /// \param[in] t_filename File to load and parse. + /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions + /// \return result of the script execution + /// \throw chaiscript::exception::eval_error In the case that evaluation fails. + /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted + /// to the requested type. + template + T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) { + try { + return m_engine.boxed_cast(do_eval(load_file(t_filename), t_filename)); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } + } + }; + +} +#endif /* CHAISCRIPT_ENGINE_HPP_ */ + diff --git a/apps/common/script/chaiscript/language/chaiscript_eval.hpp b/apps/common/script/chaiscript/language/chaiscript_eval.hpp new file mode 100644 index 0000000000..60474b4af2 --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_eval.hpp @@ -0,0 +1,1522 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_EVAL_HPP_ +#define CHAISCRIPT_EVAL_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../chaiscript_defines.hpp" +#include "../dispatchkit/boxed_cast.hpp" +#include "../dispatchkit/boxed_number.hpp" +#include "../dispatchkit/boxed_value.hpp" +#include "../dispatchkit/dispatchkit.hpp" +#include "../dispatchkit/dynamic_object_detail.hpp" +#include "../dispatchkit/proxy_functions.hpp" +#include "../dispatchkit/proxy_functions_detail.hpp" +#include "../dispatchkit/register_function.hpp" +#include "../dispatchkit/type_info.hpp" +#include "chaiscript_algebraic.hpp" +#include "chaiscript_common.hpp" + +namespace chaiscript { +namespace exception { +class bad_boxed_cast; +} // namespace exception +} // namespace chaiscript + +namespace chaiscript +{ + /// \brief Classes and functions that are part of the runtime eval system + namespace eval + { + namespace detail + { + /// Helper function that will set up the scope around a function call, including handling the named function parameters + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_NodePtr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr) { + chaiscript::detail::Dispatch_State state(t_ss); + + const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ + auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); + if (!stack.empty() && stack.back().first == "__this") { + return &stack.back().second; + } else if (!t_vals.empty()) { + return &t_vals[0]; + } else { + return nullptr; + } + }(); + + chaiscript::eval::detail::Stack_Push_Pop tpp(state); + if (thisobj) state.add_object("this", *thisobj); + + if (t_locals) { + for (const auto &local : *t_locals) { + state.add_object(local.first, local.second); + } + } + + for (size_t i = 0; i < t_param_names.size(); ++i) { + if (t_param_names[i] != "this") { + state.add_object(t_param_names[i], t_vals[i]); + } + } + + try { + return t_node->eval(state); + } catch (detail::Return_Value &rv) { + return std::move(rv.retval); + } + } + } + + struct Binary_Operator_AST_Node : AST_Node { + public: + Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector t_children) : + AST_Node(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(t_oper)) + { } + + virtual ~Binary_Operator_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + auto lhs = this->children[0]->eval(t_ss); + auto rhs = this->children[1]->eval(t_ss); + return do_oper(t_ss, m_oper, text, lhs, rhs); + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "(" + this->children[0]->pretty_print() + " " + text + " " + this->children[1]->pretty_print() + ")"; + } + + protected: + Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, + Operators::Opers t_oper, const std::string &t_oper_string, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) const + { + try { + if (t_oper != Operators::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) + { + // If it's an arithmetic operation we want to short circuit dispatch + try{ + return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs); + } catch (const chaiscript::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); + } + } else { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + fpp.save_params({t_lhs, t_rhs}); + return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}, t_ss.conversions()); + } + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } + + private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Int_AST_Node : public AST_Node { + public: + Int_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_bv) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Int, std::move(t_loc)), + m_value(std::move(t_bv)) { assert(text != ""); } + virtual ~Int_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + return m_value; + } + + private: + Boxed_Value m_value; + + }; + + struct Float_AST_Node : public AST_Node { + public: + Float_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_bv) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Float, std::move(t_loc)), + m_value(std::move(t_bv)) { } + virtual ~Float_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + return m_value; + } + + private: + Boxed_Value m_value; + + }; + + struct Id_AST_Node : public AST_Node { + public: + Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) : + AST_Node(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)), + m_value(get_value(t_ast_node_text)), m_loc(0) + { } + + virtual ~Id_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + if (!m_value.is_undef()) + { + return m_value; + } else { + try { + return t_ss->get_object(this->text, m_loc); + } + catch (std::exception &) { + throw exception::eval_error("Can not find object: " + this->text); + } + } + } + + private: + static Boxed_Value get_value(const std::string &t_text) + { + if (t_text == "true") { + return const_var(true); + } else if (t_text == "false") { + return const_var(false); + } else if (t_text == "Infinity") { + return const_var(std::numeric_limits::infinity()); + } else if (t_text == "NaN") { + return const_var(std::numeric_limits::quiet_NaN()); + } else if (t_text == "_") { + return Boxed_Value(std::make_shared()); + } else { + return Boxed_Value(); + } + } + + Boxed_Value m_value; + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Char_AST_Node : public AST_Node { + public: + Char_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Char, std::move(t_loc)) { } + virtual ~Char_AST_Node() {} + }; + + struct Str_AST_Node : public AST_Node { + public: + Str_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Str, std::move(t_loc)) { } + virtual ~Str_AST_Node() {} + }; + + struct Eol_AST_Node : public AST_Node { + public: + Eol_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Eol, std::move(t_loc)) { } + virtual ~Eol_AST_Node() {} + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "\n"; + } + }; + + + struct Fun_Call_AST_Node : public AST_Node { + public: + Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { } + virtual ~Fun_Call_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params; + + params.reserve(this->children[1]->children.size()); + for (const auto &child : this->children[1]->children) { + params.push_back(child->eval(t_ss)); + } + + fpp.save_params(params); + + Boxed_Value fn(this->children[0]->eval(t_ss)); + + try { + return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss); + } + catch(const exception::bad_boxed_cast &){ + try { + Const_Proxy_Function f = t_ss->boxed_cast(fn); + // handle the case where there is only 1 function to try to call and dispatch fails on it + throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, *t_ss); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); + } + } + catch(const exception::arity_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(const exception::guard_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; + } + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + + int count = 0; + for (const auto &child : this->children) { + oss << child->pretty_print(); + + if (count == 0) + { + oss << "("; + } + ++count; + } + + oss << ")"; + + return oss.str(); + } + + }; + + + + struct Arg_AST_Node : public AST_Node { + public: + Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } + virtual ~Arg_AST_Node() {} + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + for (size_t j = 0; j < this->children.size(); ++j) { + if (j != 0) + { + oss << " "; + } + + oss << this->children[j]->pretty_print(); + } + + return oss.str(); + } + }; + + struct Arg_List_AST_Node : public AST_Node { + public: + Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } + virtual ~Arg_List_AST_Node() {} + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + for (size_t j = 0; j < this->children.size(); ++j) { + if (j != 0) + { + oss << ", "; + } + + oss << this->children[j]->pretty_print(); + } + + return oss.str(); + } + + static std::string get_arg_name(const AST_NodePtr &t_node) { + if (t_node->children.empty()) + { + return t_node->text; + } else if (t_node->children.size() == 1) { + return t_node->children[0]->text; + } else { + return t_node->children[1]->text; + } + } + + static std::vector get_arg_names(const AST_NodePtr &t_node) { + std::vector retval; + + for (const auto &node : t_node->children) + { + retval.push_back(get_arg_name(node)); + } + + return retval; + } + + static std::pair get_arg_type(const AST_NodePtr &t_node, const chaiscript::detail::Dispatch_State &t_ss) + { + if (t_node->children.size() < 2) + { + return std::pair(); + } else { + return std::pair(t_node->children[0]->text, t_ss->get_type(t_node->children[0]->text, false)); + } + } + + static dispatch::Param_Types get_arg_types(const AST_NodePtr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + std::vector> retval; + + for (const auto &child : t_node->children) + { + retval.push_back(get_arg_type(child, t_ss)); + } + + return dispatch::Param_Types(std::move(retval)); + } + }; + + struct Equation_AST_Node : public AST_Node { + public: + Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(children[1]->text)) + { assert(children.size() == 3); } + + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_clone_loc; + + virtual ~Equation_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + Boxed_Value rhs = this->children[2]->eval(t_ss); + Boxed_Value lhs = this->children[0]->eval(t_ss); + + if (m_oper != Operators::invalid && lhs.get_type_info().is_arithmetic() && + rhs.get_type_info().is_arithmetic()) + { + try { + return Boxed_Number::do_oper(m_oper, lhs, rhs); + } catch (const std::exception &) { + throw exception::eval_error("Error with unsupported arithmetic assignment operation"); + } + } else if (m_oper == Operators::assign) { + if (lhs.is_return_value()) { + throw exception::eval_error("Error, cannot assign to temporary value."); + } + + try { + + if (lhs.is_undef()) { + if (!this->children.empty() && + !this->children[0]->children.empty() + && this->children[0]->children[0]->identifier == AST_Node_Type::Reference) + { + /// \todo This does not handle the case of an unassigned reference variable + /// being assigned outside of its declaration + lhs.assign(rhs); + lhs.reset_return_value(); + return rhs; + } else { + if (!rhs.is_return_value()) + { + rhs = t_ss->call_function("clone", m_clone_loc, {rhs}, t_ss.conversions()); + } + rhs.reset_return_value(); + } + } + + try { + return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Missing clone or copy constructor for right hand side of equation", e.parameters, e.functions, false, *t_ss); + } + } + else if (this->children[1]->text == ":=") { + if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) { + lhs.assign(rhs); + lhs.reset_return_value(); + } else { + throw exception::eval_error("Mismatched types in equation"); + } + } + else { + try { + return t_ss->call_function(this->children[1]->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); + } catch(const exception::dispatch_error &e){ + throw exception::eval_error("Unable to find appropriate'" + this->children[1]->text + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } + + return rhs; + } + }; + + struct Global_Decl_AST_Node : public AST_Node { + public: + Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { } + virtual ~Global_Decl_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + + const std::string &idname = + [&]()->const std::string &{ + if (children[0]->identifier == AST_Node_Type::Reference) { + return children[0]->children[0]->text; + } else { + return children[0]->text; + } + }(); + + try { + return t_ss->add_global_no_throw(Boxed_Value(), idname); + } + catch (const exception::reserved_word_error &) { + throw exception::eval_error("Reserved word used as global '" + idname + "'"); + } + + } + }; + + + struct Var_Decl_AST_Node : public AST_Node { + public: + Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { } + virtual ~Var_Decl_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + if (this->children[0]->identifier == AST_Node_Type::Reference) + { + return this->children[0]->eval(t_ss); + } else { + const std::string &idname = this->children[0]->text; + + try { + Boxed_Value bv; + t_ss.add_object(idname, bv); + return bv; + } + catch (const exception::reserved_word_error &) { + throw exception::eval_error("Reserved word used as variable '" + idname + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); + } + } + + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "var " + this->children[0]->text; + } + + }; + + + struct Array_Call_AST_Node : public AST_Node { + public: + Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { } + virtual ~Array_Call_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params{children[0]->eval(t_ss), children[1]->eval(t_ss)}; + + try { + fpp.save_params(params); + return t_ss->call_function("[]", m_loc, params, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); + } + + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + std::ostringstream oss; + oss << this->children[0]->pretty_print(); + + for (size_t i = 1; i < this->children.size(); ++i) + { + oss << "["; + oss << this->children[i]->pretty_print(); + oss << "]"; + } + + return oss.str(); + } + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Dot_Access_AST_Node : public AST_Node { + public: + Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)), + m_fun_name( + ((children[2]->identifier == AST_Node_Type::Fun_Call) || (children[2]->identifier == AST_Node_Type::Array_Call))? + children[2]->children[0]->text:children[2]->text) { } + + virtual ~Dot_Access_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + + + Boxed_Value retval = children[0]->eval(t_ss); + std::vector params{retval}; + + bool has_function_params = false; + if (children[2]->children.size() > 1) { + has_function_params = true; + for (const auto &child : children[2]->children[1]->children) { + params.push_back(child->eval(t_ss)); + } + } + + fpp.save_params(params); + + try { + retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + if (e.functions.empty()) + { + throw exception::eval_error("'" + m_fun_name + "' is not a function."); + } else { + throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss); + } + } + catch(detail::Return_Value &rv) { + retval = std::move(rv.retval); + } + + if (this->children[2]->identifier == AST_Node_Type::Array_Call) { + try { + retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[2]->children[1]->eval(t_ss)}, t_ss.conversions()); + } + catch(const exception::dispatch_error &e){ + throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); + } + } + + return retval; + } + + private: + mutable std::atomic_uint_fast32_t m_loc; + mutable std::atomic_uint_fast32_t m_array_loc; + std::string m_fun_name; + }; + + struct Quoted_String_AST_Node : public AST_Node { + public: + Quoted_String_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Quoted_String, std::move(t_loc)), + m_value(const_var(text)) { } + virtual ~Quoted_String_AST_Node() {} + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE { + return m_value; + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "\"" + text + "\""; + } + + private: + Boxed_Value m_value; + + }; + + struct Single_Quoted_String_AST_Node : public AST_Node { + public: + Single_Quoted_String_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Single_Quoted_String, std::move(t_loc)), + m_value(const_var(char(text.at(0)))) { } + + virtual ~Single_Quoted_String_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + return m_value; + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "'" + text + "'"; + } + + private: + Boxed_Value m_value; + }; + + struct Lambda_AST_Node : public AST_Node { + public: + Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)), + m_param_names(Arg_List_AST_Node::get_arg_names(children[1])) { } + + virtual ~Lambda_AST_Node() {} + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + + const auto captures = [&]()->std::map{ + std::map named_captures; + for (const auto &capture : children[0]->children) { + named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss))); + } + return named_captures; + }(); + + const auto numparams = this->children[1]->children.size(); + const auto param_names = m_param_names; + const auto param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); + + const auto &lambda_node = this->children.back(); + std::reference_wrapper engine(*t_ss); + + return Boxed_Value( + dispatch::make_dynamic_proxy_function( + [engine, lambda_node, param_names, captures](const std::vector &t_params) + { + return detail::eval_function(engine, lambda_node, param_names, t_params, &captures); + }, + static_cast(numparams), lambda_node, param_types + ) + ); + } + + private: + std::vector m_param_names; + + }; + + struct Block_AST_Node : public AST_Node { + public: + Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { } + virtual ~Block_AST_Node() {} + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + const auto num_children = children.size(); + for (size_t i = 0; i < num_children-1; ++i) { + children[i]->eval(t_ss); + } + return children.back()->eval(t_ss); + + } + }; + + struct Def_AST_Node : public AST_Node { + public: + Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::move(t_children)) { } + + virtual ~Def_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + std::vector t_param_names; + size_t numparams = 0; + AST_NodePtr guardnode; + + dispatch::Param_Types param_types; + + if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { + numparams = this->children[1]->children.size(); + t_param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); + param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); + + if (this->children.size() > 3) { + guardnode = this->children[2]; + } + } + else { + //no parameters + numparams = 0; + + if (this->children.size() > 2) { + guardnode = this->children[1]; + } + } + + std::reference_wrapper engine(*t_ss); + std::shared_ptr guard; + if (guardnode) { + guard = dispatch::make_dynamic_proxy_function( + [engine, guardnode, t_param_names](const std::vector &t_params) + { + return detail::eval_function(engine, guardnode, t_param_names, t_params); + }, + static_cast(numparams), guardnode); + } + + try { + const std::string & l_function_name = this->children[0]->text; + const std::string & l_annotation = this->annotation?this->annotation->text:""; + const auto & func_node = this->children.back(); + t_ss->add( + dispatch::make_dynamic_proxy_function( + [engine, guardnode, func_node, t_param_names](const std::vector &t_params) + { + return detail::eval_function(engine, func_node, t_param_names, t_params); + }, + static_cast(numparams), this->children.back(), + param_types, l_annotation, guard), l_function_name); + } + catch (const exception::reserved_word_error &e) { + throw exception::eval_error("Reserved word used as function name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Function redefined '" + e.name() + "'"); + } + return Boxed_Value(); + } + + }; + + struct While_AST_Node : public AST_Node { + public: + While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { } + virtual ~While_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + while (get_bool_condition(this->children[0]->eval(t_ss))) { + try { + this->children[1]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next condition test + } + } + } catch (detail::Break_Loop &) { + // loop was broken intentionally + } + + return Boxed_Value(); + } + }; + + struct Class_AST_Node : public AST_Node { + public: + Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { } + virtual ~Class_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + /// \todo do this better + // put class name in current scope so it can be looked up by the attrs and methods + t_ss.add_object("_current_class_name", const_var(children[0]->text)); + + children[1]->eval(t_ss); + + return Boxed_Value(); + } + }; + + struct Ternary_Cond_AST_Node : public AST_Node { + public: + Ternary_Cond_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 3); } + virtual ~Ternary_Cond_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + if (get_bool_condition(children[0]->eval(t_ss))) { + return children[1]->eval(t_ss); + } + else { + return children[2]->eval(t_ss); + } + } + + }; + + struct If_AST_Node : public AST_Node { + public: + If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) { } + virtual ~If_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + + if (get_bool_condition(children[0]->eval(t_ss))) { + return children[1]->eval(t_ss); + } else { + if (children.size() > 2) { + size_t i = 2; + while (i < children.size()) { + if (children[i]->text == "else") { + return children[i+1]->eval(t_ss); + } + else if (children[i]->text == "else if") { + if (get_bool_condition(children[i+1]->eval(t_ss))) { + return children[i+2]->eval(t_ss); + } + } + i += 3; + } + } + } + + return Boxed_Value(); + } + + }; + + struct For_AST_Node : public AST_Node { + public: + For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 4); } + virtual ~For_AST_Node() {} + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + for ( + children[0]->eval(t_ss); + get_bool_condition(children[1]->eval(t_ss)); + children[2]->eval(t_ss) + ) { + try { + // Body of Loop + children[3]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next iteration step + } + } + } catch (detail::Break_Loop &) { + // loop broken + } + + return Boxed_Value(); + } + + }; + + struct Switch_AST_Node : public AST_Node { + public: + Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { } + virtual ~Switch_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + bool breaking = false; + size_t currentCase = 1; + bool hasMatched = false; + + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + Boxed_Value match_value(this->children[0]->eval(t_ss)); + + while (!breaking && (currentCase < this->children.size())) { + try { + if (this->children[currentCase]->identifier == AST_Node_Type::Case) { + //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. + try { + if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}, t_ss.conversions()))) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; + } + } + catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Internal error: case guard evaluation not boolean"); + } + } + else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; + } + } + catch (detail::Break_Loop &) { + breaking = true; + } + ++currentCase; + } + return Boxed_Value(); + } + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Case_AST_Node : public AST_Node { + public: + Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 2); /* how many children does it have? */ } + + virtual ~Case_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + children[1]->eval(t_ss); + + return Boxed_Value(); + } + }; + + struct Default_AST_Node : public AST_Node { + public: + Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 1); } + virtual ~Default_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + children[0]->eval(t_ss); + + return Boxed_Value(); + } + }; + + + struct Inline_Array_AST_Node : public AST_Node { + public: + Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { } + virtual ~Inline_Array_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + try { + std::vector vec; + if (!children.empty()) { + vec.reserve(children[0]->children.size()); + for (const auto &child : children[0]->children) { + auto obj = child->eval(t_ss); + if (!obj.is_return_value()) { + vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions())); + } else { + vec.push_back(std::move(obj)); + } + } + } + return const_var(std::move(vec)); + } + catch (const exception::dispatch_error &) { + throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements"); + } + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "[" + AST_Node::pretty_print() + "]"; + } + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Inline_Map_AST_Node : public AST_Node { + public: + Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { } + virtual ~Inline_Map_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + try { + std::map retval; + + for (const auto &child : children[0]->children) { + auto obj = child->children[1]->eval(t_ss); + if (!obj.is_return_value()) { + obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()); + } + + retval[t_ss->boxed_cast(child->children[0]->eval(t_ss))] = std::move(obj); + } + + return const_var(std::move(retval)); + } + catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.", e.parameters, e.functions, false, *t_ss); + } + } + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Return_AST_Node : public AST_Node { + public: + Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { } + virtual ~Return_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + if (!this->children.empty()) { + throw detail::Return_Value(children[0]->eval(t_ss)); + } + else { + throw detail::Return_Value(Boxed_Value()); + } + } + + }; + + struct File_AST_Node : public AST_Node { + public: + File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { } + virtual ~File_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE { + try { + const auto num_children = children.size(); + + if (num_children > 0) { + for (size_t i = 0; i < num_children-1; ++i) { + children[i]->eval(t_ss); + } + return children.back()->eval(t_ss); + } else { + return Boxed_Value(); + } + } catch (const detail::Continue_Loop &) { + throw exception::eval_error("Unexpected `continue` statement outside of a loop"); + } catch (const detail::Break_Loop &) { + throw exception::eval_error("Unexpected `break` statement outside of a loop"); + } + } + }; + + struct Reference_AST_Node : public AST_Node { + public: + Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 1); } + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + try { + Boxed_Value bv; + t_ss.add_object(this->children[0]->text, bv); + return bv; + } + catch (const exception::reserved_word_error &) { + throw exception::eval_error("Reserved word used as variable '" + this->children[0]->text + "'"); + } + } + + virtual ~Reference_AST_Node() {} + }; + + struct Prefix_AST_Node : public AST_Node { + public: + Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(children[0]->text, true)) + { } + + virtual ~Prefix_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + Boxed_Value bv(children[1]->eval(t_ss)); + + try { + // short circuit arithmetic operations + if (m_oper != Operators::invalid && m_oper != Operators::bitwise_and && bv.get_type_info().is_arithmetic()) + { + return Boxed_Number::do_oper(m_oper, bv); + } else { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + fpp.save_params({bv}); + return t_ss->call_function(children[0]->text, m_loc, {std::move(bv)}, t_ss.conversions()); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Error with prefix operator evaluation: '" + children[0]->text + "'", e.parameters, e.functions, false, *t_ss); + } + } + + private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Break_AST_Node : public AST_Node { + public: + Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { } + virtual ~Break_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + throw detail::Break_Loop(); + } + }; + + struct Continue_AST_Node : public AST_Node { + public: + Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { } + virtual ~Continue_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + throw detail::Continue_Loop(); + } + }; + + struct Noop_AST_Node : public AST_Node { + public: + Noop_AST_Node() : + AST_Node("", AST_Node_Type::Noop, Parse_Location()), + m_value(const_var(true)) + { } + + virtual ~Noop_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const CHAISCRIPT_OVERRIDE{ + // It's a no-op, that evaluates to "true" + return m_value; + } + + private: + Boxed_Value m_value; + }; + + struct Map_Pair_AST_Node : public AST_Node { + public: + Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { } + virtual ~Map_Pair_AST_Node() {} + }; + + struct Value_Range_AST_Node : public AST_Node { + public: + Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { } + virtual ~Value_Range_AST_Node() {} + }; + + struct Inline_Range_AST_Node : public AST_Node { + public: + Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { } + virtual ~Inline_Range_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + try { + auto oper1 = children[0]->children[0]->children[0]->eval(t_ss); + auto oper2 = children[0]->children[0]->children[1]->eval(t_ss); + return t_ss->call_function("generate_range", m_loc, {oper1, oper2}, t_ss.conversions()); + } + catch (const exception::dispatch_error &e) { + throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); + } + } + + mutable std::atomic_uint_fast32_t m_loc; + }; + + struct Annotation_AST_Node : public AST_Node { + public: + Annotation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Annotation, std::move(t_loc)) { } + virtual ~Annotation_AST_Node() {} + }; + + struct Try_AST_Node : public AST_Node { + public: + Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { } + virtual ~Try_AST_Node() {} + + Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const + { + Boxed_Value retval; + + size_t end_point = this->children.size(); + if (this->children.back()->identifier == AST_Node_Type::Finally) { + assert(end_point > 0); + end_point = this->children.size() - 1; + } + for (size_t i = 1; i < end_point; ++i) { + chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); + AST_NodePtr catch_block = this->children[i]; + + if (catch_block->children.size() == 1) { + //No variable capture, no guards + retval = catch_block->children[0]->eval(t_ss); + break; + } else if (catch_block->children.size() == 2 || catch_block->children.size() == 3) { + const auto name = Arg_List_AST_Node::get_arg_name(catch_block->children[0]); + + if (dispatch::Param_Types( + std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} + ).match(std::vector{t_except}, t_ss.conversions())) + { + t_ss.add_object(name, t_except); + + if (catch_block->children.size() == 2) { + //Variable capture, no guards + retval = catch_block->children[1]->eval(t_ss); + break; + } + else if (catch_block->children.size() == 3) { + //Variable capture, guards + + bool guard = false; + try { + guard = boxed_cast(catch_block->children[1]->eval(t_ss)); + } catch (const exception::bad_boxed_cast &) { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw exception::eval_error("Guard condition not boolean"); + } + if (guard) { + retval = catch_block->children[2]->eval(t_ss); + break; + } + } + } + } + else { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw exception::eval_error("Internal error: catch block size unrecognized"); + } + } + + return retval; + } + + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + Boxed_Value retval; + + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + + try { + retval = this->children[0]->eval(t_ss); + } + catch (exception::eval_error &) { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw; + } + catch (const std::runtime_error &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } + catch (const std::out_of_range &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } + catch (const std::exception &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } + catch (Boxed_Value &e) { + retval = handle_exception(t_ss, e); + } + catch (...) { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw; + } + + + if (this->children.back()->identifier == AST_Node_Type::Finally) { + retval = this->children.back()->children[0]->eval(t_ss); + } + + return retval; + } + + }; + + struct Catch_AST_Node : public AST_Node { + public: + Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { } + virtual ~Catch_AST_Node() {} + }; + + struct Finally_AST_Node : public AST_Node { + public: + Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { } + virtual ~Finally_AST_Node() {} + }; + + struct Method_AST_Node : public AST_Node { + public: + Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::move(t_children)) { } + virtual ~Method_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + + AST_NodePtr guardnode; + + const auto d = t_ss->get_parent_locals(); + const auto itr = d.find("_current_class_name"); + const auto class_offset = (itr != d.end())?-1:0; + const std::string & class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + + //The first param of a method is always the implied this ptr. + std::vector t_param_names{"this"}; + dispatch::Param_Types param_types; + + if ((children.size() > static_cast(3 + class_offset)) && (children[static_cast(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) { + auto args = Arg_List_AST_Node::get_arg_names(children[static_cast(2 + class_offset)]); + t_param_names.insert(t_param_names.end(), args.begin(), args.end()); + param_types = Arg_List_AST_Node::get_arg_types(children[static_cast(2 + class_offset)], t_ss); + + if (children.size() > static_cast(4 + class_offset)) { + guardnode = children[static_cast(3 + class_offset)]; + } + } + else { + //no parameters + + if (children.size() > static_cast(3 + class_offset)) { + guardnode = children[static_cast(2 + class_offset)]; + } + } + + const size_t numparams = t_param_names.size(); + + std::shared_ptr guard; + std::reference_wrapper engine(*t_ss); + if (guardnode) { + guard = dispatch::make_dynamic_proxy_function( + [engine, t_param_names, guardnode](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params); + }, + static_cast(numparams), guardnode); + } + + try { + const std::string & l_annotation = annotation?annotation->text:""; + const std::string & function_name = children[static_cast(1 + class_offset)]->text; + auto node = children.back(); + + if (function_name == class_name) { + param_types.push_front(class_name, Type_Info()); + + t_ss->add( + std::make_shared(class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + }, + static_cast(numparams), node, param_types, l_annotation, guard + ) + ), + function_name); + + } else { + // if the type is unknown, then this generates a function that looks up the type + // at runtime. Defining the type first before this is called is better + auto type = t_ss->get_type(class_name, false); + param_types.push_front(class_name, type); + + t_ss->add(std::make_shared(class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + }, + static_cast(numparams), node, param_types, l_annotation, guard), type), + function_name); + } + } + catch (const exception::reserved_word_error &e) { + throw exception::eval_error("Reserved word used as method name '" + e.word() + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Method redefined '" + e.name() + "'"); + } + return Boxed_Value(); + } + + }; + + struct Attr_Decl_AST_Node : public AST_Node { + public: + Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { } + virtual ~Attr_Decl_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE + { + const auto &d = t_ss->get_parent_locals(); + const auto itr = d.find("_current_class_name"); + const auto class_offset = (itr != d.end())?-1:0; + std::string class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + + try { + std::string attr_name = this->children[static_cast(1 + class_offset)]->text; + + t_ss->add( + std::make_shared( + std::move(class_name), + fun([attr_name](dispatch::Dynamic_Object &t_obj) { + return t_obj.get_attr(attr_name); + }), + true + + ), this->children[static_cast(1 + class_offset)]->text); + + } + catch (const exception::reserved_word_error &) { + throw exception::eval_error("Reserved word used as attribute '" + this->children[static_cast(1 + class_offset)]->text + "'"); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Attribute redefined '" + e.name() + "'"); + } + return Boxed_Value(); + } + + }; + + + struct Logical_And_AST_Node : public AST_Node { + public: + Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 3); } + + virtual ~Logical_And_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + return const_var(get_bool_condition(children[0]->eval(t_ss)) + && get_bool_condition(children[2]->eval(t_ss))); + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "(" + AST_Node::pretty_print() + ")"; + } + }; + + struct Logical_Or_AST_Node : public AST_Node { + public: + Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector t_children) : + AST_Node(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) + { assert(children.size() == 3); } + virtual ~Logical_Or_AST_Node() {} + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const CHAISCRIPT_OVERRIDE{ + return const_var(get_bool_condition(children[0]->eval(t_ss)) + || get_bool_condition(children[2]->eval(t_ss))); + } + + virtual std::string pretty_print() const CHAISCRIPT_OVERRIDE + { + return "(" + AST_Node::pretty_print() + ")"; + } + + }; + } + + +} +#endif /* CHAISCRIPT_EVAL_HPP_ */ diff --git a/apps/common/script/chaiscript/language/chaiscript_parser.hpp b/apps/common/script/chaiscript/language/chaiscript_parser.hpp new file mode 100644 index 0000000000..0bfabe56fd --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_parser.hpp @@ -0,0 +1,2447 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_PARSER_HPP_ +#define CHAISCRIPT_PARSER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include "../dispatchkit/boxed_value.hpp" +#include "chaiscript_common.hpp" + + +#if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) +#pragma push_macro("max") // Why Microsoft? why? This is worse than bad +#undef max +#pragma push_macro("min") +#undef min +#endif + + +namespace chaiscript +{ + /// \brief Classes and functions used during the parsing process. + namespace parser + { + /// \brief Classes and functions internal to the parsing process. Not supported for the end user. + namespace detail + { + enum Alphabet + { symbol_alphabet = 0 + , keyword_alphabet + , int_alphabet + , float_alphabet + , x_alphabet + , hex_alphabet + , b_alphabet + , bin_alphabet + , id_alphabet + , white_alphabet + , int_suffix_alphabet + , float_suffix_alphabet + , max_alphabet + , lengthof_alphabet = 256 + }; + } + + class ChaiScript_Parser { + + std::string m_multiline_comment_begin; + std::string m_multiline_comment_end; + std::string m_singleline_comment; + std::shared_ptr m_filename; + std::vector m_match_stack; + bool m_alphabet[detail::max_alphabet][detail::lengthof_alphabet]; + + std::vector> m_operator_matches; + std::vector m_operators; + + struct Position + { + Position() + : line(-1), col(-1), m_last_col(-1) {} + + Position(std::string::const_iterator t_pos, std::string::const_iterator t_end) + : line(1), col(1), m_pos(std::move(t_pos)), m_end(std::move(t_end)), m_last_col(1) + { + } + + static std::string str(const Position &t_begin, const Position &t_end) { + return std::string(t_begin.m_pos, t_end.m_pos); + } + + Position &operator++() { + if (m_pos != m_end) { + if (*m_pos == '\n') { + ++line; + m_last_col = col; + col = 1; + } else { + ++col; + } + + ++m_pos; + } + return *this; + } + + Position &operator--() { + --m_pos; + if (*m_pos == '\n') { + --line; + col = m_last_col; + } else { + --col; + } + return *this; + } + + Position &operator+=(size_t t_distance) { + *this = (*this) + t_distance; + return *this; + } + + Position operator+(size_t t_distance) const { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + ++ret; + } + return ret; + } + + Position &operator-=(size_t t_distance) { + *this = (*this) - t_distance; + return *this; + } + + Position operator-(size_t t_distance) const { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + --ret; + } + return ret; + } + + bool operator==(const Position &t_rhs) const { + return m_pos == t_rhs.m_pos; + } + + bool operator!=(const Position &t_rhs) const { + return m_pos != t_rhs.m_pos; + } + + bool has_more() const { + return m_pos != m_end; + } + + size_t remaining() const { + return std::distance(m_pos, m_end); + } + + char operator*() const { + if (m_pos == m_end) { + return '\0'; + } else { + return *m_pos; + } + } + + int line; + int col; + + private: + std::string::const_iterator m_pos; + std::string::const_iterator m_end; + int m_last_col; + }; + + Position m_position; + + public: + ChaiScript_Parser() + : m_multiline_comment_begin("/*"), + m_multiline_comment_end("*/"), + m_singleline_comment("//") + { + m_match_stack.reserve(2); + setup_operators(); + } + + ChaiScript_Parser(const ChaiScript_Parser &) = delete; + ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete; + + void setup_operators() + { + m_operators.emplace_back(AST_Node_Type::Ternary_Cond); + m_operator_matches.emplace_back(std::initializer_list({"?"})); + + m_operators.emplace_back(AST_Node_Type::Logical_Or); + m_operator_matches.emplace_back(std::initializer_list({"||"})); + + m_operators.emplace_back(AST_Node_Type::Logical_And); + m_operator_matches.emplace_back(std::initializer_list({"&&"})); + + m_operators.emplace_back(AST_Node_Type::Bitwise_Or); + m_operator_matches.emplace_back(std::initializer_list({"|"})); + + m_operators.emplace_back(AST_Node_Type::Bitwise_Xor); + m_operator_matches.emplace_back(std::initializer_list({"^"})); + + m_operators.emplace_back(AST_Node_Type::Bitwise_And); + m_operator_matches.emplace_back(std::initializer_list({"&"})); + + m_operators.emplace_back(AST_Node_Type::Equality); + m_operator_matches.emplace_back(std::initializer_list({"==", "!="})); + + m_operators.emplace_back(AST_Node_Type::Comparison); + m_operator_matches.emplace_back(std::initializer_list({"<", "<=", ">", ">="})); + + m_operators.emplace_back(AST_Node_Type::Shift); + m_operator_matches.emplace_back(std::initializer_list({"<<", ">>"})); + + //We share precedence here but then separate them later + m_operators.emplace_back(AST_Node_Type::Addition); + m_operator_matches.emplace_back(std::initializer_list({"+", "-"})); + + //We share precedence here but then separate them later + m_operators.emplace_back(AST_Node_Type::Multiplication); + m_operator_matches.emplace_back(std::initializer_list({"*", "/", "%"})); + + for (auto & elem : m_alphabet) { + std::fill(std::begin(elem), std::end(elem), false); + } + + m_alphabet[detail::symbol_alphabet][static_cast('?')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('+')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('-')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('*')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('/')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('|')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('&')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('^')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('=')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('.')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('<')]=true; + m_alphabet[detail::symbol_alphabet][static_cast('>')]=true; + + for ( int c = 'a' ; c <= 'z' ; ++c ) { m_alphabet[detail::keyword_alphabet][c]=true; } + for ( int c = 'A' ; c <= 'Z' ; ++c ) { m_alphabet[detail::keyword_alphabet][c]=true; } + for ( int c = '0' ; c <= '9' ; ++c ) { m_alphabet[detail::keyword_alphabet][c]=true; } + m_alphabet[detail::keyword_alphabet][static_cast('_')]=true; + + for ( int c = '0' ; c <= '9' ; ++c ) { m_alphabet[detail::int_alphabet][c]=true; } + for ( int c = '0' ; c <= '9' ; ++c ) { m_alphabet[detail::float_alphabet][c]=true; } + m_alphabet[detail::float_alphabet][static_cast('.')]=true; + + for ( int c = '0' ; c <= '9' ; ++c ) { m_alphabet[detail::hex_alphabet][c]=true; } + for ( int c = 'a' ; c <= 'f' ; ++c ) { m_alphabet[detail::hex_alphabet][c]=true; } + for ( int c = 'A' ; c <= 'F' ; ++c ) { m_alphabet[detail::hex_alphabet][c]=true; } + + m_alphabet[detail::x_alphabet][static_cast('x')]=true; + m_alphabet[detail::x_alphabet][static_cast('X')]=true; + + for ( int c = '0' ; c <= '1' ; ++c ) { m_alphabet[detail::bin_alphabet][c]=true; } + m_alphabet[detail::b_alphabet][static_cast('b')]=true; + m_alphabet[detail::b_alphabet][static_cast('B')]=true; + + for ( int c = 'a' ; c <= 'z' ; ++c ) { m_alphabet[detail::id_alphabet][c]=true; } + for ( int c = 'A' ; c <= 'Z' ; ++c ) { m_alphabet[detail::id_alphabet][c]=true; } + m_alphabet[detail::id_alphabet][static_cast('_')] = true; + + m_alphabet[detail::white_alphabet][static_cast(' ')]=true; + m_alphabet[detail::white_alphabet][static_cast('\t')]=true; + + m_alphabet[detail::int_suffix_alphabet][static_cast('l')] = true; + m_alphabet[detail::int_suffix_alphabet][static_cast('L')] = true; + m_alphabet[detail::int_suffix_alphabet][static_cast('u')] = true; + m_alphabet[detail::int_suffix_alphabet][static_cast('U')] = true; + + m_alphabet[detail::float_suffix_alphabet][static_cast('l')] = true; + m_alphabet[detail::float_suffix_alphabet][static_cast('L')] = true; + m_alphabet[detail::float_suffix_alphabet][static_cast('f')] = true; + m_alphabet[detail::float_suffix_alphabet][static_cast('F')] = true; + } + + /// test a char in an m_alphabet + bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast(c)]; } + + /// Prints the parsed ast_nodes as a tree + void debug_print(AST_NodePtr t, std::string prepend = "") const { + std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n'; + for (unsigned int j = 0; j < t->children.size(); ++j) { + debug_print(t->children[j], prepend + " "); + } + } + + + /// Shows the current stack of matched ast_nodes + void show_match_stack() const { + for (auto & elem : m_match_stack) { + //debug_print(match_stack[i]); + std::cout << elem->to_string(); + } + } + + /// Clears the stack of matched ast_nodes + void clear_match_stack() { + m_match_stack.clear(); + } + + /// Returns the front-most AST node + AST_NodePtr ast() const { + if (m_match_stack.empty()) throw exception::eval_error("Attempted to access AST of failed parse."); + return m_match_stack.front(); + } + + static std::map count_fun_calls(const AST_NodePtr &p, bool in_loop) { + if (p->identifier == AST_Node_Type::Fun_Call) { + if (p->children[0]->identifier == AST_Node_Type::Id) { + return std::map{{p->children[0]->text, in_loop?99:1}}; + } + return std::map(); + } else { + std::map counts; + for (const auto &child : p->children) { + auto childcounts = count_fun_calls(child, in_loop || p->identifier == AST_Node_Type::For || p->identifier == AST_Node_Type::While); + for (const auto &count : childcounts) { + counts[count.first] += count.second; + } + } + return counts; + } + + } + + + static void optimize_blocks(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Block) { + if (c->children.size() == 1) { + // std::cout << "swapping out block child for block\n"; + c = c->children[0]; + } + } + optimize_blocks(c); + } + } + + static void optimize_returns(AST_NodePtr &p) + { + for (auto &c : p->children) + { + if (c->identifier == AST_Node_Type::Def && c->children.size() > 0) { + auto &last_child = c->children.back(); + if (last_child->identifier == AST_Node_Type::Block) { + auto &block_last_child = last_child->children.back(); + if (block_last_child->identifier == AST_Node_Type::Return) { + if (block_last_child->children.size() == 1) { + block_last_child = block_last_child->children[0]; + } + } + } + } + optimize_returns(c); + } + } + + + static int count_nodes(const AST_NodePtr &p) + { + int count = 1; + for (auto &c : p->children) { + count += count_nodes(c); + } + return count; + } + + AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true) { + AST_NodePtr p = ast(); + //Note, optimize_blocks is currently broken; it breaks stack management + if (t_optimize_blocks) { optimize_blocks(p); } + if (t_optimize_returns) { optimize_returns(p); } + return p; + } + + + /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node + template + void build_match(size_t t_match_start, std::string t_text = "") { + bool is_deep = false; + + Parse_Location filepos = [&]()->Parse_Location{ + //so we want to take everything to the right of this and make them children + if (t_match_start != m_match_stack.size()) { + is_deep = true; + return Parse_Location( + m_filename, + m_match_stack[t_match_start]->location.start.line, + m_match_stack[t_match_start]->location.start.column, + m_position.line, + m_position.col + ); + } else { + return Parse_Location( + m_filename, + m_position.line, + m_position.col, + m_position.line, + m_position.col + ); + } + }(); + + std::vector new_children; + + if (is_deep) { + new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast(t_match_start)), + std::make_move_iterator(m_match_stack.end())); + m_match_stack.erase(m_match_stack.begin() + static_cast(t_match_start), m_match_stack.end()); + } + + /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position + m_match_stack.push_back( + chaiscript::make_shared( + std::move(t_text), + std::move(filepos), + std::move(new_children))); + } + + + /// Skips any multi-line or single-line comment + bool SkipComment() { + if (Symbol_(m_multiline_comment_begin.c_str())) { + while (m_position.has_more()) { + if (Symbol_(m_multiline_comment_end.c_str())) { + break; + } else if (!Eol_()) { + ++m_position; + } + } + return true; + } else if (Symbol_(m_singleline_comment.c_str())) { + while (m_position.has_more()) { + if (Symbol_("\r\n")) { + m_position -= 2; + break; + } else if (Char_('\n')) { + --m_position; + break; + } else { + ++m_position; + } + } + return true; + } + return false; + } + + + /// Skips ChaiScript whitespace, which means space and tab, but not cr/lf + /// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR ("\r\n") + bool SkipWS(bool skip_cr=false) { + bool retval = false; + + while (m_position.has_more()) { + auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n')); + + if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) { + + if(end_line) { + if(*m_position == '\r') { + // discards lf + ++m_position; + } + } + + ++m_position; + + retval = true; + } + else if (SkipComment()) { + retval = true; + } else { + break; + } + } + return retval; + } + + /// Reads the optional exponent (scientific notation) and suffix for a Float + bool read_exponent_and_suffix() { + // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19 + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { + ++m_position; + if (m_position.has_more() && ((*m_position == '-') || (*m_position == '+'))) { + ++m_position; + } + auto exponent_pos = m_position; + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; + } + if (m_position == exponent_pos) { + // Require at least one digit after the exponent + return false; + } + } + + // Parse optional float suffix + while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet)) + { + ++m_position; + } + + return true; + } + + + /// Reads a floating point value from input, without skipping initial whitespace + bool Float_() { + if (m_position.has_more() && char_in_alphabet(*m_position,detail::float_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; + } + + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { + // The exponent is valid even without any decimal in the Float (1e8, 3e-15) + return read_exponent_and_suffix(); + } + else if (m_position.has_more() && (*m_position == '.')) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + ++m_position; + } + + // After any decimal digits, support an optional exponent (3.7e3) + return read_exponent_and_suffix(); + } else { + --m_position; + } + } + } + return false; + } + + /// Reads a hex value from input, without skipping initial whitespace + bool Hex_() { + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; + + if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet) ) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet) ) { + ++m_position; + } + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) + { + ++m_position; + } + + return true; + } + else { + --m_position; + } + } + else { + --m_position; + } + } + + return false; + } + + /// Reads an integer suffix + void IntSuffix_() { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) + { + ++m_position; + } + } + + /// Reads a binary value from input, without skipping initial whitespace + bool Binary_() { + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; + + if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet) ) { + ++m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { + ++m_position; + } + return true; + } else { + --m_position; + } + } else { + --m_position; + } + } + + return false; + } + + /// Parses a floating point value and returns a Boxed_Value representation of it + static Boxed_Value buildFloat(const std::string &t_val) + { + bool float_ = false; + bool long_ = false; + + auto i = t_val.size(); + + for (; i > 0; --i) + { + char val = t_val[i-1]; + + if (val == 'f' || val == 'F') + { + float_ = true; + } else if (val == 'l' || val == 'L') { + long_ = true; + } else { + break; + } + } + + if (float_) + { + return const_var(std::stof(t_val.substr(0,i))); + } else if (long_) { + return const_var(std::stold(t_val.substr(0,i))); + } else { + return const_var(std::stod(t_val.substr(0,i))); + } + } + + + + static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed) + { + bool unsigned_ = false; + bool long_ = false; + bool longlong_ = false; + + auto i = t_val.size(); + + for (; i > 0; --i) + { + const char val = t_val[i-1]; + + if (val == 'u' || val == 'U') + { + unsigned_ = true; + } else if (val == 'l' || val == 'L') { + if (long_) + { + longlong_ = true; + } + + long_ = true; + } else { + break; + } + } + + const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" + +#ifdef CHAISCRIPT_CLANG +#pragma GCC diagnostic ignored "-Wtautological-compare" +#endif + +#endif + + try { + auto u = std::stoll(val,nullptr,base); + + + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); + } + + } catch (const std::out_of_range &) { + // too big to be signed + try { + auto u = std::stoull(val,nullptr,base); + + if (u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); + } + } catch (const std::out_of_range &) { + // it's just simply too big + return const_var(std::numeric_limits::max()); + } + } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + } + + template + std::shared_ptr make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) + { + return chaiscript::make_shared(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); + } + + /// Reads a number from the input, detecting if it's an integer or floating point + bool Num(const bool t_capture = false) { + SkipWS(); + + if (!t_capture) { + return Hex_() || Float_(); + } else { + const auto start = m_position; + if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { + try { + if (Hex_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(16, match, true); + m_match_stack.emplace_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; + } + + if (Binary_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(2, match, true); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; + } + if (Float_()) { + auto match = Position::str(start, m_position); + auto bv = buildFloat(match); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + return true; + } + else { + IntSuffix_(); + auto match = Position::str(start, m_position); + if (!match.empty() && (match[0] == '0')) { + auto bv = buildInt(8, match, false); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + } + else if (!match.empty()) { + auto bv = buildInt(10, match, false); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col, std::move(bv))); + } else { + return false; + } + return true; + } + } catch (const std::invalid_argument &) { + // error parsing number passed in to buildFloat/buildInt + return false; + } + } + else { + return false; + } + } + } + + /// Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace + bool Id_() { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::id_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + ++m_position; + } + + return true; + } else if (m_position.has_more() && (*m_position == '`')) { + ++m_position; + const auto start = m_position; + + while (m_position.has_more() && (*m_position != '`')) { + if (Eol()) { + throw exception::eval_error("Carriage return in identifier literal", File_Position(m_position.line, m_position.col), *m_filename); + } + else { + ++m_position; + } + } + + if (start == m_position) { + throw exception::eval_error("Missing contents of identifier literal", File_Position(m_position.line, m_position.col), *m_filename); + } + else if (!m_position.has_more()) { + throw exception::eval_error("Incomplete identifier literal", File_Position(m_position.line, m_position.col), *m_filename); + } + + ++m_position; + + return true; + } + return false; + } + + /// Reads (and potentially captures) an identifier from input + bool Id() { + SkipWS(); + + const auto start = m_position; + if (Id_()) { + m_match_stack.push_back(make_node( + [&]()->std::string{ + if (*start == '`') { + //Id Literal + return Position::str(start+1, m_position-1); + } else { + return Position::str(start, m_position); + } + }(), + start.line, start.col)); + return true; + } else { + return false; + } + } + + /// Reads an argument from input + bool Arg(const bool t_type_allowed = true) { + const auto prev_stack_top = m_match_stack.size(); + SkipWS(); + + if (!Id()) { + return false; + } + + SkipWS(); + + if (t_type_allowed) { + Id(); + } + + build_match(prev_stack_top); + + return true; + } + + + + /// Checks for a node annotation of the form "#" + bool Annotation() { + SkipWS(); + const auto start = m_position; + if (Symbol_("#")) { + do { + while (m_position.has_more()) { + if (Eol_()) { + break; + } + else { + ++m_position; + } + } + } while (Symbol("#")); + + auto match = Position::str(start, m_position); + m_match_stack.push_back(make_node(std::move(match), start.line, start.col)); + return true; + } + else { + return false; + } + } + + /// Reads a quoted string from input, without skipping initial whitespace + bool Quoted_String_() { + if (m_position.has_more() && (*m_position == '\"')) { + char prev_char = *m_position; + ++m_position; + + int in_interpolation = 0; + bool in_quote = false; + + while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) || ((*m_position == '\"') && (prev_char == '\\')))) { + + if (!Eol_()) { + if (prev_char == '$' && *m_position == '{') { + ++in_interpolation; + } else if (prev_char != '\\' && *m_position == '"') { + in_quote = !in_quote; + } else if (*m_position == '}' && !in_quote) { + --in_interpolation; + } + + if (prev_char == '\\') { + prev_char = 0; + } else { + prev_char = *m_position; + } + ++m_position; + } + } + + if (m_position.has_more()) { + ++m_position; + } else { + throw exception::eval_error("Unclosed quoted string", File_Position(m_position.line, m_position.col), *m_filename); + } + + return true; + } + return false; + } + + template + struct Char_Parser + { + string_type &match; + typedef typename string_type::value_type char_type; + bool is_escaped; + bool is_interpolated; + bool saw_interpolation_marker; + bool is_octal; + bool is_hex; + const bool interpolation_allowed; + + string_type octal_matches; + string_type hex_matches; + + Char_Parser(string_type &t_match, const bool t_interpolation_allowed) + : match(t_match), + is_escaped(false), + is_interpolated(false), + saw_interpolation_marker(false), + is_octal(false), + is_hex(false), + interpolation_allowed(t_interpolation_allowed) + { + } + + Char_Parser &operator=(const Char_Parser &) = delete; + + ~Char_Parser(){ + if (is_octal) { + process_octal(); + } + + if (is_hex) { + process_hex(); + } + } + + void process_hex() + { + auto val = stoll(hex_matches, 0, 16); + match.push_back(char_type(val)); + hex_matches.clear(); + is_escaped = false; + is_hex = false; + } + + + void process_octal() + { + auto val = stoll(octal_matches, 0, 8); + match.push_back(char_type(val)); + octal_matches.clear(); + is_escaped = false; + is_octal = false; + } + + void parse(const char_type t_char, const int line, const int col, const std::string &filename) { + const bool is_octal_char = t_char >= '0' && t_char <= '7'; + + if (is_octal) { + if (is_octal_char) { + octal_matches.push_back(t_char); + + if (octal_matches.size() == 3) { + process_octal(); + } + return; + } else { + process_octal(); + } + } else if (is_hex) { + const bool is_hex_char = (t_char >= '0' && t_char <= '9') + || (t_char >= 'a' && t_char <= 'f') + || (t_char >= 'A' && t_char <= 'F'); + + if (is_hex_char) { + hex_matches.push_back(t_char); + + if (hex_matches.size() == 2*sizeof(char_type)) { + // This rule differs from the C/C++ standard, but ChaiScript + // does not offer the same workaround options, and having + // hexadecimal sequences longer than can fit into the char + // type is undefined behavior anyway. + process_hex(); + } + return; + } else { + process_hex(); + } + } + + if (t_char == '\\') { + if (is_escaped) { + match.push_back('\\'); + is_escaped = false; + } else { + is_escaped = true; + } + } else { + if (is_escaped) { + if (is_octal_char) { + is_octal = true; + octal_matches.push_back(t_char); + } else if (t_char == 'x') { + is_hex = true; + } else { + switch (t_char) { + case ('\'') : match.push_back('\''); break; + case ('\"') : match.push_back('\"'); break; + case ('?') : match.push_back('?'); break; + case ('a') : match.push_back('\a'); break; + case ('b') : match.push_back('\b'); break; + case ('f') : match.push_back('\f'); break; + case ('n') : match.push_back('\n'); break; + case ('r') : match.push_back('\r'); break; + case ('t') : match.push_back('\t'); break; + case ('v') : match.push_back('\v'); break; + case ('$') : match.push_back('$'); break; + default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + } + is_escaped = false; + } + } else if (interpolation_allowed && t_char == '$') { + saw_interpolation_marker = true; + } else { + match.push_back(t_char); + } + } + } + + }; + + + /// Reads (and potentially captures) a quoted string from input. Translates escaped sequences. + bool Quoted_String(const bool t_capture = false) { + SkipWS(); + + if (!t_capture) { + return Quoted_String_(); + } else { + const auto start = m_position; + + if (Quoted_String_()) { + std::string match; + const auto prev_stack_top = m_match_stack.size(); + + bool is_interpolated = [&]()->bool { + Char_Parser cparser(match, true); + + + auto s = start + 1, end = m_position - 1; + + while (s != end) { + if (cparser.saw_interpolation_marker) { + if (*s == '{') { + //We've found an interpolation point + + if (cparser.is_interpolated) { + //If we've seen previous interpolation, add on instead of making a new one + m_match_stack.push_back(make_node(match, start.line, start.col)); + + build_match(prev_stack_top, "+"); + } else { + m_match_stack.push_back(make_node(match, start.line, start.col)); + } + + //We've finished with the part of the string up to this point, so clear it + match.clear(); + + std::string eval_match; + + ++s; + while ((s != end) && (*s != '}')) { + eval_match.push_back(*s); + ++s; + } + + if (*s == '}') { + cparser.is_interpolated = true; + ++s; + + const auto tostr_stack_top = m_match_stack.size(); + + m_match_stack.push_back(make_node("to_string", start.line, start.col)); + + const auto ev_stack_top = m_match_stack.size(); + + try { + ChaiScript_Parser parser; + parser.parse(eval_match, "instr eval"); + m_match_stack.push_back(parser.ast()); + } catch (const exception::eval_error &e) { + throw exception::eval_error(e.what(), File_Position(start.line, start.col), *m_filename); + } + + build_match(ev_stack_top); + build_match(tostr_stack_top); + build_match(prev_stack_top, "+"); + } else { + throw exception::eval_error("Unclosed in-string eval", File_Position(start.line, start.col), *m_filename); + } + } else { + match.push_back('$'); + } + cparser.saw_interpolation_marker = false; + } else { + cparser.parse(*s, start.line, start.col, *m_filename); + ++s; + } + } + + return cparser.is_interpolated; + }(); + + if (is_interpolated) { + m_match_stack.push_back(make_node(match, start.line, start.col)); + + build_match(prev_stack_top, "+"); + } else { + m_match_stack.push_back(make_node(match, start.line, start.col)); + } + return true; + } else { + return false; + } + } + } + + /// Reads a character group from input, without skipping initial whitespace + bool Single_Quoted_String_() { + bool retval = false; + if (m_position.has_more() && (*m_position == '\'')) { + retval = true; + char prev_char = *m_position; + ++m_position; + + while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) { + if (!Eol_()) { + if (prev_char == '\\') { + prev_char = 0; + } else { + prev_char = *m_position; + } + ++m_position; + } + } + + if (m_position.has_more()) { + ++m_position; + } else { + throw exception::eval_error("Unclosed single-quoted string", File_Position(m_position.line, m_position.col), *m_filename); + } + } + return retval; + } + + /// Reads (and potentially captures) a char group from input. Translates escaped sequences. + bool Single_Quoted_String(const bool t_capture = false) { + SkipWS(); + + if (!t_capture) { + return Single_Quoted_String_(); + } else { + const auto start = m_position; + if (Single_Quoted_String_()) { + std::string match; + + { + // scope for cparser destrutor + Char_Parser cparser(match, false); + + for (auto s = start + 1, end = m_position - 1; s != end; ++s) { + cparser.parse(*s, start.line, start.col, *m_filename); + } + } + + m_match_stack.push_back(make_node(match, start.line, start.col)); + return true; + } + else { + return false; + } + } + } + + /// Reads a char from input if it matches the parameter, without skipping initial whitespace + bool Char_(const char c) { + if (m_position.has_more() && (*m_position == c)) { + ++m_position; + return true; + } else { + return false; + } + } + + /// Reads (and potentially captures) a char from input if it matches the parameter + bool Char(const char t_c, bool t_capture = false) { + SkipWS(); + + if (!t_capture) { + return Char_(t_c); + } else { + const auto start = m_position; + if (Char_(t_c)) { + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); + return true; + } else { + return false; + } + } + } + + /// Reads a string from input if it matches the parameter, without skipping initial whitespace + bool Keyword_(const char *t_s) { + const auto len = strlen(t_s); + + if (m_position.remaining() >= len) { + auto tmp = m_position; + for (size_t i = 0; tmp.has_more() && i < len; ++i) { + if (*tmp != t_s[i]) { + return false; + } + ++tmp; + } + m_position = tmp; + return true; + } + + return false; + } + + /// Reads (and potentially captures) a string from input if it matches the parameter + bool Keyword(const char *t_s, bool t_capture = false) { + SkipWS(); + const auto start = m_position; + bool retval = Keyword_(t_s); + // ignore substring matches + if ( retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + m_position = start; + retval = false; + } + + if ( t_capture && retval ) { + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); + } + return retval; + } + + /// Reads a symbol group from input if it matches the parameter, without skipping initial whitespace + bool Symbol_(const char *t_s) { + const auto len = strlen(t_s); + + if (m_position.remaining() >= len) { + auto tmp = m_position; + for (size_t i = 0; m_position.has_more() && i < len; ++i) { + if (*tmp != t_s[i]) { + return false; + } + ++tmp; + } + m_position = tmp; + return true; + } + + return false; + } + + bool is_operator(const std::string &t_s) const { + return std::any_of(m_operator_matches.begin(), m_operator_matches.end(), + [t_s](const std::vector &opers) { + return std::any_of(opers.begin(), opers.end(), + [t_s](const std::string &s) { + return s == t_s; + }); + }); + } + + /// Reads (and potentially captures) a symbol group from input if it matches the parameter + bool Symbol(const char *t_s, const bool t_capture = false, const bool t_disallow_prevention=false) { + SkipWS(); + const auto start = m_position; + bool retval = Symbol_(t_s); + + // ignore substring matches + if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position,detail::symbol_alphabet)) { + if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position+1))) { + // don't throw this away, it's a good match and the next is not + } else { + m_position = start; + retval = false; + } + } + + if ( t_capture && retval ) { + m_match_stack.push_back(make_node(Position::str(start, m_position), start.line, start.col)); + } + + return retval; + } + + /// Reads an end-of-line group from input, without skipping initial whitespace + bool Eol_(const bool t_eos = false) { + bool retval = false; + + if (m_position.has_more() && (Symbol_("\r\n") || Char_('\n'))) { + retval = true; + //++m_position.line; + m_position.col = 1; + } else if (m_position.has_more() && !t_eos && Char_(';')) { + retval = true; + } + + return retval; + } + + /// Reads until the end of the current statement + bool Eos() { + SkipWS(); + + return Eol_(true); + } + + /// Reads (and potentially captures) an end-of-line group from input + bool Eol() { + SkipWS(); + + return Eol_(); + } + + /// Reads a comma-separated list of values from input. Id's only, no types allowed + bool Id_Arg_List() { + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg(false)) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Arg(false)) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Char(',')); + } + } + build_match(prev_stack_top); + + SkipWS(true); + + return retval; + } + + /// Reads a comma-separated list of values from input, for function declarations + bool Decl_Arg_List() { + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg()) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Arg()) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Char(',')); + } + } + build_match(prev_stack_top); + + SkipWS(true); + + return retval; + } + + + /// Reads a comma-separated list of values from input + bool Arg_List() { + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Equation()) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Equation()) { + throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Char(',')); + } + } + + build_match(prev_stack_top); + + SkipWS(true); + + return retval; + } + + /// Reads possible special container values, including ranges and map_pairs + bool Container_Arg_List() { + bool retval = false; + SkipWS(true); + + const auto prev_stack_top = m_match_stack.size(); + + if (Value_Range()) { + retval = true; + build_match(prev_stack_top); + } else if (Map_Pair()) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Map_Pair()) { + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Char(',')); + } + build_match(prev_stack_top); + } else if (Operator()) { + retval = true; + while (Eol()) {} + if (Char(',')) { + do { + while (Eol()) {} + if (!Operator()) { + throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Char(',')); + } + build_match(prev_stack_top); + } + + SkipWS(true); + + return retval; + } + + /// Reads a lambda (anonymous function) from input + bool Lambda() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("fun")) { + retval = true; + + if (Char('[')) { + Id_Arg_List(); + if (!Char(']')) { + throw exception::eval_error("Incomplete anonymous function bind", File_Position(m_position.line, m_position.col), *m_filename); + } + } else { + // make sure we always have the same number of nodes + build_match(prev_stack_top); + } + + if (Char('(')) { + Decl_Arg_List(); + if (!Char(')')) { + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); + } + } else { + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); + } + + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads a function definition from input + bool Def(const bool t_class_context = false) { + bool retval = false; + AST_NodePtr annotation; + + if (Annotation()) { + while (Eol_()) {} + annotation = m_match_stack.back(); + m_match_stack.pop_back(); + } + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("def")) { + retval = true; + + if (!Id()) { + throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename); + } + + bool is_method = false; + + if (Symbol("::", false)) { + //We're now a method + is_method = true; + + if (!Id()) { + throw exception::eval_error("Missing method name in definition", File_Position(m_position.line, m_position.col), *m_filename); + } + } + + if (Char('(')) { + Decl_Arg_List(); + if (!Char(')')) { + throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename); + } + } + + while (Eos()) {} + + if (Char(':')) { + if (!Operator()) { + throw exception::eval_error("Missing guard expression for function", File_Position(m_position.line, m_position.col), *m_filename); + } + } + + while (Eol()) {} + if (!Block()) { + throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (is_method || t_class_context) { + build_match(prev_stack_top); + } else { + build_match(prev_stack_top); + } + + if (annotation) { + m_match_stack.back()->annotation = std::move(annotation); + } + } + + return retval; + } + + /// Reads a function definition from input + bool Try() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("try")) { + retval = true; + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'try' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()) {} + has_matches = false; + if (Keyword("catch", false)) { + const auto catch_stack_top = m_match_stack.size(); + if (Char('(')) { + if (!(Arg() && Char(')'))) { + throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + if (Char(':')) { + if (!Operator()) { + throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename); + } + } + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename); + } + build_match(catch_stack_top); + has_matches = true; + } + } + while (Eol()) {} + if (Keyword("finally", false)) { + const auto finally_stack_top = m_match_stack.size(); + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename); + } + build_match(finally_stack_top); + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads an if/else if/else block from input + bool If() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("if")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'if' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()) {} + has_matches = false; + if (Keyword("else", true)) { + if (Keyword("if")) { + const AST_NodePtr back(m_match_stack.back()); + m_match_stack.back() = + chaiscript::make_shared("else if", back->location, back->children); + m_match_stack.back()->annotation = back->annotation; + if (!Char('(')) { + throw exception::eval_error("Incomplete 'else if' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'else if' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'else if' block", File_Position(m_position.line, m_position.col), *m_filename); + } + has_matches = true; + } else { + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'else' block", File_Position(m_position.line, m_position.col), *m_filename); + } + has_matches = true; + } + } + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads a class block from input + bool Class() { + bool retval = false; + + size_t prev_stack_top = m_match_stack.size(); + + if (Keyword("class")) { + retval = true; + + if (!Id()) { + throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename); + } + + + while (Eol()) {} + + if (!Class_Block()) { + throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + + return retval; + } + + + /// Reads a while block from input + bool While() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("while")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + + return retval; + } + + + /// Reads the C-style for conditions from input + bool For_Guards() { + if (!(Equation() && Eol())) + { + if (!Eol()) + { + throw exception::eval_error("'for' loop initial statment missing", File_Position(m_position.line, m_position.col), *m_filename); + } else { + m_match_stack.push_back(chaiscript::make_shared()); + } + } + + if (!(Equation() && Eol())) + { + if (!Eol()) + { + throw exception::eval_error("'for' loop condition missing", File_Position(m_position.line, m_position.col), *m_filename); + } else { + m_match_stack.push_back(chaiscript::make_shared()); + } + } + + if (!Equation()) + { + m_match_stack.push_back(chaiscript::make_shared()); + } + + return true; + } + + /// Reads a for block from input + bool For() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("for")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(For_Guards() && Char(')'))) { + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads a case block from input + bool Case() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("case")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } else if (Keyword("default")) { + retval = true; + + while (Eol()) {} + + if (!Block()) { + throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + + return retval; + } + + + /// Reads a switch statement from input + bool Switch() { + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("switch")) { + + if (!Char('(')) { + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + while (Eol()) {} + + if (Char('{')) { + while (Eol()) {} + + while (Case()) { + while (Eol()) { } // eat + } + + while (Eol()) { } // eat + + if (!Char('}')) { + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); + } + } + else { + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + return true; + + } else { + return false; + } + + } + + + /// Reads a curly-brace C-style class block from input + bool Class_Block() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Char('{')) { + retval = true; + + Class_Statements(); + if (!Char('}')) { + throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back(chaiscript::make_shared()); + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads a curly-brace C-style block from input + bool Block() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Char('{')) { + retval = true; + + Statements(); + if (!Char('}')) { + throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); + } + + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back(chaiscript::make_shared()); + } + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads a return statement from input + bool Return() { + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("return")) { + Operator(); + build_match(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a break statement from input + bool Break() { + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("break")) { + build_match(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a continue statement from input + bool Continue() { + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("continue")) { + build_match(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a dot expression(member access), then proceeds to check if it's a function or array call + bool Dot_Fun_Array() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + if (Lambda() || Num(true) || Quoted_String(true) || Single_Quoted_String(true) || + Paren_Expression() || Inline_Container() || Id()) + { + retval = true; + bool has_more = true; + + while (has_more) { + has_more = false; + + if (Char('(')) { + has_more = true; + + Arg_List(); + if (!Char(')')) { + throw exception::eval_error("Incomplete function call", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + /// \todo Work around for method calls until we have a better solution + if (!m_match_stack.back()->children.empty()) { + if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Dot_Access) { + if (m_match_stack.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + if (m_match_stack.back()->children.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + AST_NodePtr dot_access = m_match_stack.back()->children[0]; + AST_NodePtr func_call = m_match_stack.back(); + m_match_stack.pop_back(); + func_call->children.erase(func_call->children.begin()); + if (dot_access->children.empty()) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + func_call->children.insert(func_call->children.begin(), dot_access->children.back()); + dot_access->children.pop_back(); + dot_access->children.push_back(std::move(func_call)); + if (dot_access->children.size() != 3) throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + m_match_stack.push_back(std::move(dot_access)); + } + } + } else if (Char('[')) { + has_more = true; + + if (!(Operator() && Char(']'))) { + throw exception::eval_error("Incomplete array access", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + else if (Symbol(".", true)) { + has_more = true; + if (!(Id())) { + throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + } + + if ( std::distance(m_match_stack.begin() + static_cast(prev_stack_top), m_match_stack.end()) != 3) { + throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); + } + build_match(prev_stack_top); + } + } + } + + return retval; + } + + /// Reads a variable declaration from input + bool Var_Decl(const bool t_class_context = false) { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) { + retval = true; + + if (!Id()) { + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } else if (Keyword("auto") || Keyword("var") ) { + retval = true; + + if (!(Reference() || Id())) { + throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } else if (Keyword("GLOBAL") || Keyword("global")) { + retval = true; + + if (!(Reference() || Id())) { + throw exception::eval_error("Incomplete global declaration", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } else if (Keyword("attr")) { + retval = true; + + if (!Id()) { + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); + } + if (!Symbol("::", false)) { + throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); + } + if (!Id()) { + throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename); + } + + + build_match(prev_stack_top); + } + + return retval; + } + + /// Reads an expression surrounded by parentheses from input + bool Paren_Expression() { + if (Char('(')) { + if (!Operator()) { + throw exception::eval_error("Incomplete expression", File_Position(m_position.line, m_position.col), *m_filename); + } + if (!Char(')')) { + throw exception::eval_error("Missing closing parenthesis ')'", File_Position(m_position.line, m_position.col), *m_filename); + } + return true; + } else { + return false; + } + } + + /// Reads, and identifies, a short-form container initialization from input + bool Inline_Container() { + const auto prev_stack_top = m_match_stack.size(); + + if (Char('[')) { + Container_Arg_List(); + + if (!Char(']')) { + throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_position.line, m_position.col), *m_filename); + } + if ((prev_stack_top != m_match_stack.size()) && (m_match_stack.back()->children.size() > 0)) { + if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Value_Range) { + build_match(prev_stack_top); + } + else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) { + build_match(prev_stack_top); + } + else { + build_match(prev_stack_top); + } + } + else { + build_match(prev_stack_top); + } + + return true; + } else { + return false; + } + } + + /// Parses a variable specified with a & aka reference + bool Reference() { + const auto prev_stack_top = m_match_stack.size(); + + if (Symbol("&", false)) { + if (!Id()) { + throw exception::eval_error("Incomplete '&' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a unary prefixed expression from input + bool Prefix() { + const auto prev_stack_top = m_match_stack.size(); + const std::vector prefix_opers{"++", "--", "-", "+", "!", "~", "&"}; + + for (const auto &oper : prefix_opers) + { + bool is_char = oper.size() == 1; + if ((is_char && Char(oper[0], true)) || (!is_char && Symbol(oper.c_str(), true))) + { + if (!Operator(m_operators.size()-1)) { + throw exception::eval_error("Incomplete prefix '" + oper + "' expression", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + return true; + } + } + + return false; + } + + /// Parses any of a group of 'value' style ast_node groups from input + bool Value() { + return Var_Decl() || Dot_Fun_Array() || Prefix(); + } + + bool Operator_Helper(const size_t t_precedence) { + for (auto & elem : m_operator_matches[t_precedence]) { + if (Symbol(elem.c_str(), true)) { + return true; + } + } + return false; + } + + bool Operator(const size_t t_precedence = 0) { + bool retval = false; + const auto prev_stack_top = m_match_stack.size(); + + if (t_precedence < m_operators.size()) { + if (Operator(t_precedence+1)) { + retval = true; + if (Operator_Helper(t_precedence)) { + do { + while (Eol()) {} + if (!Operator(t_precedence+1)) { + throw exception::eval_error("Incomplete " + + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + File_Position(m_position.line, m_position.col), *m_filename); + } + + AST_NodePtr oper = m_match_stack.at(m_match_stack.size()-2); + + switch (m_operators[t_precedence]) { + case(AST_Node_Type::Ternary_Cond) : + m_match_stack.erase(m_match_stack.begin() + m_match_stack.size() - 2, + m_match_stack.begin() + m_match_stack.size() - 1); + if (Symbol(":")) { + if (!Operator(t_precedence+1)) { + throw exception::eval_error("Incomplete " + + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + File_Position(m_position.line, m_position.col), *m_filename); + } + build_match(prev_stack_top); + } + else { + throw exception::eval_error("Incomplete " + + std::string(ast_node_type_to_string(m_operators[t_precedence])) + " expression", + File_Position(m_position.line, m_position.col), *m_filename); + } + break; + + case(AST_Node_Type::Addition) : + case(AST_Node_Type::Multiplication) : + case(AST_Node_Type::Shift) : + case(AST_Node_Type::Equality) : + case(AST_Node_Type::Bitwise_And) : + case(AST_Node_Type::Bitwise_Xor) : + case(AST_Node_Type::Bitwise_Or) : + case(AST_Node_Type::Comparison) : + assert(m_match_stack.size() > 1); + m_match_stack.erase(m_match_stack.begin() + m_match_stack.size() - 2, m_match_stack.begin() + m_match_stack.size() - 1); + build_match(prev_stack_top, oper->text); + break; + + case(AST_Node_Type::Logical_And) : + build_match(prev_stack_top); + break; + case(AST_Node_Type::Logical_Or) : + build_match(prev_stack_top); + break; + + default: + throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename); + } + } while (Operator_Helper(t_precedence)); + } + } + } + else { + return Value(); + } + + return retval; + } + + /// Reads a pair of values used to create a map initialization from input + bool Map_Pair() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + const auto prev_pos = m_position; + + if (Operator()) { + if (Symbol(":")) { + retval = true; + if (!Operator()) { + throw exception::eval_error("Incomplete map pair", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + else { + m_position = prev_pos; + while (prev_stack_top != m_match_stack.size()) { + m_match_stack.pop_back(); + } + } + } + + return retval; + } + + /// Reads a pair of values used to create a range initialization from input + bool Value_Range() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + const auto prev_pos = m_position; + + if (Operator()) { + if (Symbol("..")) { + retval = true; + if (!Operator()) { + throw exception::eval_error("Incomplete value range", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + else { + m_position = prev_pos; + while (prev_stack_top != m_match_stack.size()) { + m_match_stack.pop_back(); + } + } + } + + return retval; + } + + /// Parses a string of binary equation operators + bool Equation() { + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Operator()) { + retval = true; + if (Symbol("=", true, true) || Symbol(":=", true, true) || Symbol("+=", true, true) || + Symbol("-=", true, true) || Symbol("*=", true, true) || Symbol("/=", true, true) || + Symbol("%=", true, true) || Symbol("<<=", true, true) || Symbol(">>=", true, true) || + Symbol("&=", true, true) || Symbol("^=", true, true) || Symbol("|=", true, true)) { + SkipWS(true); + if (!Equation()) { + throw exception::eval_error("Incomplete equation", File_Position(m_position.line, m_position.col), *m_filename); + } + + build_match(prev_stack_top); + } + } + + return retval; + } + + /// Parses statements allowed inside of a class block + bool Class_Statements() { + bool retval = false; + + bool has_more = true; + bool saw_eol = true; + + while (has_more) { + const auto start = m_position; + if (Def(true) || Var_Decl(true)) { + if (!saw_eol) { + throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = true; + } else if (Eol()) { + has_more = true; + retval = true; + saw_eol = true; + } else { + has_more = false; + } + } + + return retval; + } + + /// Top level parser, starts parsing of all known parses + bool Statements() { + bool retval = false; + + bool has_more = true; + bool saw_eol = true; + + while (has_more) { + const auto start = m_position; + if (Def() || Try() || If() || While() || Class() || For() || Switch()) { + if (!saw_eol) { + throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (Return() || Break() || Continue() || Equation()) { + if (!saw_eol) { + throw exception::eval_error("Two expressions missing line separator", File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = false; + } + else if (Block() || Eol()) { + has_more = true; + retval = true; + saw_eol = true; + } + else { + has_more = false; + } + } + + return retval; + } + + /// Parses the given input string, tagging parsed ast_nodes with the given m_filename. + bool parse(const std::string &t_input, std::string t_fname) { + m_position = Position(t_input.begin(), t_input.end()); + m_filename = std::make_shared(std::move(t_fname)); + + if ((t_input.size() > 1) && (t_input[0] == '#') && (t_input[1] == '!')) { + while (m_position.has_more() && (!Eol())) { + ++m_position; + } + /// \todo respect // -*- coding: utf-8 -*- on line 1 or 2 see: http://evanjones.ca/python-utf8.html) + } + + if (Statements()) { + if (m_position.has_more()) { + throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname); + } else { + build_match(0); + //debug_print(ast()); + return true; + } + } else { + return false; + } + } + }; + } +} + + +#ifdef CHAISCRIPT_MSVC +#pragma pop_macro("min") +#pragma pop_macro("max") +#endif + + +#endif /* CHAISCRIPT_PARSER_HPP_ */ + diff --git a/apps/common/script/chaiscript/language/chaiscript_prelude.chai b/apps/common/script/chaiscript/language/chaiscript_prelude.chai new file mode 100644 index 0000000000..55514bbaff --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_prelude.chai @@ -0,0 +1,539 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// and Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_PRELUDE_HPP_ +#define CHAISCRIPT_PRELUDE_HPP_ + +namespace chaiscript { +struct ChaiScript_Prelude { + static std::string chaiscript_prelude() { return R""( + +def lt(l, r) { + if (call_exists(`<`, l, r)) { + l < r + } else { + type_name(l) < type_name(r) + } +} + + +def gt(l, r) { + if (call_exists(`>`, l, r)) { + l > r + } else { + type_name(l) > type_name(r) + } +} + +def eq(l, r) { + if (call_exists(`==`, l, r)) { + l == r + } else { + false + } +} + +def new(x) { + eval(type_name(x))(); +} + +def clone(double x) { + double(x).clone_var_attrs(x) +} + +def clone(string x) { + string(x).clone_var_attrs(x) +} + +def clone(vector x) { + vector(x).clone_var_attrs(x) +} + + +def clone(int x) { + int(x).clone_var_attrs(x) +} + +def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) +{ + eval(type_name(x))(x).clone_var_attrs(x); +} + + +# to_string for Pair() +def to_string(x) : call_exists(first, x) && call_exists(second, x) { + "<" + x.first.to_string() + ", " + x.second.to_string() + ">"; +} + +# to_string for containers +def to_string(x) : call_exists(range, x) && !x.is_type("string"){ + "[" + x.join(", ") + "]"; +} + +# Prints to console with no carriage return +def puts(x) { + print_string(x.to_string()); +} + +# Prints to console with carriage return +def print(x) { + println_string(x.to_string()); +} + +# Returns the maximum value of two numbers +def max(a, b) { + if (a>b) { + a + } else { + b + } +} + +# Returns the minimum value of two numbers +def min(a, b) +{ + if (a 0) && (!r.empty())) { + inserter(r.front()); + r.pop_front(); + --i; + } +} + + +# Returns a new container with the given number of elements taken from the container +def take(container, num) { + auto retval := new(container); + take(container, num, back_inserter(retval)); + retval; +} + + +def take_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty()) && f(r.front())) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given elements match the second value function +def take_while(container, f) { + auto retval := new(container); + take_while(container, f, back_inserter(retval)); + retval; +} + + +def drop(container, num, inserter) : call_exists(range, container) { + auto r := range(container); + auto i = num; + while ((i > 0) && (!r.empty())) { + r.pop_front(); + --i; + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given number of elements dropped from the given container +def drop(container, num) { + auto retval := new(container); + drop(container, num, back_inserter(retval)); + retval; +} + + +def drop_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty())&& f(r.front())) { + r.pop_front(); + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given elements dropped that match the second value function +def drop_while(container, f) { + auto retval := new(container); + drop_while(container, f, back_inserter(retval)); + retval; +} + + +# Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements. +def reduce(container, func) : container.size() >= 2 && call_exists(range, container) { + auto r := range(container); + auto retval = r.front(); + r.pop_front(); + retval = func(retval, r.front()); + r.pop_front(); + while (!r.empty()) { + retval = func(retval, r.front()); + r.pop_front(); + } + retval; +} + + +# Returns a string of the elements in container delimited by the second value string +def join(container, delim) { + auto retval = ""; + auto range := range(container); + if (!range.empty()) { + retval += to_string(range.front()); + range.pop_front(); + while (!range.empty()) { + retval += delim; + retval += to_string(range.front()); + range.pop_front(); + } + } + retval; +} + + +def filter(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while (!r.empty()) { + if (f(r.front())) { + inserter(r.front()); + } + r.pop_front(); + } +} + + +# Returns a new Vector which match the second value function +def filter(container, f) { + auto retval := new(container); + filter(container, f, back_inserter(retval)); + retval; +} + + +def generate_range(x, y, inserter) { + auto i = x; + while (i <= y) { + inserter(i); + ++i; + } +} + + +# Returns a new Vector which represents the range from the first value to the second value +def generate_range(x, y) { + auto retval := Vector(); + generate_range(x,y,back_inserter(retval)); + retval; +} + + +# Returns a new Vector with the first value to the second value as its elements +def collate(x, y) { + return [x, y]; +} + + +def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) { + auto r_x := range(x); + auto r_y := range(y); + while (!r_x.empty() && !r_y.empty()) { + inserter(f(r_x.front(), r_y.front())); + r_x.pop_front(); + r_y.pop_front(); + } +} + + +# Returns a new Vector which joins matching elements of the second and third value with the first value function +def zip_with(f, x, y) { + auto retval := Vector(); + zip_with(f,x,y,back_inserter(retval)); + retval; +} + + +# Returns a new Vector which joins matching elements of the first and second +def zip(x, y) { + zip_with(collate, x, y); +} + + +# Returns the position of the second value string in the first value string +def string::find(string substr) { + find(this, substr, size_t(0)); +} + + +# Returns the position of last match of the second value string in the first value string +def string::rfind(string substr) { + rfind(this, substr, size_t(-1)); +} + + +# Returns the position of the first match of elements in the second value string in the first value string +def string::find_first_of(string list) { + find_first_of(this, list, size_t(0)); +} + + +# Returns the position of the last match of elements in the second value string in the first value string +def string::find_last_of(string list) { + find_last_of(this, list, size_t(-1)); +} + + +# Returns the position of the first non-matching element in the second value string in the first value string +def string::find_first_not_of(string list) { + find_first_not_of(this, list, size_t(0)); +} + + +# Returns the position of the last non-matching element in the second value string in the first value string +def string::find_last_not_of(string list) { + find_last_not_of(this, list, size_t(-1)); +} + + +def string::ltrim() { + drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}); +} + + +def string::rtrim() { + reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'})); +} + + +def string::trim() { + ltrim(rtrim(this)); +} + + +def find(container, value, Function compare_func) : call_exists(range, container) { + auto range := range(container); + while (!range.empty()) { + if (compare_func(range.front(), value)) { + return range; + } else { + range.pop_front(); + } + } + range; +} + + +def find(container, value) { + find(container, value, eq) +} + + +)""; +} + +}; +} + +#endif /* CHAISCRIPT_PRELUDE_HPP_ */ diff --git a/apps/common/script/chaiscript/language/chaiscript_prelude_docs.hpp b/apps/common/script/chaiscript/language/chaiscript_prelude_docs.hpp new file mode 100644 index 0000000000..25d32d108b --- /dev/null +++ b/apps/common/script/chaiscript/language/chaiscript_prelude_docs.hpp @@ -0,0 +1,830 @@ +/// This file is not technically part of the ChaiScript API. It is used solely for generating Doxygen docs +/// regarding the ChaiScript standard runtime library. + +/// \brief Items in this namespace exist in the ChaiScript language runtime. They are not part of the C++ API +namespace ChaiScript_Language +{ + +/// \page LangStandardLibraryRef ChaiScript Language Standard Library Reference +/// +/// ChaiScript, at its core, has some very functional programming-inspired habits. Few places show this off as clearly +/// as the prelude, itself a name taken as a nod to the popular functional language Haskell. This prelude is available +/// to all standard ChaiScript applications, and provides a simple foundation for using numbers, strings, and ranges +/// (the general category of Range cs and their iteration). +/// + + +/// \brief Generic concept of a value in ChaiScript. +/// +/// The Object type exists merely as a concept. All objects in ChaiScript support this concept +/// and have the following methods available to them. All objects are stored internally as chaiscript::Boxed_Value types. +/// +/// \sa chaiscript::Boxed_Value +class Object +{ + public: + /// \brief Returns the Type_Info value for this Object + Type_Info get_type_info() const; + + /// \brief Returns true if the Object is of the named type + bool is_type(string) const; + + /// \brief Returns true if the Object is of the Type_Info passed in + bool is_type(Type_Info) const; + + /// \brief Returns true if the Object is immutable + bool is_var_const() const; + + /// \brief Returns true if the Object is a pointer and the pointer is null + bool is_var_null() const; + + /// \brief Returns true if the Object is stored as a pointer + bool is_var_pointer() const; + + /// \brief Returns true if the Object is stored as a reference + bool is_var_reference() const; + + /// \brief Returns true if the Object does not contain a value is is undefined. + bool is_var_undef() const; + + /// \brief Returns the registered name of the type of the object. + /// + /// \sa Type_Info::name(); + string type_name() const; +}; + +/// \brief Item returned from a Range object from a Map +class Map_Pair +{ + public: + /// \brief Returns the key of the Map entry + const string first(); + + /// \brief Returns the value Object of the Map entry + Object second(); +}; + + +/// \brief Maps strings to Objects +/// +/// ChaiScript has a built in shortcut for generating Map objects: +/// +/// Example: +/// \code +/// eval> var m = ["a":1, "b":2]; +/// [, ] +/// eval> m.count("a"); +/// 1 +/// eval> m.count("c"); +/// 0 +/// eval> m.size(); +/// 2 +/// \endcode +/// +/// Implemented as std::map +/// +/// \sa Map_Pair +/// \sa chaiscript::bootstrap::standard_library::map_type +class Map +{ + public: + /// \brief Returns an object that implements the Range concept for the Map_Pair's in this Map + Range range(); + + /// \brief Returns an object that implements the Const_Range concept for the Map_Pair's in this Map + Const_Range range() const; + + /// \brief Returns the number of elements in the Map + int size() const; + + /// \brief Returns the item at the given key, creating an undefined Object if the key does not yet exist in the map + Object operator[](string); + + /// \brief Clears the map of all items + void clear(); + + /// \brief Returns the number of items in the Map with the given key. Returns 0 or 1 since this is not an std::multimap. + int count(string) const; + + /// \brief Returns true if the map contains no items + bool empty() const; + +}; + + +/// \brief A concept implemented by string, Vector and Map. It is convertible to Range, default constructable and back_insertable +class Container +{ + public: + void push_back(Object); + Range range(); + Const_Range range() const; +}; + + +/// \brief Converts o into a string. +/// +/// \code +/// eval> to_string(3).is_type("string")
+/// true
+/// \endcode +string to_string(Object o); + + +/// \brief Prints o to the terminal, without a trailing carriage return. Applies conversions to string automatically. +/// \code +/// eval> puts("hi, "); puts("there") +/// hi, thereeval> +/// \endcode +/// \sa to_string +/// \sa print +void puts(Object o); + + +/// \brief Prints o to the terminal, with a trailing carriage return. Applies conversions to string automatically +/// \code +/// eval> print("hello") +/// hello +/// eval> +/// \endcode +/// \sa to_string +/// \sa puts +void print(Object o); + +/// \brief ChaiScript representation of std::string. It is an std::string but only some member are exposed to ChaiScript. +/// +/// Because the ChaiScript string object is an std::string, it is directly convertible to and from std::string +/// using the chaiscript::boxed_cast and chaiscript::var functions. +/// +/// With the exception of string::trim, string::rtrim, string::ltrim, all members are direct pass-throughs to the +/// std::string of the same name. +/// +/// \note Object and function notations are equivalent in ChaiScript. This means that +/// \c "bob".find("b") and \c find("bob", "b") are exactly the same. Most examples below follow the +/// second formation of the function calls. +/// \sa \ref keyworddef for extending existing C++ classes in ChaiScript +/// \sa chaiscript::bootstrap::standard_library::string_type +class string +{ + public: + /// \brief Finds the first instance of substr. + /// \code + /// eval> find("abab", "ab") + /// 0 + /// \endcode + int find(string s) const; + + + /// \brief Finds the last instance of substr. + /// \code + /// eval> rfind("abab", "ab") + /// 2 + /// \endcode + int rfind(string s) const; + + /// \brief Finds the first of characters in list in the string. + /// + /// \code + /// eval> find_first_of("abab", "bec") + /// 1 + /// \endcode + int find_first_of(string list) const; + + /// \brief Finds the last of characters in list in the string. + /// + /// \code + /// eval> find_last_of("abab", "bec") + /// 3 + /// \endcode + int find_last_of(string list) const; + + /// \brief Finds the first non-matching character to list in the str string. + /// + /// \code + /// eval> find_first_not_of("abcd", "fec") + /// 0 + /// \endcode + int find_first_not_of(string list) const; + + /// \brief Finds the last non-matching character to list in the list string. + /// + /// \code + /// eval> find_last_not_of("abcd", "fec") + /// 3 + /// \endcode + int find_last_not_of(string list) const; + + /// \brief Removes whitespace from the front of the string, returning a new string + /// + /// \note This function is implemented as a ChaiScript function using the def member function notation. + /// + /// \code + /// eval> ltrim(" bob") + /// bob + /// \endcode + /// + /// \sa \ref keyworddef + string lstrim() const; + + /// \brief Removes whitespace from the back of the string, returning a new string + /// + /// \note This function is implemented as a ChaiScript function using the def member function notation. + /// + /// \code + /// eval> rtrim("bob ") + "|" + /// bob| + /// \endcode + /// + /// \sa \ref keyworddef + string rtrim() const; + + /// \brief Removes whitespace from the front and back of the string, returning a new string + /// + /// \note This function is implemented as a ChaiScript function using the def member function notation. + /// + /// \code + /// eval> trim(" bob ") + "|" + /// bob| + /// \endcode + /// + /// Equivalent to rtrim(ltrim(" bob ")); + /// + /// \sa \ref keyworddef + string trim() const; + + /// \brief Returns the character at the given index in the string, const version + const char &operator[](int t_index) const; + + /// \brief Returns the character at the given index in the string + char &operator[](int t_index); + + /// \brief Returns underlying const char * for C api compatibility + const char *c_str() const; + + /// \brief Returns a pointer to the raw data in the string + const char *data() const; + + /// \brief Resets the string to empty + void clear(); + + /// \brief Returns true if the string is empty + bool empty() const; + + /// \brief Returns the size of the string in bytes. + /// + /// This function normally returns size_t in C++. In ChaiScript the return value is cast to int + /// for ease of use. + int size() const; + + /// \brief Returns an object that implements the Range concept for the characters of this string + Range range(); + + /// \brief Returns an object that implements the Const_Range concept for the characters of this string + Const_Range range() const; +}; + +/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides +/// easy iteration over the elements in a container. +/// +/// Implemented by the template chaiscript::bootstrap::standard_library::Bidir_Range +/// +/// \sa Const_Range +class Range +{ + public: + /// \brief Returns the last item of the range + Object back(); + + /// \brief Returns true if the front and back pointers have passed each other, if no items + /// are left in the Range + bool empty() const; + + /// \brief Returns the first item of the range + Object front(); + + /// \brief Moves the back pointer back one. + /// + /// \post back() returns the element at back() - 1; + void pop_back(); + + /// \brief Moves the front pointer forward one + /// + /// \post front() returns the element at front() + 1; + void pop_front(); + +}; + +/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides +/// easy iteration over the elements in a container. Contained values are const. +/// +/// Implemented by the template chaiscript::bootstrap::standard_library::Const_Bidir_Range +/// +/// \sa Range +class Const_Range +{ + public: + /// \brief Returns the last item of the range + const Object back(); + + /// \brief Returns true if the front and back pointers have passed each other, if no items + /// are left in the Range + bool empty() const; + + /// \brief Returns the first item of the range + const Object front(); + + /// \brief Moves the back pointer back one. + /// + /// \post back() returns the element at back() - 1; + void pop_back(); + + /// \brief Moves the front pointer forward one + /// + /// \post front() returns the element at front() + 1; + void pop_front(); + +}; + +/// \brief A vector of Objects +/// +/// ChaiScript includes a shortcut for creating a Vector of Objects +/// +/// Example: +/// \code +/// eval> var v = [1,2,3,4] +/// [1, 2, 3, 4] +/// eval> v[0]; +/// 1 +/// eval> v.size(); +/// 4 +/// \endcode +/// +/// Implemented with std::vector +/// +/// \sa chaiscript::bootstrap::standard_library::vector_type +class Vector +{ + public: + /// \brief returns the Object at the given index. Throws an exception if the index does not exist + Object operator[](int t_index); + + /// \brief returns a const Object at the given index. Throws an exception if the index does not exist. + const Object operator[](int t_index) const; + + /// \brief returns the last item in the Vector + Object back(); + + /// \brief Clears the Vector of all items + void clear(); + + /// \brief Returns true if the Vector is contains 0 items + bool empty(); + + /// \brief Erases the element at the given index + void erase_at(int t_index); + + /// \brief Returns the first item in the Vector + Object front(); + + /// \brief Inserts a new item in the Vector at the given index. The item is not cloned on insert + /// + /// \sa insert_ref + void insert_ref_at(int, Object); + + /// \brief Inserts a new item in the Vector at the given index. The item is cloned on insert + /// + /// \sa insert_ref + void insert_at(int, Object); + + /// \brief Removes the last item from the Vector + void pop_back(); + + /// \brief Adds an item to the end of the Vector. The item is not cloned. + /// + /// \sa push_back + void push_back_ref(Object); + + /// \brief Adds an item to the end of the Vector. The item is cloned. + /// + /// \sa push_back_ref + void push_back(Object); + + /// \brief Returns a Range object for the entire vector + Range range(); + + /// \brief Returns a Const_Range object for the entire vector + Const_Range range() const; + + /// \brief Returns the number of elements in the Vector + int size() const; + +}; + +class Type_Info +{ + public: + /// \brief Compares this Type_Info object with another one and returns true if the two types are the same + /// after const, pointer, reference are removed. + bool bare_equal(Type_Info t_ti) const; + + /// \brief Returns the mangled C++ name for the type given by the compiler after const, pointer, reference is removed. + string cpp_bare_name() const; + + /// \brief Returns the mangled C++ name for the type given by the compiler. + string cpp_name() const; + + /// \brief Returns true if the type is const + bool is_type_const() const; + + /// \brief Returns true if the type is a pointer + bool is_type_pointer() const; + + /// \brief Returns true if the type is a reference + bool is_type_reference() const; + + /// \brief Returns true if the type is undefined + bool is_type_undef() const; + + /// \brief Returns true if the type is "void" + bool is_type_void() const; + + /// \brief Returns the ChaiScript registered name for the type if one exists. + string name() const; + +}; + + +/// \brief Represents a function object in ChaiScript +/// +/// A function object may be one function, such as: +/// \code +/// var f = fun(x) { return x; } +/// \endcode +/// +/// Or it may represent multiple functions +/// \code +/// var f2 = `-`; // represents the unary - as well as the set of binary - operators +/// \endcode +/// +/// Guarded function example +/// \code +/// def f3(x) : x > 2 { +/// return x; +/// } +/// \endcode +/// +/// Examples in the function definitions below will reference these examples +class Function +{ + public: + /// \brief Returns the annotation description of the function + string get_annotation() const; + + /// \brief Returns the arity of the function, -1 if the function takes a variable number of parameters + /// + /// Example: + /// \code + /// eval> f.get_arity() + /// 1 + /// eval> f2.get_arity() + /// -1 + /// \endcode + int get_arity() const; + + /// \brief Returns a vector of the contained functions + /// + /// Example: + /// \code + /// eval> f.get_contained_functions().size() + /// 0 + /// eval> f2.get_contained_functions().size() + /// 11 + /// eval> var v = f2.get_contained_functions(); + /// v[0].get_arity() + /// 2 + /// \endcode + Vector get_contained_functions() const; + + /// \brief Returns a vector of the contained functions + /// + /// Example: + /// \code + /// eval> f.get_guard() // Throws exception + /// Function does not have a guard + /// eval> f3.get_guard().get_arity() + /// 1 + /// \endcode + Function get_guard() const; + + /// \brief Returns a vector of Type_Info objects that represent the param types for this function. + /// The first value in the list is the return type. + /// + /// If this function is a conglomerate of several functions (get_contained_values().size() > 0) + /// then the function returns as many Type_Info objects as it can. If the functions contained all have + /// the same arity, then it represents the arity. If they have different arities, it returns only + /// one value - the return type. + /// + /// For each parameter that is the same type, the type is returned. If the types are different + /// then a Type_Info for Object is returned. + /// + /// Example: + /// \code + /// eval> f2.get_param_types().size(); // Returns a Type_Info for Object for the return type + /// 1 + /// \endcode + Vector get_param_types() const; + + /// \brief Returns true if the function has a guard to it. Always returns false for a conglomerate function + bool has_guard() const; + + /// \brief Calls the function with the given set of parameters and returns the value; + /// + /// Example: + /// \code + /// eval> `-`.call([2,1]); + /// 1 + /// \endcode + Object call(Vector t_params) const; +} + + + +/// \brief Returns the max of a or b. Requires that operator>(a, b) exists +/// Equivalent to +/// \code +/// return a>b?a:b; +/// \endcode +/// +/// Example: +/// \code +/// eval> max(4, 10) +/// 10 +/// \endcode +Object max(Object a, Object b); + +/// \brief Returns the min of a or b. Requires that operator<(a, b) exists +/// +/// Equivalent to +/// \code +/// return a min(4, 10) +/// 4 +/// \endcode +Object min(Object a, Object b); + +/// \brief Returns true if x is an even integer. +/// +/// Will also work on any non-integer type for which an operator%(x, int) exists +/// +/// Example: +/// \code +/// eval> even(4) +/// true +/// \endcode +bool even(Object x); + +/// \brief Returns true if x is an odd integer. +/// +/// Will also work on any non-integer type for which an operator%(x, int) exists +/// +/// Example: +/// \code +/// eval> odd(4) +/// false +/// \endcode +bool even(Object x); + + +/// \brief Applies the function f over each element in the Range c. +/// +/// Example: +/// \code +/// eval> for_each([1, 2, 3], print) +/// 1 +/// 2 +/// 3 +/// \endcode +void for_each(Range c, Function f); + + +/// \brief Applies f over each element in the Range c, joining all the results. +/// +/// Example: +/// \code +/// eval> map([1, 2, 3], odd) +/// [true, false, true] +/// \endcode +Object map(Range c, Function f); + + +/// \brief Starts with the initial value and applies the function f to it and the first element of the Range c. +/// The result is then applied to the second element, and so on until the elements are exhausted. +/// +/// Example: +/// \code +/// eval> foldl([1, 2, 3, 4], `+`, 0) +/// 10 +/// \endcode +Object foldl(Range c, Function f, Object initial); + + +/// \brief Returns the sum total of the values in the Range c. +/// +/// Example: +/// \code +/// eval> sum([1, 2, 3, 4]) +/// 10 +/// \endcode +/// +/// Equivalent to: +/// \code +/// foldl(c, `+`, 0.0); +/// \endcode +Numeric sum(Range c); + + +/// \brief Returns the product of the value in the Range c. +/// +/// Example: +/// \code +/// eval> product([1, 2, 3, 4]) +/// 24 +/// \endcode +/// +/// Equivalent to: +/// \code +/// foldl(c, `*`, 1.0); +/// \endcode +Numeric product(Range c); + + +/// \brief Takes num elements from the Range c, returning them. +/// +/// Example: +/// \code +/// eval> take([1, 2, 3, 4], 2) +/// [1, 2] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object take(Range c, int num); + + +/// \brief Takes elements from the Range c that match function f, stopping at the first non-match, returning them as a new Vector. +/// +/// Example: +/// \code +/// eval> take_while([1, 2, 3], odd) +/// [1] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object take_while(Range c, Function f); + + +/// \brief Drops num elements from the Range c, returning the remainder. +/// +/// Example: +/// \code +/// eval> drop([1, 2, 3, 4], 2) +/// [3, 4] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object drop(Range c, int num); + + +/// \brief Drops elements from the Range c that match f, stopping at the first non-match, returning the remainder. +/// +/// Example: +/// \code +/// eval> drop_while([1, 2, 3], odd) +/// [2, 3] +/// \endcode +Object drop_while(Range c, Function f); + + +/// \brief Similar to foldl, this takes the first two elements as its starting values for f. This assumes Range c has at least 2 elements. +/// +/// Example: +/// \code +/// eval> reduce([1, 2, 3, 4], `+`) +/// 10 +/// \endcode +Object reduce(Range c, Function f); + + +/// \brief Takes elements from Container c that match function f, return them. +/// +/// Example: +/// \code +/// eval> filter([1, 2, 3, 4], odd) +/// [1, 3] +/// \endcode +Object filter(Container c, Function f); + + +/// \brief Joins the elements of the Range c into a string, delimiting each with the delim string. +/// +/// Example: +/// \code +/// eval> join([1, 2, 3], "*") +/// 1*2*3 +/// \endcode +string join(Range c, string delim); + + +/// \brief Returns the contents of the Container c in reversed order. +/// +/// Example: +/// \code +/// eval> reverse([1, 2, 3, 4, 5, 6, 7]) +/// [7, 6, 5, 4, 3, 2, 1] +/// \endcode +Container reverse(Container c); + + +/// \brief Generates a new Vector filled with values starting at x and ending with y. +/// +/// Works on types supporting operator<=(x, y) and operator++(x) +/// +/// Example: +/// \code +/// eval> generate_range(1, 10) +/// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +/// \endcode +Vector generate_range(Object x, Object y); + + +/// \brief Returns a new Range with x and y concatenated. +/// +/// Example: +/// \code +/// eval> concat([1, 2, 3], [4, 5, 6]) +/// [1, 2, 3, 4, 5, 6] +/// \endcode +Object concat(Range x, Range y); + + +/// \brief Returns a new Vector with x and y as its values. +/// +/// Example: +/// \code +/// eval> collate(1, 2) +/// [1, 2] +/// \endcode +Vector collate(Object x, Object y); + + +/// \brief Applies f to elements of x and y, returning a new Vector with the result of each application. +/// +/// Example: +/// \code +/// eval> zip_with(`+`, [1, 2, 3], [4, 5, 6]) +/// [5, 7, 9] +/// \endcode +Vector zip_with(Function f, Range x, Range y); + + +/// \brief Collates elements of x and y, returning a new Vector with the result. +/// +/// Example: +/// \code +/// eval> zip([1, 2, 3], [4, 5, 6]) +/// [[1, 4], [2, 5], [3, 6]] +/// \endcode +Vector zip(Range x, Range y); + + +/// \brief returns true if there exists a call to the Function f that takes the given parameters +/// +/// Example: +/// \code +/// eval> call_exists(`+`, 1, 2) +/// true +/// \endcode +bool call_exists(Function f, ...); + +/// \brief Reverses a Range object so that the elements are accessed in reverse +Range retro(Range); + +/// \brief Reverses a Const_Range object so that the elements are accessed in reverse +Const_Range retro(Const_Range); + + +/// \brief Raises the given object as an exception. Any type of object can be thrown. +/// +/// Example: +/// \code +/// eval> try { throw(1); } catch (e) { print("Exception caught: " + to_string(e)); } +/// Exception caught: 1 +/// \endcode +/// +/// \sa \ref keywordtry +void throw(Object); +} + diff --git a/apps/common/script/chaiscript/utility/json.hpp b/apps/common/script/chaiscript/utility/json.hpp new file mode 100644 index 0000000000..56f28e7585 --- /dev/null +++ b/apps/common/script/chaiscript/utility/json.hpp @@ -0,0 +1,663 @@ +// From github.com/nbsdx/SimpleJSON. +// Released under the DWTFYW PL +// + + +#pragma once + +#ifndef SIMPLEJSON_HPP +#define SIMPLEJSON_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../chaiscript_defines.hpp" + +namespace json { + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +namespace { + string json_escape( const string &str ) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return output; + } + + bool isspace(const char c) + { +#ifdef CHAISCRIPT_MSVC + // MSVC warns on these line in some circumstances +#pragma warning(push) +#pragma warning(disable : 6330) +#endif + return ::isspace(c) != 0; +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + + + } +} + +class JSON +{ + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( string s ) : String( new string( s ) ){} + BackingData() : Int( 0 ){} + + deque *List; + map *Map; + string *String; + double Float; + long Int; + bool Bool; + } Internal; + + public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON() : Internal(), Type( Class::Null ){} + + explicit JSON(Class type) + : Internal(), Type(Class::Null) + { + SetType( type ); + } + + JSON( initializer_list list ) + : Internal(), Type(Class::Null) + { + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->ToString() ) = *std::next( i ); + } + + JSON( JSON&& other ) + : Internal( other.Internal ) + , Type( other.Type ) + { other.Type = Class::Null; other.Internal.Map = nullptr; } + + JSON& operator=( JSON&& other ) { + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; + } + + JSON( const JSON &other ) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + } + + JSON& operator=( const JSON &other ) { + if (&other == this) return *this; + + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; + } + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + template + JSON( T b, typename enable_if::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){} + + template + JSON( T i, typename enable_if::value && !is_same::value>::type* = 0 ) : Internal( long(i) ), Type( Class::Integral ){} + + template + JSON( T f, typename enable_if::value>::type* = 0 ) : Internal( double(f) ), Type( Class::Floating ){} + + template + JSON( T s, typename enable_if::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){} + + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + + static JSON Make( Class type ) { + return JSON(type); + } + + static JSON Load( const string & ); + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + template + typename enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename enable_if::value && !is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = string( s ); return *this; + } + + JSON& operator[]( const string &key ) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); + } + + JSON& operator[]( const size_t index ) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); + } + + JSON &at( const string &key ) { + return operator[]( key ); + } + + const JSON &at( const string &key ) const { + return Internal.Map->at( key ); + } + + JSON &at( unsigned index ) { + return operator[]( index ); + } + + const JSON &at( unsigned index ) const { + return Internal.List->at( index ); + } + + int length() const { + if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + bool hasKey( const string &key ) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; + } + + int size() const { + if( Type == Class::Object ) + return static_cast(Internal.Map->size()); + else if( Type == Class::Array ) + return static_cast(Internal.List->size()); + else + return -1; + } + + Class JSONType() const { return Type; } + + /// Functions for getting primitives from the JSON object. + bool IsNull() const { return Type == Class::Null; } + + string ToString() const { bool b; return ToString( b ); } + string ToString( bool &ok ) const { + ok = (Type == Class::String); + return ok ? *Internal.String : string(""); + } + + double ToFloat() const { bool b; return ToFloat( b ); } + double ToFloat( bool &ok ) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; + } + + long ToInt() const { bool b; return ToInt( b ); } + long ToInt( bool &ok ) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; + } + + bool ToBool() const { bool b; return ToBool( b ); } + bool ToBool( bool &ok ) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; + } + + JSONWrapper> ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); + } + + JSONWrapper> ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); + } + + JSONConstWrapper> ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); + } + + + JSONConstWrapper> ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); + } + + string dump( int depth = 1, string tab = " ") const { + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + string s = "{\n"; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ",\n"; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + } + + throw std::runtime_error("Unhandled JSON type"); + } + + friend std::ostream& operator<<( std::ostream&, const JSON & ); + + private: + void SetType( Class type ) { + if( type == Type ) + return; + + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new map(); break; + case Class::Array: Internal.List = new deque(); break; + case Class::String: Internal.String = new string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; + } + + private: + + Class Type; +}; + +inline JSON Array() { + return JSON::Make( JSON::Class::Array ); +} + +template +inline JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return arr; +} + +inline JSON Object() { + return JSON::Make( JSON::Class::Object ); +} + +inline std::ostream& operator<<( std::ostream &os, const JSON &json ) { + os << json.dump(); + return os; +} + +namespace { + JSON parse_next( const string &, size_t & ); + + void consume_ws( const string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; + } + + JSON parse_object( const string &str, size_t &offset ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return Object; + } + + for (;offset= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + return JSON(val); + } + + JSON parse_number( const string &str, size_t &offset ) { + JSON Number; + string val, exp_str; + char c = '\0'; + bool isDouble = false; + long exp = 0; + for (; offset < str.size() ;) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( offset < str.size() && (c == 'E' || c == 'e' )) { + c = str[ offset++ ]; + if( c == '-' ) { exp_str += '-';} + else if( c == '+' ) { } + else --offset; + + for (; offset < str.size() ;) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'"); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { + throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return Number; + } + + JSON parse_bool( const string &str, size_t &offset ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) { + offset += 4; + Bool = true; + } else if( str.substr( offset, 5 ) == "false" ) { + offset += 5; + Bool = false; + } else { + throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); + } + return Bool; + } + + JSON parse_null( const string &str, size_t &offset ) { + if( str.substr( offset, 4 ) != "null" ) { + throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'"); + } + offset += 4; + return JSON(); + } + + JSON parse_next( const string &str, size_t &offset ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return parse_array( str, offset ); + case '{' : return parse_object( str, offset ); + case '\"': return parse_string( str, offset ); + case 't' : + case 'f' : return parse_bool( str, offset ); + case 'n' : return parse_null( str, offset ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return parse_number( str, offset ); + } + throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'"); + } +} + +inline JSON JSON::Load( const string &str ) { + size_t offset = 0; + return parse_next( str, offset ); +} + +} // End Namespace json + + +#endif diff --git a/apps/common/script/chaiscript/utility/json_wrap.hpp b/apps/common/script/chaiscript/utility/json_wrap.hpp new file mode 100644 index 0000000000..d15528ffb8 --- /dev/null +++ b/apps/common/script/chaiscript/utility/json_wrap.hpp @@ -0,0 +1,158 @@ +#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP +#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP + +#include "json.hpp" + +namespace chaiscript +{ + class json_wrap + { + public: + + static ModulePtr library(ModulePtr m = std::make_shared()) + { + + m->add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json"); + m->add(chaiscript::fun(&json_wrap::to_json), "to_json"); + + return m; + + } + + private: + + static Boxed_Value from_json(const json::JSON &t_json) + { + switch( t_json.JSONType() ) { + case json::JSON::Class::Null: + return Boxed_Value(); + case json::JSON::Class::Object: + { + std::map m; + + for (const auto &p : t_json.ObjectRange()) + { + m.insert(std::make_pair(p.first, from_json(p.second))); + } + + return Boxed_Value(m); + } + case json::JSON::Class::Array: + { + std::vector vec; + + for (const auto &p : t_json.ArrayRange()) + { + vec.emplace_back(from_json(p)); + } + + return Boxed_Value(vec); + } + case json::JSON::Class::String: + return Boxed_Value(t_json.ToString()); + case json::JSON::Class::Floating: + return Boxed_Value(t_json.ToFloat()); + case json::JSON::Class::Integral: + return Boxed_Value(t_json.ToInt()); + case json::JSON::Class::Boolean: + return Boxed_Value(t_json.ToBool()); + } + + throw std::runtime_error("Unknown JSON type"); + } + + static Boxed_Value from_json(const std::string &t_json) + { + return from_json( json::JSON::Load(t_json) ); + } + + static std::string to_json(const Boxed_Value &t_bv) + { + return to_json_object(t_bv).dump(); + } + + static json::JSON to_json_object(const Boxed_Value &t_bv) + { + try { + const std::map m = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (const auto &o : m) + { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj; + for (size_t i = 0; i < v.size(); ++i) + { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a vector + } + + + try { + Boxed_Number bn(t_bv); + json::JSON obj; + if (Boxed_Number::is_floating_point(t_bv)) + { + obj = bn.get_as(); + } else { + obj = bn.get_as(); + } + return obj; + } catch (const chaiscript::detail::exception::bad_any_cast &) { + // not a number + } + + try { + bool b = boxed_cast(t_bv); + json::JSON obj; + obj = b; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a bool + } + + try { + std::string s = boxed_cast(t_bv); + json::JSON obj; + obj = s; + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a string + } + + + try { + const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); + + json::JSON obj; + for (const auto &attr : o.get_attrs()) + { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a dynamic object + } + + throw std::runtime_error("Unknown object type to convert to JSON"); + } + + + }; + + +} + +#endif diff --git a/apps/common/script/chaiscript/utility/utility.hpp b/apps/common/script/chaiscript/utility/utility.hpp new file mode 100644 index 0000000000..22ab8e96dd --- /dev/null +++ b/apps/common/script/chaiscript/utility/utility.hpp @@ -0,0 +1,107 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2016, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_UTILITY_UTILITY_HPP_ +#define CHAISCRIPT_UTILITY_UTILITY_HPP_ + +#include +#include +#include +#include + +#include "../chaiscript.hpp" +#include "../dispatchkit/proxy_functions.hpp" +#include "../dispatchkit/type_info.hpp" +#include "../dispatchkit/operators.hpp" + + +namespace chaiscript +{ + namespace utility + { + + /// Single step command for registering a class with ChaiScript + /// + /// \param[in,out] t_module Model to add class to + /// \param[in] t_class_name Name of the class being registered + /// \param[in] t_constructors Vector of constructors to add + /// \param[in] t_funcs Vector of methods to add + /// + /// \example Adding a basic class to ChaiScript in one step + /// + /// \code + /// chaiscript::utility::add_class(*m, + /// "test", + /// { constructor(), + /// constructor() }, + /// { {fun(&test::function), "function"}, + /// {fun(&test::function2), "function2"}, + /// {fun(&test::function3), "function3"}, + /// {fun(static_cast(&test::function_overload)), "function_overload" }, + /// {fun(static_cast(&test::function_overload)), "function_overload" }, + /// {fun(static_cast(&test::operator=)), "=" } + /// } + /// ); + /// + template + void add_class(ModuleType &t_module, + const std::string &t_class_name, + const std::vector &t_constructors, + const std::vector> &t_funcs) + { + t_module.add(chaiscript::user_type(), t_class_name); + + for(const chaiscript::Proxy_Function &ctor: t_constructors) + { + t_module.add(ctor, t_class_name); + } + + for(const auto &fun: t_funcs) + { + t_module.add(fun.first, fun.second); + } + } + + template + typename std::enable_if::value, void>::type + add_class(ModuleType &t_module, + const std::string &t_class_name, +#ifdef CHAISCRIPT_GCC_4_6 + const std::vector> &t_constants +#else + const std::vector::type, std::string>> &t_constants +#endif + ) + { + t_module.add(chaiscript::user_type(), t_class_name); + + t_module.add(chaiscript::constructor(), t_class_name); + t_module.add(chaiscript::constructor(), t_class_name); + + using namespace chaiscript::bootstrap::operators; + t_module.add([](){ + // add some comparison and assignment operators + return assign(not_equal(equal())); + }()); + +#ifdef CHAISCRIPT_GCC_4_6 + t_module.add(chaiscript::fun([](const Enum &e, const int &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const int &i, const Enum &e) { return i == e; }), "=="); +#else + t_module.add(chaiscript::fun([](const Enum &e, const typename std::underlying_type::type &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const typename std::underlying_type::type &i, const Enum &e) { return i == e; }), "=="); +#endif + + for (const auto &constant : t_constants) + { + t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second); + } + } + } +} + +#endif + diff --git a/apps/modelViewer/CMakeLists.txt b/apps/common/tfn_lib/CMakeLists.txt similarity index 71% rename from apps/modelViewer/CMakeLists.txt rename to apps/common/tfn_lib/CMakeLists.txt index e1bfffb69b..d0b8466bb1 100644 --- a/apps/modelViewer/CMakeLists.txt +++ b/apps/common/tfn_lib/CMakeLists.txt @@ -14,26 +14,12 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() +OSPRAY_CREATE_LIBRARY(tfn tfn_lib.cpp LINK ospray_common) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/apps/common) - -ADD_SUBDIRECTORY(miniSG) - -ADD_EXECUTABLE(ospModelViewer - # CPP sources - modelViewer.cpp - ) -TARGET_LINK_LIBRARIES(ospModelViewer - ospray_minisg - ospray_glut3d - ${OPENGL_LIBRARIES} - ${GLUT_LIBRARIES} - ${TBB_LIBRARY_MALLOC} - ${TBB_LIBRARY} - ) -INSTALL(TARGETS ospModelViewer - DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT apps +OSPRAY_CREATE_APPLICATION(CvtParaViewTfcn + convertParaViewTfcn.cpp + jsoncpp.cpp +LINK + ospray_common + ospray_tfn ) diff --git a/apps/common/tfn_lib/README.md b/apps/common/tfn_lib/README.md new file mode 100644 index 0000000000..8162861afb --- /dev/null +++ b/apps/common/tfn_lib/README.md @@ -0,0 +1,6 @@ +# tfn\_lib + +Utility and standalone library for saving/loading transfer functions across the different viewers. +Ideally will also have a standalone app for converting a ParaView exported transfer function to our +internal binary format. + diff --git a/apps/common/tfn_lib/convertParaViewTfcn.cpp b/apps/common/tfn_lib/convertParaViewTfcn.cpp new file mode 100644 index 0000000000..4efcdb6785 --- /dev/null +++ b/apps/common/tfn_lib/convertParaViewTfcn.cpp @@ -0,0 +1,157 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include +#include +#include +#include +#include +#include "json/json.h" +#include "tfn_lib.h" + +const static std::string USAGE = + "Usage: ./ospCvtParaViewTfcn \n" + "----\n" + "Converts the exported ParaView transfer functon JSON format to the\n" + " transfer function format used by OSPRay's sample apps\n"; + +using namespace ospcommon; + +inline float cvtSrgb(const float x) { + if (x <= 0.0031308) { + return 12.92 * x; + } else { + return (1.055) * std::pow(x, 1.0 / 2.4) - 0.055; + } +} + +int main(int argc, char **argv) { + if (argc < 3 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0) { + std::cout << USAGE; + return 1; + } + + std::ifstream paraViewFile(argv[1]); + if (!paraViewFile.is_open()) { + throw std::runtime_error("#ospCvtParaViewTfcn: Error - failed to open file " + std::string(argv[2])); + } + Json::Value paraViewFcn; + paraViewFile >> paraViewFcn; + if (paraViewFcn.isArray()) { + std::cout << "#ospCvtParaViewTfcn: Found array of transfer functions, exporting the first one\n"; + paraViewFcn = paraViewFcn[0]; + } + if (!paraViewFcn.isObject()) { + throw std::runtime_error("#ospCvtParaViewTfcn: Error - no transfer function object to import!\n"); + } + + std::string tfcnName; + if (paraViewFcn["Name"].type() == Json::stringValue) { + tfcnName = paraViewFcn["Name"].asString(); + std::cout << "#ospCvtParaViewTfcn: Converting transfer function '" << tfcnName << "'\n"; + } else { + throw std::runtime_error("#ospCvtParaViewTfcn: Error - failed to read transfer function name\n"); + } + + if (paraViewFcn["ColorSpace"].type() == Json::stringValue + && paraViewFcn["ColorSpace"].asString() == "Diverging") { + std::cout << "#ospCvtParaViewTfcn: WARNING: ParaView's diverging color space " + << "interpolation is not supported, colors may be incorrect\n"; + } + + + // Read the value, opacity pairs and ignore the strange extra 0.5, 0 entries + std::cout << "#ospCvtParaViewTfcn: Reading value, opacity pairs\n"; + std::vector opacities; + Json::Value pvOpacities = paraViewFcn["Points"]; + if (!pvOpacities.isArray()) { + std::cout << "#ospCvtParaViewTfcn: No opacity data, setting default of linearly increasing [0, 1]\n"; + opacities.push_back(vec2f(0.f, 0.f)); + opacities.push_back(vec2f(1.f, 1.f)); + } else { + // We the first 2 of every 4 values which are the (value, opacity) pair + // followed by some random (0.5, 0) value pair that ParaView throws in there + for (Json::Value::ArrayIndex i = 0; i < pvOpacities.size(); i += 4) { + const float val = pvOpacities[i].asFloat(); + const float opacity = pvOpacities[i + 1].asFloat(); + opacities.push_back(vec2f(val, opacity)); + } + } + const float dataValueMin = opacities[0].x; + const float dataValueMax = opacities.back().x; + // Re-scale the opacity value entries into [0, 1] + for (auto &v : opacities) { + v.x = (v.x - dataValueMin) / (dataValueMax - dataValueMin); + } + + // Read the (val, RGB) pairs + std::cout << "#ospCvtParaViewTfcn: Reading value, r, g, b tuples\n"; + std::vector rgbPoints; + Json::Value pvColors = paraViewFcn["RGBPoints"]; + if (!pvColors.isArray()) { + throw std::runtime_error("#ospCvtParaViewTfcn: Error - failed to find value, r, g, b 'RGBPoints' array\n"); + } + for (Json::Value::ArrayIndex i = 0; i < pvColors.size(); i += 4) { + const float val = (pvColors[i].asFloat() - dataValueMin) / (dataValueMax - dataValueMin); + const float r = pvColors[i + 1].asFloat(); + const float g = pvColors[i + 2].asFloat(); + const float b = pvColors[i + 3].asFloat(); + rgbPoints.push_back(vec4f(val, r, g, b)); + } + + // Sample the color values since the piecewise_linear transferfunction doesn't + // allow for value, RGB pairs to be set and assumes the RGB values are spaced uniformly + std::vector rgbSamples; + const int N_SAMPLES = 256; + size_t lo = 0; + size_t hi = 1; + for (int i = 0; i < N_SAMPLES; ++i) { + const float x = float(i) / float(N_SAMPLES - 1); + vec3f color(0); + if (i == 0) { + color = vec3f(rgbPoints[0].y, rgbPoints[0].z, rgbPoints[0].w); + } else if (i == N_SAMPLES - 1) { + color = vec3f(rgbPoints.back().y, rgbPoints.back().z, rgbPoints.back().w); + } else { + // If we're over this val, find the next one + if (x > rgbPoints[lo].x) { + for (size_t j = lo; j < rgbPoints.size() - 1; ++j) { + if (x <= rgbPoints[j + 1].x) { + lo = j; + hi = j + 1; + break; + } + } + } + const float delta = x - rgbPoints[lo].x; + const float interval = rgbPoints[hi].x - rgbPoints[lo].x; + if (delta == 0 || interval == 0) { + color = vec3f(rgbPoints[lo].y, rgbPoints[lo].z, rgbPoints[lo].w); + } else { + vec3f loColor = vec3f(rgbPoints[lo].y, rgbPoints[lo].z, rgbPoints[lo].w); + vec3f hiColor = vec3f(rgbPoints[hi].y, rgbPoints[hi].z, rgbPoints[hi].w); + color = loColor + delta / interval * (hiColor - loColor); + } + } + rgbSamples.push_back(color); + } + + tfn::TransferFunction converted(tfcnName, rgbSamples, opacities, dataValueMin, dataValueMax, 0.5f); + converted.save(argv[2]); + + return 0; +} + diff --git a/apps/common/tfn_lib/json/json-forwards.h b/apps/common/tfn_lib/json/json-forwards.h new file mode 100644 index 0000000000..ba8bd512de --- /dev/null +++ b/apps/common/tfn_lib/json/json-forwards.h @@ -0,0 +1,323 @@ +/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json-forwards.h" +/// This header provides forward declaration for all JsonCpp types. + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED +# define JSON_FORWARD_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include +#include //typdef String + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +# if _MSC_VER <= 1200 // MSVC 6 + // Microsoft Visual Studio 6 only support conversion from __int64 to double + // (no conversion from unsigned __int64). +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 + // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' + // characters in the debug information) + // All projects I've ever seen with VS6 were using this globally (not bothering + // with pragma push/pop). +# pragma warning(disable : 4786) +# endif // MSVC 6 + +# if _MSC_VER >= 1500 // MSVC 2008 + /// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +# endif + +#endif // defined(_MSC_VER) + +// In c++11 the override keyword allows you to explicity define that a function +// is intended to override the base-class version. This makes the code more +// managable and fixes a set of common hard-to-find bugs. +#if __cplusplus >= 201103L +# define JSONCPP_OVERRIDE override +#elif defined(_MSC_VER) && _MSC_VER > 1600 +# define JSONCPP_OVERRIDE override +#else +# define JSONCPP_OVERRIDE +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2010 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +# endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +# include "version.h" + +# if JSONCPP_USING_SECURE_MEMORY +# include "allocator.h" //typedef Allocator +# endif + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +#if JSONCPP_USING_SECURE_MEMORY +#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > +#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > +#define JSONCPP_OSTREAM std::basic_ostream> +#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > +#define JSONCPP_ISTREAM std::istream +#else +#define JSONCPP_STRING std::string +#define JSONCPP_OSTRINGSTREAM std::ostringstream +#define JSONCPP_OSTREAM std::ostream +#define JSONCPP_ISTRINGSTREAM std::istringstream +#define JSONCPP_ISTREAM std::istream +#endif // if JSONCPP_USING_SECURE_MEMORY +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED diff --git a/apps/common/tfn_lib/json/json.h b/apps/common/tfn_lib/json/json.h new file mode 100644 index 0000000000..c77ff47384 --- /dev/null +++ b/apps/common/tfn_lib/json/json.h @@ -0,0 +1,2139 @@ +/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGATED_H_INCLUDED +# define JSON_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "1.7.3" +# define JSONCPP_VERSION_MAJOR 1 +# define JSONCPP_VERSION_MINOR 7 +# define JSONCPP_VERSION_PATCH 3 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#ifdef JSONCPP_USING_SECURE_MEMORY +#undef JSONCPP_USING_SECURE_MEMORY +#endif +#define JSONCPP_USING_SECURE_MEMORY 0 +// If non-zero, the library zeroes any memory that it has allocated before +// it frees its memory. + +#endif // JSON_VERSION_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/version.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED +#include +#include //typdef String + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) || defined(__MINGW32__) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) // MSVC +# if _MSC_VER <= 1200 // MSVC 6 + // Microsoft Visual Studio 6 only support conversion from __int64 to double + // (no conversion from unsigned __int64). +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 + // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' + // characters in the debug information) + // All projects I've ever seen with VS6 were using this globally (not bothering + // with pragma push/pop). +# pragma warning(disable : 4786) +# endif // MSVC 6 + +# if _MSC_VER >= 1500 // MSVC 2008 + /// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +# endif + +#endif // defined(_MSC_VER) + +// In c++11 the override keyword allows you to explicity define that a function +// is intended to override the base-class version. This makes the code more +// managable and fixes a set of common hard-to-find bugs. +#if __cplusplus >= 201103L +# define JSONCPP_OVERRIDE override +#elif defined(_MSC_VER) && _MSC_VER > 1600 +# define JSONCPP_OVERRIDE override +#else +# define JSONCPP_OVERRIDE +#endif + +#ifndef JSON_HAS_RVALUE_REFERENCES + +#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // MSVC >= 2010 + +#ifdef __clang__ +#if __has_feature(cxx_rvalue_references) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // has_feature + +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +#define JSON_HAS_RVALUE_REFERENCES 1 +#endif // GXX_EXPERIMENTAL + +#endif // __clang__ || __GNUC__ + +#endif // not defined JSON_HAS_RVALUE_REFERENCES + +#ifndef JSON_HAS_RVALUE_REFERENCES +#define JSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifdef __clang__ +#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +# endif // GNUC version +#endif // __clang__ || __GNUC__ + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +#if __GNUC__ >= 6 +# define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif + +#if !defined(JSON_IS_AMALGAMATION) + +# include "version.h" + +# if JSONCPP_USING_SECURE_MEMORY +# include "allocator.h" //typedef Allocator +# endif + +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +#if JSONCPP_USING_SECURE_MEMORY +#define JSONCPP_STRING std::basic_string, Json::SecureAllocator > +#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream, Json::SecureAllocator > +#define JSONCPP_OSTREAM std::basic_ostream> +#define JSONCPP_ISTRINGSTREAM std::basic_istringstream, Json::SecureAllocator > +#define JSONCPP_ISTREAM std::istream +#else +#define JSONCPP_STRING std::string +#define JSONCPP_OSTRINGSTREAM std::ostringstream +#define JSONCPP_OSTREAM std::ostream +#define JSONCPP_ISTRINGSTREAM std::istringstream +#define JSONCPP_ISTREAM std::istream +#endif // if JSONCPP_USING_SECURE_MEMORY +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +//Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +# if defined(_MSC_VER) +# define JSONCPP_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) +# else +# define JSONCPP_NORETURN +# endif +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(JSONCPP_STRING const& msg); + ~Exception() throw() JSONCPP_OVERRIDE; + char const* what() const throw() JSONCPP_OVERRIDE; +protected: + JSONCPP_STRING msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(JSONCPP_STRING const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(JSONCPP_STRING const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). + static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null + static Value const& nullSingleton(); ///< Prefer this to null or nullRef. + + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); +#if JSON_HAS_RVALUE_REFERENCES + CZString(CZString&& other); +#endif + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); +#if JSON_HAS_RVALUE_REFERENCES + /// Move constructor + Value(Value&& other); +#endif + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value& operator=(Value other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! +#if JSONCPP_USING_SECURE_MEMORY + unsigned getCStringLength() const; //Allows you to understand the length of the CString +#endif + JSONCPP_STRING asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const JSONCPP_STRING& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const JSONCPP_STRING& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const JSONCPP_STRING& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(JSONCPP_STRING const& key, Value* removed); + /// Same as removeMember(JSONCPP_STRING const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const JSONCPP_STRING& key) const; + /// Same as isMember(JSONCPP_STRING const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const JSONCPP_STRING& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + JSONCPP_STRING getComment(CommentPlacement placement) const; + + JSONCPP_STRING toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(ptrdiff_t start); + void setOffsetLimit(ptrdiff_t limit); + ptrdiff_t getOffsetStart() const; + ptrdiff_t getOffsetLimit() const; + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + ptrdiff_t start_; + ptrdiff_t limit_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const JSONCPP_STRING& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + JSONCPP_STRING key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const JSONCPP_STRING& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const JSONCPP_STRING& path, const InArgs& in); + void addPathInArg(const JSONCPP_STRING& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const JSONCPP_STRING& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + JSONCPP_STRING name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + ValueConstIterator(ValueIterator const& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + JSONCPP_STRING getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + JSONCPP_STRING getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const JSONCPP_STRING& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) = 0; + + class JSON_API Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + JSONCPP_STRING errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + ~CharReaderBuilder() JSONCPP_OVERRIDE; + + CharReader* newCharReader() const JSONCPP_OVERRIDE; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](JSONCPP_STRING key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + JSONCPP_ISTREAM&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + JSONCPP_OSTREAM* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + ~StreamWriterBuilder() JSONCPP_OVERRIDE; + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](JSONCPP_STRING key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual JSONCPP_STRING write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + ~FastWriter() JSONCPP_OVERRIDE {} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + +private: + void writeValue(const Value& value); + + JSONCPP_STRING document_; + bool yamlCompatiblityEnabled_; + bool dropNullPlaceholders_; + bool omitEndingLineFeed_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + ~StyledWriter() JSONCPP_OVERRIDE {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE; + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const JSONCPP_STRING& value); + void writeIndent(); + void writeWithIndent(const JSONCPP_STRING& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_STRING document_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + unsigned int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(JSONCPP_STRING indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(JSONCPP_OSTREAM& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const JSONCPP_STRING& value); + void writeIndent(); + void writeWithIndent(const JSONCPP_STRING& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_OSTREAM* document_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +JSONCPP_STRING JSON_API valueToString(Int value); +JSONCPP_STRING JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +JSONCPP_STRING JSON_API valueToString(LargestInt value); +JSONCPP_STRING JSON_API valueToString(LargestUInt value); +JSONCPP_STRING JSON_API valueToString(double value); +JSONCPP_STRING JSON_API valueToString(bool value); +JSONCPP_STRING JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + JSONCPP_OSTRINGSTREAM oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + JSONCPP_OSTRINGSTREAM oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/apps/common/tfn_lib/jsoncpp.cpp b/apps/common/tfn_lib/jsoncpp.cpp new file mode 100644 index 0000000000..eed23ef866 --- /dev/null +++ b/apps/common/tfn_lib/jsoncpp.cpp @@ -0,0 +1,5261 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intended to be used with #include "json/json.h" + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json/json.h" + +#ifndef JSON_IS_AMALGAMATION +#error "Compile with -I PATH_TO_JSON_DIRECTORY" +#endif + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { + JSONCPP_STRING result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast(0xE0 | (0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [1,31]). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = static_cast(value % 10U + static_cast('0')); + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__QNXNTO__) +#define sscanf std::sscanf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +static int const stackLimit_g = 1000; +static int stackDepth_g = 0; // see readValue() + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr CharReaderPtr; +#else +typedef std::auto_ptr CharReaderPtr; +#endif + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + JSONCPP_STRING documentCopy(document.data(), document.data() + document.capacity()); + std::swap(documentCopy, document_); + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since JSONCPP_STRING is reference-counted, this at least does not + // create an extra copy. + JSONCPP_STRING doc; + std::getline(sin, doc, (char)EOF); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_g = 0; // Yes, this is bad coding, but options are limited. + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + // This is a non-reentrant way to support a stackLimit. Terrible! + // But this deprecated class has a security problem: Bad input can + // cause a seg-fault. This seems like a fair, binary-compatible way + // to prevent the problem. + if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_g; + + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_g; + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +static JSONCPP_STRING normalizeEOL(Reader::Location begin, Reader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +void Reader::readNumber() { + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } +} + +bool Reader::readString() { + Char c = '\0'; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = JSONCPP_STRING(numberName.asCString()); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + JSONCPP_STRING buffer(token.start_, token.end_); + JSONCPP_ISTRINGSTREAM is(buffer); + if (!(is >> value)) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool +Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +JSONCPP_STRING Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +JSONCPP_STRING Reader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + JSONCPP_STRING getFormattedErrorMessages() const; + std::vector getStructuredErrors() const; + bool pushError(const Value& value, const JSONCPP_STRING& message); + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + int stackDepth_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + stackDepth_(0), + features_(features), collectComments_() { +} + +bool OurReader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + stackDepth_ = 0; + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if (token.type_ != tokenError && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool OurReader::readValue() { + if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + ++stackDepth_; + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value v(std::numeric_limits::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value v(std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value v(-std::numeric_limits::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + --stackDepth_; + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool OurReader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char *p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool OurReader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + ptrdiff_t const length = token.end_ - token.start_; + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + size_t const ulength = static_cast(length); + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, ulength); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + JSONCPP_STRING buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool OurReader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast(token.end_ - token.start_ - 2)); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool OurReader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool OurReader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + ret_unicode = static_cast(unicode); + return true; +} + +bool +OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& OurReader::currentValue() { return *(nodes_.top()); } + +OurReader::Char OurReader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void OurReader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +JSONCPP_STRING OurReader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector OurReader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + OurReader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool OurReader::good() const { + return !errors_.size(); +} + + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); + valid_keys->insert("allowSpecialFloats"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, + Value* root, JSONCPP_STRING* errs) +{ + JSONCPP_OSTRINGSTREAM ssin; + ssin << sin.rdbuf(); + JSONCPP_STRING doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { + CharReaderBuilder b; + JSONCPP_STRING errs; + bool ok = parseFromStream(b, sin, &root, &errs); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + errs.c_str()); + + throwRuntimeError(errs); + } + return sin; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() + : current_(), isNull_(true) { +} + +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} + +Value& ValueIteratorBase::deref() const { + return current_->second; +} + +void ValueIteratorBase::increment() { + ++current_; +} + +void ValueIteratorBase::decrement() { + --current_; +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifdef JSON_USE_CPPTL_SMALLMAP + return other.current_ - current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +} + +void ValueIteratorBase::copy(const SelfType& other) { + current_ = other.current_; + isNull_ = other.isNull_; +} + +Value ValueIteratorBase::key() const { + const Value::CZString czstring = (*current_).first; + if (czstring.data()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); + } + return Value(czstring.index()); +} + +UInt ValueIteratorBase::index() const { + const Value::CZString czstring = (*current_).first; + if (!czstring.data()) + return czstring.index(); + return Value::UInt(-1); +} + +JSONCPP_STRING ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return JSONCPP_STRING(); + return JSONCPP_STRING(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueConstIterator::ValueConstIterator(ValueIterator const& other) + : ValueIteratorBase(other) {} + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t +#include // min() + +#define JSON_ASSERT_UNREACHABLE assert(false) + +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +//const unsigned char& kNullRef = kNull[0]; +//const Value& Value::null = reinterpret_cast(kNullRef); +//const Value& Value::nullRef = null; + +// static +Value const& Value::nullSingleton() +{ + static Value const* nullStatic = new Value; + return *nullStatic; +} + +// for backwards compatibility, we'll leave these global references around, but DO NOT +// use them in JSONCPP library code any more! +Value const& Value::null = Value::nullSingleton(); +Value const& Value::nullRef = Value::nullSingleton(); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + //return d >= static_cast(min) && d <= static_cast(max); + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + static_cast(Int64(value & 1)); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + size_t length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= static_cast(Value::maxInt)) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast(Value::maxInt) - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast(sizeof(unsigned)) + 1U; + char* newString = static_cast(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). + */ +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length==0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + free(value); +} +static inline void releaseStringValue(char* value, unsigned) { + free(value); +} +#endif // JSONCPP_USING_SECURE_MEMORY + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +Exception::Exception(JSONCPP_STRING const& msg) + : msg_(msg) +{} +Exception::~Exception() throw() +{} +char const* Exception::what() const throw() +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +LogicError::LogicError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) +{ + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) +{ + throw LogicError(msg); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) +{} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_, 0u); +} + +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_, 0u); + comment_ = 0; + } + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text, len); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +// Notes: policy_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} + +Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} + +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = static_cast(other.cstr_ + ? (static_cast(other.storage_.policy_) == noDuplication + ? noDuplication : duplicate) + : static_cast(other.storage_.policy_)) & 3U; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif + +Value::CZString::~CZString() { + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + } +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); +} + +bool Value::CZString::operator==(const CZString& other) const { + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType vtype) { + initBasic(vtype); + switch (vtype) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(strlen(value))); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(beginValue, static_cast(endValue - beginValue)); +} + +Value::Value(const JSONCPP_STRING& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateAndPrefixStringValue(value.data(), static_cast(value.length())); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast(value.length())); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(Value const& other) + : type_(other.type_), allocated_(false) + , + comments_(0), start_(other.start_), limit_(other.limit_) +{ + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); + allocated_ = true; + } else { + value_.string_ = other.value_.string_; + allocated_ = false; + } + break; + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); + } + } +} + +#if JSON_HAS_RVALUE_REFERENCES +// Move constructor +Value::Value(Value&& other) { + initBasic(nullValue); + swap(other); +} +#endif + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releasePrefixedStringValue(value_.string_); + break; + case arrayValue: + case objectValue: + delete value_.map_; + break; + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; + + value_.uint_ = 0; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swapPayload(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2 & 0x1; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; +} + +JSONCPP_STRING Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return JSONCPP_STRING(this_str, this_len); + } + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast(integerToDouble(value_.uint_)); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { + case arrayValue: + case objectValue: + value_.map_->clear(); + break; + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + JSON_ASSERT(size() == newSize); + } +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return nullSingleton(); + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return nullSingleton(); + return (*it).second; +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; + allocated_ = allocated; + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(strlen(key)), CZString::noDuplication); // NOTE! + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &nullSingleton() ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +Value const* Value::find(char const* key, char const* cend) const +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullSingleton(); + return *found; +} +Value const& Value::operator[](JSONCPP_STRING const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullSingleton(); + return *found; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); +} + +Value& Value::operator[](const JSONCPP_STRING& key) { + return resolveReference(key.data(), key.data() + key.length()); +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return resolveReference(key.c_str(), key.end_c_str()); +} +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullSingleton(); + return *found; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); +} + + +bool Value::removeMember(const char* key, const char* cend, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast(cend-key), CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return false; + *removed = it->second; + value_.map_->erase(it); + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); +} +bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullSingleton(); + + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const JSONCPP_STRING& key) +{ + return removeMember(key.c_str()); +} + +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), key.end_c_str(), defaultValue); +} +#endif + +bool Value::isMember(char const* key, char const* cend) const +{ + Value const* value = find(key, cend); + return NULL != value; +} +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(JSONCPP_STRING const& key) const +{ + return isMember(key.data(), key.data() + key.length()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str(), key.end_c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) { + members.push_back(JSONCPP_STRING((*it).first.data(), + (*it).first.length())); + } + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == realValue || isIntegral(); } + +bool Value::isNumeric() const { return isIntegral() || isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); +} + +void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +JSONCPP_STRING Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } + +ptrdiff_t Value::getOffsetStart() const { return start_; } + +ptrdiff_t Value::getOffsetLimit() const { return limit_; } + +JSONCPP_STRING Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const JSONCPP_STRING& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const JSONCPP_STRING& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(JSONCPP_STRING(beginName, current)); + } + } +} + +void Path::addPathInArg(const JSONCPP_STRING& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::nullSingleton()) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(_AIX) +#if !defined(isfinite) +#include +#define isfinite finite +#endif +#elif defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ + _Isfinitef(x) : _IsFinite(x))) +#else +#include +#define isfinite finite +#endif +#endif +#else +#include +#if !(defined(__QNXNTO__)) // QNX already defines isfinite +#define isfinite std::isfinite +#endif +#endif + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__BORLANDC__) +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace Json { + +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr StreamWriterPtr; +#else +typedef std::auto_ptr StreamWriterPtr; +#endif + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +JSONCPP_STRING valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); + *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } + assert(current >= buffer); + return current; +} + +JSONCPP_STRING valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +JSONCPP_STRING valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +JSONCPP_STRING valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +namespace { +JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[32]; + int len = -1; + + char formatString[6]; + sprintf(formatString, "%%.%dg", precision); + + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distingish the + // concepts of reals and integers. + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), formatString, value); + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} +} + +JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } + +JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } + +JSONCPP_STRING valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something.) + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +JSONCPP_STRING FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast(end-str)); + break; + } + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += '['; + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { + if (index > 0) + document_ += ','; + writeValue(value[index]); + } + document_ += ']'; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += '{'; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const JSONCPP_STRING& name = *it; + if (it != members.begin()) + document_ += ','; + document_ += valueToQuotedStringN(name.data(), static_cast(name.length())); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += '}'; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +JSONCPP_STRING StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ','; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(indentString_.size() >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + document_ += *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + writeIndent(); + ++iter; + } + + // Comments are stripped of trailing newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += root.getComment(commentAfter); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + indented_ = true; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + { + // Is NULL possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const JSONCPP_STRING& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { + if (!indented_) writeIndent(); + *document_ << value; + indented_ = false; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << ' ' << root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *document_ << root.getComment(commentAfter); + } + indented_ = false; +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(JSONCPP_STRING const& value); + void writeIndent(); + void writeWithIndent(JSONCPP_STRING const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector ChildValues; + + ChildValues childValues_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + CommentStyle::Enum cs_; + JSONCPP_STRING colonSymbol_; + JSONCPP_STRING nullSymbol_; + JSONCPP_STRING endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) + , useSpecialFloats_(useSpecialFloats) + , precision_(precision) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_ = ""; + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + break; + case stringValue: + { + // Is NULL is possible for value.string_? + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast(end-str))); + else pushValue(""); + break; + } + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + JSONCPP_STRING const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ", "; + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; + } + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + JSONCPP_STRING indentation = settings_["indentation"].asString(); + JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + JSONCPP_STRING colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + JSONCPP_STRING nullSymbol = "null"; + if (dnp) { + nullSymbol = ""; + } + if (pre > 17) pre = 17; + JSONCPP_STRING endingLineFeedSymbol = ""; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +static void getValidWriterKeys(std::set* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); + valid_keys->insert("useSpecialFloats"); + valid_keys->insert("precision"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { + JSONCPP_OSTRINGSTREAM sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); +} + +JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/apps/common/tfn_lib/tfn_lib.cpp b/apps/common/tfn_lib/tfn_lib.cpp new file mode 100644 index 0000000000..998c37cc08 --- /dev/null +++ b/apps/common/tfn_lib/tfn_lib.cpp @@ -0,0 +1,134 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include +#include +#include "tfn_lib.h" + +using namespace ospcommon; +using namespace tfn; + +// The magic number is 'OSTF' in ASCII +const static uint32_t MAGIC_NUMBER = 0x4f535446; +const static uint64_t CURRENT_VERSION = 1; + +TransferFunction::TransferFunction(const FileName &fileName) { + std::ifstream fin(fileName.c_str(), std::ios::binary); + if (!fin.is_open()) { + throw std::runtime_error("File " + fileName.str() + " not found"); + } + // Verify this is actually a tfn::TransferFunction data file + uint32_t magic = 0; + if (!fin.read(reinterpret_cast(&magic), sizeof(uint32_t))) { + throw std::runtime_error("Failed to read magic number header from " + fileName.str()); + } + if (magic != MAGIC_NUMBER) { + throw std::runtime_error("Read invalid identification header from " + fileName.str()); + } + uint64_t version = 0; + if (!fin.read(reinterpret_cast(&version), sizeof(uint64_t))) { + throw std::runtime_error("Failed to read version header from " + fileName.str()); + } + // Check if it's a supported version we can parse + if (version != CURRENT_VERSION) { + throw std::runtime_error("Got invalid version number from " + fileName.str()); + } + + uint64_t nameLen = 0; + if (!fin.read(reinterpret_cast(&nameLen), sizeof(uint64_t))) { + throw std::runtime_error("Failed to read nameLength header from " + fileName.str()); + } + name.resize(nameLen); + if (!fin.read(&name[0], nameLen)) { + throw std::runtime_error("Failed to read name from " + fileName.str()); + } + uint64_t numColors = 0; + if (!fin.read(reinterpret_cast(&numColors), sizeof(uint64_t))) { + throw std::runtime_error("Failed to read numColors header from " + fileName.str()); + } + uint64_t numOpacities = 0; + if (!fin.read(reinterpret_cast(&numOpacities), sizeof(uint64_t))) { + throw std::runtime_error("Failed to read numOpacities header from " + fileName.str()); + } + if (!fin.read(reinterpret_cast(&dataValueMin), sizeof(double))) { + throw std::runtime_error("Failed to read dataValueMin header from " + fileName.str()); + } + if (!fin.read(reinterpret_cast(&dataValueMax), sizeof(double))) { + throw std::runtime_error("Failed to read dataValueMax header from " + fileName.str()); + } + if (!fin.read(reinterpret_cast(&opacityScaling), sizeof(float))) { + throw std::runtime_error("Failed to read opacityScaling header from " + fileName.str()); + } + rgbValues.resize(numColors, vec3f(0)); + if (!fin.read(reinterpret_cast(rgbValues.data()), numColors * 3 * sizeof(float))) { + throw std::runtime_error("Failed to read color values from " + fileName.str()); + } + opacityValues.resize(numOpacities, vec2f(0)); + if (!fin.read(reinterpret_cast(opacityValues.data()), numOpacities * 2 * sizeof(float))) { + throw std::runtime_error("Failed to read opacity values from " + fileName.str()); + } +} +TransferFunction::TransferFunction(const std::string &name, + const std::vector &rgbValues, + const std::vector &opacityValues, const double dataValueMin, + const double dataValueMax, const float opacityScaling) + : name(name), rgbValues(rgbValues), opacityValues(opacityValues), dataValueMin(dataValueMin), + dataValueMax(dataValueMax), opacityScaling(opacityScaling) +{} +void TransferFunction::save(const FileName &fileName) const { + std::ofstream fout(fileName.c_str(), std::ios::binary); + if (!fout.is_open()) { + throw std::runtime_error("Failed to open " + fileName.str() + " for writing"); + } + if (!fout.write(reinterpret_cast(&MAGIC_NUMBER), sizeof(uint32_t))) { + throw std::runtime_error("Failed to write magic number header to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(&CURRENT_VERSION), sizeof(uint64_t))) { + throw std::runtime_error("Failed to write version header to " + fileName.str()); + } + + const uint64_t nameLen = name.size(); + if (!fout.write(reinterpret_cast(&nameLen), sizeof(uint64_t))) { + throw std::runtime_error("Failed to write nameLength header to " + fileName.str()); + } + if (!fout.write(name.data(), nameLen)) { + throw std::runtime_error("Failed to write name to " + fileName.str()); + } + const uint64_t numColors = rgbValues.size(); + if (!fout.write(reinterpret_cast(&numColors), sizeof(uint64_t))) { + throw std::runtime_error("Failed to write numColors header to " + fileName.str()); + } + const uint64_t numOpacities = opacityValues.size(); + if (!fout.write(reinterpret_cast(&numOpacities), sizeof(uint64_t))) { + throw std::runtime_error("Failed to write numOpacities header to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(&dataValueMin), sizeof(double))) { + throw std::runtime_error("Failed to write dataValueMin header to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(&dataValueMax), sizeof(double))) { + throw std::runtime_error("Failed to write dataValueMax header to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(&opacityScaling), sizeof(float))) { + throw std::runtime_error("Failed to write opacityScaling header to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(rgbValues.data()), numColors * 3 * sizeof(float))) { + throw std::runtime_error("Failed to write color values to " + fileName.str()); + } + if (!fout.write(reinterpret_cast(opacityValues.data()), numOpacities * 2 * sizeof(float))) { + throw std::runtime_error("Failed to write opacity values to " + fileName.str()); + } +} + diff --git a/apps/common/tfn_lib/tfn_lib.h b/apps/common/tfn_lib/tfn_lib.h new file mode 100644 index 0000000000..6d5b622ad2 --- /dev/null +++ b/apps/common/tfn_lib/tfn_lib.h @@ -0,0 +1,71 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include "ospcommon/vec.h" +#include "ospcommon/FileName.h" + +#ifdef _WIN32 + #ifdef ospray_tfn_EXPORTS + #define OSPTFNLIB_INTERFACE __declspec(dllexport) + #else + #define OSPTFNLIB_INTERFACE __declspec(dllimport) + #endif +#else + #define OSPTFNLIB_INTERFACE +#endif + +/* The transfer function file format used by the OSPRay sample apps is a + * little endian binary format with the following layout: + * uint32: magic number identifying the file + * uint64: version number + * uint64: length of the name of the transfer function (not including \0) + * [char...]: name of the transfer function (without \0) + * uint64: number of vec3f color values + * uint64: numer of vec2f data value, opacity value pairs + * float64: data value min + * float64: data value max + * float32: opacity scaling value, opacity values should be scaled + * by this factor + * [ospcommon::vec3f...]: RGB values + * [ospcommon::vec2f...]: data value, opacity value pairs + */ + +namespace tfn { + +struct OSPTFNLIB_INTERFACE TransferFunction { + std::string name; + std::vector rgbValues; + std::vector opacityValues; + double dataValueMin; + double dataValueMax; + float opacityScaling; + + // Load the transfer function data in the file + TransferFunction(const ospcommon::FileName &fileName); + // Construct a transfer function from some existing one + TransferFunction(const std::string &name, + const std::vector &rgbValues, + const std::vector &opacityValues, const double dataValueMin, + const double dataValueMax, const float opacityScaling); + // Save the transfer function data to the file + void save(const ospcommon::FileName &fileName) const; +}; + +} + diff --git a/apps/common/widgets/CMakeLists.txt b/apps/common/widgets/CMakeLists.txt index db2b79a839..725e9dd6b1 100644 --- a/apps/common/widgets/CMakeLists.txt +++ b/apps/common/widgets/CMakeLists.txt @@ -14,29 +14,23 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) +IF (OSPRAY_MODULE_DISPLAY_WALD) + INCLUDE_DIRECTORIES(${DISPLAY_WALD_INCLUDE_DIR}) + ADD_DEFINITIONS(-DOSPRAY_DISPLAY_WALD=1) +ENDIF() -ADD_LIBRARY(ospray_glut3d SHARED glut3D.cpp) -TARGET_LINK_LIBRARIES(ospray_glut3d ospray +SET(LIBS + ospray + ospray_common ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} -) -OSPRAY_SET_LIBRARY_VERSION(ospray_glut3d) -INSTALL(TARGETS ospray_glut3d - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT lib - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT lib - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel -) + ) - -############################################################## -# redistribute freeglut -############################################################## - -IF (WIN32) - INSTALL(PROGRAMS ${GLUT_DLL} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT apps) # 3rd party? -ENDIF() +OSPRAY_CREATE_LIBRARY(glut3d + Glut3dExport.h + glut3D.cpp + OSPGlutViewer.cpp + LINK + ${LIBS} +) diff --git a/apps/common/widgets/Glut3dExport.h b/apps/common/widgets/Glut3dExport.h new file mode 100644 index 0000000000..fff08f745c --- /dev/null +++ b/apps/common/widgets/Glut3dExport.h @@ -0,0 +1,27 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#ifdef _WIN32 +# ifdef ospray_glut3d_EXPORTS +# define OSPRAY_GLUT3D_INTERFACE __declspec(dllexport) +# else +# define OSPRAY_GLUT3D_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPRAY_GLUT3D_INTERFACE +#endif diff --git a/apps/common/widgets/OSPGlutViewer.cpp b/apps/common/widgets/OSPGlutViewer.cpp new file mode 100644 index 0000000000..e72219cf5e --- /dev/null +++ b/apps/common/widgets/OSPGlutViewer.cpp @@ -0,0 +1,338 @@ +// ======================================================================== // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "OSPGlutViewer.h" + +using std::cout; +using std::endl; + +using std::string; + +using std::lock_guard; +using std::mutex; + +using namespace ospcommon; + +// Static local helper functions ////////////////////////////////////////////// + +// helper function to write the rendered image as PPM file +static void writePPM(const string &fileName, const int sizeX, const int sizeY, + const uint32_t *pixel) +{ + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); +} + +// MSGViewer definitions ////////////////////////////////////////////////////// + +namespace ospray { + +OSPGlutViewer::OSPGlutViewer(const box3f &worldBounds, cpp::Model model, + cpp::Renderer renderer, cpp::Camera camera) + : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), + m_model(model), + m_fb(nullptr), + m_renderer(renderer), + m_camera(camera), + m_queuedRenderer(nullptr), + m_alwaysRedraw(true), + m_accumID(-1), + m_fullScreen(false), + m_useDisplayWall(false) +{ + setWorldBounds(worldBounds); + + m_renderer.set("world", m_model); + m_renderer.set("model", m_model); + m_renderer.set("camera", m_camera); + m_renderer.commit(); + +#if 0 + cout << "#ospGlutViewer: set world bounds " << worldBounds + << ", motion speed " << motionSpeed << endl; +#endif + + m_resetAccum = false; +} + +void OSPGlutViewer::setRenderer(OSPRenderer renderer) +{ + lock_guard lock{m_rendererMutex}; + m_queuedRenderer = renderer; +} + +void OSPGlutViewer::resetAccumulation() +{ + m_resetAccum = true; +} + +void OSPGlutViewer::toggleFullscreen() +{ + m_fullScreen = !m_fullScreen; + + if(m_fullScreen) { + glutFullScreen(); + } else { + glutPositionWindow(0,10); + } +} + +void OSPGlutViewer::resetView() +{ + viewPort = m_viewPort; +} + +void OSPGlutViewer::printViewport() +{ + printf("-vp %f %f %f -vu %f %f %f -vi %f %f %f\n", + viewPort.from.x, viewPort.from.y, viewPort.from.z, + viewPort.up.x, viewPort.up.y, viewPort.up.z, + viewPort.at.x, viewPort.at.y, viewPort.at.z); + fflush(stdout); +} + +void OSPGlutViewer::saveScreenshot(const std::string &basename) +{ + const uint32_t *p = (uint32_t*)m_fb.map(OSP_FB_COLOR); + writePPM(basename + ".ppm", m_windowSize.x, m_windowSize.y, p); + cout << "#ospGlutViewer: saved current frame to '" << basename << ".ppm'" + << endl; +} + +void OSPGlutViewer::setDisplayWall(const OSPGlutViewer::DisplayWall &dw) +{ + displayWall = dw; + m_useDisplayWall = true; +} + +void OSPGlutViewer::reshape(const vec2i &newSize) +{ + Glut3DWidget::reshape(newSize); + m_windowSize = newSize; + m_fb = cpp::FrameBuffer(osp::vec2i{newSize.x, newSize.y}, OSP_FB_SRGBA, + OSP_FB_COLOR | OSP_FB_DEPTH | OSP_FB_ACCUM); + + m_fb.clear(OSP_FB_ACCUM); + + /*! for now, let's just attach the pixel op to the _main_ frame + buffer - eventually we need to have a _second_ frame buffer + of the proper (much higher) size, but for now let's just use + the existing one... */ + if (m_useDisplayWall && displayWall.fb.handle() != m_fb.handle()) { + PRINT(displayWall.size); + displayWall.fb = + ospray::cpp::FrameBuffer((const osp::vec2i&)displayWall.size, + OSP_FB_NONE, + OSP_FB_COLOR | OSP_FB_DEPTH | OSP_FB_ACCUM); + + displayWall.fb.clear(OSP_FB_ACCUM); + + if (displayWall.po.handle() == nullptr) { +#if OSPRAY_DISPLAY_WALD + displayWall.po = ospray::cpp::PixelOp("display_wald"); +#else + displayWall.po = ospray::cpp::PixelOp("display_wall"); +#endif + displayWall.po.set("hostname", displayWall.hostname); + displayWall.po.set("streamName", displayWall.streamName); + displayWall.po.commit(); + } + + displayWall.fb.setPixelOp(displayWall.po); + } + + m_camera.set("aspect", viewPort.aspect); + m_camera.commit(); + viewPort.modified = true; + forceRedraw(); +} + +void OSPGlutViewer::keypress(char key, const vec2i &where) +{ + switch (key) { + case 'R': + m_alwaysRedraw = !m_alwaysRedraw; + forceRedraw(); + break; + case '!': + saveScreenshot("ospglutviewer"); + break; + case 'X': + if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(1,0,0); + } + viewPort.modified = true; + forceRedraw(); + break; + case 'Y': + if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,1,0); + } + viewPort.modified = true; + forceRedraw(); + break; + case 'Z': + if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,0,1); + } + viewPort.modified = true; + forceRedraw(); + break; + case 'f': + toggleFullscreen(); + break; + case 'r': + resetView(); + break; + case 'p': + printViewport(); + break; + default: + Glut3DWidget::keypress(key,where); + } +} + +void OSPGlutViewer::mouseButton(int32_t whichButton, + bool released, + const vec2i &pos) +{ + Glut3DWidget::mouseButton(whichButton, released, pos); + if((currButtonState == (1< 0) m_fps.doneRender(); + + // NOTE: consume a new renderer if one has been queued by another thread + switchRenderers(); + + if (m_resetAccum) { + m_fb.clear(OSP_FB_ACCUM); + m_resetAccum = false; + } + + m_fps.startRender(); + //} + + ++frameID; + + if (viewPort.modified) { + static bool once = true; + if(once) { + m_viewPort = viewPort; + once = false; + } + Assert2(m_camera.handle(),"ospray camera is null"); + m_camera.set("pos", viewPort.from); + auto dir = viewPort.at - viewPort.from; + m_camera.set("dir", dir); + m_camera.set("up", viewPort.up); + m_camera.set("aspect", viewPort.aspect); + m_camera.commit(); + viewPort.modified = false; + m_accumID=0; + m_fb.clear(OSP_FB_ACCUM); + + if (m_useDisplayWall) + displayWall.fb.clear(OSP_FB_ACCUM); + } + + m_renderer.renderFrame(m_fb, OSP_FB_COLOR | OSP_FB_ACCUM); + if (m_useDisplayWall) { + m_renderer.renderFrame(displayWall.fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } + ++m_accumID; + + // set the glut3d widget's frame buffer to the opsray frame buffer, + // then display + ucharFB = (uint32_t *)m_fb.map(OSP_FB_COLOR); + frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; + Glut3DWidget::display(); + + m_fb.unmap(ucharFB); + + // that pointer is no longer valid, so set it to null + ucharFB = nullptr; + + std::string title("OSPRay GLUT Viewer"); + + if (m_alwaysRedraw) { + title += " (" + std::to_string((long double)m_fps.getFPS()) + " fps)"; + setTitle(title); + forceRedraw(); + } else { + setTitle(title); + } +} + +void OSPGlutViewer::switchRenderers() +{ + lock_guard lock{m_rendererMutex}; + + if (m_queuedRenderer.handle()) { + m_renderer = m_queuedRenderer; + m_queuedRenderer = nullptr; + m_fb.clear(OSP_FB_ACCUM); + } +} + +}// namepace ospray diff --git a/apps/common/widgets/OSPGlutViewer.h b/apps/common/widgets/OSPGlutViewer.h new file mode 100644 index 0000000000..342c4b4047 --- /dev/null +++ b/apps/common/widgets/OSPGlutViewer.h @@ -0,0 +1,107 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +// viewer widget +#include "common/widgets/glut3D.h" +// mini scene graph for loading the model +#include "common/miniSG/miniSG.h" + +#include +#include +#include + +#include "common/widgets/Glut3dExport.h" + +namespace ospray { + + /*! mini scene graph viewer widget. \internal Note that all handling + of camera is almost exactly similar to the code in volView; + might make sense to move that into a common class! */ + class OSPRAY_GLUT3D_INTERFACE OSPGlutViewer + : public ospray::glut3D::Glut3DWidget + { + public: + + OSPGlutViewer(const ospcommon::box3f &worldBounds, + cpp::Model model, + cpp::Renderer renderer, + cpp::Camera camera); + + void setRenderer(OSPRenderer renderer); + void resetAccumulation(); + void toggleFullscreen(); + void resetView(); + void printViewport(); + void saveScreenshot(const std::string &basename); + + // Helper types // + + struct DisplayWall + { + std::string hostname; + std::string streamName; + ospcommon::vec2i size{-1}; + ospray::cpp::FrameBuffer fb; + ospray::cpp::PixelOp po; + } displayWall; + + void setDisplayWall(const DisplayWall &dw); + + protected: + + virtual void reshape(const ospcommon::vec2i &newSize) override; + virtual void keypress(char key, const ospcommon::vec2i &where) override; + virtual void mouseButton(int32_t whichButton, bool released, + const ospcommon::vec2i &pos) override; + + private: + + // Private functions // + + void display() override; + + void switchRenderers(); + + // Data // + + cpp::Model m_model; + cpp::FrameBuffer m_fb; + cpp::Renderer m_renderer; + cpp::Camera m_camera; + + ospray::glut3D::FPSCounter m_fps; + + std::mutex m_rendererMutex; + cpp::Renderer m_queuedRenderer; + + bool m_alwaysRedraw; + + ospcommon::vec2i m_windowSize; + int m_accumID; + bool m_fullScreen; + glut3D::Glut3DWidget::ViewPort m_viewPort; + + std::atomic m_resetAccum; + + bool m_useDisplayWall; + }; + +}// namespace ospray diff --git a/apps/common/widgets/glut3D.cpp b/apps/common/widgets/glut3D.cpp index a5af149fc8..1bdf0af2cc 100644 --- a/apps/common/widgets/glut3D.cpp +++ b/apps/common/widgets/glut3D.cpp @@ -65,15 +65,16 @@ namespace ospray { { FILE *file = fopen(fileName,"wb"); if (!file) { - std::cerr << "#osp:glut3D: Warning - could not create screen shot file '" + std::cerr << "#osp:glut3D: Warning - could not create screenshot file '" << fileName << "'" << std::endl; return; } fprintf(file,"P6\n%i %i\n255\n",sizeX,sizeY); unsigned char *out = (unsigned char *)alloca(3*sizeX); - for (int y=0;ymotionSpeed * dv * cam.frame.l.vz ) - * AffineSpace3fa::translate( -1.0f * widget->motionSpeed * du * cam.frame.l.vx ); + AffineSpace3fa xfm = + AffineSpace3fa::translate(widget->motionSpeed * dv * cam.frame.l.vz ) + * AffineSpace3fa::translate(-1.0f * widget->motionSpeed + * du * cam.frame.l.vx); cam.frame = xfm * cam.frame; cam.from = xfmPoint(xfm, cam.from); @@ -629,14 +637,11 @@ namespace ospray { void InspectCenter::dragLeft(Glut3DWidget *widget, const vec2i &to, const vec2i &from) { - // std::cout << "-------------------------------------------------------" << std::endl; Glut3DWidget::ViewPort &cam = widget->viewPort; float du = (to.x - from.x) * widget->rotateSpeed; float dv = (to.y - from.y) * widget->rotateSpeed; - vec2i delta_mouse = to - from; - - const vec3f pivot = cam.at; //center(widget->worldBounds); + const vec3f pivot = cam.at; AffineSpace3fa xfm = AffineSpace3fa::translate(pivot) * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) @@ -726,8 +731,6 @@ namespace ospray { float du = (to.x - from.x); float dv = (to.y - from.y); - vec2i delta_mouse = (to - from); - AffineSpace3fa xfm = AffineSpace3fa::translate( widget->motionSpeed * dv * cam.frame.l.vz ) * AffineSpace3fa::translate( -1.0f * widget->motionSpeed * du * cam.frame.l.vx ); @@ -744,8 +747,6 @@ namespace ospray { float du = (to.x - from.x) * widget->rotateSpeed; float dv = (to.y - from.y) * widget->rotateSpeed; - vec2i delta_mouse = to - from; - const vec3f pivot = cam.from; //center(widget->worldBounds); AffineSpace3fa xfm = AffineSpace3fa::translate(pivot) @@ -775,7 +776,8 @@ namespace ospray { dumpFileRoot = getenv("OSPRAY_SCREEN_DUMP_ROOT"); #ifndef _WIN32 if (!dumpFileRoot) { - mkstemp(tmpFileName); + auto rc = mkstemp(tmpFileName); + (void)rc; dumpFileRoot = tmpFileName; } #endif @@ -824,20 +826,19 @@ namespace ospray { } - - void Manipulator::keypress(Glut3DWidget *widget, const int32_t key) { switch(key) { case 27 /*ESC*/: case 'q': case 'Q': - _exit(0); + std::exit(0); } - }; + } + void Manipulator::specialkey(Glut3DWidget *widget, const int32_t key) { - }; + } std::ostream &operator<<(std::ostream &o, const Glut3DWidget::ViewPort &cam) diff --git a/apps/common/widgets/glut3D.h b/apps/common/widgets/glut3D.h index 1cb7ac8b12..296775a99d 100644 --- a/apps/common/widgets/glut3D.h +++ b/apps/common/widgets/glut3D.h @@ -16,9 +16,9 @@ #pragma once -#include "common/common.h" -#include "common/box.h" -#include "common/AffineSpace.h" +#include "ospcommon/common.h" +#include "ospcommon/box.h" +#include "ospcommon/AffineSpace.h" #ifdef __APPLE__ #include @@ -30,16 +30,7 @@ #include #endif -#ifdef _WIN32 -# ifdef ospray_glut3d_EXPORTS -# define OSPRAY_GLUT3D_INTERFACE __declspec(dllexport) -# else -# define OSPRAY_GLUT3D_INTERFACE __declspec(dllimport) -# endif -#else -# define OSPRAY_GLUT3D_INTERFACE -#endif - +#include "common/widgets/Glut3dExport.h" namespace ospray { //! dedicated namespace for 3D glut viewer widget @@ -79,7 +70,7 @@ namespace ospray { // this is the fct that gets called when any mouse button got // pressed or released in the associated window OSPRAY_GLUT3D_INTERFACE virtual void button(Glut3DWidget *widget, - const vec2i &pos) {} + const vec2i &pos); /*! key press handler - override this fct to catch keyboard. */ OSPRAY_GLUT3D_INTERFACE virtual void keypress(Glut3DWidget *widget, const int32_t key); @@ -175,8 +166,8 @@ namespace ospray { its values get changed. */ vec3f from; - vec3f up; vec3f at; + vec3f up; float openingAngle; //!< in radians, along Y direction float aspect; //!< aspect ratio X:Y // float focalDistance; diff --git a/apps/common/xml/CMakeLists.txt b/apps/common/xml/CMakeLists.txt index 1c2924f8f6..e0478e133c 100644 --- a/apps/common/xml/CMakeLists.txt +++ b/apps/common/xml/CMakeLists.txt @@ -14,17 +14,4 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - -ADD_LIBRARY(ospray_xml SHARED XML.cpp) - -TARGET_LINK_LIBRARIES(ospray_xml ospray ospray_common) -OSPRAY_SET_LIBRARY_VERSION(ospray_xml) -INSTALL(TARGETS ospray_xml - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT lib - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT lib - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT devel -) +OSPRAY_CREATE_LIBRARY(xml XML.cpp LINK ospray_common) diff --git a/apps/common/xml/XML.cpp b/apps/common/xml/XML.cpp index 60c8337e2a..fdbd6202e0 100644 --- a/apps/common/xml/XML.cpp +++ b/apps/common/xml/XML.cpp @@ -28,8 +28,8 @@ namespace ospray { Node::~Node() { - for (int i=0;iname) - throw std::runtime_error("invalid XML node - started with '<"+node->name+"...'>, but ended with '"); + throw std::runtime_error("invalid XML node - started with'<" + + node->name + + "...'>, but ended with '"); consume(s,">"); break; // either end of current node @@ -185,11 +190,15 @@ namespace ospray { Node *child = parseNode(s, doc); node->child.push_back(child); } else if (*s == 0) { - std::cout << "#osp:xml: warning: xml file ended with still-open nodes (this typically indicates a partial xml file)" << std::endl; + std::cout << "#osp:xml: warning: xml file ended with still-open" + " nodes (this typically indicates a partial xml file)" + << std::endl; return node; } else { - if (node->content != "") - throw std::runtime_error("invalid XML node - two different contents!?"); + if (node->content != "") { + throw std::runtime_error("invalid XML node - two different" + " contents!?"); + } // content char *begin = s; while (*s != '<' && *s != 0) ++s; @@ -240,21 +249,25 @@ namespace ospray { skipWhites(s); } - if (*s != 0) throw std::runtime_error("un-parsed junk at end of file"); ++s; + if (*s != 0) + throw std::runtime_error("un-parsed junk at end of file"); + ++s; return xml; } void Writer::spaces() { - for (int i=0;ihasContent); // content may not be written before properties fprintf(xml," %s=\"%s\"",name.c_str(),value.c_str()); @@ -287,7 +300,8 @@ namespace ospray { { FILE *file = fopen(fn.c_str(),"r"); if (!file) { - throw std::runtime_error("ospray::XML error: could not open file '"+fn+"'"); + throw std::runtime_error("ospray::XML error: could not open file '" + + fn +"'"); } fseek(file,0,SEEK_END); @@ -300,7 +314,8 @@ namespace ospray { fseek(file,0,SEEK_SET); char *mem = new char[numBytes+1]; mem[numBytes] = 0; - fread(mem,1,numBytes,file); + auto rc = fread(mem,1,numBytes,file); + (void)rc; XMLDoc *xml = new XMLDoc; xml->fileName = fn; bool valid = false; @@ -316,7 +331,7 @@ namespace ospray { if (!valid) { delete xml; - return NULL; + return nullptr; } return xml; } diff --git a/apps/common/xml/XML.h b/apps/common/xml/XML.h index 0ab4f56cce..46bd61dfeb 100644 --- a/apps/common/xml/XML.h +++ b/apps/common/xml/XML.h @@ -16,15 +16,10 @@ #pragma once -// // ospray -// #include "ospray/common/OSPCommon.h" -// // embree -// #include "common/sys/filename.h" - // ospcomon -#include "common/common.h" -#include "common/vec.h" -#include "common/FileName.h" +#include "ospcommon/common.h" +#include "ospcommon/vec.h" +#include "ospcommon/FileName.h" // stl #include #include @@ -63,31 +58,38 @@ namespace ospray { virtual ~Node(); inline bool hasProp(const std::string &name) const { - for (int i=0;iname == name) return true; return false; } inline std::string getProp(const std::string &name) const { - for (int i=0;iname == name) return prop[i]->value; return ""; } /*! find properly with given name, and return as long ('l') int. return undefined if prop does not exist */ - inline size_t getPropl(const std::string &name, const size_t defaultValue = 0) const - { - const std::string prop = getProp(name); - if (prop == "") return defaultValue; else return atol(getProp(name).c_str()); + inline size_t getPropl(const std::string &name, const size_t defaultValue=0) const + { + const std::string val = getProp(name); + if (val.empty()) + return defaultValue; + else + return atol(val.c_str()); } + /*! find properly with given name, and return as long ('l') int. return undefined if prop does not exist */ - inline float getPropf(const std::string &name, const float defaultValue = 0.f) const - { - const std::string prop = getProp(name); - if (prop == "") return defaultValue; else return atof(getProp(name).c_str()); + inline float getPropf(const std::string &name, const float defaultValue=0.f) const + { + const std::string val = getProp(name); + if (val.empty()) + return defaultValue; + else + return atof(val.c_str()); } - + /*! name of the xml node (i.e., the thing that's in "....") */ std::string name; diff --git a/apps/glutViewer/CMakeLists.txt b/apps/glutViewer/CMakeLists.txt new file mode 100644 index 0000000000..147a987fc7 --- /dev/null +++ b/apps/glutViewer/CMakeLists.txt @@ -0,0 +1,76 @@ +## ======================================================================== ## +## Copyright 2009-2016 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +FIND_PACKAGE(Readline) + +IF(READLINE_FOUND) + ADD_DEFINITIONS(-DUSE_SYSTEM_READLINE) + SET(Readline_INC ${Readline_INCLUDE_DIR}) + SET(Readline_LIB ${Readline_LIBRARY}) +ELSE() + MESSAGE(STATUS "Readline NOT found, command history feature NOT enabled.") +ENDIF() + +IF (OSPRAY_MODULE_DISPLAY_WALD) + INCLUDE_DIRECTORIES(${DISPLAY_WALD_DIR}) + ADD_DEFINITIONS(-DOSPRAY_DISPLAY_WALD=1) + CONFIGURE_MPI() +ENDIF() + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/script + ${Readline_INC} +) + +# -------------------------------------------- +# main executable +# -------------------------------------------- + +SET(APP_SRCS + glutViewer.cpp +) + +IF(OSPRAY_APPS_ENABLE_SCRIPTING) + LIST(APPEND APP_SRCS + ScriptedOSPGlutViewer.cpp + ScriptedOSPGlutViewer.h + GlutViewerScriptHandler.cpp + GlutViewerScriptHandler.h + ) +ENDIF() + +SET(LIBS + ospray + ospray_commandline + ospray_common + ospray_glut3d + ospray_minisg + ospray_importer + ${OPENGL_LIBRARIES} + ${GLUT_LIBRARIES} + ${Readline_LIB} +) + +IF (OSPRAY_APPS_ENABLE_SCRIPTING) + LIST(APPEND LIBS ospray_script) +ENDIF() + +IF (OSPRAY_MODULE_DISPLAY_WALD) + LIST(APPEND LIBS ospray_displayWald_client) +ENDIF() + +OSPRAY_CREATE_APPLICATION(GlutViewer ${APP_SRCS} LINK ${LIBS}) diff --git a/apps/glutViewer/GlutViewerScriptHandler.cpp b/apps/glutViewer/GlutViewerScriptHandler.cpp new file mode 100644 index 0000000000..23ae6a3346 --- /dev/null +++ b/apps/glutViewer/GlutViewerScriptHandler.cpp @@ -0,0 +1,89 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "GlutViewerScriptHandler.h" +#include "ScriptedOSPGlutViewer.h" + +#include +using std::endl; + +namespace ospray { + + GlutViewerScriptHandler::GlutViewerScriptHandler(OSPModel model, + OSPRenderer renderer, + OSPCamera camera, + ScriptedOSPGlutViewer *viewer) + : OSPRayScriptHandler(model, renderer, camera), + m_viewer(viewer) + { + registerScriptFunctions(); + + std::stringstream ss; + + ss << "Viewer functions available:" << endl << endl; + ss << "setRenderer(renderer) --> set the renderer in the viewer" << endl; + ss << "refresh() --> reset the accumulation buffer" << endl; + ss << "toggleFullScreen() --> toggle fullscreen mode" << endl; + ss << "resetView() --> reset camera view" << endl; + ss << "printViewport() --> print view params in the console" << endl; + ss << "screenshot(filename) --> save a screenshot (adds '.ppm')" << endl; + + m_helpText += ss.str(); + } + + void GlutViewerScriptHandler::registerScriptFunctions() + { + auto &chai = this->scriptEngine(); + + // setRenderer() + auto setRenderer = [&](ospray::cpp::Renderer &r) { + m_viewer->setRenderer((OSPRenderer)r.handle()); + }; + + // refresh() + auto refresh = [&]() { + m_viewer->resetAccumulation(); + }; + + // toggleFullscreen() + auto toggleFullscreen = [&]() { + m_viewer->toggleFullscreen(); + }; + + // resetView() + auto resetView = [&]() { + m_viewer->resetView(); + }; + + // printViewport() + auto printViewport = [&]() { + m_viewer->printViewport(); + }; + + // screenshot() + auto screenshot = [&](const std::string &name) { + m_viewer->saveScreenshot(name); + }; + + chai.add(chaiscript::fun(setRenderer), "setRenderer" ); + chai.add(chaiscript::fun(refresh), "refresh" ); + chai.add(chaiscript::fun(toggleFullscreen), "toggleFullscreen"); + chai.add(chaiscript::fun(resetView), "resetView" ); + chai.add(chaiscript::fun(printViewport), "printViewport" ); + chai.add(chaiscript::fun(screenshot), "screenshot" ); + } + +}// namespace ospray diff --git a/apps/glutViewer/GlutViewerScriptHandler.h b/apps/glutViewer/GlutViewerScriptHandler.h new file mode 100644 index 0000000000..fc0dac36ce --- /dev/null +++ b/apps/glutViewer/GlutViewerScriptHandler.h @@ -0,0 +1,43 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "common/script/OSPRayScriptHandler.h" + +namespace ospray { + + class ScriptedOSPGlutViewer; + + class GlutViewerScriptHandler : public OSPRayScriptHandler + { + public: + + GlutViewerScriptHandler(OSPModel model, + OSPRenderer renderer, + OSPCamera camera, + ScriptedOSPGlutViewer *viewer); + + private: + + void registerScriptFunctions(); + + // Data // + + ScriptedOSPGlutViewer *m_viewer; + }; + +}// namespace ospray diff --git a/apps/glutViewer/ScriptedOSPGlutViewer.cpp b/apps/glutViewer/ScriptedOSPGlutViewer.cpp new file mode 100644 index 0000000000..063810684f --- /dev/null +++ b/apps/glutViewer/ScriptedOSPGlutViewer.cpp @@ -0,0 +1,58 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "ScriptedOSPGlutViewer.h" + +// MSGViewer definitions ////////////////////////////////////////////////////// + +namespace ospray { + + using std::cout; + using std::endl; + + using std::string; + + using std::lock_guard; + using std::mutex; + + using namespace ospcommon; + + ScriptedOSPGlutViewer::ScriptedOSPGlutViewer(const box3f &worldBounds, + cpp::Model model, + cpp::Renderer renderer, + cpp::Camera camera, + std::string scriptFileName) + : OSPGlutViewer(worldBounds, model, renderer, camera), + m_scriptHandler(model.handle(), renderer.handle(), camera.handle(), this) + { + if (!scriptFileName.empty()) + m_scriptHandler.runScriptFromFile(scriptFileName); + } + + void ScriptedOSPGlutViewer::keypress(char key, const vec2i &where) + { + switch (key) { + case ':': + if (!m_scriptHandler.running()) { + m_scriptHandler.start(); + } + break; + default: + OSPGlutViewer::keypress(key,where); + } + } + +}// namepace ospray diff --git a/apps/glutViewer/ScriptedOSPGlutViewer.h b/apps/glutViewer/ScriptedOSPGlutViewer.h new file mode 100644 index 0000000000..f360d8766e --- /dev/null +++ b/apps/glutViewer/ScriptedOSPGlutViewer.h @@ -0,0 +1,39 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "common/widgets/OSPGlutViewer.h" +#include "GlutViewerScriptHandler.h" + +namespace ospray { + + class ScriptedOSPGlutViewer : public OSPGlutViewer + { + public: + + ScriptedOSPGlutViewer(const ospcommon::box3f &worldBounds, + cpp::Model model, cpp::Renderer renderer, + cpp::Camera camera, std::string scriptFileName = ""); + + private: + + void keypress(char key, const ospcommon::vec2i &where) override; + + GlutViewerScriptHandler m_scriptHandler; + }; + +}// namespace ospray diff --git a/apps/glutViewer/glutViewer.cpp b/apps/glutViewer/glutViewer.cpp new file mode 100644 index 0000000000..d29f91bf92 --- /dev/null +++ b/apps/glutViewer/glutViewer.cpp @@ -0,0 +1,105 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "common/commandline/Utility.h" + +#ifdef OSPRAY_APPS_ENABLE_SCRIPTING +# include "ScriptedOSPGlutViewer.h" +#else +# include "common/widgets/OSPGlutViewer.h" +#endif + +#if OSPRAY_DISPLAY_WALD +# include "client/Client.h" +#endif + +std::string scriptFileFromCommandLine(int ac, const char **&av) +{ + std::string scriptFileName; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--script" || arg == "-s") { + scriptFileName = av[++i]; + } + } + + return scriptFileName; +} + +void parseForDisplayWall(int ac, const char **&av, ospray::OSPGlutViewer &v) +{ + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--display-wall") { + ospray::OSPGlutViewer::DisplayWall displayWall; +#if OSPRAY_DISPLAY_WALD + std::cout << "#osp.glutViewer: using displayWald-style display wall module" + << std::endl; + const std::string hostName = av[++i]; + const int portNo = 2903; /* todo: extract that from hostname if required */ + + std::cout << "#osp.glutViewer: trying to get display wall config from " + << hostName << ":" << portNo << std::endl; + ospray::dw::ServiceInfo service; + service.getFrom(hostName,portNo); + const ospcommon::vec2i size = service.totalPixelsInWall; + std::cout << "#osp.glutViewer: found display wald with " + << size.x << "x" << size.y << " pixels " + << "(" << ospcommon::prettyNumber(size.product()) << "pixels)" + << std::endl; + displayWall.hostname = service.mpiPortName; + displayWall.streamName = service.mpiPortName; + displayWall.size = size; +#else + displayWall.hostname = av[++i]; + displayWall.streamName = av[++i]; + displayWall.size.x = atof(av[++i]); + displayWall.size.y = atof(av[++i]); +#endif + v.setDisplayWall(displayWall); + } + } +} + +int main(int ac, const char **av) +{ + ospInit(&ac,av); + ospray::glut3D::initGLUT(&ac,av); + + auto ospObjs = parseWithDefaultParsers(ac, av); + + ospcommon::box3f bbox; + ospray::cpp::Model model; + ospray::cpp::Renderer renderer; + ospray::cpp::Camera camera; + + std::tie(bbox, model, renderer, camera) = ospObjs; + +#ifdef OSPRAY_APPS_ENABLE_SCRIPTING + auto scriptFileName = scriptFileFromCommandLine(ac, av); + + ospray::ScriptedOSPGlutViewer window(bbox, model, renderer, + camera, scriptFileName); +#else + ospray::OSPGlutViewer window(bbox, model, renderer, camera); +#endif + window.create("ospGlutViewer: OSPRay Mini-Scene Graph test viewer"); + + parseForDisplayWall(ac, av, window); + + ospray::glut3D::runGLUT(); +} diff --git a/apps/modelViewer/miniSG/CMakeLists.txt b/apps/modelViewer/miniSG/CMakeLists.txt deleted file mode 100644 index 6223b8591f..0000000000 --- a/apps/modelViewer/miniSG/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2016 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -IF (NOT THIS_IS_MIC) - SET(USE_IMAGE_MAGICK OFF CACHE BOOL "Use ImageMagick for ModelViewer's Texture loaders.") - MARK_AS_ADVANCED(USE_IMAGE_MAGICK) - IF (USE_IMAGE_MAGICK) - IF (APPLE) - MESSAGE(ERROR "ImageMagick currently not supported under Apple") - # the magick libraries apparently have some issues on mac that - # requrie ospray to be built with very special compile flags; we - # can specify those, but then it no longer works with the - # mac-version of Qt, so we currently pick qt compatibliity over - # magick compatibility. - ELSE(APPLE) - FIND_PACKAGE(ImageMagick COMPONENTS Magick++) - IF (IMAGEMAGICK_FOUND) - ADD_DEFINITIONS(-DUSE_IMAGEMAGICK) - INCLUDE_DIRECTORIES(${ImageMagick_Magick++_INCLUDE_DIR}) - SET(MAGICK_LIBRARIES ${ImageMagick_Magick++_LIBRARY}) - ELSE (IMAGEMAGICK_FOUND) - MESSAGE(STATUS "ImageMagick not found. Texture loaders other than PPM are disabled.") - ENDIF (IMAGEMAGICK_FOUND) - ENDIF(APPLE) - ENDIF (USE_IMAGE_MAGICK) - - INCLUDE_DIRECTORIES(${OSPRAY_DIR}/ospray) - - INCLUDE_DIRECTORIES(${OSPRAY_DIR}) - - ADD_LIBRARY(ospray_minisg STATIC - miniSG.cpp - importer.cpp - importOBJ.cpp - importHBP.cpp - importSTL.cpp - importMSG.cpp - importTRI.cpp - importX3D.cpp - importRIVL.cpp - ) - - TARGET_LINK_LIBRARIES(ospray_minisg - ospray_xml - ${MAGICK_LIBRARIES} - ) -ENDIF(NOT THIS_IS_MIC) diff --git a/apps/modelViewer/miniSG/miniSG.cpp b/apps/modelViewer/miniSG/miniSG.cpp deleted file mode 100644 index 4e4ac703e3..0000000000 --- a/apps/modelViewer/miniSG/miniSG.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "miniSG.h" - -#ifdef USE_IMAGEMAGICK -//#define MAGICKCORE_QUANTUM_DEPTH 16 -//#define MAGICKCORE_HDRI_ENABLE 1 -# include -using namespace Magick; -# ifndef MaxRGB -# define MaxRGB QuantumRange -# endif -#endif - -namespace ospray { - namespace miniSG { - - Texture2D::Texture2D() - : channels(0) - , depth(0) - , width(0) - , height(0) - , data(NULL) - {} - - Material::Material() - { - // setParam( "Kd", vec3f(.7f) ); - // setParam( "Ks", vec3f(0.f) ); - // setParam( "Ka", vec3f(0.f) ); - } - - Texture2D *loadTexture(const std::string &path, const std::string &fileNameBase, const bool prefereLinear) - { - const FileName fileName = path+"/"+fileNameBase; - - static std::map textureCache; - if (textureCache.find(fileName.str()) != textureCache.end()) - return textureCache[fileName.str()]; - - Texture2D *tex = NULL; - const std::string ext = fileName.ext(); - if (ext == "ppm") { - try { - int rc, peekchar; - - // open file - FILE *file = fopen(fileName.str().c_str(),"rb"); - const int LINESZ=10000; - char lineBuf[LINESZ+1]; - - if (!file) - throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); - - // read format specifier: - int format=0; - fscanf(file,"P%i\n",&format); - if (format != 6) - throw std::runtime_error("#osp:miniSG: can currently load only binary P6 subformats for PPM texture files. " - "Please report this bug at ospray.github.io."); - - // skip all comment lines - peekchar = getc(file); - while (peekchar == '#') { - fgets(lineBuf,LINESZ,file); - peekchar = getc(file); - } ungetc(peekchar,file); - - // read width and height from first non-comment line - int width=-1,height=-1; - rc = fscanf(file,"%i %i\n",&width,&height); - if (rc != 2) - throw std::runtime_error("#osp:miniSG: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " - "Please report this bug at ospray.github.io, and include named file to reproduce the error."); - - // skip all comment lines - peekchar = getc(file); - while (peekchar == '#') { - fgets(lineBuf,LINESZ,file); - peekchar = getc(file); - } ungetc(peekchar,file); - - // read maxval - int maxVal=-1; - rc = fscanf(file,"%i",&maxVal); - peekchar = getc(file); - - if (rc != 1) - throw std::runtime_error("#osp:miniSG: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " - "Please report this bug at ospray.github.io, and include named file to reproduce the error."); - if (maxVal != 255) - throw std::runtime_error("#osp:miniSG: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." - "Please report this bug at ospray.github.io, and include named file to reproduce the error."); - - tex = new Texture2D; - tex->width = width; - tex->height = height; - tex->channels = 3; - tex->depth = 1; - tex->prefereLinear = prefereLinear; - tex->data = new unsigned char[width*height*3]; - fread(tex->data,width*height*3,1,file); - // flip in y, because OSPRay's textures have the origin at the lower left corner - unsigned char *texels = (unsigned char *)tex->data; - for (size_t y=0; y < height/2; y++) - for (size_t x=0; x < width*3; x++) - std::swap(texels[y*width*3+x], texels[(height-1-y)*width*3+x]); - } catch(std::runtime_error e) { - std::cerr << e.what() << std::endl; - } - } else { -#ifdef USE_IMAGEMAGICK - Magick::Image image(fileName.str().c_str()); - tex = new Texture2D; - tex->width = image.columns(); - tex->height = image.rows(); - tex->channels = image.matte() ? 4 : 3; - tex->depth = 4; - tex->prefereLinear = prefereLinear; - float rcpMaxRGB = 1.0f/float(MaxRGB); - const Magick::PixelPacket* pixels = image.getConstPixels(0,0,tex->width,tex->height); - if (!pixels) { - std::cerr << "#osp:minisg: failed to load texture '"+fileName.str()+"'" << std::endl; - delete tex; - tex = NULL; - } else { - tex->data = new float[tex->width*tex->height*tex->channels]; - // convert pixels and flip image (because OSPRay's textures have the origin at the lower left corner) - for (size_t y=0; yheight; y++) { - for (size_t x=0; xwidth; x++) { - const Magick::PixelPacket &pixel = pixels[y*tex->width+x]; - float *dst = &((float*)tex->data)[(x+(tex->height-1-y)*tex->width)*tex->channels]; - *dst++ = pixel.red * rcpMaxRGB; - *dst++ = pixel.green * rcpMaxRGB; - *dst++ = pixel.blue * rcpMaxRGB; - if (tex->channels == 4) - *dst++ = pixel.opacity * rcpMaxRGB; - } - } - } -#endif - } - textureCache[fileName.str()] = tex; - return tex; - } - - - float Material::getParam(const char *name, float defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::FLOAT && "Param type mismatch" ); - return it->second->f[0]; - } - - return defaultVal; - } - - vec2f Material::getParam(const char *name, vec2f defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::FLOAT_2 && "Param type mismatch" ); - return vec2f(it->second->f[0], it->second->f[1]); - } - - return defaultVal; - } - - vec3f Material::getParam(const char *name, vec3f defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::FLOAT_3 && "Param type mismatch" ); - return vec3f( it->second->f[0], it->second->f[1], it->second->f[2] ); - } - - return defaultVal; - } - - vec4f Material::getParam(const char *name, vec4f defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::FLOAT_4 && "Param type mismatch" ); - return vec4f( it->second->f[0], it->second->f[1], it->second->f[2], it->second->f[3] ); - } - - return defaultVal; - } - - int32_t Material::getParam(const char *name, int32_t defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::INT && "Param type mismatch" ); - return it->second->i[0]; - } - - return defaultVal; - } - - vec2i Material::getParam(const char *name, vec2i defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::INT_2 && "Param type mismatch" ); - return vec2i(it->second->i[0], it->second->i[1]); - } - - return defaultVal; - } - - vec3i Material::getParam(const char *name, vec3i defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::INT_3 && "Param type mismatch" ); - return vec3i(it->second->i[0], it->second->i[1], it->second->i[2]); - } - - return defaultVal; - } - - vec4i Material::getParam(const char *name, vec4i defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::INT_4 && "Param type mismatch" ); - return vec4i(it->second->i[0], it->second->i[1], it->second->i[2], it->second->i[3]); - } - - return defaultVal; - } - - - uint32_t Material::getParam(const char *name, uint32_t defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::UINT && "Param type mismatch" ); - return it->second->ui[0]; - } - - return defaultVal; - } - - vec2ui Material::getParam(const char *name, vec2ui defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::UINT_2 && "Param type mismatch" ); - return vec2ui(it->second->ui[0], it->second->ui[1]); - } - - return defaultVal; - } - - vec3ui Material::getParam(const char *name, vec3ui defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::UINT_3 && "Param type mismatch" ); - return vec3ui(it->second->ui[0], it->second->ui[1], it->second->ui[2]); - } - - return defaultVal; - } - - vec4ui Material::getParam(const char *name, vec4ui defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::UINT_4 && "Param type mismatch" ); - return vec4ui(it->second->ui[0], it->second->ui[1], it->second->ui[2], it->second->ui[3]); - } - - return defaultVal; - } - - const char *Material::getParam(const char *name, const char *defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::STRING && "Param type mismatch" ); - return it->second->s; - } - - return defaultVal; - } - - void *Material::getParam(const char *name, void *defaultVal) - { - ParamMap::iterator it = params.find(name); - if (it != params.end()) { - assert( it->second->type == Param::TEXTURE /*|| other 'data' types*/&& "Param type mismatch" ); - return it->second->ptr; - } - - return defaultVal; - } - - void error(const std::string &err) - { - throw std::runtime_error("ospray::miniSG fatal error : "+err); - } - - bool operator==(const Instance &a, const Instance &b) - { return a.meshID == b.meshID && a.xfm == b.xfm; } - - bool operator!=(const Instance &a, const Instance &b) - { return !(a==b); } - - - /*! computes and returns the world-space bounding box of given mesh */ - box3f Mesh::getBBox() - { - if (bounds.empty()) { - for (int i=0;i meshBounds; - for (int i=0;igetBBox()); - for (int i=0;igetBBox()); - } - return bBox; - } - - size_t Model::numUniqueTriangles() const - { - size_t sum = 0; - for (int i=0;itriangle.size(); - return sum; - } - - } // ::ospray::minisg -} // ::ospray diff --git a/apps/modelViewer/modelViewer.cpp b/apps/modelViewer/modelViewer.cpp deleted file mode 100644 index 80e22f0f79..0000000000 --- a/apps/modelViewer/modelViewer.cpp +++ /dev/null @@ -1,965 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#define WARN_ON_INCLUDING_OSPCOMMON 1 - -// viewer widget -#include "apps/common/widgets/glut3D.h" -// mini scene graph for loading the model -#include "miniSG/miniSG.h" -// ospray, for rendering -#include "ospray/ospray.h" - -// stl -#include -#include - -namespace ospray { - using namespace ospcommon; - - using std::cout; - using std::endl; - bool doShadows = 1; - OSPTexture2D g_tex; // holds last loaded texture, for debugging - - const char *outFileName = NULL; - size_t numAccumsFrameInFileOutput = 1; - size_t numSPPinFileOutput = 1; - - float g_near_clip = 1e-6f; - bool g_fullScreen = false; - glut3D::Glut3DWidget::ViewPort g_viewPort; - - vec2i g_windowSize; - - int g_benchWarmup = 0, g_benchFrames = 0; - bool g_alpha = false; - bool g_createDefaultMaterial = true; - int accumID = -1; - int maxAccum = 64; - int spp = 1; /*! number of samples per pixel */ - int maxDepth = 2; // only set with home/end - unsigned int maxObjectsToConsider = (uint32_t)-1; - // if turned on, we'll put each triangle mesh into its own instance, no matter what - bool forceInstancing = false; - /*! if turned on we're showing the depth buffer rather than the (accum'ed) color buffer */ - bool showDepthBuffer = 0; - glut3D::Glut3DWidget::FrameBufferMode g_frameBufferMode = glut3D::Glut3DWidget::FRAMEBUFFER_UCHAR; - - /*! when using the OBJ renderer, we create a automatic dirlight with this direction; use ''--sun-dir x y z' to change */ - vec3f defaultDirLight_direction(.3, -1, -.2); - - Ref msgModel = NULL; - OSPModel ospModel = NULL; - OSPRenderer ospRenderer = NULL; - bool alwaysRedraw = false; - - //! the renderer we're about to use - std::string rendererType = "ao1"; - - std::vector msgAnimation; - - void error(const std::string &msg) - { - cout << "#ospModelViewer fatal error : " << msg << endl; - cout << endl; - cout << "Proper usage: " << endl; - cout << " ./ospModelViewer [-bench x] [-model] " << endl; - cout << endl; - exit(1); - } - - struct DisplayWall { - std::string hostname; - std::string streamName; - vec2i size; - OSPFrameBuffer fb; - OSPPixelOp po; - - DisplayWall() : hostname(""), streamName(""), size(-1), fb(NULL), po(NULL) - {} - }; - DisplayWall *displayWall = NULL; - - using ospray::glut3D::Glut3DWidget; - - // helper function to write the rendered image as PPM file - void writePPM(const char *fileName, - const int sizeX, const int sizeY, - const uint32_t *pixel) - { - FILE *file = fopen(fileName, "wb"); - fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); - unsigned char *out = (unsigned char *)alloca(3*sizeX); - for (int y = 0; y < sizeY; y++) { - const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; - for (int x = 0; x < sizeX; x++) { - out[3*x + 0] = in[4*x + 0]; - out[3*x + 1] = in[4*x + 1]; - out[3*x + 2] = in[4*x + 2]; - } - fwrite(out, 3*sizeX, sizeof(char), file); - } - fprintf(file, "\n"); - fclose(file); - - std::string alphaName(fileName); - alphaName.resize(alphaName.length()-4); // remove ".ppm" - alphaName.append("_alpha.pgm"); - - file = fopen(alphaName.c_str(), "wb"); - fprintf(file, "P5\n%i %i\n255\n", sizeX, sizeY); - for (int y = 0; y < sizeY; y++) { - const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; - for (int x = 0; x < sizeX; x++) - out[x] = in[4*x + 3]; - fwrite(out, sizeX, sizeof(char), file); - } - fprintf(file, "\n"); - fclose(file); - } - - /*! mini scene graph viewer widget. \internal Note that all handling - of camera is almost exactly similar to the code in volView; - might make sense to move that into a common class! */ - struct MSGViewer : public Glut3DWidget { - MSGViewer(OSPModel model, OSPRenderer renderer) - : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), - fb(NULL), renderer(renderer), model(model) - { - Assert(model && "null model handle"); - camera = ospNewCamera("perspective"); - Assert(camera != NULL && "could not create camera"); - ospSet3f(camera,"pos",-1,1,-1); - ospSet3f(camera,"dir",+1,-1,+1); - ospCommit(camera); - - ospSetObject(renderer,"world",model); - ospSetObject(renderer,"model",model); - ospSetObject(renderer,"camera",camera); - ospSet1i(renderer,"spp",spp); - ospCommit(camera); - ospCommit(renderer); - - } - - void reshape(const ospray::vec2i &newSize) override - { - Glut3DWidget::reshape(newSize); - g_windowSize = newSize; - if (fb) ospFreeFrameBuffer(fb); - fb = ospNewFrameBuffer((const osp::vec2i&)newSize, - OSP_FB_SRGBA, - OSP_FB_COLOR|OSP_FB_DEPTH| - OSP_FB_ACCUM|OSP_FB_VARIANCE); - ospFrameBufferClear(fb,OSP_FB_ACCUM); - - /*! for now, let's just attach the pixel op to the _main_ frame - buffer - eventually we need to have a _second_ frame buffer - of the proper (much higher) size, but for now let's just use - the existing one... */ - if (displayWall && displayWall->fb != fb) { - PRINT(displayWall->size); - displayWall->fb = ospNewFrameBuffer((const osp::vec2i&)displayWall->size, - OSP_FB_NONE,OSP_FB_COLOR| - OSP_FB_DEPTH|OSP_FB_ACCUM); - ospFrameBufferClear(displayWall->fb,OSP_FB_ACCUM); - if (displayWall->po == NULL) { - displayWall->po = ospNewPixelOp("display_wall"); - if (!displayWall->po) - throw std::runtime_error("could not load 'display_wall' component."); - ospSetString(displayWall->po, "hostname", displayWall->hostname.c_str()); - ospSetString(displayWall->po, "streamName", displayWall->streamName.c_str()); - ospCommit(displayWall->po); - } - - ospSetPixelOp(displayWall->fb,displayWall->po); - } - - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - viewPort.modified = true; - forceRedraw(); - } - - void keypress(char key, const vec2i &where) override - { - switch (key) { - case 'R': - alwaysRedraw = !alwaysRedraw; - forceRedraw(); - break; - case 'S': - doShadows = !doShadows; - cout << "Switching shadows " << (doShadows?"ON":"OFF") << endl; - ospSet1i(renderer,"shadowsEnabled",doShadows); - ospCommit(renderer); - accumID=0; - ospFrameBufferClear(fb,OSP_FB_ACCUM); - forceRedraw(); - break; - case 'D': - showDepthBuffer = !showDepthBuffer; - // switch(g_frameBufferMode) { - // case Glut3DWidget::FRAMEBUFFER_DEPTH: - // g_frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - // break; - // case Glut3DWidget::FRAMEBUFFER_UCHAR: - // g_frameBufferMode = Glut3DWidget::FRAMEBUFFER_DEPTH; - // break; - // } - ospFrameBufferClear(fb,OSP_FB_ACCUM); - forceRedraw(); - break; - case '!': { - const uint32_t * p = (uint32_t*)ospMapFrameBuffer(fb, OSP_FB_COLOR); - writePPM("ospmodelviewer.ppm", g_windowSize.x, g_windowSize.y, p); - // ospUnmapFrameBuffer(fb,p); - printf("#ospModelViewer: saved current frame to 'ospmodelviewer.ppm'\n"); - } break; - case 'X': - if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(1,0,0); - viewPort.modified = true; - forceRedraw(); - break; - case 'Y': - if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(0,1,0); - viewPort.modified = true; - forceRedraw(); - break; - case 'Z': - if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(0,0,1); - viewPort.modified = true; - forceRedraw(); - break; - case 'f': - g_fullScreen = !g_fullScreen; - if(g_fullScreen) glutFullScreen(); - else glutPositionWindow(0,10); - break; - case 'r': - viewPort = g_viewPort; - break; - case 'p': - printf("-vp %f %f %f -vu %f %f %f -vi %f %f %f\n", viewPort.from.x, viewPort.from.y, viewPort.from.z, viewPort.up.x, viewPort.up.y, viewPort.up.z, viewPort.at.x, viewPort.at.y, viewPort.at.z); - fflush(stdout); - break; - default: - Glut3DWidget::keypress(key,where); - } - } - - void specialkey(int32_t key, const vec2i &where) override - { - switch(key) { - case GLUT_KEY_PAGE_UP: - g_near_clip += 20.f * motionSpeed; - case GLUT_KEY_PAGE_DOWN: - g_near_clip -= 10.f * motionSpeed; - g_near_clip = std::max(g_near_clip, 1e-6f); - ospSet1f(camera, "near_clip", g_near_clip); - ospCommit(camera); - accumID=0; - ospFrameBufferClear(fb,OSP_FB_ACCUM); - forceRedraw(); - break; - case GLUT_KEY_HOME: - maxDepth += 2; - case GLUT_KEY_END: - maxDepth--; - ospSet1i(ospRenderer, "maxDepth", maxDepth); - PRINT(maxDepth); - ospCommit(ospRenderer); - accumID=0; - ospFrameBufferClear(fb,OSP_FB_ACCUM); - forceRedraw(); - break; - default: - Glut3DWidget::specialkey(key,where); - } - } - - void mouseButton(int32_t whichButton, bool released, const vec2i &pos) override - { - Glut3DWidget::mouseButton(whichButton, released, pos); - if(currButtonState == (1< 0) fps.doneRender(); - fps.startRender(); - //} - static double benchStart=0; - static double fpsSum=0; - if (g_benchFrames > 0 && frameID == g_benchWarmup) - { - benchStart = ospray::getSysTime(); - } - if (g_benchFrames > 0 && frameID >= g_benchWarmup) - fpsSum += fps.getFPS(); - if (g_benchFrames > 0 && frameID== g_benchWarmup+g_benchFrames) - { - double time = ospray::getSysTime()-benchStart; - double avgFps = fpsSum/double(frameID-g_benchWarmup); - printf("Benchmark: time: %f avg fps: %f avg frame time: %f\n", time, avgFps, time/double(frameID-g_benchWarmup)); - - const uint32_t * p = (uint32_t*)ospMapFrameBuffer(fb, OSP_FB_COLOR); - writePPM("benchmark.ppm", g_windowSize.x, g_windowSize.y, p); - - exit(0); - } - ++frameID; - - if (viewPort.modified) { - static bool once = true; - if(once) { - g_viewPort = viewPort; - once = false; - } - Assert2(camera,"ospray camera is null"); - ospSetVec3f(camera,"pos",(osp::vec3f&)viewPort.from); - vec3f dir = viewPort.at-viewPort.from; - ospSetVec3f(camera,"dir",(osp::vec3f&)dir); - ospSetVec3f(camera,"up",(osp::vec3f&)viewPort.up); - ospSetf(camera,"aspect",viewPort.aspect); -// ospSetf(camera,"apertureRadius", 0.01); -// ospSetf(camera,"focusDistance", viewPort."focusDistance"); - ospCommit(camera); - viewPort.modified = false; - accumID=0; - ospFrameBufferClear(fb,OSP_FB_ACCUM); - if (displayWall) - ospFrameBufferClear(displayWall->fb,OSP_FB_ACCUM); - } - - if (outFileName) { - ospSet1i(renderer,"spp",numSPPinFileOutput); - ospCommit(renderer); - std::cout << "#ospModelViewer: Renderering offline image with " << numSPPinFileOutput << " samples per pixel per frame, and accumulation of " << numAccumsFrameInFileOutput << " such frames" << endl; - for (int i=0;ifb,renderer,OSP_FB_COLOR|OSP_FB_ACCUM); - ++accumID; - - // set the glut3d widget's frame buffer to the opsray frame buffer, then display - ucharFB = (uint32_t *) ospMapFrameBuffer(fb, OSP_FB_COLOR); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - - Glut3DWidget::display(); - - // that pointer is no longer valid, so set it to null - ucharFB = NULL; - - char title[1000]; - - if (alwaysRedraw) { - sprintf(title,"OSPRay Model Viewer (%f fps)", - fps.getFPS()); - setTitle(title); - forceRedraw(); - } else if (accumID < maxAccum) { - forceRedraw(); - } else { - // sprintf(title,"OSPRay Model Viewer"); - // setTitle(title); - } - } - - OSPModel model; - OSPFrameBuffer fb; - OSPRenderer renderer; - OSPCamera camera; - ospray::glut3D::FPSCounter fps; - }; - - void warnMaterial(const std::string &type) - { - static std::map numOccurances; - if (numOccurances[type] == 0) - cout << "could not create material type '"< alreadyCreatedTextures; - if (alreadyCreatedTextures.find(msgTex) != alreadyCreatedTextures.end()) - return alreadyCreatedTextures[msgTex]; - - //TODO: We need to come up with a better way to handle different possible pixel layouts - OSPTextureFormat type = OSP_TEXTURE_R8; - - if (msgTex->depth == 1) { - if( msgTex->channels == 1 ) type = OSP_TEXTURE_R8; - if( msgTex->channels == 3 ) - type = msgTex->prefereLinear ? OSP_TEXTURE_RGB8 : OSP_TEXTURE_SRGB; - if( msgTex->channels == 4 ) - type = msgTex->prefereLinear ? OSP_TEXTURE_RGBA8 : OSP_TEXTURE_SRGBA; - } else if (msgTex->depth == 4) { - if( msgTex->channels == 1 ) type = OSP_TEXTURE_R32F; - if( msgTex->channels == 3 ) type = OSP_TEXTURE_RGB32F; - if( msgTex->channels == 4 ) type = OSP_TEXTURE_RGBA32F; - } - - vec2i texSize(msgTex->width, msgTex->height); - OSPTexture2D ospTex = ospNewTexture2D( (osp::vec2i&)texSize, - type, - msgTex->data, - 0); - - alreadyCreatedTextures[msgTex] = ospTex; - - ospCommit(ospTex); - g_tex = ospTex; // remember last texture for debugging - - return ospTex; - } - - OSPMaterial createMaterial(OSPRenderer renderer, - miniSG::Material *mat) - { - if (mat == NULL) { - static int numWarnings = 0; - if (++numWarnings < 10) - cout << "WARNING: model does not have materials! (assigning default)" << endl; - return createDefaultMaterial(renderer); - } - static std::map alreadyCreatedMaterials; - if (alreadyCreatedMaterials.find(mat) != alreadyCreatedMaterials.end()) - return alreadyCreatedMaterials[mat]; - - const char *type = mat->getParam("type","OBJMaterial"); - assert(type); - OSPMaterial ospMat = alreadyCreatedMaterials[mat] = ospNewMaterial(renderer,type); - if (!ospMat) { - warnMaterial(type); - return createDefaultMaterial(renderer); - } - const bool isOBJMaterial = !strcmp(type, "OBJMaterial"); - - for (miniSG::Material::ParamMap::const_iterator it = mat->params.begin(); - it != mat->params.end(); ++it) { - const char *name = it->first.c_str(); - const miniSG::Material::Param *p = it->second.ptr; - - switch(p->type) { - case miniSG::Material::Param::INT: - ospSet1i(ospMat,name,p->i[0]); - break; - case miniSG::Material::Param::FLOAT: { - float f = p->f[0]; - /* many mtl materials of obj models wrongly store the phong exponent - 'Ns' in range [0..1], whereas OSPRay's material implementations - correctly interpret it to be in [0..inf), thus we map ranges here */ - if (isOBJMaterial && (!strcmp(name, "Ns") || !strcmp(name, "ns")) && f < 1.f) - f = 1.f/(1.f - f) - 1.f; - ospSet1f(ospMat,name,f); - } break; - case miniSG::Material::Param::FLOAT_3: - ospSet3fv(ospMat,name,p->f); - break; - case miniSG::Material::Param::STRING: - ospSetString(ospMat,name,p->s); - break; - case miniSG::Material::Param::TEXTURE: - { - miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; - if (tex) { - OSPTexture2D ospTex = createTexture2D(tex); - assert(ospTex); - ospCommit(ospTex); - ospSetObject(ospMat, name, ospTex); - } - break; - } - default: - throw std::runtime_error("unknown material parameter type"); - }; - } - - ospCommit(ospMat); - return ospMat; - } - - void ospModelViewerMain(int &ac, const char **&av) - { - msgModel = new miniSG::Model; - - cout << "#ospModelViewer: starting to process cmdline arguments" << endl; - for (int i=1;ihostname = av[++i]; - displayWall->streamName = av[++i]; - displayWall->size.x = atof(av[++i]); - displayWall->size.y = atof(av[++i]); - } else if (arg == "-bench") { - if (++i < ac) - { - std::string arg2(av[i]); - size_t pos = arg2.find("x"); - if (pos != std::string::npos) - { - arg2.replace(pos, 1, " "); - std::stringstream ss(arg2); - ss >> g_benchWarmup >> g_benchFrames; - } - } - } else if (arg == "--no-default-material") { - g_createDefaultMaterial = false; - } else if (av[i][0] == '-') { - error("unknown commandline argument '"+arg+"'"); - } else { - FileName fn = arg; - if (fn.ext() == "stl") { - miniSG::importSTL(*msgModel,fn); - } else if (fn.ext() == "msg") { - miniSG::importMSG(*msgModel,fn); - } else if (fn.ext() == "tri") { - miniSG::importTRI(*msgModel,fn); - } else if (fn.ext() == "xml") { - miniSG::importRIVL(*msgModel,fn); - } else if (fn.ext() == "obj") { - miniSG::importOBJ(*msgModel,fn); - } else if (fn.ext() == "hbp") { - miniSG::importHBP(*msgModel,fn); - } else if (fn.ext() == "x3d") { - miniSG::importX3D(*msgModel,fn); - } else if (fn.ext() == "astl") { - miniSG::importSTL(msgAnimation,fn); - } - } - } - - // ------------------------------------------------------- - // done parsing - // -------------------------------------------------------] - cout << "#ospModelViewer: done parsing. found model with" << endl; - // cout << " - num materials: " << msgModel->material.size() << endl; - cout << " - num meshes : " << msgModel->mesh.size() << " "; - size_t numUniqueTris = 0; - size_t numInstancedTris = 0; - for (size_t i=0;imesh.size();i++) { - if (i < 10) - cout << "[" << msgModel->mesh[i]->size() << "]"; - else - if (i == 10) cout << "..."; - numUniqueTris += msgModel->mesh[i]->size(); - } - cout << endl; - cout << " - num instances: " << msgModel->instance.size() << " "; - for (size_t i=0;iinstance.size();i++) { - if (i < 10) - cout << "[" << msgModel->mesh[msgModel->instance[i].meshID]->size() << "]"; - else - if (i == 10) cout << "..."; - numInstancedTris += msgModel->mesh[msgModel->instance[i].meshID]->size(); - } - cout << endl; - cout << " - num unique triangles : " << numUniqueTris << endl; - cout << " - num instanced triangles: " << numInstancedTris << endl; - - if (numInstancedTris == 0 && msgAnimation.empty()) - error("no (valid) input files specified - model contains no triangles"); - - // ------------------------------------------------------- - // create ospray model - // ------------------------------------------------------- - ospModel = ospNewModel(); - - ospRenderer = ospNewRenderer(rendererType.c_str()); - - // Set renderer defaults (if not using 'aoX' renderers) - if (rendererType[0] != 'a' && rendererType[1] != 'o') - { - ospSet1i(ospRenderer, "aoSamples", 1); - ospSet1i(ospRenderer, "shadowsEnabled", 1); - } - - // ospSet1f(ospRenderer, "varianceThreshold", 0.0002); - if (!ospRenderer) - throw std::runtime_error("could not create ospRenderer '"+rendererType+"'"); - Assert(ospRenderer != NULL && "could not create ospRenderer"); - ospCommit(ospRenderer); - - // code does not yet do instancing ... check that the model doesn't contain instances - bool doesInstancing = 0; - - if (forceInstancing) { - std::cout << "#ospModelViewer: forced instancing - instances on." << std::endl; - doesInstancing = true; - } else if (msgModel->instance.size() > msgModel->mesh.size()) { - std::cout << "#ospModelViewer: found more object instances than meshes - turning on instancing" << std::endl; - doesInstancing = true; - } else { - std::cout << "#ospModelViewer: number of instances matches number of meshes, creating single model that contains all meshes" << std::endl; - doesInstancing = false; - } - if (doesInstancing) { - if (msgModel->instance.size() > maxObjectsToConsider) { - cout << "cutting down on the number of meshes as requested on cmdline..." << endl; - msgModel->instance.resize(maxObjectsToConsider); - } - } else { - if (msgModel->instance.size() > maxObjectsToConsider) { - cout << "cutting down on the number of meshes as requested on cmdline..." << endl; - msgModel->instance.resize(maxObjectsToConsider); - msgModel->mesh.resize(maxObjectsToConsider); - } - } - - - cout << "#ospModelViewer: adding parsed geometries to ospray model" << endl; - std::vector instanceModels; - - for (size_t i=0;imesh.size();i++) { - Ref msgMesh = msgModel->mesh[i]; - - // create ospray mesh - OSPGeometry ospMesh = g_alpha ? ospNewGeometry("alpha_aware_triangle_mesh") : ospNewGeometry("trianglemesh"); - - // check if we have to transform the vertices: - if (doesInstancing == false && msgModel->instance[i] != miniSG::Instance(i)) { - for (size_t vID=0;vIDposition.size();vID++) { - msgMesh->position[vID] = xfmPoint(msgModel->instance[i].xfm, - msgMesh->position[vID]); - } - } - - // add position array to mesh - OSPData position = ospNewData(msgMesh->position.size(),OSP_FLOAT3A, - &msgMesh->position[0],OSP_DATA_SHARED_BUFFER); - ospSetData(ospMesh,"position",position); - - // add triangle index array to mesh - if (!msgMesh->triangleMaterialId.empty()) { - OSPData primMatID = ospNewData(msgMesh->triangleMaterialId.size(),OSP_INT, - &msgMesh->triangleMaterialId[0],OSP_DATA_SHARED_BUFFER); - ospSetData(ospMesh,"prim.materialID",primMatID); - } - - // add triangle index array to mesh - OSPData index = ospNewData(msgMesh->triangle.size(),OSP_INT3, - &msgMesh->triangle[0],OSP_DATA_SHARED_BUFFER); - assert(msgMesh->triangle.size() > 0); - ospSetData(ospMesh,"index",index); - - // add normal array to mesh - if (!msgMesh->normal.empty()) { - OSPData normal = ospNewData(msgMesh->normal.size(),OSP_FLOAT3A, - &msgMesh->normal[0],OSP_DATA_SHARED_BUFFER); - assert(msgMesh->normal.size() > 0); - ospSetData(ospMesh,"vertex.normal",normal); - } else { - // cout << "no vertex normals!" << endl; - } - - // add color array to mesh - if (!msgMesh->color.empty()) { - OSPData color = ospNewData(msgMesh->color.size(),OSP_FLOAT3A, - &msgMesh->color[0],OSP_DATA_SHARED_BUFFER); - assert(msgMesh->color.size() > 0); - ospSetData(ospMesh,"vertex.color",color); - } else { - // cout << "no vertex colors!" << endl; - } - - // add texcoord array to mesh - if (!msgMesh->texcoord.empty()) { - OSPData texcoord = ospNewData(msgMesh->texcoord.size(), OSP_FLOAT2, - &msgMesh->texcoord[0], OSP_DATA_SHARED_BUFFER); - assert(msgMesh->texcoord.size() > 0); - ospSetData(ospMesh,"vertex.texcoord",texcoord); - } - - ospSet1i(ospMesh, "alpha_type", 0); - ospSet1i(ospMesh, "alpha_component", 4); - - // add triangle material id array to mesh - if (msgMesh->materialList.empty()) { - // we have a single material for this mesh... - OSPMaterial singleMaterial = createMaterial(ospRenderer, msgMesh->material.ptr); - ospSetMaterial(ospMesh,singleMaterial); - } else { - // we have an entire material list, assign that list - std::vector materialList; - std::vector alphaMaps; - std::vector alphas; - for (int i=0;imaterialList.size();i++) { - materialList.push_back(createMaterial(ospRenderer, msgMesh->materialList[i].ptr)); - - for (miniSG::Material::ParamMap::const_iterator it = msgMesh->materialList[i]->params.begin(); - it != msgMesh->materialList[i]->params.end(); it++) { - const char *name = it->first.c_str(); - const miniSG::Material::Param *p = it->second.ptr; - if(p->type == miniSG::Material::Param::TEXTURE) { - if(!strcmp(name, "map_kd") || !strcmp(name, "map_Kd")) { - miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; - OSPTexture2D ospTex = createTexture2D(tex); - ospCommit(ospTex); - alphaMaps.push_back(ospTex); - } - } else if(p->type == miniSG::Material::Param::FLOAT) { - if(!strcmp(name, "d")) alphas.push_back(p->f[0]); - } - } - - while(materialList.size() > alphaMaps.size()) { - alphaMaps.push_back(NULL); - } - while(materialList.size() > alphas.size()) { - alphas.push_back(0.f); - } - } - OSPData ospMaterialList = ospNewData(materialList.size(), OSP_OBJECT, &materialList[0], 0); - ospSetData(ospMesh,"materialList",ospMaterialList); - - // only set these if alpha aware mode enabled - // this currently doesn't work on the MICs! - if(g_alpha) { - OSPData ospAlphaMapList = ospNewData(alphaMaps.size(), OSP_OBJECT, &alphaMaps[0], 0); - ospSetData(ospMesh, "alpha_maps", ospAlphaMapList); - - OSPData ospAlphaList = ospNewData(alphas.size(), OSP_OBJECT, &alphas[0], 0); - ospSetData(ospMesh, "alphas", ospAlphaList); - } - } - - ospCommit(ospMesh); - - if (doesInstancing) { - OSPModel model_i = ospNewModel(); - ospAddGeometry(model_i,ospMesh); - ospCommit(model_i); - instanceModels.push_back(model_i); - } else - ospAddGeometry(ospModel,ospMesh); - - } - if (doesInstancing) { - for (int i=0;iinstance.size();i++) { - OSPGeometry inst = ospNewInstance(instanceModels[msgModel->instance[i].meshID], - (const osp::affine3f&) msgModel->instance[i].xfm); - msgModel->instance[i].ospGeometry = inst; - ospAddGeometry(ospModel,inst); - } - } - cout << "#ospModelViewer: committing model" << endl; - ospCommit(ospModel); - cout << "#ospModelViewer: done creating ospray model." << endl; - - //TODO: Need to figure out where we're going to read lighting data from - //begin light test - std::vector lights; - if (defaultDirLight_direction != vec3f(0.f)) { - cout << "#ospModelViewer: Adding a hard coded directional light as the sun." << endl; - OSPLight ospLight = ospNewLight(ospRenderer, "DirectionalLight"); - ospSetString(ospLight, "name", "sun" ); - ospSet3f(ospLight, "color", 1, 1, 1); - ospSet3fv(ospLight, "direction", &defaultDirLight_direction.x); - ospSet1f(ospLight, "angularDiameter", 0.53f); - ospCommit(ospLight); - lights.push_back(ospLight); - } -#if 0 - //spot light - cout << "#ospModelViewer: Adding a hard coded spotlight for test." << endl; - OSPLight ospSpot = ospNewLight(ospRenderer, "SpotLight"); - ospSetString(ospSpot, "name", "spot_test"); - ospSet3f(ospSpot, "position", 0.f, 2.f, 0.f); - ospSet3f(ospSpot, "direction", 0.f, -1.f, 0.7f); - ospSet3f(ospSpot, "color", 1.f, 1.f, .5f); - ospSet1f(ospSpot, "intensity", 17.f); - ospSet1f(ospSpot, "openingAngle", 50.f); - ospSet1f(ospSpot, "penumbraAngle", 2.f); - ospCommit(ospSpot); - lights.push_back(ospSpot); - //point light - cout << "#ospModelViewer: Adding a hard coded pointlight for test." << endl; - OSPLight ospPoint = ospNewLight(ospRenderer, "PointLight"); - ospSetString(ospPoint, "name", "point_test"); - ospSet3f(ospPoint, "position", -5.f, 20.f, 10.f); - ospSet3f(ospPoint, "color", .5f, 1.f, 1.f); - ospSet1f(ospPoint, "intensity", 200.f); - ospSet1f(ospPoint, "radius", 4.f); - ospCommit(ospPoint); - lights.push_back(ospPoint); - //ambient light - cout << "#ospModelViewer: Adding a hard coded ambientlight for test." << endl; - OSPLight ospAmbient = ospNewLight(ospRenderer, "AmbientLight"); - ospSetString(ospAmbient, "name", "ambient_test"); - ospSet1f(ospAmbient, "intensity", 0.2f); - ospCommit(ospAmbient); - lights.push_back(ospAmbient); - //quad light - cout << "#ospModelViewer: Adding a hard coded quadlight for test." << endl; - OSPLight ospQuad = ospNewLight(ospRenderer, "QuadLight"); - ospSetString(ospQuad, "name", "quad_test"); - ospSet3f(ospQuad, "position", 1.f, 3.5f, 0.f); - ospSet3f(ospQuad, "edge1", 0.f, 0.f, 0.3f); - ospSet3f(ospQuad, "edge2", 2.f, 0.f, 0.f); - ospSet3f(ospQuad, "color", .5f, 1.f, .5f); - ospSet1f(ospQuad, "intensity", 45.f); - ospCommit(ospQuad); - lights.push_back(ospQuad); - //HDRI light - cout << "#ospModelViewer: Adding a hard coded hdrilight for test." << endl; - OSPLight ospHdri = ospNewLight(ospRenderer, "hdri"); - ospSetString(ospHdri, "name", "hdri_test"); - ospSet3f(ospHdri, "up", 0.f, 0.f, 1.f); - ospSet3f(ospHdri, "dir", 0.f, 1.f, 0.0f); - ospSet1f(ospHdri, "intensity", 10.f); - ospSetObject(ospHdri, "map", g_tex); - ospCommit(ospHdri); - lights.push_back(ospHdri); -#endif - OSPData lightArray = ospNewData(lights.size(), OSP_OBJECT, &lights[0], 0); - ospSetData(ospRenderer, "lights", lightArray); - //end light test - ospCommit(ospRenderer); - - // ------------------------------------------------------- - // create viewer window - // ------------------------------------------------------- - MSGViewer window(ospModel,ospRenderer); - window.create("ospModelViewer: OSPRay Mini-Scene Graph test viewer"); - printf("#ospModelViewer: done creating window. Press 'Q' to quit.\n"); - const box3f worldBounds(msgModel->getBBox()); - window.setWorldBounds(worldBounds); - std::cout << "#ospModelViewer: set world bounds " << worldBounds << ", motion speed " << window.motionSpeed << std::endl; - if (msgModel->camera.size() > 0) { - window.setViewPort(msgModel->camera[0]->from, - msgModel->camera[0]->at, - msgModel->camera[0]->up); - } - ospray::glut3D::runGLUT(); - } -} - - -int main(int ac, const char **av) -{ - ospInit(&ac,av); - ospray::glut3D::initGLUT(&ac,av); - ospray::ospModelViewerMain(ac,av); -} diff --git a/apps/modelViewer/modelViewer.dox b/apps/modelViewer/modelViewer.dox deleted file mode 100644 index 8dd214417c..0000000000 --- a/apps/modelViewer/modelViewer.dox +++ /dev/null @@ -1,39 +0,0 @@ -/*! \defgroup ospray_apps_ospModelViewer ospModelViewer: A simple model viewer for OBJ and rivl files - - \ingroup ospray_apps - -*/ - -/*! \page modelViewer The OSPRay model viewer - - \ingroup ospray_apps_ospModelViewer - - \section modelViewer_usage Usage - - usage: ./msgView models - - supported model formats: OBJ (.obj/.mtl), rivl (.xml/.bin) - - supported parameters: -

-
--sun-dir x y z :
specify direction of automatic 'sun' dir-light -
- - parameters inherited from glut3D: - -
-
-vp x y z :
specify camera point of origin -
-vi x y z :
specify camera point of interest -
-vu x y z :
specify up-vector -
--spp n :
number of samples per pixel (for renderers that respect the 'spp' parameter) -
--renderer :
where rendertype is one of raycast,obj,geomID,primID, or any loaded renderer -
- - parameters inherited from ospray -
-
--osp:debug :
run ospray in single-threaded 'debug' mode -
--osp:coi :
run ospray in coi mode -
- */ - - diff --git a/apps/particleViewer/CMakeLists.txt b/apps/particleViewer/CMakeLists.txt deleted file mode 100644 index 034423b0a7..0000000000 --- a/apps/particleViewer/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2016 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -CONFIGURE_OSPRAY() - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/apps/util) -INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}) -INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}/ospray) -INCLUDE_DIRECTORIES_ISPC(${EMBREE_DIR}/include) - -IF (NOT THIS_IS_MIC) - ADD_EXECUTABLE(ospParticleViewer - ParticleViewer.cpp - Model.cpp - uintah.cpp - ) - TARGET_LINK_LIBRARIES(ospParticleViewer - ospray - ospray_glut3d - ospray_xml - ${TBB_LIBRARY_MALLOC} - ${TBB_LIBRARY} - ) -ENDIF() - diff --git a/apps/particleViewer/ParticleViewer.cpp b/apps/particleViewer/ParticleViewer.cpp deleted file mode 100644 index 0b9215c4c7..0000000000 --- a/apps/particleViewer/ParticleViewer.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -/*! \file apps/particleViewer/viewer.cpp - \brief A GLUT-based viewer for simple particle data */ - -// viewer widget -#include "apps/common/widgets/glut3D.h" -// ospray, for rendering -#include "ospray/ospray.h" -#include "common/tasking/parallel_for.h" -// particle viewer -#include "Model.h" -#include "uintah.h" -// ospcommon -#include "common/FileName.h" - -namespace ospray { - namespace particle { - - using std::cout; - using std::endl; - - bool showFPS = false; - int maxAccum = 64; - int accumID = 0; - int timeStep = 0; - std::vector modelTimeStep; - - std::string modelSaveFileName = ""; - OSPRenderer ospRenderer = NULL; - typedef enum { LAMMPS_XYZ, DAT_XYZ } InputFormat; - - /*! when using the OBJ renderer, we create a automatic dirlight with this direction; use ''--sun-dir x y z' to change */ - // vec3f defaultDirLight_direction(-.3, -1, .4); - vec3f defaultDirLight_direction(.3, -1, -.2); - - //! the renderer we're about to use - std::string rendererType = "ao1"; - // std::string rendererType = "raycast_eyelight"; - // float defaultRadius = 1.f; - InputFormat inputFormat = LAMMPS_XYZ; - void error(const std::string &err) - { - cout << "#osp::ospParticleViewer fatal error : " << err << endl; - cout << endl; - cout << "Proper usage: " << endl; - cout << " ./ospParticleViewer " << endl; - cout << endl; - exit(1); - } - - using ospray::glut3D::Glut3DWidget; - - /*! mini scene graph viewer widget. \internal Note that all handling - of camera is almost exactly similar to the code in volView; - might make sense to move that into a common class! */ - struct ParticleViewer : public Glut3DWidget { - ParticleViewer(OSPModel model, OSPRenderer renderer) - : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), - fb(NULL), renderer(renderer), model(model) - { - Assert(model && "null model handle"); - camera = ospNewCamera("perspective"); - Assert(camera != NULL && "could not create camera"); - ospSet3f(camera,"pos",-1,1,-1); - ospSet3f(camera,"dir",+1,-1,+1); - ospCommit(camera); - - ospSetObject(renderer,"model",model); - ospSetObject(renderer,"camera",camera); - ospCommit(camera); - ospCommit(renderer); - }; - - virtual void reshape(const ospcommon::vec2i &_newSize) - { - Glut3DWidget::reshape(_newSize); - if (fb) ospFreeFrameBuffer(fb); - const auto &newSize = reinterpret_cast(_newSize); - fb = ospNewFrameBuffer(newSize, OSP_FB_SRGBA, OSP_FB_COLOR | OSP_FB_ACCUM); - ospFrameBufferClear(fb,OSP_FB_ACCUM); - accumID = 0; - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - - setTitle("OSPRay Particle Viewer"); - } - - virtual void keypress(char key, const vec2i &where) - { - switch(key) { - case 'Q': exit(0); - case '>': { - timeStep = (timeStep+1)%modelTimeStep.size(); - model = modelTimeStep[timeStep]; - ospSetObject(renderer,"model",model); - PRINT(timeStep); - PRINT(model); - ospCommit(renderer); - viewPort.modified = true; - forceRedraw(); - } break; - case '<': { - timeStep = (timeStep+modelTimeStep.size()-1)%modelTimeStep.size(); - model = modelTimeStep[timeStep]; - PRINT(timeStep); - PRINT(model); - ospSetObject(renderer,"model",model); - ospCommit(renderer); - viewPort.modified = true; - forceRedraw(); - } break; - default: - Glut3DWidget::keypress(key,where); - } - } - - virtual void display() - { - if (!fb || !renderer) return; - - static int frameID = 0; - - //{ - // note that the order of 'start' and 'end' here is - // (intentionally) reversed: due to our asynchrounous rendering - // you cannot place start() and end() _around_ the renderframe - // call (which in itself will not do a lot other than triggering - // work), but the average time between ttwo calls is roughly the - // frame rate (including display overhead, of course) - if (frameID > 0) fps.doneRender(); - fps.startRender(); - //} - ++frameID; - - if (viewPort.modified) { - Assert2(camera,"ospray camera is null"); - auto from = reinterpret_cast(viewPort.from); - auto dir = viewPort.at-viewPort.from; - auto up = reinterpret_cast(viewPort.up); - ospSetVec3f(camera,"pos",from); - ospSetVec3f(camera,"dir",reinterpret_cast(dir)); - ospSetVec3f(camera,"up",up); - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - ospFrameBufferClear(fb,OSP_FB_ACCUM); - viewPort.modified = false; - accumID = 0; - } - - ospRenderFrame(fb,renderer,OSP_FB_COLOR|OSP_FB_ACCUM); - ++accumID; - if (accumID < maxAccum) - forceRedraw(); - - ucharFB = (uint32_t *) ospMapFrameBuffer(fb); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - Glut3DWidget::display(); - - ospUnmapFrameBuffer(ucharFB,fb); - - if (showFPS) { - char title[1000]; - - sprintf(title,"OSPRay Particle Viewer (%f fps)",fps.getFPS()); - setTitle(title); - } - } - - OSPModel model; - OSPFrameBuffer fb; - OSPRenderer renderer; - OSPCamera camera; - ospray::glut3D::FPSCounter fps; - }; - - particle::Model *createTestCube(int numPerSide) - { - particle::Model *m = new particle::Model; - int type = m->getAtomType("testParticle"); - for (int z=0;zatom.push_back(a); - } - return m; - } - - OSPData makeMaterials(OSPRenderer renderer,particle::Model *model) - { - int numMaterials = model->atomType.size(); - OSPMaterial matArray[numMaterials]; - for (int i=0;iatomType[i]->color.x); - ospCommit(mat); - matArray[i] = mat; - } - OSPData data = ospNewData(numMaterials,OSP_OBJECT,matArray); - ospCommit(data); - return data; - } - - struct DeferredLoadJob { - DeferredLoadJob(particle::Model *model, - const FileName &xyzFileName, - const FileName &defFileName) - : model(model), xyzFileName(xyzFileName), defFileName(defFileName) - {} - - //! the mode we still have to load - particle::Model *model; - //! file name of xyz file to be loaded into this model - FileName xyzFileName; - //! name of atom type defintion file active when this xyz file was added - FileName defFileName; - }; - - void ospParticleViewerMain(int &ac, const char **&av) - { - std::vector particleModel; - - cout << "ospParticleViewer: starting to process cmdline arguments" << endl; - std::vector deferredLoadingListXYZ; - FileName defFileName = ""; - - for (int i=1;iloadXYZ(fn); - // std::pair loadJob(m,fn.str()); - deferredLoadingListXYZ.push_back(new DeferredLoadJob(m,fn,defFileName)); - particleModel.push_back(m); - } else if (fn.ext() == "xyz2") { - particle::Model *m = new particle::Model; - m->loadXYZ2(fn); - particleModel.push_back(m); - } else if (fn.ext() == "xml") { - particle::Model *m = parse__Uintah_timestep_xml(fn); - particleModel.push_back(m); - } else - error("unknown file format "+fn.str()); - } - } - - if (particleModel.empty()) - error("no input file specified"); - - parallel_for(deferredLoadingListXYZ.size(), [&](int i){ - FileName defFileName = deferredLoadingListXYZ[i]->defFileName; - FileName xyzFileName = deferredLoadingListXYZ[i]->xyzFileName; - particle::Model *model = deferredLoadingListXYZ[i]->model; - - if (defFileName.str() != "") - model->readAtomTypeDefinitions(defFileName); - model->loadXYZ(xyzFileName); - }); - - - // ------------------------------------------------------- - // done parsings - // -------------------------------------------------------] - cout << "ospParticleViewer: done parsing. found model with" << endl; - cout << " - num atoms: " << particleModel[0]->atom.size() << endl; - if (modelSaveFileName != "") { - particleModel[0]->saveToFile(modelSaveFileName); - exit(0); - } - - // ------------------------------------------------------- - // create ospray model - // ------------------------------------------------------- - ospRenderer = ospNewRenderer(rendererType.c_str()); - if (!ospRenderer) - error("could not create ospRenderer '"+rendererType+"'"); - Assert(ospRenderer != NULL && "could not create ospRenderer"); - - for (int i=0;iatom.size()*5,OSP_FLOAT, - &particleModel[i]->atom[0],OSP_DATA_SHARED_BUFFER); - ospCommit(data); - - OSPGeometry geom = ospNewGeometry("spheres"); - ospSet1i(geom,"bytes_per_sphere",sizeof(Model::Atom)); - ospSet1i(geom,"offset_center",0); - ospSet1i(geom,"offset_radius",3*sizeof(float)); - ospSet1i(geom,"offset_materialID",4*sizeof(float)); - ospSetData(geom,"spheres",data); - ospSetData(geom,"materialList",materialData); - ospCommit(geom); - - ospAddGeometry(model,geom); - ospCommit(model); - - modelTimeStep.push_back(model); - } - - OSPModel model = modelTimeStep[timeStep]; - - std::vector dirLights; - cout << "msgView: Adding a hard coded directional light as the sun." << endl; - OSPLight ospLight = ospNewLight(ospRenderer, "DirectionalLight"); - ospSetString(ospLight, "name", "sun" ); - ospSet3f(ospLight, "color", 1, 1, 1); - ospSet3fv(ospLight, "direction", &defaultDirLight_direction.x); - ospCommit(ospLight); - dirLights.push_back(ospLight); - OSPData dirLightArray = ospNewData(dirLights.size(), OSP_OBJECT, &dirLights[0], 0); - ospSetData(ospRenderer, "directionalLights", dirLightArray); - - ospCommit(ospRenderer); - - - cout << "ospParticleViewer: done creating ospray model." << endl; - - // ------------------------------------------------------- - // create viewer window - // ------------------------------------------------------- - ParticleViewer window(model,ospRenderer); - window.create("ospray particle viewer"); - printf("OSPRay Particle Viewer created. Press 'Q' to quit.\n"); - box3f wb(particleModel[0]->getBBox()); - PRINT(wb); - window.setWorldBounds(wb); - ospray::glut3D::runGLUT(); - } - - } // ::ospray::particle -} // ::ospray - - -int main(int ac, const char **av) -{ - ospInit(&ac,av); - ospray::glut3D::initGLUT(&ac,av); - ospray::particle::ospParticleViewerMain(ac,av); -} diff --git a/apps/particleViewer/particleViewer.dox b/apps/particleViewer/particleViewer.dox deleted file mode 100644 index fe5efc4503..0000000000 --- a/apps/particleViewer/particleViewer.dox +++ /dev/null @@ -1,52 +0,0 @@ -/*! \defgroup ospray_apps_ospParticleViewer ospParticleViewer: The OSPRay Particle Viewer (e.g., for LAMMPS .xyz files) - - \ingroup ospray_apps -*/ - -/*! \page particleViewer The "ospParticleView" Particle Viewer - - \ingroup ospray_apps_ospParticleViewer - - \section particleViewer_usage Usage - - usage: -
-  ./ospXYZ  models
-  
- - supported model formats: LAMMPs .xyz - - supported parameters: - -
-
- --radius r : radius to be used per sphere -
- - parameters inherited from glut3D: - -
-
- -vp x y z :
specify camera point of origin -
- -vi x y z :
specify camera point of interest -
- -vu x y z :
specify up-vector -
- --sun-dir x y z :
specify direction of automatic 'sun' dir-light -
- --spp n :
number of samples per pixel (for renderers that respect the 'spp' parameter) -
- --renderer :
where rendertype is one of raycast,obj,geomID,primID, or any loaded renderer -
- - parameters inherited from ospray -
-
- --osp:debug :
run ospray in single-threaded 'debug' mode -
- --osp:coi :
run ospray in coi mode -
- */ - - diff --git a/apps/qtViewer/CMakeLists.txt b/apps/qtViewer/CMakeLists.txt index 0ad957c3d9..d3f3ce12ac 100644 --- a/apps/qtViewer/CMakeLists.txt +++ b/apps/qtViewer/CMakeLists.txt @@ -14,8 +14,6 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) @@ -54,13 +52,12 @@ SET(MOC_HEADERS QT4_WRAP_CPP(MOC_OUTFILES ${MOC_HEADERS}) -ADD_EXECUTABLE(ospQtViewer ${SRCS} ${MOC_OUTFILES}) -TARGET_LINK_LIBRARIES(ospQtViewer ${LIBS} +OSPRAY_CREATE_APPLICATION(QtViewer + ${SRCS} + ${MOC_OUTFILES} +LINK + ${LIBS} ospray_sg ${TBB_LIBRARY_MALLOC} ${TBB_LIBRARY} - ) -INSTALL(TARGETS ospQtViewer - DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT apps ) diff --git a/apps/qtViewer/sg/CMakeLists.txt b/apps/qtViewer/sg/CMakeLists.txt index f52f6eb690..df0c564b94 100644 --- a/apps/qtViewer/sg/CMakeLists.txt +++ b/apps/qtViewer/sg/CMakeLists.txt @@ -14,10 +14,7 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) -INCLUDE_DIRECTORIES(${OSPRAY_SOURCE_DIR}) ADD_LIBRARY(ospray_sg STATIC # scene graph nodes @@ -48,7 +45,6 @@ ADD_LIBRARY(ospray_sg STATIC importer/ImportOBJ.cpp importer/ImportPLY.cpp importer/ImportRIVL.cpp - # ImportX3D.cpp ) TARGET_LINK_LIBRARIES(ospray_sg diff --git a/apps/qtViewer/sg/SceneGraph.h b/apps/qtViewer/sg/SceneGraph.h index 392a91b7ad..48485fba1c 100644 --- a/apps/qtViewer/sg/SceneGraph.h +++ b/apps/qtViewer/sg/SceneGraph.h @@ -32,7 +32,7 @@ #include "sg/common/FrameBuffer.h" // ospcommon -#include "common/FileName.h" +#include "ospcommon/FileName.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/common/Common.cpp b/apps/qtViewer/sg/common/Common.cpp index 081523a102..dae763543c 100644 --- a/apps/qtViewer/sg/common/Common.cpp +++ b/apps/qtViewer/sg/common/Common.cpp @@ -17,9 +17,24 @@ // scene graph common stuff #include "Common.h" +// stdlib, for mmap +#include +#include +#ifdef _WIN32 +# include +#else +# include +#endif +#include + +// O_LARGEFILE is a GNU extension. +#ifdef __APPLE__ +#define O_LARGEFILE 0 +#endif + namespace ospray { namespace sg { - + //! parse vec3i from std::string (typically an xml-node's content string) vec3i parseVec3i(const std::string &text) { @@ -37,6 +52,41 @@ namespace ospray { assert(rc == 2); return ret; } - + + const unsigned char * mapFile(const std::string &fileName) + { + FILE *file = fopen(fileName.c_str(), "rb"); + if (!file) + THROW_SG_ERROR("could not open binary file"); + fseek(file, 0, SEEK_END); + ssize_t fileSize = +#ifdef _WIN32 + _ftelli64(file); +#else + ftell(file); +#endif + fclose(file); + +#ifdef _WIN32 + HANDLE fileHandle = CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (fileHandle == nullptr) + THROW_SG_ERROR("could not open file '" + fileName + "' (error " + std::to_string(GetLastError()) + ")\n"); + HANDLE fileMappingHandle = CreateFileMapping(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (fileMappingHandle == nullptr) + THROW_SG_ERROR("could not create file mapping (error " + std::to_string(GetLastError()) + ")\n"); +#else + int fd = ::open(fileName.c_str(), O_LARGEFILE | O_RDONLY); + if (fd == -1) + THROW_SG_ERROR("could not open file '" + fileName + "'\n"); +#endif + + return (unsigned char *) +#ifdef _WIN32 + MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, fileSize); +#else + mmap(nullptr, fileSize, PROT_READ, MAP_SHARED, fd, 0); +#endif + } + } // ::ospray::sg } // ::ospray diff --git a/apps/qtViewer/sg/common/Common.h b/apps/qtViewer/sg/common/Common.h index e2bfa751c0..8037f95908 100644 --- a/apps/qtViewer/sg/common/Common.h +++ b/apps/qtViewer/sg/common/Common.h @@ -19,7 +19,7 @@ // use ospcommon vector types in ospray.h #define OSPRAY_EXTERNAL_VECTOR_TYPES 1 // ospcommon -#include "common/AffineSpace.h" +#include "ospcommon/AffineSpace.h" namespace osp { using ospcommon::vec2i; @@ -33,7 +33,6 @@ namespace osp { // ospray API #include "ospray/ospray.h" -// STL namespace ospray { namespace sg { @@ -42,8 +41,8 @@ namespace ospray { typedef AffineSpace3f affine3f; typedef LinearSpace3f linear3f; -#define THROW_SG_ERROR(where,err) \ - throw std::runtime_error("in "+std::string(__PRETTY_FUNCTION__)+":"+std::string(err)); +#define THROW_SG_ERROR(err) \ + throw std::runtime_error("in "+std::string(__PRETTY_FUNCTION__)+":"+std::string(err)) typedef unsigned int uint; @@ -74,6 +73,9 @@ namespace ospray { /*! @} */ + //! map the given file to memory and return that pointer + const unsigned char * mapFile(const std::string &fileName); + } // ::ospray::sg } // ::ospray diff --git a/apps/qtViewer/sg/common/Data.h b/apps/qtViewer/sg/common/Data.h index 97c62b6e3c..8477f3ae3a 100644 --- a/apps/qtViewer/sg/common/Data.h +++ b/apps/qtViewer/sg/common/Data.h @@ -22,7 +22,7 @@ // stl #include // xml -#include "apps/common/xml/XML.h" +#include "common/xml/XML.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/common/Node.h b/apps/qtViewer/sg/common/Node.h index eb7fa0fdaa..23f9570321 100644 --- a/apps/qtViewer/sg/common/Node.h +++ b/apps/qtViewer/sg/common/Node.h @@ -22,9 +22,9 @@ // stl #include // xml -#include "apps/common/xml/XML.h" +#include "../../../common/xml/XML.h" // ospcommon -#include "common/vec.h" +#include "ospcommon/vec.h" namespace ospray { diff --git a/apps/qtViewer/sg/common/Serialization.h b/apps/qtViewer/sg/common/Serialization.h index 1566a7a1ba..40d8e55fa0 100644 --- a/apps/qtViewer/sg/common/Serialization.h +++ b/apps/qtViewer/sg/common/Serialization.h @@ -18,8 +18,8 @@ #include "sg/common/Common.h" // ospcommon -#include "common/RefCount.h" -#include "common/AffineSpace.h" +#include "ospcommon/RefCount.h" +#include "ospcommon/AffineSpace.h" // std #include diff --git a/apps/qtViewer/sg/common/Texture2D.cpp b/apps/qtViewer/sg/common/Texture2D.cpp index f94bbed5f1..ce479e1e40 100644 --- a/apps/qtViewer/sg/common/Texture2D.cpp +++ b/apps/qtViewer/sg/common/Texture2D.cpp @@ -41,13 +41,13 @@ namespace ospray { char lineBuf[LINESZ+1]; if (!file) - throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); + throw std::runtime_error("#osp:sg: could not open texture file '"+fileName.str()+"'."); // read format specifier: int format=0; fscanf(file,"P%i\n",&format); if (format != 6) - throw std::runtime_error("#osp:miniSG: can currently load only binary P6 subformats for PPM texture files. " + throw std::runtime_error("#osp:sg: can currently load only binary P6 subformats for PPM texture files. " "Please report this bug at ospray.github.io."); // skip all comment lines @@ -61,7 +61,7 @@ namespace ospray { int width=-1,height=-1; rc = fscanf(file,"%i %i\n",&width,&height); if (rc != 2) - throw std::runtime_error("#osp:miniSG: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " + throw std::runtime_error("#osp:sg: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " "Please report this bug at ospray.github.io, and include named file to reproduce the error."); // skip all comment lines @@ -77,10 +77,10 @@ namespace ospray { peekchar = getc(file); if (rc != 1) - throw std::runtime_error("#osp:miniSG: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " + throw std::runtime_error("#osp:sg: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " "Please report this bug at ospray.github.io, and include named file to reproduce the error."); if (maxVal != 255) - throw std::runtime_error("#osp:miniSG: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." + throw std::runtime_error("#osp:sg: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." "Please report this bug at ospray.github.io, and include named file to reproduce the error."); tex = new Texture2D; diff --git a/apps/qtViewer/sg/common/Texture2D.h b/apps/qtViewer/sg/common/Texture2D.h index e1a76dae8c..4f66e7cd79 100644 --- a/apps/qtViewer/sg/common/Texture2D.h +++ b/apps/qtViewer/sg/common/Texture2D.h @@ -19,7 +19,7 @@ // sg #include "sg/common/Node.h" // ospcommon -#include "common/FileName.h" +#include "ospcommon/FileName.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/common/TimeStamp.h b/apps/qtViewer/sg/common/TimeStamp.h index 5c97757f78..f6ce817a12 100644 --- a/apps/qtViewer/sg/common/TimeStamp.h +++ b/apps/qtViewer/sg/common/TimeStamp.h @@ -17,7 +17,7 @@ #pragma once #include "sg/common/Common.h" -#include "common/intrinsics.h" +#include "ospcommon/intrinsics.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/common/TransferFunction.cpp b/apps/qtViewer/sg/common/TransferFunction.cpp index 4e5e51a1dc..d1eaf791db 100644 --- a/apps/qtViewer/sg/common/TransferFunction.cpp +++ b/apps/qtViewer/sg/common/TransferFunction.cpp @@ -88,7 +88,7 @@ namespace ospray { lastModified = TimeStamp::now(); } if (ospAlphaData == NULL) { - float alpha[numSamples]; + float *alpha = (float*)alloca(sizeof(float)*numSamples); float x0 = alphaArray.front().first; float dx = (alphaArray.back().first - x0) / (numSamples-1); diff --git a/apps/qtViewer/sg/geometry/Spheres.cpp b/apps/qtViewer/sg/geometry/Spheres.cpp index fbaf4b0083..4cedfbc95a 100644 --- a/apps/qtViewer/sg/geometry/Spheres.cpp +++ b/apps/qtViewer/sg/geometry/Spheres.cpp @@ -19,7 +19,7 @@ #include "sg/geometry/Spheres.h" #include "sg/common/Integrator.h" // xml parser -#include "apps/common/xml/XML.h" +#include "common/xml/XML.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/importer/ImportOBJ.cpp b/apps/qtViewer/sg/importer/ImportOBJ.cpp index f43fa4699b..eae0a11c4e 100644 --- a/apps/qtViewer/sg/importer/ImportOBJ.cpp +++ b/apps/qtViewer/sg/importer/ImportOBJ.cpp @@ -416,10 +416,10 @@ namespace ospray { void importOBJ(const Ref &world, const FileName &fileName) { - std::cout << "ospray::miniSG::importOBJ: importing from " << fileName << endl; + std::cout << "ospray::sg::importOBJ: importing from " << fileName << endl; OBJLoader loader(world.ptr,fileName); } - } // ::ospray::minisg + } } // ::ospray diff --git a/apps/qtViewer/sg/importer/ImportOSP.cpp b/apps/qtViewer/sg/importer/ImportOSP.cpp index ca0aa58d20..7a770206ce 100644 --- a/apps/qtViewer/sg/importer/ImportOSP.cpp +++ b/apps/qtViewer/sg/importer/ImportOSP.cpp @@ -16,22 +16,12 @@ #undef NDEBUG -// O_LARGEFILE is a GNU extension. -#ifdef __APPLE__ -#define O_LARGEFILE 0 -#endif - // header #include "SceneGraph.h" // stl #include -// stdlib, for mmap -#include -#include -#include -#include // xml -#include "apps/common/xml/XML.h" +#include "common/xml/XML.h" namespace ospray { namespace sg { @@ -202,28 +192,7 @@ namespace ospray { cout << "#osp:sg: XML file read, starting to parse content..." << endl; const std::string binFileName = fileName+"bin"; - - const unsigned char *binBasePtr = NULL; - FILE *file = fopen(binFileName.c_str(),"r"); - if (!file) { - std::cout << "#osp:sg:loadOSP: Warning - binary file '"+binFileName+"' could not be found" << std::endl; - } else { - fseek(file,0,SEEK_END); - ssize_t fileSize = -#ifdef _WIN32 - _ftelli64(file); -#else - ftell(file); -#endif - fclose(file); - - int fd = ::open(binFileName.c_str(),O_LARGEFILE|O_RDWR); - if (fd == -1) { - std::cout << "#osp:sg:loadOSP: Warning - binary file '"+binFileName+"' could not be found" << std::endl; - } else { - binBasePtr = (const unsigned char *)mmap(NULL,fileSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); - } - } + const unsigned char * const binBasePtr = mapFile(binFileName); if (!doc) throw std::runtime_error("could not parse "+fileName); diff --git a/apps/qtViewer/sg/importer/ImportPLY.cpp b/apps/qtViewer/sg/importer/ImportPLY.cpp index 79e8083135..8f524cf8bf 100644 --- a/apps/qtViewer/sg/importer/ImportPLY.cpp +++ b/apps/qtViewer/sg/importer/ImportPLY.cpp @@ -145,11 +145,15 @@ namespace ospray { bool isPipe = false; if (strlen(filename) > 7 && !strcmp(filename+strlen(filename)-7,".ply.gz")) { +#ifdef _WIN32 + THROW_SG_ERROR("#osp:sg:ply: gzipped file not supported yet on Windows"); +#else isPipe = true; char cmd[10000]; sprintf(cmd,"/usr/bin/gunzip -c %s",filename); file = popen(cmd,"r"); - } else +#endif + } else file = fopen(filename,"rb"); if (!file) diff --git a/apps/qtViewer/sg/importer/ImportRIVL.cpp b/apps/qtViewer/sg/importer/ImportRIVL.cpp index e83c07013b..51e1754df2 100644 --- a/apps/qtViewer/sg/importer/ImportRIVL.cpp +++ b/apps/qtViewer/sg/importer/ImportRIVL.cpp @@ -16,11 +16,6 @@ #undef NDEBUG -// O_LARGEFILE is a GNU extension. -#ifdef __APPLE__ -#define O_LARGEFILE 0 -#endif - #define WARN_ON_INCLUDING_OSPCOMMON 1 #include "SceneGraph.h" @@ -28,11 +23,6 @@ #include "sg/geometry/TriangleMesh.h" // stl #include -// stdlib, for mmap -#include -#include -#include -#include namespace ospray { namespace sg { @@ -468,23 +458,7 @@ namespace ospray { { string xmlFileName = fileName; string binFileName = fileName+".bin"; - - FILE *file = fopen(binFileName.c_str(),"rb"); - if (!file) - perror("could not open binary file"); - fseek(file,0,SEEK_END); - ssize_t fileSize = -#ifdef _WIN32 - _ftelli64(file); -#else - ftell(file); -#endif - fclose(file); - - int fd = ::open(binFileName.c_str(),O_LARGEFILE|O_RDWR); - if (fd == -1) - perror("could not open file"); - binBasePtr = (unsigned char *)mmap(NULL,fileSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); + const unsigned char * const binBasePtr = mapFile(binFileName); xml::XMLDoc *doc = xml::readXML(fileName); if (doc->child.size() != 1 || doc->child[0]->name != "BGFscene") diff --git a/apps/qtViewer/sg/importer/ImportSpheres.cpp b/apps/qtViewer/sg/importer/ImportSpheres.cpp index ee4d6f2667..90095d887e 100644 --- a/apps/qtViewer/sg/importer/ImportSpheres.cpp +++ b/apps/qtViewer/sg/importer/ImportSpheres.cpp @@ -27,12 +27,7 @@ // stl #include // xml -#include "apps/common/xml/XML.h" -// stdlib, for mmap -#include -#include -#include -#include +#include "common/xml/XML.h" namespace ospray { namespace sg { diff --git a/apps/qtViewer/sg/importer/Importer.h b/apps/qtViewer/sg/importer/Importer.h index 0c7f4e3eca..46dcfc24de 100644 --- a/apps/qtViewer/sg/importer/Importer.h +++ b/apps/qtViewer/sg/importer/Importer.h @@ -18,7 +18,7 @@ // ospray::sg #include "../common/World.h" -#include "common/FileName.h" +#include "ospcommon/FileName.h" /*! \file sg/module/Importer.h Defines the interface for writing file importers for the ospray::sg */ diff --git a/apps/qtViewer/sg/module/Module.cpp b/apps/qtViewer/sg/module/Module.cpp index 2821b54fbf..d3c7d54f02 100644 --- a/apps/qtViewer/sg/module/Module.cpp +++ b/apps/qtViewer/sg/module/Module.cpp @@ -18,7 +18,7 @@ #include "Module.h" #include "../common/RuntimeError.h" // ospray -#include "common/common.h" +#include "ospcommon/common.h" // std #include diff --git a/apps/qtViewer/sg/volume/Volume.cpp b/apps/qtViewer/sg/volume/Volume.cpp index 9539697d5e..1c09ca001a 100644 --- a/apps/qtViewer/sg/volume/Volume.cpp +++ b/apps/qtViewer/sg/volume/Volume.cpp @@ -89,7 +89,7 @@ namespace ospray { ? "data_distributed_volume" : "block_bricked_volume"); if (!volume) - THROW_SG_ERROR(__PRETTY_FUNCTION__,"could not allocate volume"); + THROW_SG_ERROR("could not allocate volume"); ospSetString(volume,"voxelType",voxelType.c_str()); ospSetVec3i(volume,"dimensions",(const osp::vec3i&)dimensions); @@ -169,7 +169,7 @@ namespace ospray { else volume = ospNewVolume(useBlockBricked ? "block_bricked_volume" : "shared_structured_volume"); if (!volume) - THROW_SG_ERROR(__PRETTY_FUNCTION__,"could not allocate volume"); + THROW_SG_ERROR("could not allocate volume"); PING; PRINT(voxelType); ospSetString(volume,"voxelType",voxelType.c_str()); @@ -217,7 +217,7 @@ namespace ospray { float *voxels = new float[nVoxels]; size_t nRead = fread(voxels,sizeof(float),nVoxels,file); if (nRead != nVoxels) - THROW_SG_ERROR(__PRETTY_FUNCTION__,"read incomplete data (truncated file or wrong format?!)"); + THROW_SG_ERROR("read incomplete data (truncated file or wrong format?!)"); OSPData data = ospNewData(nVoxels,OSP_FLOAT,voxels,OSP_DATA_SHARED_BUFFER); ospSetData(volume,"voxelData",data); } @@ -282,15 +282,15 @@ namespace ospray { volume = ospNewVolume("block_bricked_volume"); if (!volume) - THROW_SG_ERROR(__PRETTY_FUNCTION__,"could not allocate volume"); + THROW_SG_ERROR("could not allocate volume"); ospSetString(volume,"voxelType",voxelType.c_str()); ospSetVec3i(volume,"dimensions",(const osp::vec3i&)dimensions); size_t nPerSlice = dimensions.x*dimensions.y; uint8_t *slice = new uint8_t[nPerSlice]; for (int sliceID=0;sliceID #include #if __APPLE__ diff --git a/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.h b/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.h index 778c5e60a7..1d5c927842 100644 --- a/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.h +++ b/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.h @@ -21,8 +21,8 @@ // ospray public api #include "ospray/ospray.h" // ospcomon -#include "common/box.h" -#include "common/LinearSpace.h" +#include "ospcommon/box.h" +#include "ospcommon/LinearSpace.h" // qt #include #include diff --git a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h b/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h index 4f239e2552..b8358d3a29 100644 --- a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h +++ b/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h @@ -24,7 +24,7 @@ //sg #include "sg/Renderer.h" // ospcomon -#include "common/common.h" +#include "ospcommon/common.h" namespace ospray { namespace viewer { diff --git a/apps/streamLineViewer/StreamLineViewer.cpp b/apps/streamLineViewer/StreamLineViewer.cpp deleted file mode 100644 index ad10fffd29..0000000000 --- a/apps/streamLineViewer/StreamLineViewer.cpp +++ /dev/null @@ -1,707 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#undef NDEBUG -// viewer widget -#include "apps/common/widgets/glut3D.h" -// ospray, for rendering -#include "ospray/ospray.h" -// embree -#include "common/FileName.h" -// xml -#include "apps/common/xml/XML.h" - -namespace ospray { - using std::cout; - using std::endl; - using namespace ospcommon; - - const char *rendererType = "raycast_eyelight"; - bool doShadows = 1; - - OSPModel model = NULL; - - void error(const std::string &err) - { - cout << "ospray::ospDVR fatal error : " << err << endl; - cout << endl; - cout << "Proper usage: " << endl; - cout << " ./streamLineView ...." << std::endl; - cout << "or" << endl; - cout << " ./streamLineView ...." << std::endl; - cout << "or" << endl; - cout << " ./streamLineView ...." << std::endl; - cout << endl; - exit(1); - } - - using ospray::glut3D::Glut3DWidget; - - struct Triangles { - std::vector vertex; - std::vector color; // vertex color, from sv's 'v' value - std::vector index; - - struct SVVertex { - float v; - vec3f pos; //float x,y,z; - }; - - struct SVTriangle { - SVVertex vertex[3]; - }; - - vec3f lerpf(float x, float x0,float x1,vec3f y0, vec3f y1) - { - float f = (x-x0)/(x1-x0); - return f*y1+(1-f)*y0; - } - vec3f colorOf(const float f) - { - if (f < .5f) - return vec3f(lerpf(f, 0.f,.5f,vec3f(0),vec3f(0,1,0))); - else - return vec3f(lerpf(f, .5f,1.f,vec3f(0,1,0),vec3f(1,0,0))); - } - void parseSV(const FileName &fn) - { - FILE *file = fopen(fn.str().c_str(),"rb"); - if (!file) return; - SVTriangle triangle; - while (fread(&triangle,sizeof(triangle),1,file)) { - index.push_back(vec3i(0,1,2)+vec3i(vertex.size())); - vertex.push_back(vec3fa(triangle.vertex[0].pos)); - vertex.push_back(vec3fa(triangle.vertex[1].pos)); - vertex.push_back(vec3fa(triangle.vertex[2].pos)); - color.push_back(colorOf(triangle.vertex[0].v)); - color.push_back(colorOf(triangle.vertex[1].v)); - color.push_back(colorOf(triangle.vertex[2].v)); - } - fclose(file); - } - - box3f getBounds() const - { - box3f bounds = empty; - for (int i=0;i vertex; - std::vector index; - float radius; - - StreamLines() : radius(0.001f) {}; - - void parsePNT(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"r"); - if (!file) { - cout << "WARNING: could not open file " << fn << endl; - return; - } - vec3fa pnt; - static size_t totalSegments = 0; - size_t segments = 0; - // cout << "parsing file " << fn << ":" << std::flush; - int rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z); - vertex.push_back(pnt); - Assert(rc == 3); - while ((rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z)) == 3) { - index.push_back(vertex.size()-1); - vertex.push_back(pnt); - segments++; - } - totalSegments += segments; - // cout << " " << segments << " segments (" << totalSegments << " total)" << endl; - fclose(file); - } - - - void parsePNTlist(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - char *eol = strstr(line,"\n"); if (eol) *eol = 0; - parsePNT(line); - } - fclose(file); - } - - void parseSLRAW(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"rb"); - - if (!file) { - cout << "WARNING: could not open file " << fn << endl; - return; - } - - int numStreamlines; - int rc = fread(&numStreamlines, sizeof(int), 1, file); - Assert(rc == 1); - - for(int s=0; s filePoints; - - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - radius = 99.f; - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - if (line[0] == '#') continue; - - vec3fa v; float f1; int ID, i, j; - sscanf(line,"%i %i %f %f %f %f %i\n",&ID,&i,&v.x,&v.y,&v.z,&f1,&j); - if (f1 < radius) - radius = f1; - filePoints.push_back(v); - - index.push_back(vertex.size()); - vertex.push_back(v); - vertex.push_back(filePoints[j-1]); - } - fclose(file); - } - - void parse(const FileName &fn) - { - if (fn.ext() == "pnt") - parsePNT(fn); - else if (fn.ext() == "pntlist") { - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - char *eol = strstr(line,"\n"); if (eol) *eol = 0; - parsePNT(line); - } - fclose(file); - } else if (fn.ext() == "slraw") { - parseSLRAW(fn); - } else - throw std::runtime_error("unknown input file format "+fn.str()); - } - box3f getBounds() const - { - box3f bounds = empty; - for (int i=0;i spheres[3]; - std::vector cylinders[3]; - box3f bounds; - - void parse(const FileName &fn) - { - std::vector filePoints; - - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - bounds = empty; - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - if (line[0] == '#') continue; - - vec3f v; float radius; int id, parent, type; - sscanf(line,"%i %i %f %f %f %f %i\n", &id, &type, &v.x, &v.y, &v.z, &radius, &parent); - filePoints.push_back(v); - Assert(filePoints.size() == id); // assumes index-1==id - bounds.extend(v); // neglects radius - - if (parent == -1) // root soma, just one sphere - spheres[0].push_back((Sphere){v, radius}); - else { // cylinder with sphere at end - int idx; - switch (type) { - case 3: idx = 1; break; // basal dendrite - case 4: idx = 2; break; // apical dendrite - default: idx = 0; break; // soma / axon - } - spheres[idx].push_back((Sphere){v, radius}); - cylinders[idx].push_back((Cylinder){filePoints[parent-1], v, radius}); - } - } - fclose(file); - } - - box3f getBounds() const - { - return bounds; - } - }; - - void osxParseInts(std::vector &vec, const std::string &content) - { - char *s = strdup(content.c_str()); - const char *delim = "\n\t\r "; - char *tok = strtok(s,delim); - while (tok) { - vec.push_back(atol(tok)); - tok = strtok(NULL,delim); - } - free(s); - } - - void osxParseVec3is(std::vector &vec, const std::string &content) - { - char *s = strdup(content.c_str()); - const char *delim = "\n\t\r "; - char *tok = strtok(s,delim); - while (tok) { - vec3i v; - assert(tok); - v.x = atol(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.y = atol(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.z = atol(tok); - tok = strtok(NULL,delim); - - vec.push_back(v); - } - free(s); - } - - void osxParseVec3fas(std::vector &vec, const std::string &content) - { - char *s = strdup(content.c_str()); - const char *delim = "\n\t\r "; - char *tok = strtok(s,delim); - while (tok) { - vec3fa v; - assert(tok); - v.x = atof(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.y = atof(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.z = atof(tok); - tok = strtok(NULL,delim); - - vec.push_back(v); - } - free(s); - } - - /*! parse ospray xml file */ - void parseOSX(StreamLines *streamLines, - Triangles *triangles, - const std::string &fn) - { - xml::XMLDoc *doc = xml::readXML(fn); - assert(doc); - if (doc->child.size() != 1 || doc->child[0]->name != "OSPRay") - throw std::runtime_error("could not parse osx file: Not in OSPRay format!?"); - xml::Node *root_element = doc->child[0]; - for (int childID=0;childIDchild.size();childID++) { - xml::Node *node = root_element->child[childID]; - if (node->name == "Info") { - // ignore - continue; - } - - if (node->name == "Model") { - xml::Node *model_node = node; - for (int childID=0;childIDchild.size();childID++) { - xml::Node *node = model_node->child[childID]; - - if (node->name == "StreamLines") { - - xml::Node *sl_node = node; - for (int childID=0;childIDchild.size();childID++) { - xml::Node *node = sl_node->child[childID]; - if (node->name == "vertex") { - osxParseVec3fas(streamLines->vertex,node->content); - continue; - }; - if (node->name == "index") { - osxParseInts(streamLines->index,node->content); - continue; - }; - } - continue; - } - - if (node->name == "TriangleMesh") { - xml::Node *tris_node = node; - for (int childID=0;childIDchild.size();childID++) { - xml::Node *node = tris_node->child[childID]; - if (node->name == "vertex") { - osxParseVec3fas(triangles->vertex,node->content); - continue; - }; - if (node->name == "color") { - osxParseVec3fas(triangles->color,node->content); - continue; - }; - if (node->name == "index") { - osxParseVec3is(triangles->index,node->content); - continue; - }; - } - continue; - } - } - } - } - } - - void exportOSX(const char *fn,StreamLines *streamLines, Triangles *triangles) - { - FILE *file = fopen(fn,"w"); - fprintf(file,"\n\n"); - fprintf(file,"\n"); - { - fprintf(file,"\n"); - { - fprintf(file,"\n"); - { - fprintf(file,"\n"); - for (int i=0;ivertex.size();i++) - fprintf(file,"%f %f %f\n", - streamLines->vertex[i].x, - streamLines->vertex[i].y, - streamLines->vertex[i].z); - fprintf(file,"\n"); - - fprintf(file,"\n"); - for (int i=0;iindex.size();i++) - fprintf(file,"%i ",streamLines->index[i]); - fprintf(file,"\n\n"); - } - fprintf(file,"\n"); - - - fprintf(file,"\n"); - { - fprintf(file,"\n"); - for (int i=0;ivertex.size();i++) - fprintf(file,"%f %f %f\n", - triangles->vertex[i].x, - triangles->vertex[i].y, - triangles->vertex[i].z); - fprintf(file,"\n"); - - fprintf(file,"\n"); - for (int i=0;icolor.size();i++) - fprintf(file,"%f %f %f\n", - triangles->color[i].x, - triangles->color[i].y, - triangles->color[i].z); - fprintf(file,"\n"); - - fprintf(file,"\n"); - for (int i=0;iindex.size();i++) - fprintf(file,"%i %i %i\n", - triangles->index[i].x, - triangles->index[i].y, - triangles->index[i].z); - fprintf(file,"\n"); - - } - fprintf(file,"\n"); - } - fprintf(file,"\n"); - } - fprintf(file,"\n"); - fclose(file); - } - - struct StreamLineViewer : public Glut3DWidget { - /*! construct volume from file name and dimensions \see volview_notes_on_volume_interface */ - StreamLineViewer(StreamLines *sl, Triangles *tris, StockleyWhealCannon *swc) - : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), - fb(NULL), renderer(NULL), - sl(sl), tris(tris) - { - camera = ospNewCamera("perspective"); - Assert2(camera,"could not create camera"); - ospSet3f(camera,"pos",-1,1,-1); - ospSet3f(camera,"dir",+1,-1,+1); - ospCommit(camera); - ospCommit(camera); - - renderer = ospNewRenderer(rendererType); - - OSPMaterial mat = ospNewMaterial(renderer,"default"); - if (mat) { - ospSet3f(mat,"kd",.7,.7,.7); // OBJ renderer, default ... - ospCommit(mat); - } - - if (std::string(rendererType) == "obj") { - OSPLight topLight = ospNewLight(renderer,"DirectionalLight"); - if (topLight) { - ospSet3f(topLight,"direction",-1,-2,1); - ospSet3f(topLight,"color",1,1,1); - ospCommit(topLight); - OSPData lights = ospNewData(1,OSP_OBJECT,&topLight); - ospCommit(lights); - ospSetData(renderer,"directionalLights",lights); - } - } - - ospCommit(renderer); - - model = ospNewModel(); - - if (sl && !sl->index.empty()) { - OSPGeometry geom = ospNewGeometry("streamlines"); - Assert(geom); - OSPData vertex = ospNewData(sl->vertex.size(),OSP_FLOAT3A,&sl->vertex[0]); - OSPData index = ospNewData(sl->index.size(),OSP_UINT,&sl->index[0]); - ospSetObject(geom,"vertex",vertex); - ospSetObject(geom,"index",index); - ospSet1f(geom,"radius",sl->radius); - if (mat) - ospSetMaterial(geom,mat); - ospCommit(geom); - ospAddGeometry(model,geom); - } - - if (tris && !tris->index.empty()) { - OSPGeometry geom = ospNewGeometry("triangles"); - Assert(geom); - OSPData vertex = ospNewData(tris->vertex.size(),OSP_FLOAT3A,&tris->vertex[0]); - OSPData index = ospNewData(tris->index.size(),OSP_INT3,&tris->index[0]); - OSPData color = ospNewData(tris->color.size(),OSP_FLOAT3A,&tris->color[0]); - ospSetObject(geom,"vertex",vertex); - ospSetObject(geom,"index",index); - ospSetObject(geom,"vertex.color",color); - ospSetMaterial(geom,mat); - ospCommit(geom); - ospAddGeometry(model,geom); - } - - if (swc && !swc->bounds.empty()) { - OSPMaterial material[3]; - material[0] = mat; - material[1] = ospNewMaterial(renderer, "default"); - if (material[1]) { - ospSet3f(material[1], "kd", .0, .7, .0); // OBJ renderer, green - ospCommit(material[1]); - } - material[2] = ospNewMaterial(renderer, "default"); - if (material[2]) { - ospSet3f(material[2], "kd", .7, .0, .7); // OBJ renderer, magenta - ospCommit(material[2]); - } - - OSPGeometry spheres[3], cylinders[3]; - for (int i=0;i<3;i++) { - spheres[i] = ospNewGeometry("spheres"); - Assert(spheres[i]); - - OSPData data = ospNewData(swc->spheres[i].size(), OSP_FLOAT4, &swc->spheres[i][0]); - ospSetObject(spheres[i], "spheres", data); - ospSet1i(spheres[i], "offset_radius", 3*sizeof(float)); - - if (material[i]) - ospSetMaterial(spheres[i], material[i]); - - ospCommit(spheres[i]); - ospAddGeometry(model, spheres[i]); - - - cylinders[i] = ospNewGeometry("cylinders"); - Assert(cylinders[i]); - - data = ospNewData(swc->cylinders[i].size()*7, OSP_FLOAT, &swc->cylinders[i][0]); - ospSetObject(cylinders[i], "cylinders", data); - - if (material[i]) - ospSetMaterial(cylinders[i], material[i]); - - ospCommit(cylinders[i]); - ospAddGeometry(model, cylinders[i]); - } - } - - ospCommit(model); - - Assert2(renderer,"could not create renderer"); - ospSetObject(renderer,"world",model); - ospSetObject(renderer,"camera",camera); - ospCommit(renderer); - - }; - virtual void reshape(const vec2i &newSize) - { - Glut3DWidget::reshape(newSize); - if (fb) ospFreeFrameBuffer(fb); - fb = ospNewFrameBuffer((const osp::vec2i&)newSize, OSP_FB_SRGBA, OSP_FB_COLOR | OSP_FB_ACCUM); - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - } - - virtual void keypress(char key, const vec2i where) - { - switch (key) { - case 'S': - doShadows = !doShadows; - cout << "Switching shadows " << (doShadows?"ON":"OFF") << endl; - ospSet1i(renderer,"shadowsEnabled",doShadows); - ospCommit(renderer); - ospFrameBufferClear(fb,OSP_FB_ACCUM); - break; - default: - Glut3DWidget::keypress(key, where); - } - } - virtual void display() - { - if (!fb || !renderer) return; - - if (viewPort.modified) { - Assert2(camera,"ospray camera is null"); - - ospSetVec3f(camera,"pos",(const osp::vec3f&)viewPort.from); - const vec3f dir = viewPort.at - viewPort.from; - ospSetVec3f(camera,"dir",(const osp::vec3f&)dir); - ospSetVec3f(camera,"up",(const osp::vec3f&)viewPort.up); - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - viewPort.modified = false; - ospFrameBufferClear(fb,OSP_FB_ACCUM); - } - - fps.startRender(); - ospRenderFrame(fb,renderer,OSP_FB_COLOR|OSP_FB_ACCUM - ); - fps.doneRender(); - - ucharFB = (unsigned int *)ospMapFrameBuffer(fb); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - Glut3DWidget::display(); - - ospUnmapFrameBuffer(ucharFB,fb); - - char title[1000]; - - sprintf(title,"OSPRay StreamLines test viewer (%f fps)", - fps.getFPS()); - setTitle(title); - forceRedraw(); - } - - OSPFrameBuffer fb; - OSPRenderer renderer; - OSPCamera camera; - int resampleSize; - ospray::glut3D::FPSCounter fps; - StreamLines *sl; - Triangles *tris; - }; - - void ospDVRMain(int &ac, const char **&av) - { - StreamLines *streamLines = new StreamLines; - Triangles *triangles = new Triangles; - StockleyWhealCannon *swc = new StockleyWhealCannon; - for (int i=1;iparsePNT(fn); - else if (fn.ext() == "swc") - swc->parse(fn); -// streamLines->parseSWC(fn); - else if (fn.ext() == "pntlist") - streamLines->parsePNTlist(fn); - else if (fn.ext() == "slraw") - streamLines->parseSLRAW(fn); - else if (fn.ext() == "sv") - triangles->parseSV(fn); - else - throw std::runtime_error("unknown file format "+fn.str()); - } else if (arg == "--module") { - ospLoadModule(av[++i]); - } else if (arg == "--renderer") { - rendererType = av[++i]; - } else if (arg == "--radius") { - streamLines->radius = atof(av[++i]); - } else if (arg == "--export") { - exportOSX(av[++i],streamLines,triangles); - } else - throw std::runtime_error("unknown parameter "+arg); - } - // ------------------------------------------------------- - // create viewer window - // ------------------------------------------------------- - StreamLineViewer window(streamLines,triangles,swc); - window.create("ospDVR: OSPRay miniature stream line viewer"); - printf("Viewer created. Press 'Q' to quit.\n"); - box3f bounds = streamLines->getBounds(); - bounds.extend(triangles->getBounds()); - bounds.extend(swc->getBounds()); - window.setWorldBounds(bounds); - ospray::glut3D::runGLUT(); - } - -} // ::ospray - -int main(int ac, const char **av) -{ - ospInit(&ac,av); - ospray::glut3D::initGLUT(&ac,av); - ospray::ospDVRMain(ac,av); -} diff --git a/apps/volumeViewer/CMakeLists.txt b/apps/volumeViewer/CMakeLists.txt index 1c329fe7b0..f4d7dcd169 100644 --- a/apps/volumeViewer/CMakeLists.txt +++ b/apps/volumeViewer/CMakeLists.txt @@ -16,15 +16,16 @@ IF (NOT OSPRAY_MODULE_OPENGL_UTIL) message(FATAL_ERROR "The 'OpenGL util' module must be enabled to build the 'ospVolumeViewer' application.") -ENDIF (NOT OSPRAY_MODULE_OPENGL_UTIL) +ENDIF() INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) # setup dependencies SET(LIBS ${LIBS} ospray) SET(LIBS ${LIBS} ospray_common) -SET(LIBS ${LIBS} ospray_module_loaders) +SET(LIBS ${LIBS} ospray_importer) SET(LIBS ${LIBS} ospray_module_opengl_util) +SET(LIBS ${LIBS} ospray_tfn) # link optional loaders-based modules if built IF (OSPRAY_MODULE_SEISMIC) @@ -55,7 +56,7 @@ SET(SRCS ${SRCS} SliceWidget.cpp TransferFunctionEditor.cpp VolumeViewer.cpp - ) +) SET(MOC_HEADERS IsosurfaceEditor.h @@ -70,14 +71,13 @@ SET(MOC_HEADERS SliceWidget.h TransferFunctionEditor.h VolumeViewer.h - ) +) QT4_WRAP_CPP(MOC_OUTFILES ${MOC_HEADERS}) -ADD_SUBDIRECTORY(loaders) -ADD_EXECUTABLE(ospVolumeViewer ${SRCS} ${MOC_OUTFILES}) -TARGET_LINK_LIBRARIES(ospVolumeViewer ${LIBS}) -INSTALL(TARGETS ospVolumeViewer - DESTINATION ${CMAKE_INSTALL_BINDIR} - COMPONENT apps +OSPRAY_CREATE_APPLICATION(VolumeViewer + ${SRCS} + ${MOC_OUTFILES} +LINK + ${LIBS} ) diff --git a/apps/volumeViewer/ColorMap.cpp b/apps/volumeViewer/ColorMap.cpp index 90d67b439d..95ef0182e8 100644 --- a/apps/volumeViewer/ColorMap.cpp +++ b/apps/volumeViewer/ColorMap.cpp @@ -22,12 +22,12 @@ ColorMap::ColorMap(std::string name, std::vector colors) this->colors = colors; } -std::string ColorMap::getName() +std::string ColorMap::getName() const { return name; } -std::vector ColorMap::getColors() +std::vector ColorMap::getColors() const { return colors; } diff --git a/apps/volumeViewer/ColorMap.h b/apps/volumeViewer/ColorMap.h index e0d63b3d93..04d3e629f0 100644 --- a/apps/volumeViewer/ColorMap.h +++ b/apps/volumeViewer/ColorMap.h @@ -19,7 +19,7 @@ // ospray public #include // ospcommon -#include "common/vec.h" +#include "ospcommon/vec.h" // std #include #include @@ -31,8 +31,8 @@ class ColorMap ColorMap(std::string name, std::vector colors); - std::string getName(); - std::vector getColors(); + std::string getName() const; + std::vector getColors() const; QImage getImage(); diff --git a/apps/volumeViewer/IsosurfaceEditor.h b/apps/volumeViewer/IsosurfaceEditor.h index a044a4708e..50609f6eed 100644 --- a/apps/volumeViewer/IsosurfaceEditor.h +++ b/apps/volumeViewer/IsosurfaceEditor.h @@ -17,7 +17,7 @@ #pragma once #include -#include "common/vec.h" +#include "ospcommon/vec.h" #include #include diff --git a/apps/volumeViewer/IsovalueWidget.h b/apps/volumeViewer/IsovalueWidget.h index 96a8c8b8ee..673777315f 100644 --- a/apps/volumeViewer/IsovalueWidget.h +++ b/apps/volumeViewer/IsovalueWidget.h @@ -17,7 +17,7 @@ #pragma once #include -#include "common/vec.h" +#include "ospcommon/vec.h" #include class IsosurfaceEditor; diff --git a/apps/volumeViewer/PreferencesDialog.h b/apps/volumeViewer/PreferencesDialog.h index 3e8611174e..e38861d851 100644 --- a/apps/volumeViewer/PreferencesDialog.h +++ b/apps/volumeViewer/PreferencesDialog.h @@ -17,7 +17,7 @@ #pragma once #include -#include "common/box.h" +#include "ospcommon/box.h" #include #include diff --git a/apps/volumeViewer/ProbeWidget.h b/apps/volumeViewer/ProbeWidget.h index f5e550abb3..261b457a24 100644 --- a/apps/volumeViewer/ProbeWidget.h +++ b/apps/volumeViewer/ProbeWidget.h @@ -17,7 +17,7 @@ #pragma once #include -#include "common/box.h" +#include "ospcommon/box.h" #include #include diff --git a/apps/volumeViewer/QOSPRayWindow.cpp b/apps/volumeViewer/QOSPRayWindow.cpp index 3416ee7275..09ff0a2005 100644 --- a/apps/volumeViewer/QOSPRayWindow.cpp +++ b/apps/volumeViewer/QOSPRayWindow.cpp @@ -14,6 +14,8 @@ // limitations under the License. // // ======================================================================== // +#include + #include "QOSPRayWindow.h" #include "modules/opengl/util.h" @@ -25,9 +27,9 @@ std::ostream &operator<<(std::ostream &o, const Viewport &viewport) { - o << "--vp " << viewport.from.x << " " << viewport.from.y << " " << viewport.from.z - << " --vi " << viewport.at.x << " " << viewport.at.y << " " << viewport.at.z - << " --vu " << viewport.up.x << " " << viewport.up.y << " " << viewport.up.z + o << "-vp " << viewport.from.x << " " << viewport.from.y << " " << viewport.from.z + << " -vi " << viewport.at.x << " " << viewport.at.y << " " << viewport.at.z + << " -vu " << viewport.up.x << " " << viewport.up.y << " " << viewport.up.z << std::endl; return o; @@ -99,10 +101,12 @@ void QOSPRayWindow::setRotationRate(float rotationRate) this->rotationRate = rotationRate; } -void QOSPRayWindow::setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames) +void QOSPRayWindow::setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames, + const std::string &benchmarkFilename) { this->benchmarkWarmUpFrames = benchmarkWarmUpFrames; this->benchmarkFrames = benchmarkFrames; + this->benchmarkFilename = benchmarkFilename; } void QOSPRayWindow::setWorldBounds(const ospcommon::box3f &worldBounds) @@ -190,7 +194,10 @@ void QOSPRayWindow::paintGL() uint32_t *mappedFrameBuffer = (unsigned int *) ospMapFrameBuffer(frameBuffer); glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_UNSIGNED_BYTE, mappedFrameBuffer); - if (writeFramesFilename.length()) writeFrameBufferToFile(mappedFrameBuffer); + if (writeFramesFilename.length()) { + std::string filename = writeFramesFilename + std::to_string(static_cast(frameCount)); + writeFrameBufferToFile(filename, mappedFrameBuffer); + } ospUnmapFrameBuffer(mappedFrameBuffer, frameBuffer); @@ -208,7 +215,12 @@ void QOSPRayWindow::paintGL() float elapsedSeconds = float(benchmarkTimer.elapsed()) / 1000.f; - std::cout << "benchmark: " << elapsedSeconds << " elapsed seconds ==> " << float(benchmarkFrames) / elapsedSeconds << " fps" << std::endl; + std::cout << "benchmark: " << elapsedSeconds << " elapsed seconds ==> " + << float(benchmarkFrames) / elapsedSeconds << " fps" << std::endl; + + uint32_t *mappedFrameBuffer = (unsigned int *) ospMapFrameBuffer(frameBuffer); + + writeFrameBufferToFile(benchmarkFilename, mappedFrameBuffer); QCoreApplication::quit(); } @@ -362,15 +374,16 @@ void QOSPRayWindow::renderGL() emit(renderGLComponents()); } -void QOSPRayWindow::writeFrameBufferToFile(const uint32_t *pixelData) +void QOSPRayWindow::writeFrameBufferToFile(std::string filename, const uint32_t *pixelData) { - static uint32_t frameNumber = 0; - char filename[1024]; - sprintf(filename, "%s_%05u.ppm", writeFramesFilename.c_str(), frameNumber++); - FILE *file = fopen(filename, "wb"); if (!file) { std::cerr << "unable to write to file '" << filename << "'" << std::endl; return; } - + filename += ".ppm"; + FILE *file = fopen(filename.c_str(), "wb"); + if (!file) { + std::cerr << "unable to write to file '" << filename << "'" << std::endl; + return; + } fprintf(file, "P6\n%i %i\n255\n", windowSize.x, windowSize.y); - unsigned char out[3 * windowSize.x]; + unsigned char *out = (unsigned char *)alloca(3 * windowSize.x); for (int y=0 ; y < windowSize.y ; y++) { const unsigned char *in = (const unsigned char *) &pixelData[(windowSize.y - 1 - y) * windowSize.x]; for (int x=0 ; x < windowSize.x ; x++) { @@ -378,7 +391,7 @@ void QOSPRayWindow::writeFrameBufferToFile(const uint32_t *pixelData) out[3 * x + 1] = in[4 * x + 1]; out[3 * x + 2] = in[4 * x + 2]; } - fwrite(&out, 3 * windowSize.x, sizeof(char), file); + fwrite(out, 3 * windowSize.x, sizeof(char), file); } fprintf(file, "\n"); fclose(file); diff --git a/apps/volumeViewer/QOSPRayWindow.h b/apps/volumeViewer/QOSPRayWindow.h index 5ebe855d51..7ffe405d61 100644 --- a/apps/volumeViewer/QOSPRayWindow.h +++ b/apps/volumeViewer/QOSPRayWindow.h @@ -19,7 +19,7 @@ // ospray public #include // ospcommon -#include "common/AffineSpace.h" +#include "ospcommon/AffineSpace.h" // qt #include #include @@ -89,7 +89,7 @@ Q_OBJECT void setRenderingEnabled(bool renderingEnabled); void setRotationRate(float rotationRate); - void setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames); + void setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames, const std::string &benchmarkFilename); virtual void setWorldBounds(const ospcommon::box3f &worldBounds); Viewport * getViewport() { return &viewport; } @@ -147,6 +147,9 @@ Q_OBJECT /*! benchmarking: number of frames over which to measure frame rate */ int benchmarkFrames; + /*! benchmarking: file name to save the final image to */ + std::string benchmarkFilename; + /*! benchmarking: timer to measure elapsed time over benchmark frames */ QTime benchmarkTimer; @@ -164,6 +167,6 @@ Q_OBJECT OSPTexture2D maxDepthTexture; std::string writeFramesFilename; - void writeFrameBufferToFile(const uint32_t *pixelData); + void writeFrameBufferToFile(std::string filename, const uint32_t *pixelData); }; diff --git a/apps/volumeViewer/SliceWidget.h b/apps/volumeViewer/SliceWidget.h index 1551a1c05e..3a8adb29a0 100644 --- a/apps/volumeViewer/SliceWidget.h +++ b/apps/volumeViewer/SliceWidget.h @@ -17,7 +17,7 @@ #pragma once #include -#include "common/box.h" +#include "ospcommon/box.h" #include struct SliceParameters diff --git a/apps/volumeViewer/TransferFunctionEditor.cpp b/apps/volumeViewer/TransferFunctionEditor.cpp index bffa02c2ad..db7a77dddb 100644 --- a/apps/volumeViewer/TransferFunctionEditor.cpp +++ b/apps/volumeViewer/TransferFunctionEditor.cpp @@ -15,6 +15,8 @@ // ======================================================================== // #include "TransferFunctionEditor.h" +#include +#include #include TransferFunctionEditor::TransferFunctionEditor(OSPTransferFunction transferFunction) @@ -118,40 +120,86 @@ TransferFunctionEditor::TransferFunctionEditor(OSPTransferFunction transferFunct void TransferFunctionEditor::load(std::string filename) { // Get filename if not specified. - if(filename.empty()) - filename = QFileDialog::getOpenFileName(this, tr("Load transfer function"), ".", "Transfer function files (*.tfn)").toStdString(); + if(filename.empty()) { + filename = QFileDialog::getOpenFileName(this, tr("Load transfer function"), + ".", "Transfer function files (*.tfn)").toStdString(); + } if(filename.empty()) return; // Get serialized transfer function state from file. - QFile file(filename.c_str()); - bool success = file.open(QIODevice::ReadOnly); - - if(!success) { - std::cerr << "unable to open " << filename << std::endl; - return; + bool invalidIdentificationHeader = false; + try { + tfn::TransferFunction loadedTfn{ospcommon::FileName(filename)}; + + // Update transfer function state. Update values of the UI elements directly to signal appropriate slots. + std::vector::iterator fnd = std::find_if(colorMaps.begin(), colorMaps.end(), + [&](const ColorMap &m) { + return m.getName() == loadedTfn.name; + }); + int index = 0; + if (fnd != colorMaps.end()) { + *fnd = ColorMap(loadedTfn.name, loadedTfn.rgbValues); + index = std::distance(colorMaps.begin(), fnd); + } else { + colorMaps.push_back(ColorMap(loadedTfn.name, loadedTfn.rgbValues)); + colorMapComboBox.addItem(loadedTfn.name.c_str()); + index = colorMaps.size() - 1; + } + + // Commit and emit signal. + setDataValueRange(ospcommon::vec2f(loadedTfn.dataValueMin, loadedTfn.dataValueMax), true); + // Convert over to QPointF to pass to the widget + QVector points; + for (const auto &x : loadedTfn.opacityValues) { + points.push_back(QPointF(x.x, x.y)); + } + opacityValuesWidget.setPoints(points); + opacityScalingSlider.setValue(loadedTfn.opacityScaling + * (opacityScalingSlider.maximum() - opacityScalingSlider.minimum())); + updateOpacityValues(); + + colorMapComboBox.setCurrentIndex(index); + } catch (const std::runtime_error &e) { + const std::string errMsg = e.what(); + std::cout << "#ospVolumeViewer error loading transferfunction: " << errMsg << "\n"; + // If it's an invalid identification header error we can try loading an old style + // transfer function, otherwise something else we can't handle is wrong. + if (errMsg.find("Read invalid identification header") != std::string::npos) { + // Get serialized transfer function state from file. + QFile file(filename.c_str()); + bool success = file.open(QIODevice::ReadOnly); + + if (!success) { + std::cerr << "unable to open " << filename << std::endl; + return; + } + + QDataStream in(&file); + int colorMapIndex; + in >> colorMapIndex; + double dataValueMin, dataValueMax; + in >> dataValueMin >> dataValueMax; + QVector points; + in >> points; + int opacityScalingIndex; + in >> opacityScalingIndex; + + // Update transfer function state. Update values of the UI elements directly to signal appropriate slots. + colorMapComboBox.setCurrentIndex(colorMapIndex); + setDataValueRange(ospcommon::vec2f(dataValueMin, dataValueMax), true); + opacityValuesWidget.setPoints(points); + opacityScalingSlider.setValue(opacityScalingIndex); + + std::cout << "#ospVolumeViewer WARNING: using old-style transfer function, save the loaded function " + << "out to switch to the new format\n"; + } else { + throw e; + } } - - QDataStream in(&file); - - int colorMapIndex; - in >> colorMapIndex; - - double dataValueMin, dataValueMax; - in >> dataValueMin >> dataValueMax; - - QVector points; - in >> points; - - int opacityScalingIndex; - in >> opacityScalingIndex; - - // Update transfer function state. Update values of the UI elements directly to signal appropriate slots. - colorMapComboBox.setCurrentIndex(colorMapIndex); - setDataValueRange(ospcommon::vec2f(dataValueMin, dataValueMax), true); - opacityValuesWidget.setPoints(points); - opacityScalingSlider.setValue(opacityScalingIndex); + ospCommit(transferFunction); + emit committed(); } void TransferFunctionEditor::setDataValueRange(ospcommon::vec2f dataValueRange, bool force) @@ -181,7 +229,8 @@ void TransferFunctionEditor::updateOpacityValues() std::vector opacityValues = opacityValuesWidget.getInterpolatedValuesOverInterval(256); // Opacity scaling factor (normalized in [0, 1]). - const float opacityScalingNormalized = float(opacityScalingSlider.value() - opacityScalingSlider.minimum()) / float(opacityScalingSlider.maximum() - opacityScalingSlider.minimum()); + const float opacityScalingNormalized = float(opacityScalingSlider.value() - opacityScalingSlider.minimum()) + / float(opacityScalingSlider.maximum() - opacityScalingSlider.minimum()); // Scale opacity values. for (unsigned int i=0; i < opacityValues.size(); i++) @@ -208,25 +257,20 @@ void TransferFunctionEditor::save() if(filename.endsWith(".tfn") != true) filename += ".tfn"; - // Serialize transfer function state to file. - QFile file(filename); - bool success = file.open(QIODevice::WriteOnly); - - if(!success) { - std::cerr << "unable to open " << filename.toStdString() << std::endl; - return; + const std::vector colors = colorMaps[colorMapComboBox.currentIndex()].getColors(); + const QVector opacityPts = opacityValuesWidget.getPoints(); + std::vector opacityValues; + for (const auto &x : opacityPts) { + opacityValues.push_back(ospcommon::vec2f(x.x(), x.y())); } - - // Data value scale. - float dataValueScale = powf(10.f, float(dataValueScaleSpinBox.value())); - - QDataStream out(&file); - - out << colorMapComboBox.currentIndex(); - out << dataValueScale * dataValueMinSpinBox.value(); - out << dataValueScale * dataValueMaxSpinBox.value(); - out << opacityValuesWidget.getPoints(); - out << opacityScalingSlider.value(); + const float dataValueScale = powf(10.f, float(dataValueScaleSpinBox.value())); + const float valMin = dataValueScale * dataValueMinSpinBox.value(); + const float valMax = dataValueScale * dataValueMaxSpinBox.value(); + const float opacityScaling = opacityScalingSlider.value() + / float(opacityScalingSlider.maximum() - opacityScalingSlider.minimum()); + ospcommon::FileName ospFname{filename.toStdString()}; + tfn::TransferFunction saveTfn(ospFname.name(), colors, opacityValues, valMin, valMax, opacityScaling); + saveTfn.save(ospFname); } void TransferFunctionEditor::setColorMapIndex(int index) @@ -251,7 +295,8 @@ void TransferFunctionEditor::updateDataValueRange() float dataValueScale = powf(10.f, float(dataValueScaleSpinBox.value())); // Set the minimum and maximum values in the domain for both color and opacity components of the transfer function. - ospSet2f(transferFunction, "valueRange", dataValueScale * float(dataValueMinSpinBox.value()), dataValueScale * float(dataValueMaxSpinBox.value())); + ospcommon::vec2f range(dataValueScale * float(dataValueMinSpinBox.value()), dataValueScale * float(dataValueMaxSpinBox.value())); + ospSet2f(transferFunction, "valueRange", range.x, range.y); // Commit and emit signal. ospCommit(transferFunction); diff --git a/apps/volumeViewer/VolumeViewer.cpp b/apps/volumeViewer/VolumeViewer.cpp index 9027712f86..8e0e658906 100644 --- a/apps/volumeViewer/VolumeViewer.cpp +++ b/apps/volumeViewer/VolumeViewer.cpp @@ -17,8 +17,8 @@ #include // own #include "VolumeViewer.h" -#include "loaders/ObjectFile.h" -#include "loaders/TriangleMeshFile.h" +// #include "loaders/ObjectFile.h" +// #include "loaders/TriangleMeshFile.h" #include "TransferFunctionEditor.h" #include "IsosurfaceEditor.h" #include "LightEditor.h" @@ -27,9 +27,11 @@ #include "ProbeWidget.h" #include "OpenGLAnnotationRenderer.h" -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP -#include "../../modules/loaders/VolumeFile.h" -#endif +// #ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP +// #include "../../modules/loaders/VolumeFile.h" +// #endif + +#include "importer/Importer.h" VolumeViewer::VolumeViewer(const std::vector &objectFileFilenames, std::string renderer_type, @@ -101,29 +103,31 @@ void VolumeViewer::setModel(size_t index) ospCommit(renderer); rendererInitialized = true; - // Update transfer function and isosurface editor data value range with the voxel range of the current model's first volume. - ospcommon::vec2f voxelRange(0.f); - OSPVolume volume = modelStates[index].volumes[0]; -#ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP - voxelRange = VolumeFile::voxelRangeOf[volume]; -#else - ospGetVec2f(modelStates[index].volumes[0], "voxelRange", (osp::vec2f*)&voxelRange); -#endif - - if(voxelRange != ospcommon::vec2f(0.f)) { - transferFunctionEditor->setDataValueRange(voxelRange); - isosurfaceEditor->setDataValueRange(voxelRange); + PRINT(modelStates[index].volumes.size()); + if (!modelStates[index].volumes.empty()) { + // Update transfer function and isosurface editor data value range with the voxel range of the current model's first volume. + OSPVolume volume = modelStates[index].volumes[0]->handle; + ospcommon::vec2f voxelRange = modelStates[index].volumes[0]->voxelRange; + // #ifdef OSPRAY_VOLUME_VOXELRANGE_IN_APP + // voxelRange = VolumeFile::voxelRangeOf[volume]; + // #else + // ospGetVec2f(modelStates[index].volumes[0], "voxelRange", (osp::vec2f*)&voxelRange); + // #endif + + if(voxelRange != ospcommon::vec2f(0.f)) { + transferFunctionEditor->setDataValueRange(voxelRange); + isosurfaceEditor->setDataValueRange(voxelRange); + } + + // Update active volume on probe widget. + probeWidget->setVolume(modelStates[index].volumes[0]->handle); + + // Update current filename information label. + if (ownModelPerObject) + currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": Data value range: [") + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); + else + currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": ") + QString(objectFileFilenames[index].c_str()).split('/').back() + ". Data value range: [" + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); } - - // Update active volume on probe widget. - probeWidget->setVolume(modelStates[index].volumes[0]); - - // Update current filename information label. - if (ownModelPerObject) - currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": Data value range: [") + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); - else - currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": ") + QString(objectFileFilenames[index].c_str()).split('/').back() + ". Data value range: [" + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); - // Enable rendering on the OSPRay window. osprayWindow->setRenderingEnabled(true); } @@ -186,42 +190,45 @@ void VolumeViewer::addGeometry(std::string filename) return; // Attempt to load the geometry through the TriangleMeshFile loader. - OSPGeometry triangleMesh = ospNewGeometry("trianglemesh"); + // OSPGeometry triangleMesh = ospNewGeometry("trianglemesh"); // If successful, commit the triangle mesh and add it to all models. - if(TriangleMeshFile::importTriangleMesh(filename, triangleMesh) != NULL) { - - // For now: if this is a DDS geometry, assume it is a horizon and its color should be mapped through the first volume's transfer function. - if(QString(filename.c_str()).endsWith(".dds") && modelStates.size() > 0 && modelStates[0].volumes.size() > 0) { - - OSPMaterial material = ospNewMaterial(renderer, "default"); - ospSet3f(material, "Kd", 1,1,1); - ospSetObject(material, "volume", modelStates[0].volumes[0]); - ospCommit(material); - - ospSetMaterial(triangleMesh, material); - } - - ospCommit(triangleMesh); - - // Create an instance of the geometry and add the instance to the main model(s)--this prevents the geometry - // from being rebuilt every time the main model is committed (e.g. when slices / isosurfaces are manipulated) - OSPModel modelInstance = ospNewModel(); - ospAddGeometry(modelInstance, triangleMesh); - ospCommit(modelInstance); - - ospcommon::affine3f xfm = ospcommon::one; - OSPGeometry triangleMeshInstance = ospNewInstance(modelInstance, (osp::affine3f&)xfm); - ospCommit(triangleMeshInstance); - - for(size_t i=0; igeometry.size() != 1) return; + + OSPGeometry triangleMesh = newStuff->geometry[0]->handle; + // For now: if this is a DDS geometry, assume it is a horizon and its color should be mapped through the first volume's transfer function. + if(QString(filename.c_str()).endsWith(".dds") && modelStates.size() > 0 && modelStates[0].volumes.size() > 0) { + + OSPMaterial material = ospNewMaterial(renderer, "default"); + ospSet3f(material, "Kd", 1,1,1); + ospSetObject(material, "volume", modelStates[0].volumes[0]->handle); + ospCommit(material); + + ospSetMaterial(triangleMesh, material); + } + + ospCommit(triangleMesh); + + // Create an instance of the geometry and add the instance to the main model(s)--this prevents the geometry + // from being rebuilt every time the main model is committed (e.g. when slices / isosurfaces are manipulated) + OSPModel modelInstance = ospNewModel(); + ospAddGeometry(modelInstance, triangleMesh); + ospCommit(modelInstance); + + ospcommon::affine3f xfm = ospcommon::one; + OSPGeometry triangleMeshInstance = ospNewInstance(modelInstance, (osp::affine3f&)xfm); + ospCommit(triangleMeshInstance); + + for(size_t i=0; ihandle); } void VolumeViewer::render() @@ -299,8 +306,8 @@ void VolumeViewer::setGradientShadingEnabled(bool value) { for(size_t i=0; ihandle, "gradientShadingEnabled", value); + ospCommit(modelStates[i].volumes[j]->handle); } render(); @@ -310,8 +317,8 @@ void VolumeViewer::setSamplingRate(double value) { for(size_t i=0; ihandle, "samplingRate", value); + ospCommit(modelStates[i].volumes[j]->handle); } render(); @@ -321,9 +328,9 @@ void VolumeViewer::setVolumeClippingBox(ospcommon::box3f value) { for(size_t i=0; ihandle, "volumeClippingBoxLower", &value.lower.x); + ospSet3fv(modelStates[i].volumes[j]->handle, "volumeClippingBoxUpper", &value.upper.x); + ospCommit(modelStates[i].volumes[j]->handle); } render(); @@ -345,7 +352,7 @@ void VolumeViewer::setSlices(std::vector sliceParameters) // Remove existing slice geometries from models. for(size_t i=0; ihandle); modelStates[i].slices.clear(); } @@ -358,12 +365,12 @@ void VolumeViewer::setSlices(std::vector sliceParameters) OSPGeometry slicesGeometry = ospNewGeometry("slices"); ospSetData(slicesGeometry, "planes", planesData); - ospSetObject(slicesGeometry, "volume", modelStates[i].volumes[j]); + ospSetObject(slicesGeometry, "volume", modelStates[i].volumes[j]->handle); ospCommit(slicesGeometry); ospAddGeometry(modelStates[i].model, slicesGeometry); - modelStates[i].slices.push_back(slicesGeometry); + modelStates[i].slices.push_back(new ModelState::Geometry(slicesGeometry)); } } @@ -380,7 +387,7 @@ void VolumeViewer::setIsovalues(std::vector isovalues) // Remove existing isosurface geometries from models. for(size_t i=0; ihandle); modelStates[i].isosurfaces.clear(); } @@ -393,12 +400,12 @@ void VolumeViewer::setIsovalues(std::vector isovalues) OSPGeometry isosurfacesGeometry = ospNewGeometry("isosurfaces"); ospSetData(isosurfacesGeometry, "isovalues", isovaluesData); - ospSetObject(isosurfacesGeometry, "volume", modelStates[i].volumes[j]); + ospSetObject(isosurfacesGeometry, "volume", modelStates[i].volumes[j]->handle); ospCommit(isosurfacesGeometry); ospAddGeometry(modelStates[i].model, isosurfacesGeometry); - modelStates[i].isosurfaces.push_back(isosurfacesGeometry); + modelStates[i].isosurfaces.push_back(new ModelState::Geometry(isosurfacesGeometry)); } } @@ -411,22 +418,67 @@ void VolumeViewer::setIsovalues(std::vector isovalues) void VolumeViewer::importObjectsFromFile(const std::string &filename) { if (!ownModelPerObject) - // Create an OSPRay model and its associated model state. - modelStates.push_back(ModelState(ospNewModel())); - + // Create an OSPRay model and its associated model state. + modelStates.push_back(ModelState(ospNewModel())); + // Load OSPRay objects from a file. - OSPObject *objects = ObjectFile::importObjects(filename.c_str()); + // OSPObject *objects = ObjectFile::importObjects(filename.c_str()); + ospray::importer::Group *imported = ospray::importer::import(filename); + assert(imported); + +#if 1 + PING; + // Iterate over the GEOMETREIS contained in the object list. + PRINT(imported->geometry.size()); + + for (size_t i=0 ; i < imported->geometry.size() ; i++) { + if (ownModelPerObject) + modelStates.push_back(ModelState(ospNewModel())); + + // Commit the geometry. + ospCommit(imported->geometry[i]->handle); + + // Add the loaded geometry to the model. + ospAddGeometry(modelStates.back().model, imported->geometry[i]->handle); + if (ownModelPerObject) + ospCommit(modelStates.back().model); + } // Iterate over the objects contained in the object list. - for (size_t i=0 ; objects[i] ; i++) { + for (size_t i=0 ; i < imported->volume.size() ; i++) { if (ownModelPerObject) modelStates.push_back(ModelState(ospNewModel())); + + ospray::importer::Volume *vol = imported->volume[i]; + assert(vol); + // For now we set the same transfer function on all volumes. + ospSetObject(vol->handle, "transferFunction", transferFunction); + ospCommit(vol->handle); + + // Add the loaded volume(s) to the model. + ospAddVolume(modelStates.back().model, vol->handle); + + assert(!vol->bounds.empty()); + // Add to volumes vector for the current model. + modelStates.back().volumes.push_back(new ModelState::Volume(vol->handle, + vol->bounds, + vol->voxelRange + )); + if (ownModelPerObject) + ospCommit(modelStates.back().model); + } +#else + // Iterate over the objects contained in the object list. + for (size_t i=0 ; objects[i] ; i++) { + if (ownModelPerObject) + modelStates.push_back(ModelState(ospNewModel())); + OSPDataType type; ospGetType(objects[i], NULL, &type); - + if (type == OSP_GEOMETRY) { - + // Commit the geometry. ospCommit(objects[i]); @@ -449,10 +501,11 @@ void VolumeViewer::importObjectsFromFile(const std::string &filename) if (ownModelPerObject) ospCommit(modelStates.back().model); } +#endif if (!ownModelPerObject) - // Commit the model. - ospCommit(modelStates.back().model); + // Commit the model. + ospCommit(modelStates.back().model); } void VolumeViewer::initObjects(const std::string &renderer_type) @@ -493,19 +546,25 @@ void VolumeViewer::initObjects(const std::string &renderer_type) for (size_t i=0 ; i < objectFileFilenames.size() ; i++) importObjectsFromFile(objectFileFilenames[i]); - // Get the bounding box of all volumes of the first model. - if(modelStates.size() > 0 && modelStates[0].volumes.size() > 0) { - ospGetVec3f(modelStates[0].volumes[0], "boundingBoxMin", (osp::vec3f*)&boundingBox.lower); - ospGetVec3f(modelStates[0].volumes[0], "boundingBoxMax", (osp::vec3f*)&boundingBox.upper); - - for (size_t i=1; iboundingBox); } + PRINT(boundingBox); + // // Get the bounding box of all volumes of the first model. + // if(modelStates.size() > 0 && modelStates[0].volumes.size() > 0) { + // ospGetVec3f(modelStates[0].volumes[0], "boundingBoxMin", (osp::vec3f*)&boundingBox.lower); + // ospGetVec3f(modelStates[0].volumes[0], "boundingBoxMax", (osp::vec3f*)&boundingBox.upper); + + // for (size_t i=1; i @@ -31,13 +31,34 @@ class OpenGLAnnotationRenderer; //! OSPRay model and its volumes / geometries struct ModelState { + struct Volume { + Volume(OSPVolume handle, + const ospcommon::box3f &boundingBox, + const ospcommon::vec2f &voxelRange) + : handle(handle), + boundingBox(boundingBox), + voxelRange(voxelRange) + { + assert(!boundingBox.empty()); + } + + OSPVolume handle; + ospcommon::vec2f voxelRange; + ospcommon::box3f boundingBox; + }; + + struct Geometry { + Geometry(OSPGeometry handle=NULL) : handle(handle) {} + OSPGeometry handle; + }; + ModelState(OSPModel model) : model(model) { } OSPModel model; //!< the OSPRay model - std::vector volumes; //!< OSPRay volumes for the model - std::vector slices; //! OSPRay slice geometries for the model - std::vector isosurfaces; //! OSPRay isosurface geometries for the model + std::vector volumes; //!< OSPRay volumes for the model + std::vector slices; //! OSPRay slice geometries for the model + std::vector isosurfaces; //! OSPRay isosurface geometries for the model }; class VolumeViewer : public QMainWindow { diff --git a/apps/volumeViewer/main.cpp b/apps/volumeViewer/main.cpp index 1f5b987399..e7664a7c18 100644 --- a/apps/volumeViewer/main.cpp +++ b/apps/volumeViewer/main.cpp @@ -31,24 +31,24 @@ void printUsage(const char *exeName) std::cerr << "\n USAGE: " << exeName << " [filenames...] [options]\n" << " \n" << " Options:\n" - << " --benchmark run benchmark and report overall frame rate\n" - << " --dt
use ray cast sample step size 'dt'\n" - << " --module load the module 'moduleName'\n" - << " --ply load PLY geometry from 'filename'\n" - << " --rotate automatically rotate view according to 'rate'\n" - << " --fps,--show-framerate show the frame rate in the window title bar\n" - << " --fullscreen enter fullscreen mode\n" - << " --own-model-per-object create a separate model for each object\n" - << " --slice load volume slice from 'filename'\n" - << " --transferfunction load transfer function from 'filename'\n" - << " --viewsize x force OSPRay view size to 'width'x'height'\n" - << " --vu set viewport up vector to ('x', 'y', 'z')\n" - << " --vp set camera position ('x', 'y', 'z')\n" - << " --vi set look at position ('x', 'y', 'z')\n" - << " -r,--renderer set renderer to use\n" - << " --writeframes emit frames to 'filename_xxxxx.ppm'\n" - << " --benchmark perform benchmarking\n" - << " -h,--help print this message\n" + << " --benchmark run benchmark and report overall frame rate\n" + << " saving the final frame to \n" + << " --dt
use ray cast sample step size 'dt'\n" + << " --module load the module 'moduleName'\n" + << " --ply load PLY geometry from 'filename'\n" + << " --rotate automatically rotate view according to 'rate'\n" + << " --fps,--show-framerate show the frame rate in the window title bar\n" + << " --fullscreen enter fullscreen mode\n" + << " --own-model-per-object create a separate model for each object\n" + << " --slice load volume slice from 'filename'\n" + << " --transferfunction load transfer function from 'filename'\n" + << " --viewsize x force OSPRay view size to 'width'x'height'\n" + << " -vu set viewport up vector to ('x', 'y', 'z')\n" + << " -vp set camera position ('x', 'y', 'z')\n" + << " -vi set look at position ('x', 'y', 'z')\n" + << " -r,--renderer set renderer to use\n" + << " --writeframes emit frames to 'filename_xxxxx.ppm'\n" + << " -h,--help print this message\n" << " \n"; } @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) std::string transferFunctionFilename; int benchmarkWarmUpFrames = 0; int benchmarkFrames = 0; + std::string benchmarkFilename; int viewSizeWidth = 0; int viewSizeHeight = 0; ospcommon::vec3f viewUp(0.f); @@ -136,10 +137,13 @@ int main(int argc, char *argv[]) } else if (arg == "--benchmark") { - if (i + 2 >= argc) throw std::runtime_error("missing arguments"); + if (i + 3 >= argc) throw std::runtime_error("missing arguments"); benchmarkWarmUpFrames = atoi(argv[++i]); benchmarkFrames = atoi(argv[++i]); - std::cout << "got benchmarkWarmUpFrames = " << benchmarkWarmUpFrames << ", benchmarkFrames = " << benchmarkFrames << std::endl; + benchmarkFilename = argv[++i]; + std::cout << "got benchmarkWarmUpFrames = " + << benchmarkWarmUpFrames << ", benchmarkFrames = " + << benchmarkFrames << ", benchmarkFilename = " << benchmarkFilename << std::endl; } else if (arg == "-r" || arg == "--renderer") { @@ -160,7 +164,7 @@ int main(int argc, char *argv[]) } else throw std::runtime_error("improperly formatted x argument"); - } else if (arg == "--vu") { + } else if (arg == "-vu") { if (i + 3 >= argc) throw std::runtime_error("missing arguments"); @@ -168,9 +172,9 @@ int main(int argc, char *argv[]) viewUp.y = atof(argv[++i]); viewUp.z = atof(argv[++i]); - std::cout << "got viewup (--vu) = " << viewUp.x << " " << viewUp.y << " " << viewUp.z << std::endl; + std::cout << "got viewup (-vu) = " << viewUp.x << " " << viewUp.y << " " << viewUp.z << std::endl; - } else if (arg == "--vp") { + } else if (arg == "-vp") { if (i + 3 >= argc) throw std::runtime_error("missing arguments"); @@ -178,9 +182,9 @@ int main(int argc, char *argv[]) viewFrom.y = atof(argv[++i]); viewFrom.z = atof(argv[++i]); - std::cout << "got view-from (--vp) = " << viewFrom.x << " " << viewFrom.y << " " << viewFrom.z << std::endl; + std::cout << "got view-from (-vp) = " << viewFrom.x << " " << viewFrom.y << " " << viewFrom.z << std::endl; - } else if (arg == "--vi") { + } else if (arg == "-vi") { if (i + 3 >= argc) throw std::runtime_error("missing arguments"); @@ -188,7 +192,7 @@ int main(int argc, char *argv[]) viewAt.y = atof(argv[++i]); viewAt.z = atof(argv[++i]); - std::cout << "got view-at (--vi) = " << viewAt.x << " " << viewAt.y << " " << viewAt.z << std::endl; + std::cout << "got view-at (-vi) = " << viewAt.x << " " << viewAt.y << " " << viewAt.z << std::endl; } else if (arg == "--writeframes") { @@ -254,7 +258,8 @@ int main(int argc, char *argv[]) volumeViewer->getTransferFunctionEditor()->load(transferFunctionFilename); // Set benchmarking parameters. - volumeViewer->getWindow()->setBenchmarkParameters(benchmarkWarmUpFrames, benchmarkFrames); + volumeViewer->getWindow()->setBenchmarkParameters(benchmarkWarmUpFrames, + benchmarkFrames, benchmarkFilename); if (viewAt != viewFrom) { volumeViewer->getWindow()->getViewport()->at = viewAt; diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake new file mode 100644 index 0000000000..7d053ff670 --- /dev/null +++ b/cmake/FindReadline.cmake @@ -0,0 +1,47 @@ +# - Try to find readline include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(Readline) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Readline_ROOT_DIR Set this variable to the root installation of +# readline if the module has problems finding the +# proper installation path. +# +# Variables defined by this module: +# +# READLINE_FOUND System has readline, include and lib dirs found +# Readline_INCLUDE_DIR The readline include directories. +# Readline_LIBRARY The readline library. + +find_path(Readline_ROOT_DIR + NAMES include/readline/readline.h +) + +find_path(Readline_INCLUDE_DIR + NAMES readline/readline.h + HINTS ${Readline_ROOT_DIR}/include +) + +find_library(Readline_LIBRARY + NAMES readline + HINTS ${Readline_ROOT_DIR}/lib +) + +if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + set(READLINE_FOUND TRUE) +else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + find_library(Readline_LIBRARY NAMES readline) + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY) + mark_as_advanced(Readline_INCLUDE_DIR Readline_LIBRARY) +endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + +mark_as_advanced( + Readline_ROOT_DIR + Readline_INCLUDE_DIR + Readline_LIBRARY +) diff --git a/cmake/icc.cmake b/cmake/icc.cmake index 341724c4bc..a0f80d5e1f 100644 --- a/cmake/icc.cmake +++ b/cmake/icc.cmake @@ -14,11 +14,12 @@ ## limitations under the License. ## ## ======================================================================== ## -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -no-ansi-alias -std=c++11") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -no-ansi-alias -std=c++11 -DNOMINMAX") SET(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG -g") SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3") +# on Windows use "-fp:fast" instead of "-fp-model fast" #SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -no-ansi-alias -restrict -fp-model fast -fimf-precision=low -no-prec-div -no-prec-sqrt -fma -no-inline-max-total-size -inline-factor=200 ") -SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -g -O3 -no-ansi-alias -restrict -fp-model fast -fimf-precision=low -no-prec-div -no-prec-sqrt -fma -no-inline-max-total-size -inline-factor=200") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") IF (APPLE) diff --git a/cmake/ispc.cmake b/cmake/ispc.cmake index 41ce36ec1f..dba28e3cba 100644 --- a/cmake/ispc.cmake +++ b/cmake/ispc.cmake @@ -15,13 +15,9 @@ ## ======================================================================== ## # ISPC versions to look for, in decending order (newest first) -IF(WIN32) - SET(ISPC_VERSION_WORKING "1.9.0" "1.8.2") -ELSE() - SET(ISPC_VERSION_WORKING "1.9.0" "1.8.2" "1.8.1") -ENDIF() +SET(ISPC_VERSION_WORKING "1.9.1" "1.9.0") LIST(GET ISPC_VERSION_WORKING -1 ISPC_VERSION_REQUIRED) -SET(ISPC_VERSION_RECOMMENDED_KNC "1.8.1") +SET(ISPC_VERSION_RECOMMENDED_KNC "1.9.0") IF (NOT ISPC_EXECUTABLE) # try sibling folder as hint for path of ISPC diff --git a/cmake/msvc.cmake b/cmake/msvc.cmake index 95b633b9ed..39732d883e 100644 --- a/cmake/msvc.cmake +++ b/cmake/msvc.cmake @@ -14,7 +14,7 @@ ## limitations under the License. ## ## ======================================================================== ## -SET(COMMON_CXX_FLAGS "/EHsc /MP /GR ") +SET(COMMON_CXX_FLAGS "/EHsc /MP /GR /bigobj /D NOMINMAX ") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COMMON_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COMMON_CXX_FLAGS} /Ox /fp:fast /Oi /Gy ") diff --git a/cmake/ospray.cmake b/cmake/ospray.cmake index 24f7e0007d..124a9a216a 100644 --- a/cmake/ospray.cmake +++ b/cmake/ospray.cmake @@ -21,6 +21,8 @@ SET(OSPRAY_DIR ${PROJECT_SOURCE_DIR}) # arch-specific cmd-line flags for various arch and compiler configs SET(OSPRAY_TILE_SIZE 64 CACHE INT "Tile size") +SET_PROPERTY(CACHE OSPRAY_TILE_SIZE PROPERTY STRINGS 8 16 32 64 128 256 512) + SET(OSPRAY_PIXELS_PER_JOB 64 CACHE INT "Must be multiple of largest vector width *and* <= OSPRAY_TILE_SIZE") @@ -46,12 +48,103 @@ ENDMACRO() ## Macro check for compiler support of ISA ## MACRO(OSPRAY_CHECK_COMPILER_SUPPORT ISA) - IF (OSPRAY_EMBREE_ENABLE_${ISA} AND NOT OSPRAY_COMPILER_SUPPORTS_${ISA}) + IF (${ISA} STREQUAL "AVX512" AND (NOT OSPRAY_COMPILER_ICC OR WIN32 OR APPLE)) + OSPRAY_WARN_ONCE(MISSING_AVX512 "OSPRay Currently requires ICC on Linux for KNL support. Disabling KNL ISA target.") + SET(OSPRAY_EMBREE_ENABLE_${ISA} false) + ELSEIF (OSPRAY_EMBREE_ENABLE_${ISA} AND NOT OSPRAY_COMPILER_SUPPORTS_${ISA}) OSPRAY_WARN_ONCE(MISSING_${ISA} "Need at least version ${GCC_VERSION_REQUIRED_${ISA}} of gcc for ${ISA}. Disabling ${ISA}.\nTo compile for ${ISA}, please switch to either 'ICC'-compiler, or upgrade your gcc version.") SET(OSPRAY_EMBREE_ENABLE_${ISA} false) ENDIF() ENDMACRO() +## Macro configure ISA targets for ispc ## +MACRO(OSPRAY_CONFIGURE_ISPC_ISA) + + OSPRAY_CONFIGURE_COMPILER() + + SET(OSPRAY_EMBREE_ENABLE_SSE true) + SET(OSPRAY_EMBREE_ENABLE_AVX true) + SET(OSPRAY_EMBREE_ENABLE_AVX2 true) + SET(OSPRAY_EMBREE_ENABLE_AVX512 true) + + OSPRAY_CHECK_COMPILER_SUPPORT(AVX) + OSPRAY_CHECK_COMPILER_SUPPORT(AVX2) + OSPRAY_CHECK_COMPILER_SUPPORT(AVX512) + + # the arch we're targeting for the non-MIC/non-xeon phi part of ospray + SET(OSPRAY_BUILD_ISA "ALL" CACHE STRING + "Target ISA (SSE, AVX, AVX2, AVX512, or ALL)") + STRING(TOUPPER ${OSPRAY_BUILD_ISA} OSPRAY_BUILD_ISA) + + UNSET(OSPRAY_SUPPORTED_ISAS) + + IF(OSPRAY_EMBREE_ENABLE_SSE) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} SSE) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX2) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX2) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX512) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX512) + ENDIF() + + SET_PROPERTY(CACHE OSPRAY_BUILD_ISA PROPERTY STRINGS + ALL ${OSPRAY_SUPPORTED_ISAS}) + + UNSET(OSPRAY_ISPC_TARGET_LIST) + + IF (OSPRAY_BUILD_ISA STREQUAL "ALL") + IF(OSPRAY_EMBREE_ENABLE_SSE) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} sse4) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX2) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx2) + ENDIF() + IF(OSPRAY_EMBREE_ENABLE_AVX512) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx512knl-i32x16) + ENDIF() + + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX512") + + IF(NOT OSPRAY_EMBREE_ENABLE_AVX512) + MESSAGE(FATAL_ERROR "Compiler does not support AVX512!") + ENDIF() + SET(OSPRAY_ISPC_TARGET_LIST avx512knl-i32x16) + + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX2") + + IF(NOT OSPRAY_EMBREE_ENABLE_AVX2) + MESSAGE(FATAL_ERROR "Compiler does not support AVX2!") + ENDIF() + + SET(OSPRAY_ISPC_TARGET_LIST avx2) + SET(OSPRAY_EMBREE_ENABLE_AVX512 false) + + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX") + + IF(NOT OSPRAY_EMBREE_ENABLE_AVX) + MESSAGE(FATAL_ERROR "Compiler does not support AVX!") + ENDIF() + + SET(OSPRAY_ISPC_TARGET_LIST avx) + SET(OSPRAY_EMBREE_ENABLE_AVX512 false) + SET(OSPRAY_EMBREE_ENABLE_AVX2 false) + + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "SSE") + SET(OSPRAY_ISPC_TARGET_LIST sse4) + SET(OSPRAY_EMBREE_ENABLE_AVX512 false) + SET(OSPRAY_EMBREE_ENABLE_AVX2 false) + SET(OSPRAY_EMBREE_ENABLE_AVX false) + ELSE () + MESSAGE(ERROR "Invalid OSPRAY_BUILD_ISA value. Please select one of SSE, AVX, AVX2, AVX512, or ALL.") + ENDIF() +ENDMACRO() # Configure the output directories. To allow IMPI to do its magic we # will put *executables* into the (same) build directory, but tag @@ -59,6 +152,7 @@ ENDMACRO() # ".mic"-suffix trick, so we'll put libraries into separate # directories (names 'intel64' and 'mic', respectively) MACRO(CONFIGURE_OSPRAY) + OSPRAY_CONFIGURE_ISPC_ISA() OSPRAY_CONFIGURE_TASKING_SYSTEM() IF("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") @@ -75,6 +169,11 @@ MACRO(CONFIGURE_OSPRAY) SET(OSPRAY_RELEASE_BUILD ON ) ENDIF() + IF (WIN32) + # avoid problematic min/max defines of windows.h + ADD_DEFINITIONS(-DNOMINMAX) + ENDIF() + IF (OSPRAY_TARGET STREQUAL "mic") SET(OSPRAY_EXE_SUFFIX ".mic") SET(OSPRAY_LIB_SUFFIX "_mic") @@ -83,7 +182,6 @@ MACRO(CONFIGURE_OSPRAY) SET(THIS_IS_MIC ON) SET(__XEON__ OFF) INCLUDE(${PROJECT_SOURCE_DIR}/cmake/icc_xeonphi.cmake) - SET(OSPRAY_TARGET_MIC ON PARENT_SCOPE) ELSE() SET(OSPRAY_EXE_SUFFIX "") @@ -91,106 +189,14 @@ MACRO(CONFIGURE_OSPRAY) SET(OSPRAY_ISPC_SUFFIX ".o") SET(THIS_IS_MIC OFF) SET(__XEON__ ON) - IF (OSPRAY_COMPILER_ICC) - INCLUDE(${PROJECT_SOURCE_DIR}/cmake/icc.cmake) - ELSEIF (OSPRAY_COMPILER_GCC) - INCLUDE(${PROJECT_SOURCE_DIR}/cmake/gcc.cmake) - ELSEIF (OSPRAY_COMPILER_CLANG) - INCLUDE(${PROJECT_SOURCE_DIR}/cmake/clang.cmake) - ELSEIF (OSPRAY_COMPILER_MSVC) - INCLUDE(${PROJECT_SOURCE_DIR}/cmake/msvc.cmake) - ENDIF() - - SET(OSPRAY_EMBREE_ENABLE_AVX512 false) - IF (OSPRAY_BUILD_ISA STREQUAL "ALL") - # ------------------------------------------------------------------ - # in 'all' mode, we have a list of multiple targets for ispc, - # and enable all targets for embree (we may still disable some - # below if the compiler doesn't support them - # ------------------------------------------------------------------ - SET(OSPRAY_ISPC_TARGET_LIST sse4 avx avx2) - SET(OSPRAY_EMBREE_ENABLE_SSE true) - SET(OSPRAY_EMBREE_ENABLE_AVX true) - SET(OSPRAY_EMBREE_ENABLE_AVX2 true) - IF (OSPRAY_BUILD_ENABLE_KNL) - SET(OSPRAY_EMBREE_ENABLE_AVX512 true) - SET(OSPRAY_ISPC_TARGET_LIST sse4 avx avx2 avx512knl-i32x16) - ENDIF() - - ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX512") - # ------------------------------------------------------------------ - # in 'avx512' mode, we currently build only avx512, in generic - # mode, but enable all embree targets to fall back to (currently - # does not work since embree would require a 16-wide trace - # function which it has in neither of the three targets) - # ------------------------------------------------------------------ - SET(OSPRAY_ISPC_TARGET_LIST avx512knl-i32x16) - SET(OSPRAY_EMBREE_ENABLE_SSE true) - SET(OSPRAY_EMBREE_ENABLE_AVX true) - SET(OSPRAY_EMBREE_ENABLE_AVX2 true) - SET(OSPRAY_EMBREE_ENABLE_AVX512 true) - - ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX2") - # ------------------------------------------------------------------ - # in 'avx2' mode, we enable ONLY avx2 for ispc, and all targets - # up to avx2 for embree. note that if the compiler doesn't - # support AVX we will have a combination where embree uses AVX - # (the most the compiler can do), while ispc still uses - # avx. this works because both targets are 8 wide. it does - # however require the compiler to understand AT LEAST AVX1. - # ------------------------------------------------------------------ - SET(OSPRAY_ISPC_TARGET_LIST avx2) - SET(OSPRAY_EMBREE_ENABLE_SSE true) - SET(OSPRAY_EMBREE_ENABLE_AVX true) - SET(OSPRAY_EMBREE_ENABLE_AVX2 true) - - ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX") - # ------------------------------------------------------------------ - # in 'avx' mode, we enable ONLY avx for ispc, and both sse and - # avx for embree. note that this works ONLY works if the - # compiler knows at least AVX - # ------------------------------------------------------------------ - SET(OSPRAY_ISPC_TARGET_LIST avx) - SET(OSPRAY_EMBREE_ENABLE_SSE true) - SET(OSPRAY_EMBREE_ENABLE_AVX true) - SET(OSPRAY_EMBREE_ENABLE_AVX2 false) - - ELSEIF (OSPRAY_BUILD_ISA STREQUAL "SSE") - # ------------------------------------------------------------------ - # in 'sse' mode, we enable ONLY sse4 for ispc, and only sse for - # embree - # ------------------------------------------------------------------ - SET(OSPRAY_ISPC_TARGET_LIST sse4) - SET(OSPRAY_EMBREE_ENABLE_SSE true) - SET(OSPRAY_EMBREE_ENABLE_AVX false) - SET(OSPRAY_EMBREE_ENABLE_AVX2 false) - ELSE () - MESSAGE(ERROR "Invalid OSPRAY_BUILD_ISA value. Please select one of SSE, AVX, AVX2, or ALL.") - ENDIF() - ENDIF() - OSPRAY_CHECK_COMPILER_SUPPORT(AVX) - OSPRAY_CHECK_COMPILER_SUPPORT(AVX2) - OSPRAY_CHECK_COMPILER_SUPPORT(AVX512) - IF (THIS_IS_MIC) OPTION(OSPRAY_BUILD_COI_DEVICE "Build COI Device for OSPRay's MIC support?" ON) ENDIF() INCLUDE(${PROJECT_SOURCE_DIR}/cmake/ispc.cmake) - - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}) - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - - INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}) - INCLUDE_DIRECTORIES_ISPC(${PROJECT_SOURCE_DIR}/ospray/include) - - # for auto-generated cmakeconfig etc - INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}) - INCLUDE_DIRECTORIES_ISPC(${PROJECT_BINARY_DIR}) - ENDMACRO() ## Target creation macros ## @@ -269,6 +275,8 @@ MACRO(OSPRAY_INSTALL_LIBRARY name) ) ENDMACRO() +# only use for executables required for OSPRay functionality, e.g. the MPI +# worker, because these go into install component 'lib' MACRO(OSPRAY_INSTALL_EXE _name) SET(name ${_name}${OSPRAY_EXE_SUFFIX}) # use OSPRAY_LIB_SUFFIX for COMPONENT to get lib_mic and not lib.mic @@ -286,10 +294,100 @@ MACRO(OSPRAY_SET_LIBRARY_VERSION _name) PROPERTIES VERSION ${OSPRAY_VERSION} SOVERSION ${OSPRAY_SOVERSION}) ENDMACRO() +## Conveniance macro for creating OSPRay libraries ## +# Usage +# +# OSPRAY_CREATE_LIBRARY( source1 [source2 ...] +# [LINK lib1 [lib2 ...]]) +# +# will create and install shared library 'ospray_' from 'sources' with +# version OSPRAY_[SO]VERSION and optionally link against 'libs' + +MACRO(OSPRAY_CREATE_LIBRARY name) + SET(LIBRARY_NAME ospray_${name}) + SET(LIBRARY_SOURCES "") + SET(LINK_LIBS "") + + SET(CURRENT_LIST LIBRARY_SOURCES) + FOREACH(arg ${ARGN}) + IF (${arg} STREQUAL "LINK") + SET(CURRENT_LIST LINK_LIBS) + ELSE() + LIST(APPEND ${CURRENT_LIST} ${arg}) + ENDIF () + ENDFOREACH() + + OSPRAY_ADD_LIBRARY(${LIBRARY_NAME} SHARED ${LIBRARY_SOURCES}) + OSPRAY_LIBRARY_LINK_LIBRARIES(${LIBRARY_NAME} ${LINK_LIBS}) + OSPRAY_SET_LIBRARY_VERSION(${LIBRARY_NAME}) + OSPRAY_INSTALL_LIBRARY(${LIBRARY_NAME}) +ENDMACRO() + +## Conveniance macro for creating OSPRay applications ## +# Usage +# +# OSPRAY_CREATE_APPLICATION( source1 [source2 ...] +# [LINK lib1 [lib2 ...]]) +# +# will create and install application 'osp' from 'sources' with version +# OSPRAY_VERSION and optionally link against 'libs' + +MACRO(OSPRAY_CREATE_APPLICATION name) + SET(APP_NAME osp${name}) + SET(APP_SOURCES "") + SET(LINK_LIBS "") + + SET(CURRENT_LIST APP_SOURCES) + FOREACH(arg ${ARGN}) + IF (${arg} STREQUAL "LINK") + SET(CURRENT_LIST LINK_LIBS) + ELSE() + LIST(APPEND ${CURRENT_LIST} ${arg}) + ENDIF () + ENDFOREACH() + + ADD_EXECUTABLE(${APP_NAME} ${APP_SOURCES}) + TARGET_LINK_LIBRARIES(${APP_NAME} ${LINK_LIBS}) + IF (WIN32) + SET_TARGET_PROPERTIES(${APP_NAME} PROPERTIES VERSION ${OSPRAY_VERSION}) + ENDIF() + INSTALL(TARGETS ${APP_NAME} + DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT apps + ) +ENDMACRO() + +## Conveniance macro for installing OSPRay headers ## +# Usage +# +# OSPRAY_INSTALL_SDK_HEADERS(header1 [header2 ...] [DESTINATION destination]) +# +# will install headers into ${CMAKE_INSTALL_PREFIX}/ospray/SDK/${destination}, +# where destination is optional. + +MACRO(OSPRAY_INSTALL_SDK_HEADERS) + SET(HEADERS "") + SET(DESTINATION "") + + SET(CURRENT_LIST HEADERS) + FOREACH(arg ${ARGN}) + IF (${arg} STREQUAL "DESTINATION") + SET(CURRENT_LIST DESTINATION) + ELSE() + LIST(APPEND ${CURRENT_LIST} ${arg}) + ENDIF () + ENDFOREACH() + + INSTALL(FILES ${HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray/SDK/${DESTINATION} + COMPONENT devel + ) +ENDMACRO() + ## Compiler configuration macro ## MACRO(OSPRAY_CONFIGURE_COMPILER) - # enable ability for users to force a compiler using the pre-0.8.3 method + # enable ability for users to force a compiler using the pre-0.8.3 method (doesn't work right now!) SET(OSPRAY_COMPILER "" CACHE STRING "Force compiler: GCC, ICC, CLANG") SET_PROPERTY(CACHE OSPRAY_COMPILER PROPERTY STRINGS GCC ICC CLANG) MARK_AS_ADVANCED(OSPRAY_COMPILER) @@ -324,21 +422,25 @@ MACRO(OSPRAY_CONFIGURE_COMPILER) SET(OSPRAY_COMPILER_GCC OFF) SET(OSPRAY_COMPILER_CLANG OFF) SET(OSPRAY_COMPILER_MSVC OFF) + INCLUDE(${PROJECT_SOURCE_DIR}/cmake/icc.cmake) ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") SET(OSPRAY_COMPILER_ICC OFF) SET(OSPRAY_COMPILER_GCC ON ) SET(OSPRAY_COMPILER_CLANG OFF) SET(OSPRAY_COMPILER_MSVC OFF) + INCLUDE(${PROJECT_SOURCE_DIR}/cmake/gcc.cmake) ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") SET(OSPRAY_COMPILER_ICC OFF) SET(OSPRAY_COMPILER_GCC OFF) SET(OSPRAY_COMPILER_CLANG ON ) SET(OSPRAY_COMPILER_MSVC OFF) + INCLUDE(${PROJECT_SOURCE_DIR}/cmake/clang.cmake) ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") SET(OSPRAY_COMPILER_ICC OFF) SET(OSPRAY_COMPILER_GCC OFF) SET(OSPRAY_COMPILER_CLANG OFF) SET(OSPRAY_COMPILER_MSVC ON ) + INCLUDE(${PROJECT_SOURCE_DIR}/cmake/msvc.cmake) ELSE() MESSAGE(FATAL_ERROR "Unsupported compiler specified: '${CMAKE_CXX_COMPILER_ID}'") diff --git a/cmake/ospray_cmake_config.cmake b/cmake/ospray_cmake_config.cmake index 7a716a7a4c..906609d72d 100644 --- a/cmake/ospray_cmake_config.cmake +++ b/cmake/ospray_cmake_config.cmake @@ -52,7 +52,10 @@ ENDFOREACH(ospray_MODULE_FILENAME ${ospray_MODULE_FILES}) LIST(APPEND ospray_MODULE_INSTALL_FILES ${CMAKE_SOURCE_DIR}/cmake/FindTBB.cmake) -INSTALL(FILES ${ospray_MODULE_INSTALL_FILES} +INSTALL(FILES + ${ospray_MODULE_INSTALL_FILES} + ${CMAKE_SOURCE_DIR}/cmake/ispc.cmake + ${CMAKE_SOURCE_DIR}/cmake/ospray.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ospray-${OSPRAY_VERSION}" COMPONENT devel ) diff --git a/cmake/ospray_cmake_config/osprayConfig.cmake.in b/cmake/ospray_cmake_config/osprayConfig.cmake.in index 75e96c1652..02575cf234 100644 --- a/cmake/ospray_cmake_config/osprayConfig.cmake.in +++ b/cmake/ospray_cmake_config/osprayConfig.cmake.in @@ -96,6 +96,7 @@ endif(NOT EXISTS ${OSPRAY_INCLUDE_DIR}/ospray/version.h) set(OSPRAY_INCLUDE_DIRS ${OSPRAY_INCLUDE_DIR} ${OSPRAY_INCLUDE_DIR}/ospray + ${OSPRAY_INCLUDE_DIR}/ospray/SDK ) diff --git a/cmake/ospray_cmake_config/osprayUse.cmake.in b/cmake/ospray_cmake_config/osprayUse.cmake.in index fd6afa5a82..e1135fa897 100644 --- a/cmake/ospray_cmake_config/osprayUse.cmake.in +++ b/cmake/ospray_cmake_config/osprayUse.cmake.in @@ -19,3 +19,6 @@ # to automatically have an add_definitions(...) call for clients # which need certain preprocessor definitions based on how ospray # was built. + +include(${CMAKE_CURRENT_LIST_DIR}/ispc.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/ospray.cmake) diff --git a/common/malloc.h b/common/malloc.h deleted file mode 100644 index 55c50d8aa0..0000000000 --- a/common/malloc.h +++ /dev/null @@ -1,124 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "common.h" - -namespace ospcommon -{ -#define ALIGN_PTR(ptr,alignment) \ - ((((size_t)ptr)+alignment-1)&((size_t)-(ssize_t)alignment)) - - -// #define ALIGNED_STRUCT \ -// void* operator new(size_t size) { return alignedMalloc(size); } \ -// void operator delete(void* ptr) { alignedFree(ptr); } \ -// void* operator new[](size_t size) { return alignedMalloc(size); } \ -// void operator delete[](void* ptr) { alignedFree(ptr); } \ - -// #define ALIGNED_STRUCT_(align) \ -// void* operator new(size_t size) { return alignedMalloc(size,align); } \ -// void operator delete(void* ptr) { alignedFree(ptr); } \ -// void* operator new[](size_t size) { return alignedMalloc(size,align); } \ -// void operator delete[](void* ptr) { alignedFree(ptr); } \ - -// #define ALIGNED_CLASS \ -// public: \ -// ALIGNED_STRUCT \ -// private: - -// #define ALIGNED_CLASS_(align) \ -// public: \ -// ALIGNED_STRUCT_(align) \ -// private: - - /*! aligned allocation */ - OSPCOMMON_INTERFACE void* alignedMalloc(size_t size, size_t align = 64); - OSPCOMMON_INTERFACE void alignedFree(void* ptr); - - // /*! alloca that returns aligned data */ - // template - // __forceinline T* aligned_alloca(size_t elements, const size_t alignment = 64) { - // return (T*)ALIGN_PTR(alloca(elements * sizeof(T) + alignment),alignment); - // } - - // /*! allocator that performs aligned allocations */ - // template - // struct aligned_allocator - // { - // typedef T value_type; - // typedef T* pointer; - // typedef const T* const_pointer; - // typedef T& reference; - // typedef const T& const_reference; - // typedef std::size_t size_type; - // typedef std::ptrdiff_t difference_type; - - // __forceinline pointer allocate( size_type n ) { - // return (pointer) alignedMalloc(n*sizeof(value_type),alignment); - // } - - // __forceinline void deallocate( pointer p, size_type n ) { - // return alignedFree(p); - // } - - // __forceinline void construct( pointer p, const_reference val ) { - // new (p) T(val); - // } - - // __forceinline void destroy( pointer p ) { - // p->~T(); - // } - // }; - - // /*! allocates pages directly from OS */ - // void* os_malloc (size_t bytes, const int additional_flags = 0); - // void* os_reserve(size_t bytes); - // void os_commit (void* ptr, size_t bytes); - // size_t os_shrink (void* ptr, size_t bytesNew, size_t bytesOld); - // void os_free (void* ptr, size_t bytes); - - // /*! allocator that performs OS allocations */ - // template - // struct os_allocator - // { - // typedef T value_type; - // typedef T* pointer; - // typedef const T* const_pointer; - // typedef T& reference; - // typedef const T& const_reference; - // typedef std::size_t size_type; - // typedef std::ptrdiff_t difference_type; - - // __forceinline pointer allocate( size_type n ) { - // return (pointer) os_malloc(n*sizeof(value_type)); - // } - - // __forceinline void deallocate( pointer p, size_type n ) { - // return os_free(p,n*sizeof(value_type)); - // } - - // __forceinline void construct( pointer p, const_reference val ) { - // new (p) T(val); - // } - - // __forceinline void destroy( pointer p ) { - // p->~T(); - // } - // }; -} - diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index d4be3743a0..f8fab0b931 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -14,11 +14,26 @@ ## limitations under the License. ## ## ======================================================================== ## -IF (NOT WIN32) # not yet... - FILE(GLOB plugins RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ *) - FOREACH(plugin ${plugins}) - IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${plugin}/CMakeLists.txt) - ADD_SUBDIRECTORY(${plugin}) - ENDIF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${plugin}/CMakeLists.txt) - ENDFOREACH(plugin ${plugins}) -ENDIF() +INCLUDE_DIRECTORIES( + ${PROJECT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/ospray/include + ${CMAKE_SOURCE_DIR}/ospray + ${CMAKE_SOURCE_DIR}/apps + ${CMAKE_SOURCE_DIR}/apps/common + ${CMAKE_SOURCE_DIR} + ${EMBREE_INCLUDE_DIRS} +) +INCLUDE_DIRECTORIES_ISPC( + ${PROJECT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/ospray/include + ${CMAKE_SOURCE_DIR}/ospray + ${CMAKE_SOURCE_DIR} + ${EMBREE_INCLUDE_DIRS} +) + +FILE(GLOB plugins RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ *) +FOREACH(plugin ${plugins}) + IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${plugin}/CMakeLists.txt) + ADD_SUBDIRECTORY(${plugin}) + ENDIF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${plugin}/CMakeLists.txt) +ENDFOREACH(plugin ${plugins}) diff --git a/modules/opengl/CMakeLists.txt b/modules/opengl/CMakeLists.txt index ec779b7c31..75978b5e2f 100644 --- a/modules/opengl/CMakeLists.txt +++ b/modules/opengl/CMakeLists.txt @@ -16,8 +16,6 @@ OPTION(OSPRAY_MODULE_OPENGL_UTIL "Build OpenGL utility functionality" ON) -CONFIGURE_OSPRAY() - IF(NOT THIS_IS_MIC AND OSPRAY_MODULE_OPENGL_UTIL) FIND_PACKAGE(OpenGL REQUIRED) @@ -26,9 +24,6 @@ IF(NOT THIS_IS_MIC AND OSPRAY_MODULE_OPENGL_UTIL) LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules/opengl) - ADD_LIBRARY(ospray_module_opengl_util SHARED util.cpp) - - TARGET_LINK_LIBRARIES(ospray_module_opengl_util ospray ${OPENGL_LIBRARIES}) - OSPRAY_SET_LIBRARY_VERSION(ospray_module_opengl_util) - OSPRAY_INSTALL_LIBRARY(ospray_module_opengl_util) + OSPRAY_CREATE_LIBRARY(module_opengl_util util.cpp + LINK ospray ${OPENGL_LIBRARIES}) ENDIF() diff --git a/modules/opengl/util.cpp b/modules/opengl/util.cpp index 00f70d921c..3f0e3340c9 100644 --- a/modules/opengl/util.cpp +++ b/modules/opengl/util.cpp @@ -15,8 +15,14 @@ // ======================================================================== // #include "util.h" -#include "common/vec.h" -// #include +#include "ospcommon/vec.h" + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#endif #ifdef __APPLE__ #include diff --git a/modules/opengl/util.h b/modules/opengl/util.h index 765b3cee1c..43e6fdb2df 100644 --- a/modules/opengl/util.h +++ b/modules/opengl/util.h @@ -18,6 +18,16 @@ #include "ospray/ospray.h" +#ifdef _WIN32 +# ifdef ospray_module_opengl_EXPORTS +# define OSPGLUTIL_INTERFACE __declspec(dllexport) +# else +# define OSPGLUTIL_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPGLUTIL_INTERFACE +#endif + namespace ospray { namespace opengl { @@ -29,7 +39,7 @@ namespace ospray { buffer and transforms it to an OSPRay depth texture where the depth values represent ray distances from the camera. */ - extern OSPTexture2D getOSPDepthTextureFromOpenGLPerspective(); + OSPGLUTIL_INTERFACE OSPTexture2D getOSPDepthTextureFromOpenGLPerspective(); /*! \brief Compute and return an OSPRay depth texture from the provided view parameters and OpenGL depth buffer, assuming a perspective projection. @@ -53,7 +63,7 @@ namespace ospray { \param glDepthBufferWidth,glDepthBufferHeight Dimensions of the provided OpenGL depth buffer */ - extern OSPTexture2D getOSPDepthTextureFromOpenGLPerspective(const double &fovy, + OSPGLUTIL_INTERFACE OSPTexture2D getOSPDepthTextureFromOpenGLPerspective(const double &fovy, const double &aspect, const double &zNear, const double &zFar, @@ -75,7 +85,7 @@ namespace ospray { The OSPRay frame buffer object must have been constructed with the OSP_FB_DEPTH flag. */ - extern float * getOpenGLDepthFromOSPPerspective(OSPFrameBuffer frameBuffer, + OSPGLUTIL_INTERFACE float * getOpenGLDepthFromOSPPerspective(OSPFrameBuffer frameBuffer, const osp::vec2i &frameBufferSize); /*! \brief Compute and return OpenGL depth values from the provided view parameters and @@ -97,7 +107,7 @@ namespace ospray { \param frameBufferSize Dimensions of the provided OSPRay depth uffer */ - extern float * getOpenGLDepthFromOSPPerspective(const double &fovy, + OSPGLUTIL_INTERFACE float * getOpenGLDepthFromOSPPerspective(const double &fovy, const double &aspect, const double &zNear, const double &zFar, diff --git a/modules/seismic/CMakeLists.txt b/modules/seismic/CMakeLists.txt deleted file mode 100644 index 7cb1784feb..0000000000 --- a/modules/seismic/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2016 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -SET(OSPRAY_MODULE_SEISMIC OFF CACHE BOOL "Build seismic volume file support module") - -CONFIGURE_OSPRAY() - -IF(NOT THIS_IS_MIC AND OSPRAY_MODULE_SEISMIC) - IF (NOT OSPRAY_MODULE_LOADERS) - message(FATAL_ERROR - "The 'loaders' module must be enabled to build the 'seismic' module.") - ENDIF (NOT OSPRAY_MODULE_LOADERS) - - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) - INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - - LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules/seismic) - FIND_PACKAGE(FreeDDS REQUIRED) - INCLUDE_DIRECTORIES(${FreeDDS_INCLUDE_DIRS}) - - ADD_LIBRARY(ospray_module_seismic SHARED - SeismicHorizonFile.cpp - SeismicVolumeFile.cpp - SymbolRegistry.cpp - ) - - TARGET_LINK_LIBRARIES(ospray_module_seismic ${FreeDDS_LIBRARIES}) - OSPRAY_SET_LIBRARY_VERSION(ospray_module_seismic) - OSPRAY_INSTALL_LIBRARY(ospray_module_seismic) -ENDIF() diff --git a/modules/seismic/FindFreeDDS.cmake b/modules/seismic/FindFreeDDS.cmake deleted file mode 100644 index 710ed0e2e3..0000000000 --- a/modules/seismic/FindFreeDDS.cmake +++ /dev/null @@ -1,94 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2016 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -# - Try to find FreeDDS -# Once done, this will define -# -# FreeDDS_FOUND - system has FreeDDS -# FreeDDS_INCLUDE_DIRS - the FreeDDS include directories -# FreeDDS_LIBRARIES - link these to use FreeDDS -# -# this file is modeled after http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries - -include(LibFindMacros) - -MACRO(SUBDIRLIST result curdir) - FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) - SET(dirlist "") - FOREACH(child ${children}) - IF(IS_DIRECTORY ${curdir}/${child}) - LIST(APPEND dirlist ${child}) - ENDIF() - ENDFOREACH() - SET(${result} ${dirlist}) -ENDMACRO() - - -# Use pkg-config to get hints about paths -libfind_pkg_check_modules(FreeDDS_PKGCONF FreeDDS) - -# Root directory for FreeDDS installation -find_path(FreeDDS_ROOT_DIR - NAMES bin/dds-set-path - PATHS $ENV{DDSROOT} ENV PATH - PATH_SUFFIXES .. - ) - -# Include directory -find_path(FreeDDS_INCLUDE_DIR - NAMES cdds.h - PATHS ${FreeDDS_PKGCONF_INCLUDE_DIRS} ${FreeDDS_ROOT_DIR}/include - ) - -# Include directory for host includes -SUBDIRLIST(SUBDIRS ${FreeDDS_ROOT_DIR}/include) - -find_path(FreeDDS_INCLUDE_DIR_HOST - NAMES chost.h - PATHS ${FreeDDS_PKGCONF_INCLUDE_DIRS} ${FreeDDS_ROOT_DIR}/include - PATH_SUFFIXES ${SUBDIRS} - ) - -# Library directory -SUBDIRLIST(SUBDIRS ${FreeDDS_ROOT_DIR}/lib) - -find_path(FreeDDS_LIBRARY_DIR - NAMES libdds_r3.a - PATHS ${FreeDDS_PKGCONF_LIBRARY_DIRS} ${FreeDDS_ROOT_DIR}/lib - PATH_SUFFIXES ${SUBDIRS} - ) - -# And the libraries themselves -find_library(FreeDDS_LIBRARY - NAMES dds_r3 - PATHS ${FreeDDS_LIBRARY_DIR} - ) - -find_library(FreeDDS_CHOST_LIBRARY - NAMES chost - PATHS ${FreeDDS_LIBRARY_DIR} - ) - -find_library(FreeDDS_GIO_LIBRARY - NAMES gio - PATHS ${FreeDDS_LIBRARY_DIR} - ) - -# Set the include dir variables and the libraries and let libfind_process do the rest. -# NOTE: Singular variables for this library, plural for libraries this this lib depends on. -set(FreeDDS_PROCESS_INCLUDES FreeDDS_INCLUDE_DIR FreeDDS_INCLUDE_DIR_HOST) -set(FreeDDS_PROCESS_LIBS FreeDDS_LIBRARY FreeDDS_CHOST_LIBRARY FreeDDS_GIO_LIBRARY) -libfind_process(FreeDDS) diff --git a/modules/seismic/LibFindMacros.cmake b/modules/seismic/LibFindMacros.cmake deleted file mode 100644 index 72bec4347c..0000000000 --- a/modules/seismic/LibFindMacros.cmake +++ /dev/null @@ -1,282 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2016 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -# Version 2.2 -# Public Domain, originally written by Lasse Kärkkäinen -# Maintained at https://github.com/Tronic/cmake-modules -# Please send your improvements as pull requests on Github. - -# Find another package and make it a dependency of the current package. -# This also automatically forwards the "REQUIRED" argument. -# Usage: libfind_package( [extra args to find_package]) -macro (libfind_package PREFIX PKG) - set(${PREFIX}_args ${PKG} ${ARGN}) - if (${PREFIX}_FIND_REQUIRED) - set(${PREFIX}_args ${${PREFIX}_args} REQUIRED) - endif() - find_package(${${PREFIX}_args}) - set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG}) - unset(${PREFIX}_args) -endmacro() - -# A simple wrapper to make pkg-config searches a bit easier. -# Works the same as CMake's internal pkg_check_modules but is always quiet. -macro (libfind_pkg_check_modules) - find_package(PkgConfig QUIET) - if (PKG_CONFIG_FOUND) - pkg_check_modules(${ARGN} QUIET) - endif() -endmacro() - -# Avoid useless copy&pasta by doing what most simple libraries do anyway: -# pkg-config, find headers, find library. -# Usage: libfind_pkg_detect( FIND_PATH [other args] FIND_LIBRARY [other args]) -# E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2) -function (libfind_pkg_detect PREFIX) - # Parse arguments - set(argname pkgargs) - foreach (i ${ARGN}) - if ("${i}" STREQUAL "FIND_PATH") - set(argname pathargs) - elseif ("${i}" STREQUAL "FIND_LIBRARY") - set(argname libraryargs) - else() - set(${argname} ${${argname}} ${i}) - endif() - endforeach() - if (NOT pkgargs) - message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.") - endif() - # Find library - libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs}) - if (pathargs) - find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS}) - endif() - if (libraryargs) - find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}) - endif() -endfunction() - -# Extracts a version #define from a version.h file, output stored to _VERSION. -# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR) -# Fourth argument "QUIET" may be used for silently testing different define names. -# This function does nothing if the version variable is already defined. -function (libfind_version_header PREFIX VERSION_H DEFINE_NAME) - # Skip processing if we already have a version or if the include dir was not found - if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR) - return() - endif() - set(quiet ${${PREFIX}_FIND_QUIETLY}) - # Process optional arguments - foreach(arg ${ARGN}) - if (arg STREQUAL "QUIET") - set(quiet TRUE) - else() - message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.") - endif() - endforeach() - # Read the header and parse for version number - set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") - if (NOT EXISTS ${filename}) - if (NOT quiet) - message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") - endif() - return() - endif() - file(READ "${filename}" header) - string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}") - # No regex match? - if (match STREQUAL header) - if (NOT quiet) - message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") - endif() - return() - endif() - # Export the version string - set(${PREFIX}_VERSION "${match}" PARENT_SCOPE) -endfunction() - -# Do the final processing once the paths have been detected. -# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain -# all the variables, each of which contain one include directory. -# Ditto for ${PREFIX}_PROCESS_LIBS and library files. -# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. -# Also handles errors in case library detection was required, etc. -function (libfind_process PREFIX) - # Skip processing if already processed during this configuration run - if (${PREFIX}_FOUND) - return() - endif() - - set(found TRUE) # Start with the assumption that the package was found - - # Did we find any files? Did we miss includes? These are for formatting better error messages. - set(some_files FALSE) - set(missing_headers FALSE) - - # Shorthands for some variables that we need often - set(quiet ${${PREFIX}_FIND_QUIETLY}) - set(required ${${PREFIX}_FIND_REQUIRED}) - set(exactver ${${PREFIX}_FIND_VERSION_EXACT}) - set(findver "${${PREFIX}_FIND_VERSION}") - set(version "${${PREFIX}_VERSION}") - - # Lists of config option names (all, includes, libs) - unset(configopts) - set(includeopts ${${PREFIX}_PROCESS_INCLUDES}) - set(libraryopts ${${PREFIX}_PROCESS_LIBS}) - - # Process deps to add to - foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES}) - if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS) - # The package seems to export option lists that we can use, woohoo! - list(APPEND includeopts ${${i}_INCLUDE_OPTS}) - list(APPEND libraryopts ${${i}_LIBRARY_OPTS}) - else() - # If plural forms don't exist or they equal singular forms - if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR - ({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES)) - # Singular forms can be used - if (DEFINED ${i}_INCLUDE_DIR) - list(APPEND includeopts ${i}_INCLUDE_DIR) - endif() - if (DEFINED ${i}_LIBRARY) - list(APPEND libraryopts ${i}_LIBRARY) - endif() - else() - # Oh no, we don't know the option names - message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!") - endif() - endif() - endforeach() - - if (includeopts) - list(REMOVE_DUPLICATES includeopts) - endif() - - if (libraryopts) - list(REMOVE_DUPLICATES libraryopts) - endif() - - string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}") - if (NOT tmp STREQUAL "${includeopts} ${libraryopts}") - message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).") - endif() - - # Include/library names separated by spaces (notice: not CMake lists) - unset(includes) - unset(libs) - - # Process all includes and set found false if any are missing - foreach (i ${includeopts}) - list(APPEND configopts ${i}) - if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") - list(APPEND includes "${${i}}") - else() - set(found FALSE) - set(missing_headers TRUE) - endif() - endforeach() - - # Process all libraries and set found false if any are missing - foreach (i ${libraryopts}) - list(APPEND configopts ${i}) - if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") - list(APPEND libs "${${i}}") - else() - set (found FALSE) - endif() - endforeach() - - # Version checks - if (found AND findver) - if (NOT version) - message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.") - elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver)) - set(found FALSE) - set(version_unsuitable TRUE) - endif() - endif() - - # If all-OK, hide all config options, export variables, print status and exit - if (found) - foreach (i ${configopts}) - mark_as_advanced(${i}) - endforeach() - if (NOT quiet) - message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") - if (LIBFIND_DEBUG) - message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}") - message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}") - message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}") - message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}") - message(STATUS " ${PREFIX}_LIBRARIES=${libs}") - endif() - set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE) - set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE) - set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE) - set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE) - set (${PREFIX}_FOUND TRUE PARENT_SCOPE) - endif() - return() - endif() - - # Format messages for debug info and the type of error - set(vars "Relevant CMake configuration variables:\n") - foreach (i ${configopts}) - mark_as_advanced(CLEAR ${i}) - set(val ${${i}}) - if ("${val}" STREQUAL "${i}-NOTFOUND") - set (val "") - elseif (val AND NOT EXISTS ${val}) - set (val "${val} (does not exist)") - else() - set(some_files TRUE) - endif() - set(vars "${vars} ${i}=${val}\n") - endforeach() - set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n") - if (version_unsuitable) - set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but") - if (exactver) - set(msg "${msg} only version ${findver} is acceptable.") - else() - set(msg "${msg} version ${findver} is the minimum requirement.") - endif() - else() - if (missing_headers) - set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?") - elseif (some_files) - set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?") - if(findver) - set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).") - endif() - else() - set(msg "We were unable to find package ${PREFIX}.") - endif() - endif() - - # Fatal error out if REQUIRED - if (required) - set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.") - message(FATAL_ERROR "${msg}\n${vars}") - endif() - # Otherwise just print a nasty warning - if (NOT quiet) - message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}") - endif() -endfunction() - diff --git a/modules/seismic/README.txt b/modules/seismic/README.txt deleted file mode 100644 index 55a6d0f13d..0000000000 --- a/modules/seismic/README.txt +++ /dev/null @@ -1,36 +0,0 @@ - - Copyright 2014-2016 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - Description: - - A file loader for seismic volume data based on the FreeDDS library. - - FreeDDS: - - "The Data Dictionary System is an I/O System (and much more) capable of - handling multi-dimensional seismic datasets (up to 9 dimensions; e.g. - 'axis = t offset shot x y') in various formats including usp (BP Amoco's - Un*x Seismic Processing), segy (Society of Exploration Geophysicists - SEG-Y format), segy1 (SEG's SEG-Y Rev 1.0 format), sep (Stanford - Exploration Project's format), su (Colorado School of Mine's Seismic - Un*x format) as well as other flexible trace definitions (with or without - trace headers) such as fcube. DDS allows different processing systems to - be used together by piping directly together as well as through actual - emulation of different processing systems." - - http://freeusp.org/DDS/download.php - - diff --git a/modules/seismic/SeismicHorizonFile.cpp b/modules/seismic/SeismicHorizonFile.cpp deleted file mode 100644 index 205266428b..0000000000 --- a/modules/seismic/SeismicHorizonFile.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include -#include -#include -#include "SeismicHorizonFile.h" - -SeismicHorizonFile::SeismicHorizonFile(const std::string &filename) : - filename(filename), - scale(ospray::vec3f(1.f)), - verbose(true) -{ -} - -OSPGeometry SeismicHorizonFile::importTriangleMesh(OSPGeometry triangleMesh) -{ - // Get scaling parameter if provided. - ospGetVec3f(triangleMesh, "scale", (osp::vec3f *)&scale); - - // Open seismic data file and populate attributes. - exitOnCondition(openSeismicDataFile(triangleMesh) != true, - "unable to open file '" + filename + "'"); - - // Import the horizon data from the file into the triangle mesh. - exitOnCondition(importHorizonData(triangleMesh) != true, - "error importing horizon data."); - - // Return the triangle mesh. - return triangleMesh; -} - -std::string SeismicHorizonFile::toString() const -{ - return("ospray_module_seismic::SeismicHorizonFile"); -} - -bool SeismicHorizonFile::openSeismicDataFile(OSPGeometry /*triangleMesh*/) -{ - // Open seismic data file, keeping trace header information. // - // Sample types int, short, long, float, and double will be converted to float. - inputBinTag = cddx_in2("in", filename.c_str(), "seismic"); - exitOnCondition(inputBinTag < 0, "could not open data file " + filename); - - // Get horizon volume dimensions. - exitOnCondition(cdds_scanf("size.axis(1)", "%d", &dimensions.x) != 1, - "could not find size of dimension 1"); - exitOnCondition(cdds_scanf("size.axis(2)", "%d", &dimensions.y) != 1, - "could not find size of dimension 2"); - exitOnCondition(cdds_scanf("size.axis(3)", "%d", &dimensions.z) != 1, - "could not find size of dimension 3"); - - if(verbose) { - std::cout << toString() << " " << dimensions.z - << " horizons, dimensions = " << dimensions.x - << " " << dimensions.y << std::endl; - } - - // Get voxel spacing (deltas). - exitOnCondition(cdds_scanf("delta.axis(1)", "%f", &deltas.x) != 1, - "could not find delta of dimension 1"); - exitOnCondition(cdds_scanf("delta.axis(2)", "%f", &deltas.y) != 1, - "could not find delta of dimension 2"); - exitOnCondition(cdds_scanf("delta.axis(3)", "%f", &deltas.z) != 1, - "could not find delta of dimension 3"); - - if(verbose) { - std::cout << toString() << " volume deltas = " - << deltas.x << " " << deltas.y << " " << deltas.z << std::endl; - } - - // Get trace header information. - FIELD_TAG traceSamplesTag = cdds_member(inputBinTag, 0, "Samples"); - traceHeaderSize = cdds_index(inputBinTag, traceSamplesTag, DDS_FLOAT); - exitOnCondition(traceSamplesTag == -1 || traceHeaderSize == -1, - "could not get trace header information"); - - if(verbose) { - std::cout << toString() << " trace header size = " << traceHeaderSize - << std::endl; - } - - // Check that horizon dimensions are valid. - exitOnCondition(dimensions.x <= 1 || dimensions.y <= 1, - "improper horizon dimensions found"); - - // Need at least one horizon. - exitOnCondition(dimensions.x < 1, "no horizons found"); - - return true; -} - -bool SeismicHorizonFile::importHorizonData(OSPGeometry triangleMesh) -{ - // Allocate memory for all traces. - size_t memSize = dimensions.x*dimensions.y*dimensions.z * sizeof(float); - float *volumeBuffer = (float *)malloc(memSize); - exitOnCondition(volumeBuffer == NULL, "failed to allocate volume buffer"); - - // Allocate trace array; the trace length is given by the trace header size - // and the first dimension. Note that FreeDDS converts all trace data to - // floats. - memSize = (traceHeaderSize + dimensions.x) * sizeof(float); - float *traceBuffer = (float *)malloc(memSize); - exitOnCondition(traceBuffer == NULL, "failed to allocate trace buffer"); - - // Read all traces and copy them into the volume buffer. - for(long i3=0; i3 vertices; - std::vector vertexNormals; - std::vector triangles; - - for(long h=0; h 0 to be valid. - if (std::min(std::min(vertices[v0].x, - vertices[v1].x), - vertices[v2].x) > 0.f) { - triangles.push_back(ospray::vec3i(v0, v1, v2)); - - ospray::vec3fa triangleNormal = cross(vertices[v1] - vertices[v0], - vertices[v2] - vertices[v0]); - vertexNormals[v0] += triangleNormal; - vertexNormals[v1] += triangleNormal; - vertexNormals[v2] += triangleNormal; - } - - if (std::min(std::min(vertices[v2].x, - vertices[v3].x), - vertices[v0].x) > 0.f) { - triangles.push_back(ospray::vec3i(v2, v3, v0)); - - ospray::vec3fa triangleNormal = cross(vertices[v3] - vertices[v2], - vertices[v0] - vertices[v2]); - vertexNormals[v2] += triangleNormal; - vertexNormals[v3] += triangleNormal; - vertexNormals[v0] += triangleNormal; - } - } - } - } - - // Normalize vertex normals. - for(long i=0; i -#include -#include "apps/volumeViewer/loaders/TriangleMeshFile.h" - -//! \brief A concrete implementation of the TriangleMeshFile class -//! for reading horizon data stored in seismic file formats on disk. -//! -class SeismicHorizonFile : public TriangleMeshFile { -public: - - //! Constructor. - SeismicHorizonFile(const std::string &filename); - - //! Destructor. - virtual ~SeismicHorizonFile() {} - - //! Import the horizon data. - virtual OSPGeometry importTriangleMesh(OSPGeometry triangleMesh); - - //! A string description of this class. - virtual std::string toString() const; - -private: - - //! Path to the file containing the horizon data. - std::string filename; - - //! Scaling for vertex coordinates. - ospray::vec3f scale; - - //! Verbose logging. - bool verbose; - - //! Seismic data attributes - BIN_TAG inputBinTag; - int traceHeaderSize; - ospray::vec3i dimensions; // -#include -#include "SeismicVolumeFile.h" - -OSPVolume SeismicVolumeFile::importVolume(OSPVolume volume) { - - // Set voxel type of the volume. Note that FreeDDS converts sample types of int, short, long, float, and double to float. - ospSetString(volume, "voxelType", "float"); - - // Open seismic data file and populate attributes. - exitOnCondition(openSeismicDataFile(volume) != true, "unable to open file '" + filename + "'"); - - // Import the voxel data from the file into the volume. - exitOnCondition(importVoxelData(volume) != true, "error importing voxel data."); - - // Return the volume. - return volume; -} - -bool SeismicVolumeFile::openSeismicDataFile(OSPVolume volume) { - - // Open seismic data file, keeping trace header information. - // Sample types int, short, long, float, and double will be converted to float. - inputBinTag = cddx_in2("in", filename.c_str(), "seismic"); - exitOnCondition(inputBinTag < 0, "could not open data file " + filename); - - // Get seismic volume dimensions and volume size. - exitOnCondition(cdds_scanf("size.axis(1)", "%d", &dimensions.x) != 1, "could not find size of dimension 1"); - exitOnCondition(cdds_scanf("size.axis(2)", "%d", &dimensions.y) != 1, "could not find size of dimension 2"); - exitOnCondition(cdds_scanf("size.axis(3)", "%d", &dimensions.z) != 1, "could not find size of dimension 3"); - - if(verbose) std::cout << toString() << " volume dimensions = " << dimensions.x << " " << dimensions.y << " " << dimensions.z << std::endl; - - // Get voxel spacing (deltas). - exitOnCondition(cdds_scanf("delta.axis(1)", "%f", &deltas.x) != 1, "could not find delta of dimension 1"); - exitOnCondition(cdds_scanf("delta.axis(2)", "%f", &deltas.y) != 1, "could not find delta of dimension 2"); - exitOnCondition(cdds_scanf("delta.axis(3)", "%f", &deltas.z) != 1, "could not find delta of dimension 3"); - - if(verbose) std::cout << toString() << " volume deltas = " << deltas.x << " " << deltas.y << " " << deltas.z << std::endl; - - // Get trace header information. - FIELD_TAG traceSamplesTag = cdds_member(inputBinTag, 0, "Samples"); - traceHeaderSize = cdds_index(inputBinTag, traceSamplesTag, DDS_FLOAT); - exitOnCondition(traceSamplesTag == -1 || traceHeaderSize == -1, "could not get trace header information"); - - if(verbose) std::cout << toString() << " trace header size = " << traceHeaderSize << std::endl; - - // Check that volume dimensions are valid. If not, perform a scan of all trace headers to find actual dimensions. - if(reduce_min(dimensions) <= 1) { - if(verbose) std::cout << toString() << " improper volume dimensions found, scanning volume file for proper values." << std::endl; - - exitOnCondition(scanSeismicDataFileForDimensions(volume) != true, "failed scan of seismic data file"); - } - - // Check if a subvolume of the volume has been specified. - // Subvolume parameters: subvolumeOffsets, subvolumeDimensions, subvolumeSteps. - // The subvolume defaults to full dimensions (allowing for just subsampling, for example.) - subvolumeOffsets = ospray::vec3i(0); - ospGetVec3i(volume, "subvolumeOffsets", (osp::vec3i *)&subvolumeOffsets); - exitOnCondition(reduce_min(subvolumeOffsets) < 0 || reduce_max(subvolumeOffsets - dimensions) >= 0, "invalid subvolume offsets"); - - subvolumeDimensions = dimensions - subvolumeOffsets; - ospGetVec3i(volume, "subvolumeDimensions", (osp::vec3i *)&subvolumeDimensions); - exitOnCondition(reduce_min(subvolumeDimensions) < 1 || reduce_max(subvolumeDimensions - (dimensions - subvolumeOffsets)) > 0, "invalid subvolume dimension(s) specified"); - - subvolumeSteps = ospray::vec3i(1); - ospGetVec3i(volume, "subvolumeSteps", (osp::vec3i *)&subvolumeSteps); - exitOnCondition(reduce_min(subvolumeSteps) < 1 || reduce_max(subvolumeSteps - (dimensions - subvolumeOffsets)) >= 0, "invalid subvolume steps"); - - if(reduce_max(subvolumeOffsets) > 0 || subvolumeDimensions != dimensions || reduce_max(subvolumeSteps) > 1) - useSubvolume = true; - else - useSubvolume = false; - - // The dimensions of the volume to be imported. - volumeDimensions = ospray::vec3i(subvolumeDimensions.x / subvolumeSteps.x + (subvolumeDimensions.x % subvolumeSteps.x != 0), - subvolumeDimensions.y / subvolumeSteps.y + (subvolumeDimensions.y % subvolumeSteps.y != 0), - subvolumeDimensions.z / subvolumeSteps.z + (subvolumeDimensions.z % subvolumeSteps.z != 0)); - - // Range check. - exitOnCondition(reduce_min(volumeDimensions) <= 0, "invalid volume dimensions"); - - if(verbose) std::cout << toString() << " subvolume dimensions = " << volumeDimensions.x << " " << volumeDimensions.y << " " << volumeDimensions.z << std::endl; - - // Set dimensions of the volume. - ospSetVec3i(volume, "dimensions", (const osp::vec3i &)volumeDimensions); - - // The grid spacing of the volume to be imported. - gridSpacing = deltas; - - if(useSubvolume) { - gridSpacing.x *= float(subvolumeSteps.x); - gridSpacing.y *= float(subvolumeSteps.y); - gridSpacing.z *= float(subvolumeSteps.z); - } - - // Set voxel spacing of the volume if not already provided. - if(!ospGetVec3f(volume, "gridSpacing", (osp::vec3f *)&gridSpacing)) - ospSetVec3f(volume, "gridSpacing", (const osp::vec3f &)gridSpacing); - - return true; -} - -bool SeismicVolumeFile::scanSeismicDataFileForDimensions(OSPVolume volume) { - - // Trace fields used for coordinates in each dimension; defaults can be overridden. - std::string traceCoordinate2Field = "CdpNum"; - std::string traceCoordinate3Field = "FieldRecNum"; - - char * temp; - if(ospGetString(volume, "traceCoordinate2Field", &temp)) - traceCoordinate2Field = std::string(temp); - - if(ospGetString(volume, "traceCoordinate3Field", &temp)) - traceCoordinate3Field = std::string(temp); - - if(verbose) std::cout << toString() << " trace coordinate fields = " << traceCoordinate2Field << " " << traceCoordinate3Field << std::endl; - - // Get location of these fields in trace header. - FIELD_TAG traceCoordinate2Tag = cdds_member(inputBinTag, 0, traceCoordinate2Field.c_str()); - FIELD_TAG traceCoordinate3Tag = cdds_member(inputBinTag, 0, traceCoordinate3Field.c_str()); - exitOnCondition(traceCoordinate2Tag == -1 || traceCoordinate3Tag == -1, "could not get trace header coordinate information"); - - // Allocate trace array; the trace length is given by the trace header size and the first dimension. - // Note that FreeDDS converts all trace data to floats. - float * traceBuffer = new float[traceHeaderSize + dimensions.x]; - - // Range in second and third dimensions - int minDimension2, maxDimension2; - int minDimension3, maxDimension3; - - // Iterate through all traces. - size_t traceCount = 0; - - while(cddx_read(inputBinTag, traceBuffer, 1) == 1) { - - // Get logical coordinates. - int coordinate2, coordinate3; - cdds_geti(inputBinTag, traceCoordinate2Tag, traceBuffer, &coordinate2, 1); - cdds_geti(inputBinTag, traceCoordinate3Tag, traceBuffer, &coordinate3, 1); - - // Update dimension bounds. - if(traceCount == 0) { - minDimension2 = maxDimension2 = coordinate2; - minDimension3 = maxDimension3 = coordinate3; - } - else { - minDimension2 = std::min(minDimension2, coordinate2); - maxDimension2 = std::max(maxDimension2, coordinate2); - - minDimension3 = std::min(minDimension3, coordinate3); - maxDimension3 = std::max(maxDimension3, coordinate3); - } - - traceCount++; - } - - // Update dimensions. - dimensions.y = maxDimension2 - minDimension2 + 1; - dimensions.z = maxDimension3 - minDimension3 + 1; - exitOnCondition(dimensions.y <= 1 || dimensions.z <= 1, "could not determine volume dimensions"); - - if(verbose) std::cout << toString() << " updated volume dimensions = " << dimensions.x << " x " << dimensions.y << " (" << minDimension2 << " --> " << maxDimension2 << ") x " << dimensions.z << " (" << minDimension3 << " --> " << maxDimension3 << ")" << std::endl; - - // Clean up. - delete [] traceBuffer; - - // Seek back to the beginning of the file. - cdds_lseek(inputBinTag, 0, 0, SEEK_SET); - - return true; -} - -bool SeismicVolumeFile::importVoxelData(OSPVolume volume) { - - // Trace fields used for coordinates in each dimension; defaults can be overridden. - std::string traceCoordinate2Field = "CdpNum"; - std::string traceCoordinate3Field = "FieldRecNum"; - - char * temp; - if(ospGetString(volume, "traceCoordinate2Field", &temp)) - traceCoordinate2Field = std::string(temp); - - if(ospGetString(volume, "traceCoordinate3Field", &temp)) - traceCoordinate3Field = std::string(temp); - - if(verbose) std::cout << toString() << " trace coordinate fields = " << traceCoordinate2Field << " " << traceCoordinate3Field << std::endl; - - // Get location of these fields in trace header. If we can't find them, ignore trace header information. - bool ignoreTraceHeaders = false; - - FIELD_TAG traceCoordinate2Tag = cdds_member(inputBinTag, 0, traceCoordinate2Field.c_str()); - FIELD_TAG traceCoordinate3Tag = cdds_member(inputBinTag, 0, traceCoordinate3Field.c_str()); - - if(traceHeaderSize > 0 && (traceCoordinate2Tag == -1 || traceCoordinate3Tag == -1)) { - ignoreTraceHeaders = true; - if(verbose) - std::cout << toString() << " could not get trace header coordinate information, ignoring trace header information." << std::endl; - } - - // Find total number of traces. If we find the expected number, ignore trace headers. This speeds loading of the volume significantly. - long numTraces = cdds_lseek(inputBinTag, 0, 0, SEEK_END); - - if(numTraces == size_t(dimensions.y) * dimensions.z) { - ignoreTraceHeaders = true; - if(verbose) - std::cout << toString() << " file has expected number of traces, assuming traces are in expected order and ignoring trace header information." << std::endl; - } - - // Seek back to the beginning of the file. - cdds_lseek(inputBinTag, 0, 0, SEEK_SET); - - // Allocate trace array; the trace length is given by the trace header size and the first dimension. - // Note that FreeDDS converts all trace data to floats. - float * traceBuffer = new float[traceHeaderSize + dimensions.x]; - - // Get origin in dimension 2 and 3 from first trace if we have a trace header; otherwise assume it is (0, 0). - int origin2 = 0; - int origin3 = 0; - - if(!ignoreTraceHeaders && traceHeaderSize > 0) { - - exitOnCondition(cddx_read(inputBinTag, traceBuffer, 1) != 1, "could not read first trace"); - cdds_geti(inputBinTag, traceCoordinate2Tag, traceBuffer, &origin2, 1); - cdds_geti(inputBinTag, traceCoordinate3Tag, traceBuffer, &origin3, 1); - - if(verbose) std::cout << toString() << " trace coordinate origin: (" << origin2 << ", " << origin3 << ")" << std::endl; - - // Seek back to the beginning of the file. - cdds_lseek(inputBinTag, 0, 0, SEEK_SET); - } - - // Copy voxel data into the volume a trace at a time. - size_t traceCount = 0; - - // If no subvolume is specified and we have trace header information, read all the traces. Missing traces are allowed. - // Otherwise, read only the specified subvolume, skipping over traces as needed. Missing traces NOT allowed. - if(!useSubvolume && !ignoreTraceHeaders && traceHeaderSize > 0) { - - while(cddx_read(inputBinTag, traceBuffer, 1) == 1) { - - // Get logical coordinates. - int coordinate2, coordinate3; - cdds_geti(inputBinTag, traceCoordinate2Tag, traceBuffer, &coordinate2, 1); - cdds_geti(inputBinTag, traceCoordinate3Tag, traceBuffer, &coordinate3, 1); - - // Validate coordinates. - exitOnCondition(coordinate2-origin2 < 0 || coordinate2-origin2 >= dimensions.y || coordinate3-origin3 < 0 || coordinate3-origin3 >= dimensions.z, "invalid trace coordinates found"); - - // Copy trace into the volume. - ospray::vec3i regionCoords(0, coordinate2-origin2, coordinate3-origin3); - ospray::vec3i regionSize(volumeDimensions.x, 1, 1); - ospSetRegion(volume, &traceBuffer[traceHeaderSize], - (const osp::vec3i &)regionCoords, - (const osp::vec3i &)regionSize); - - traceCount++; - } - } - else { - - // Allocate trace buffer for subvolume data. - float * traceBufferSubvolume = new float[volumeDimensions.x]; - - // We will call ospSetRegion() with planes of traces. - float * planeBuffer = new float[volumeDimensions.y * volumeDimensions.x]; - - // Iterate through the grid of traces of the subvolume, seeking as necessary. - for(long i3=subvolumeOffsets.z; i3 0) { - cdds_geti(inputBinTag, traceCoordinate2Tag, traceBuffer, &coordinate2, 1); - cdds_geti(inputBinTag, traceCoordinate3Tag, traceBuffer, &coordinate3, 1); - } - - // Some trace headers will have improper coordinate information; correct coordinate3 if necessary. - if(coordinate2-origin2 >= dimensions.y) { - coordinate3 = origin3 + (coordinate2-origin2) / dimensions.y; - coordinate2 -= (coordinate3-origin3) * dimensions.y; - } - - // Validate we have the expected coordinates. - if((coordinate2-origin2 != i2 || coordinate3-origin3 != i3) && verbose) - std::cout << toString() << " found incorrect coordinate(s): (" << coordinate2-origin2 << ", " << coordinate3-origin3 << ") != (" << i2 << ", " << i3 << ")" << std::endl; - - exitOnCondition(coordinate2-origin2 != i2, "found invalid coordinate (dimension 2)"); - exitOnCondition(coordinate3-origin3 != i3, "found invalid coordinate (dimension 3)"); - - // Resample trace for the subvolume if necessary; copy to subvolume trace buffer. - if(subvolumeSteps.x != 1) - for(long i1=subvolumeOffsets.x; i1 -#include -#include "apps/volumeViewer/loaders/VolumeFile.h" - -//! \brief A concrete implementation of the VolumeFile class -//! for reading voxel data stored in seismic file formats on disk. -//! -class SeismicVolumeFile : public VolumeFile { -public: - - //! Constructor. - SeismicVolumeFile(const std::string &filename) : filename(filename), verbose(true) {} - - //! Destructor. - virtual ~SeismicVolumeFile() {}; - - //! Import the volume data. - virtual OSPVolume importVolume(OSPVolume volume); - - //! A string description of this class. - virtual std::string toString() const { return("ospray_module_seismic::SeismicVolumeFile"); } - -private: - - //! Path to the file containing the volume data. - std::string filename; - - //! Verbose logging. - bool verbose; - - //! Seismic data attributes - BIN_TAG inputBinTag; - int traceHeaderSize; - ospray::vec3i dimensions; // 1 allow for subsampling. - - //! The dimensions of the volume to be imported, considering any subvolume parameters. - ospray::vec3i volumeDimensions; - - //! The grid spacing of the volume to be imported. - ospray::vec3f gridSpacing; - - //! Open the seismic data file and populate attributes. - bool openSeismicDataFile(OSPVolume volume); - - //! Scan the seismic data file to determine the volume dimensions. - bool scanSeismicDataFileForDimensions(OSPVolume volume); - - //! Import the voxel data from the file into the volume. - bool importVoxelData(OSPVolume volume); - -}; diff --git a/modules/tachyon/TachyonRenderer.cpp b/modules/tachyon/TachyonRenderer.cpp deleted file mode 100644 index 1bc097ef83..0000000000 --- a/modules/tachyon/TachyonRenderer.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#undef NDEBUG - -// ospray -#include "TachyonRenderer.h" -#include "ospray/camera/PerspectiveCamera.h" -#include "common/Data.h" -// tachyon -#include "Model.h" -// ispc imports -#include "TachyonRenderer_ispc.h" - -namespace ospray { - - TachyonRenderer::TachyonRenderer() - { - this->ispcEquivalent = ispc::TachyonRenderer_create(this); - } - - void TachyonRenderer::commit() - { - Renderer::commit(); - - model = (Model *)getParamObject("world",NULL); - model = (Model *)getParamObject("model",model); - - doShadows = getParam1i("do_shadows",0); - - float lightScale = getParam1f("lightScale",1.f); - - textureData = (Data*)(model ? model->getParamObject("textureArray",NULL) : NULL); - if (!textureData) - throw std::runtime_error("#osp:tachyon::renderer: no texture data specified"); - - pointLightData = (Data*)(model ? model->getParamObject("pointLights",NULL) : NULL); - pointLightArray - = pointLightData - ? pointLightData->data - : NULL; - numPointLights - = pointLightData - ? pointLightData->size()/sizeof(ospray::tachyon::PointLight) - : 0; - - dirLightData = (Data*)(model ? model->getParamObject("dirLights",NULL) : NULL); - dirLightArray - = dirLightData - ? dirLightData->data - : NULL; - numDirLights - = dirLightData - ? dirLightData->size()/sizeof(ospray::tachyon::DirLight) - : 0; - - bool doShadows = getParam1i("do_shadows",0); - - camera = (Camera *)getParamObject("camera",NULL); - ispc::TachyonRenderer_set(getIE(), - model?model->getIE():NULL, - camera?camera->getIE():NULL, - textureData?textureData->data:NULL, - pointLightArray,numPointLights, - dirLightArray,numDirLights, - doShadows, - lightScale); - } - - // TileRenderer::RenderJob *TachyonRenderer::createRenderJob(FrameBuffer *fb) - // { - // /*! iw - actually, shoudl do all this parsing in 'commit', not in createrenderjob */ - - // RenderTask *frame = new RenderTask; - // // should actually do that in 'commit', not ever frame ... - // Model *model = (Model *)getParamObject("world",NULL); - // model = (Model *)getParamObject("model",model); - // if (!model) - // throw std::runtime_error("#osp:tachyon::renderer: no model specified"); - // frame->world = model; - - // frame->doShadows = getParam1i("do_shadows",0); - - // frame->textureData = (Data*)model->getParamObject("textureArray",NULL); - // if (!frame->textureData) - // throw std::runtime_error("#osp:tachyon::renderer: no texture data specified"); - - // frame->pointLightData = (Data*)model->getParamObject("pointLights",NULL); - // frame->pointLightArray - // = frame->pointLightData - // ? frame->pointLightData->data - // : NULL; - // frame->numPointLights - // = frame->pointLightData - // ? frame->pointLightData->size()/sizeof(ospray::tachyon::PointLight) - // : 0; - - - // frame->dirLightData = (Data*)model->getParamObject("dirLights",NULL); - // frame->dirLightArray - // = frame->dirLightData - // ? frame->dirLightData->data - // : NULL; - // frame->numDirLights - // = frame->dirLightData - // ? frame->dirLightData->size()/sizeof(ospray::tachyon::DirLight) - // : 0; - - - // frame->camera = (Camera *)getParamObject("camera",NULL); - // return frame; - // } - - OSP_REGISTER_RENDERER(TachyonRenderer,tachyon); - - extern "C" void ospray_init_module_tachyon() - { - printf("Loaded plugin 'pathtracer' ...\n"); - } -}; - diff --git a/modules/tachyon/TachyonRenderer.ispc b/modules/tachyon/TachyonRenderer.ispc deleted file mode 100644 index 6cbc2c9145..0000000000 --- a/modules/tachyon/TachyonRenderer.ispc +++ /dev/null @@ -1,266 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -// ospray -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/camera/PerspectiveCamera.ih" -#include "ospray/common/Model.ih" -#include "ospray/render/util.ih" -#include "ospray/render/Renderer.ih" - -struct Phong { - float plastic; - float size; -}; - -struct TachyonTexture { - float ambient, diffuse, specular, opacity; - Phong phong; - vec3f color; - int texFunc; -}; - -struct PointLight -{ - vec3f center; - vec3f color; - struct { - float constant, linear, quadratic; - } att; -}; - -struct DirLight -{ - vec3f color; - vec3f dir; -}; - -struct TachyonRenderer { - Renderer super; - - uniform TachyonTexture *textureArray; - uniform PointLight *pointLight; - uint32 numPointLights; - uniform DirLight *dirLight; - uint32 numDirLights; - bool doShadows; - float lightScale; -}; - - -#define EPS (1e-4f) -#define ALPHA_THRESHOLD (.05f) - -inline float lightAlpha(Ray &ray, uniform TachyonRenderer *uniform tachyon, const float weight) -{ - if (!tachyon->doShadows) - return 1.f; - - float alpha = 1.f; - int max_depth = 8; // max number of rays to be traced... - float org_t_max = ray.t; - while (1) { - traceRay(tachyon->super.model,ray); - if (ray.geomID < 0) - return alpha; - - DifferentialGeometry dg; - postIntersect(tachyon->super.model,dg,ray, - DG_MATERIALID - ); - const float material_opacity = tachyon->textureArray[dg.materialID].opacity; - alpha = alpha * (1.f-material_opacity); - - if (alpha * weight < ALPHA_THRESHOLD) return alpha; - - max_depth--; - if (max_depth <= 0) - return alpha; - - ray.t0 = ray.t+EPS; - ray.t = org_t_max; - ray.primID = -1; - ray.geomID = -1; - ray.instID = -1; - } -} - -inline vec3f shade(Ray &ray, uniform TachyonRenderer *uniform tachyon) -{ - vec3f color = make_vec3f(0.f); - float path_opacity = 1.f; - int max_depth = 10; // max number of rays to be traced... - uniform int depth = 0; - while (1) { - traceRay(tachyon->super.model,ray); - if (ray.geomID < 0) { - vec3f bgColor = make_vec3f(1.f); - // if (depth == 0) - // return bgColor; - - color = color + path_opacity * bgColor; - return color; - } - ++depth; - - vec3f localShadeColor = make_vec3f(0.f); - DifferentialGeometry dg; - postIntersect(tachyon->super.model,dg,ray, - DG_NG|DG_NS|DG_NORMALIZE|DG_FACEFORWARD - |DG_MATERIALID|DG_COLOR - ); - const float material_opacity = tachyon->textureArray[dg.materialID].opacity; - const float local_opacity = path_opacity * material_opacity; - - if (local_opacity > .01f) { - // enough weight here to shade at all ... - - TachyonTexture texture = tachyon->textureArray[dg.materialID]; - if (dg.color.x >= 0.f) - texture.color = texture.color * make_vec3f(dg.color); - - // reflective direction, for phong highlight - const vec3f R = ray.dir - (2.f*dot(ray.dir,dg.Ns))*dg.Ns; - - // ------------------------------------------------------- - // ambient - // ------------------------------------------------------- - const vec3f P = ray.org+(ray.t*(ray.dir*(1.f-EPS))+EPS*dg.Ng); - color = color + local_opacity * texture.ambient * texture.color; - - // ------------------------------------------------------- - // point lights - // ------------------------------------------------------- - for (uniform int i=0;inumPointLights;i++) { - uniform PointLight *uniform l = &tachyon->pointLight[i]; - float dist; - const vec3f L = normalize(l->center - P,dist); - const float attenuation - = l->att.constant+dist*(l->att.linear+dist*l->att.quadratic); - - const float cosNL = abs(dot(L,dg.Ns)); - const float cosLR = max(0.f,dot(L,R)); - const float diffuse = texture.diffuse * cosNL; - const float specular = (texture.specular//+texture.phong.plastic - )* powf(cosLR,texture.phong.size); - const vec3f unshaded_light_contrib - = local_opacity - * (diffuse*texture.color+make_vec3f(specular)) - * tachyon->lightScale * l->color * rcpf(attenuation); - - const float max_contrib = reduce_max(unshaded_light_contrib); - if (max_contrib < .01f) - continue; - - Ray shadowRay; - setRay(shadowRay,P,L,EPS,dist*(1.f-EPS)-EPS); - const float light_alpha = lightAlpha(shadowRay,tachyon,max_contrib); - - localShadeColor = localShadeColor + light_alpha * unshaded_light_contrib; - } - - // ------------------------------------------------------- - // dir lights - // ------------------------------------------------------- - for (uniform int i=0;inumDirLights;i++) { - uniform DirLight *uniform l = &tachyon->dirLight[i]; - const vec3f L = normalize(neg(l->dir)); - - const float cosNL = abs(dot(L,dg.Ns)); - const float cosLR = max(0.f,dot(L,R)); - const float diffuse = texture.diffuse * cosNL; - const float specular = (texture.specular+texture.phong.plastic) - * powf(cosLR,texture.phong.size); - const vec3f unshaded_light_contrib - = local_opacity - * (diffuse*texture.color+make_vec3f(specular)) - * tachyon->lightScale * l->color; - - const float max_contrib = reduce_max(unshaded_light_contrib); - if (max_contrib < .01f) - continue; - - Ray shadowRay; - setRay(shadowRay,P,L,EPS,infinity); - const float light_alpha = lightAlpha(shadowRay,tachyon,max_contrib); - - localShadeColor = localShadeColor + light_alpha * unshaded_light_contrib; - } - - color = color + localShadeColor; - } - - // done shading this hitpoint, determine opacity of remaing path ... - path_opacity = path_opacity * (1.f-material_opacity); - if (path_opacity < .01f) - // kill path if remaining contribution too low. - return color; - - max_depth--; - if (max_depth <= 0) - return color; - - ray.t0 = ray.t+EPS; - ray.t = infinity; - ray.primID = -1; - ray.geomID = -1; - ray.instID = -1; - } - - return color; -} - -void TachyonRenderer_renderSample(uniform Renderer *uniform _renderer, - void *uniform perFrameData, - varying ScreenSample &sample) -{ - uniform TachyonRenderer *uniform renderer = (uniform TachyonRenderer *uniform)_renderer; - sample.rgb = shade(sample.ray,renderer); -} - -export void *uniform TachyonRenderer_create(void *uniform cppE) -{ - uniform TachyonRenderer *uniform renderer = uniform new uniform TachyonRenderer; - Renderer_Constructor(&renderer->super,cppE); - renderer->super.renderSample = TachyonRenderer_renderSample; - return renderer; -} - -export void TachyonRenderer_set(void *uniform _renderer, - void *uniform _model, - void *uniform _camera, - void *uniform _textureArray, - void *uniform _pointLight, - uniform int32 numPointLights, - void *uniform _dirLight, - uniform int32 numDirLights, - uniform bool doShadows, - uniform float lightScale) -{ - uniform TachyonRenderer *uniform renderer = (uniform TachyonRenderer *uniform)_renderer; - renderer->super.model = (uniform Model *uniform)_model; - renderer->super.camera = (uniform Camera *uniform)_camera; - - renderer->textureArray = (uniform TachyonTexture *uniform)_textureArray; - renderer->numPointLights = numPointLights; - renderer->pointLight = (uniform PointLight *uniform)_pointLight; - renderer->numDirLights = numDirLights; - renderer->dirLight = (uniform DirLight *uniform)_dirLight; - renderer->doShadows = doShadows; - renderer->lightScale = lightScale; -} - - diff --git a/modules/tachyon/Viewer.cpp b/modules/tachyon/Viewer.cpp deleted file mode 100644 index 50ad0b3040..0000000000 --- a/modules/tachyon/Viewer.cpp +++ /dev/null @@ -1,391 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#undef NDEBUG - -// viewer widget -#include "apps/common/widgets/glut3D.h" -// ospray, for rendering -#include "ospray/ospray.h" -// tachyon module -#include "Model.h" - -namespace ospray { - namespace tachyon { - using std::cout; - using std::endl; - - float lightScale = 1.f; - const char *renderType = "tachyon"; - // const char *renderType = "ao16"; - float defaultRadius = .1f; - - struct TimeStep { - std::string modelName; - tachyon::Model tm; // tachyon model - OSPModel om; // ospray model - - TimeStep(const std::string &modelName) : modelName(modelName), om(NULL) {}; - }; - - std::vector timeStep; - int timeStepID = 0; - bool doShadows = true; - - void error(const std::string &vol) - { - if (vol != "") { - cout << "=======================================================" << endl; - cout << "ospray::tachView fatal error : " << vol << endl; - cout << "=======================================================" << endl; - } - cout << endl; - cout << "Proper usage: " << endl; - cout << " ./ospTachyon " << std::endl; - cout << "options:" << endl; - cout << " " << endl; - cout << endl; - exit(1); - } - - OSPModel specifyModel(tachyon::Model &tm) - { - OSPModel ospModel = ospNewModel(); - - if (tm.numSpheres()) { - OSPData sphereData = ospNewData(tm.numSpheres()*sizeof(Sphere)/sizeof(float), - OSP_FLOAT,tm.getSpheresPtr()); - ospCommit(sphereData); - OSPGeometry sphereGeom = ospNewGeometry("spheres"); - ospSetData(sphereGeom,"spheres",sphereData); - ospSet1i(sphereGeom,"bytes_per_sphere",sizeof(Sphere)); - ospSet1i(sphereGeom,"offset_materialID",0*sizeof(float)); - ospSet1i(sphereGeom,"offset_center",1*sizeof(float)); - ospSet1i(sphereGeom,"offset_radius",4*sizeof(float)); - ospCommit(sphereGeom); - ospAddGeometry(ospModel,sphereGeom); - } - - if (tm.numCylinders()) { - OSPData cylinderData = ospNewData(tm.numCylinders()*sizeof(Cylinder)/sizeof(float), - OSP_FLOAT,tm.getCylindersPtr()); - ospCommit(cylinderData); - OSPGeometry cylinderGeom = ospNewGeometry("cylinders"); - ospSetData(cylinderGeom,"cylinders",cylinderData); - ospSet1i(cylinderGeom,"bytes_per_cylinder",sizeof(Cylinder)); - ospSet1i(cylinderGeom,"offset_materialID",0*sizeof(float)); - ospSet1i(cylinderGeom,"offset_v0",1*sizeof(float)); - ospSet1i(cylinderGeom,"offset_v1",4*sizeof(float)); - ospSet1i(cylinderGeom,"offset_radius",7*sizeof(float)); - ospCommit(cylinderGeom); - ospAddGeometry(ospModel,cylinderGeom); - } - - cout << "#osp:tachyon: creating " << tm.numVertexArrays() << " vertex arrays" << endl; - long numTriangles = 0; - for (int vaID=0;vaIDtriangle.size(); - if (va->triangle.size()) { - OSPData data = ospNewData(va->triangle.size(),OSP_INT3,&va->triangle[0]); - ospCommit(data); - ospSetData(geom,"triangle",data); - } - if (va->coord.size()) { - OSPData data = ospNewData(va->coord.size(),OSP_FLOAT3A,&va->coord[0]); - ospCommit(data); - ospSetData(geom,"vertex",data); - } - if (va->normal.size()) { - OSPData data = ospNewData(va->normal.size(),OSP_FLOAT3A,&va->normal[0]); - ospCommit(data); - ospSetData(geom,"vertex.normal",data); - } - if (va->color.size()) { - OSPData data = ospNewData(va->color.size(),OSP_FLOAT3A,&va->color[0]); - ospCommit(data); - ospSetData(geom,"vertex.color",data); - } - if (va->perTriTextureID.size()) { - OSPData data = ospNewData(va->perTriTextureID.size(),OSP_UINT, - &va->perTriTextureID[0]); - ospCommit(data); - ospSetData(geom,"prim.materialID",data); - } else { - ospSet1i(geom,"geom.materialID",va->textureID); - } - ospCommit(geom); - ospAddGeometry(ospModel,geom); - } - - - cout << "#osp:tachyon: specifying " << tm.numTextures() << " materials..." << endl; - { - OSPData data = ospNewData(tm.numTextures()*sizeof(Texture), - OSP_UCHAR,tm.getTexturesPtr()); - ospCommit(data); - ospSetData(ospModel,"textureArray",data); - } - - cout << "#osp:tachyon: specifying " << tm.numPointLights() - << " point lights..." << endl; - if (tm.numPointLights() > 0) - { - OSPData data - = ospNewData(tm.numPointLights()*sizeof(PointLight), - OSP_UCHAR,tm.getPointLightsPtr()); - ospCommit(data); - ospSetData(ospModel,"pointLights",data); - } - - cout << "#osp:tachyon: specifying " << tm.numDirLights() - << " dir lights..." << endl; - if (tm.numDirLights() > 0) - { - OSPData data - = ospNewData(tm.numDirLights()*sizeof(DirLight), - OSP_UCHAR,tm.getDirLightsPtr()); - ospCommit(data); - ospSetData(ospModel,"dirLights",data); - } - - std::cout << "=======================================" << std::endl; - std::cout << "Tachyon Renderer: Done specifying model" << std::endl; - std::cout << "num spheres: " << tm.numSpheres() << std::endl; - std::cout << "num cylinders: " << tm.numCylinders() << std::endl; - std::cout << "num triangles: " << numTriangles << std::endl; - std::cout << "=======================================" << std::endl; - - - ospCommit(ospModel); - return ospModel; - } - - using ospray::glut3D::Glut3DWidget; - - //! volume viewer widget. - /*! \internal Note that all handling of camera is almost exactly - similar to the code in msgView; might make sense to move that into - a common class! */ - struct TACHViewer : public Glut3DWidget { - /*! construct volume from file name and dimensions \see volview_notes_on_volume_interface */ - TACHViewer(OSPModel model, const std::string &modelName) - : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), - fb(NULL), renderer(NULL), model(model), modelName(modelName) - { - camera = ospNewCamera("perspective"); - Assert2(camera,"could not create camera"); - ospSet3f(camera,"pos",-1,1,-1); - ospSet3f(camera,"dir",+1,-1,+1); - ospCommit(camera); - ospCommit(camera); - - renderer = ospNewRenderer(renderType); - ospSet1i(renderer, "aoSamples", 1); - ospSet1i(renderer, "shadowsEnabled", 1); - - Assert2(renderer,"could not create renderer"); - ospSetObject(renderer,"model",model); - ospSetObject(renderer,"camera",camera); - ospSet1i(renderer,"do_shadows",doShadows); - ospCommit(renderer); - - } - - void keypress(char key, const vec2i &where) override - { - switch (key) { - case 'X': - if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(1,0,0); - viewPort.modified = true; - break; - case 'Y': - if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(0,1,0); - viewPort.modified = true; - break; - case 'Z': - if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) - viewPort.up = - viewPort.up; - else - viewPort.up = vec3f(0,0,1); - viewPort.modified = true; - break; - case 'S': - doShadows = !doShadows; - cout << "Switching shadows " << (doShadows?"ON":"OFF") << endl; - ospSet1i(renderer,"do_shadows",doShadows); - ospCommit(renderer); - break; - case 'L': - lightScale *= 1.5f; - ospSet1f(renderer,"lightScale",lightScale); - PRINT(lightScale); - PRINT(renderer); - ospCommit(renderer); - break; - case 'l': - lightScale /= 1.5f; - PRINT(lightScale); - PRINT(renderer); - ospSet1f(renderer,"lightScale",lightScale); - ospCommit(renderer); - break; - case '<': - setTimeStep((timeStepID+timeStep.size()-1)%timeStep.size()); - break; - case '>': - setTimeStep((timeStepID+1)%timeStep.size()); - break; - default: - Glut3DWidget::keypress(key,where); - } - } - - void setTimeStep(size_t newTSID) - { - timeStepID = newTSID; - modelName = timeStep[timeStepID]->modelName; - cout << "#osp:tachyon: switching to time step " << timeStepID - << " (" << modelName << ")" << endl; - model = timeStep[timeStepID]->om; - ospSetObject(renderer,"model",model); - ospCommit(renderer); - } - - - virtual void reshape(const ospray::vec2i &_newSize) - { - Glut3DWidget::reshape(_newSize); - if (fb) ospFreeFrameBuffer(fb); - const auto &newSize = reinterpret_cast(_newSize); - fb = ospNewFrameBuffer(newSize, OSP_FB_SRGBA, OSP_FB_COLOR|OSP_FB_ACCUM); - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - } - - virtual void display() - { - if (!fb || !renderer) return; - - if (viewPort.modified) { - Assert2(camera,"ospray camera is null"); - - // PRINT(viewPort); - - auto from = reinterpret_cast(viewPort.from); - auto dir = viewPort.at-viewPort.from; - auto up = reinterpret_cast(viewPort.up); - ospSetVec3f(camera,"pos",from); - ospSetVec3f(camera,"dir",reinterpret_cast(dir)); - ospSetVec3f(camera,"up",up); - ospSetf(camera,"aspect",viewPort.aspect); - ospCommit(camera); - viewPort.modified = false; - ospFrameBufferClear(fb,OSP_FB_COLOR|OSP_FB_ACCUM); - } - - fps.startRender(); - ospRenderFrame(fb,renderer,OSP_FB_COLOR|OSP_FB_ACCUM); - fps.doneRender(); - - ucharFB = (unsigned int *)ospMapFrameBuffer(fb); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - Glut3DWidget::display(); - - ospUnmapFrameBuffer(ucharFB,fb); - -#if 1 - char title[10000]; - sprintf(title,"ospray Tachyon viewer (%s) [%f fps]", - modelName.c_str(),fps.getFPS()); - setTitle(title); -#endif - - forceRedraw(); - } - - OSPModel model; - OSPFrameBuffer fb; - OSPRenderer renderer; - OSPCamera camera; - ospray::glut3D::FPSCounter fps; - std::string modelName; - }; - - void ospTACHMain(int &ac, const char **&av) - { - ospLoadModule("tachyon"); - - for (int i=1;i modelHandle; - for (int i=0;itm,ts->modelName); - if (ts->tm.empty()) - error(ts->modelName+": no input geometry specified!?"); - ts->om = specifyModel(ts->tm); - } - - - // ------------------------------------------------------- - // parse and set up input(s) - // ------------------------------------------------------- - - // ------------------------------------------------------- - // create viewer window - // ------------------------------------------------------- - TACHViewer window(timeStep[0]->om,timeStep[0]->modelName); - window.create("ospTACH: OSPRay Tachyon-model viewer"); - printf("Viewer created. Press 'Q' to quit.\n"); - window.setWorldBounds(timeStep[0]->tm.getBounds()); - ospray::tachyon::Camera *camera = timeStep[0]->tm.getCamera(); - if (camera) { - window.viewPort.from = camera->center; - window.viewPort.at = camera->center+camera->viewDir; - window.viewPort.up = camera->upDir; - window.computeFrame(); - } - ospray::glut3D::runGLUT(); - } - } -} - -int main(int ac, const char **av) -{ - ospInit(&ac,av); - ospray::glut3D::initGLUT(&ac,av); - ospray::tachyon::ospTACHMain(ac,av); -} diff --git a/common/AffineSpace.h b/ospcommon/AffineSpace.h similarity index 100% rename from common/AffineSpace.h rename to ospcommon/AffineSpace.h diff --git a/common/CMakeLists.txt b/ospcommon/CMakeLists.txt similarity index 79% rename from common/CMakeLists.txt rename to ospcommon/CMakeLists.txt index 40db5afebe..f67d5d4420 100644 --- a/common/CMakeLists.txt +++ b/ospcommon/CMakeLists.txt @@ -14,12 +14,18 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - SET(CMAKE_THREAD_PREFER_PTHREAD TRUE) FIND_PACKAGE(Threads REQUIRED) -OSPRAY_ADD_LIBRARY(ospray_common SHARED +SET(LINK_LIBS + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS} +) +IF (WIN32) + LIST(APPEND LINK_LIBS ws2_32) +ENDIF() + +OSPRAY_CREATE_LIBRARY(common common.cpp FileName.cpp sysinfo.cpp @@ -38,15 +44,26 @@ OSPRAY_ADD_LIBRARY(ospray_common SHARED Quaternion.h RefCount.h vec.h +LINK + ${LINK_LIBS} ) -OSPRAY_LIBRARY_LINK_LIBRARIES(ospray_common - ${CMAKE_THREAD_LIBS_INIT} - ${CMAKE_DL_LIBS} +OSPRAY_INSTALL_SDK_HEADERS( + AffineSpace.h + box.h + common.h + constants.h + FileName.h + intrinsics.h + library.h + LinearSpace.h + malloc.h + math.h + platform.h + Quaternion.h + RefCount.h + sysinfo.h + thread.h + vec.h + DESTINATION ../ospcommon #NOTE: this is "next" to the SDK/ directory ) -IF (WIN32) - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray_common ws2_32) -ENDIF() - -OSPRAY_SET_LIBRARY_VERSION(ospray_common) -OSPRAY_INSTALL_LIBRARY(ospray_common) diff --git a/common/FileName.cpp b/ospcommon/FileName.cpp similarity index 100% rename from common/FileName.cpp rename to ospcommon/FileName.cpp diff --git a/common/FileName.h b/ospcommon/FileName.h similarity index 100% rename from common/FileName.h rename to ospcommon/FileName.h diff --git a/common/LinearSpace.h b/ospcommon/LinearSpace.h similarity index 100% rename from common/LinearSpace.h rename to ospcommon/LinearSpace.h diff --git a/common/Quaternion.h b/ospcommon/Quaternion.h similarity index 100% rename from common/Quaternion.h rename to ospcommon/Quaternion.h diff --git a/common/RefCount.h b/ospcommon/RefCount.h similarity index 100% rename from common/RefCount.h rename to ospcommon/RefCount.h diff --git a/common/bak.vec.h b/ospcommon/bak.vec.h similarity index 100% rename from common/bak.vec.h rename to ospcommon/bak.vec.h diff --git a/common/box.h b/ospcommon/box.h similarity index 81% rename from common/box.h rename to ospcommon/box.h index 8659980e1d..8c3f6fef43 100644 --- a/common/box.h +++ b/ospcommon/box.h @@ -20,7 +20,7 @@ namespace ospcommon { - /*! over over scalar type T and N dimensions */ + /*! over scalar type T and N dimensions */ template struct box_t { typedef T scalar_t; @@ -51,10 +51,36 @@ namespace ospcommon { template inline scalar_t area(const box_t &b) { return b.size().product(); } + template + inline scalar_t area(const box_t &b) + { + const typename box_t::vec_t size = b.size(); + return 2.f*(size.x*size.y+size.x*size.z+size.y*size.z); + } + /*! return the volume of the 3D box - undefined for empty boxes */ template inline scalar_t volume(const box_t &b) { return b.size().product(); } + /*! computes whether two boxes are either touching OR overlapping; + ie, the case where boxes just barely touch side-by side (even if + they do not have any actual overlapping _volume_!) then this is + still true */ + template + inline bool touchingOrOverlapping(const box_t &a, + const box_t &b) + { + if (a.lower.x > b.upper.x) return false; + if (a.lower.y > b.upper.y) return false; + if (a.lower.z > b.upper.z) return false; + + if (b.lower.x > a.upper.x) return false; + if (b.lower.y > a.upper.y) return false; + if (b.lower.z > a.upper.z) return false; + + return true; + } + /*! compute the intersection of two boxes */ template diff --git a/common/common.cpp b/ospcommon/common.cpp similarity index 100% rename from common/common.cpp rename to ospcommon/common.cpp diff --git a/common/common.h b/ospcommon/common.h similarity index 93% rename from common/common.h rename to ospcommon/common.h index e6d9646f6d..a59c513446 100644 --- a/common/common.h +++ b/ospcommon/common.h @@ -98,4 +98,11 @@ namespace ospcommon { return result; } + // NOTE(jda) - Implement make_unique() as it didn't show up until C++14... + template + inline std::unique_ptr make_unique(Args&& ...args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } + } // ::ospcommon diff --git a/common/constants.h b/ospcommon/constants.h similarity index 100% rename from common/constants.h rename to ospcommon/constants.h diff --git a/common/intrinsics.h b/ospcommon/intrinsics.h similarity index 99% rename from common/intrinsics.h rename to ospcommon/intrinsics.h index 1ced7ac03d..47e734e653 100644 --- a/common/intrinsics.h +++ b/ospcommon/intrinsics.h @@ -73,7 +73,6 @@ #endif #ifdef _WIN32 -# define NOMINMAX # include #endif diff --git a/common/library.cpp b/ospcommon/library.cpp similarity index 92% rename from common/library.cpp rename to ospcommon/library.cpp index 1359b1c3c0..1e931294f3 100644 --- a/common/library.cpp +++ b/ospcommon/library.cpp @@ -56,8 +56,17 @@ namespace ospcommon { } #endif - if (lib == NULL) + if (lib == NULL) { +#ifdef _WIN32 + // TODO: Must use GetLastError and FormatMessage on windows + // to log out the error that occurred when calling LoadLibrary throw std::runtime_error("could not open module lib "+name); +#else + std::string error = dlerror(); + throw std::runtime_error("could not open module lib "+name + +" due to "+error); +#endif + } } Library::Library(void* const lib) : lib(lib) {}; diff --git a/common/library.h b/ospcommon/library.h similarity index 100% rename from common/library.h rename to ospcommon/library.h diff --git a/common/malloc.cpp b/ospcommon/malloc.cpp similarity index 100% rename from common/malloc.cpp rename to ospcommon/malloc.cpp diff --git a/ospcommon/malloc.h b/ospcommon/malloc.h new file mode 100644 index 0000000000..c82461662b --- /dev/null +++ b/ospcommon/malloc.h @@ -0,0 +1,42 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "common.h" + +namespace ospcommon +{ +#define ALIGN_PTR(ptr,alignment) \ + ((((size_t)ptr)+alignment-1)&((size_t)-(ssize_t)alignment)) + + /*! aligned allocation */ + OSPCOMMON_INTERFACE void* alignedMalloc(size_t size, size_t align = 64); + OSPCOMMON_INTERFACE void alignedFree(void* ptr); + +// NOTE(jda) - can't use function wrapped alloca solution as Clang won't inline +// a function containing alloca()...but it works with gcc/icc +#if 0 + template + __forceinline T* stackBuffer(size_t nElements) + { + return static_cast(alloca(sizeof(T) * nElements)); + } +#else +# define STACK_BUFFER(TYPE, nElements) (TYPE*)alloca(sizeof(TYPE)*nElements) +#endif +} + diff --git a/common/math.h b/ospcommon/math.h similarity index 100% rename from common/math.h rename to ospcommon/math.h diff --git a/common/platform.h b/ospcommon/platform.h similarity index 100% rename from common/platform.h rename to ospcommon/platform.h diff --git a/common/sysinfo.cpp b/ospcommon/sysinfo.cpp similarity index 100% rename from common/sysinfo.cpp rename to ospcommon/sysinfo.cpp diff --git a/common/sysinfo.h b/ospcommon/sysinfo.h similarity index 100% rename from common/sysinfo.h rename to ospcommon/sysinfo.h diff --git a/common/thread.cpp b/ospcommon/thread.cpp similarity index 100% rename from common/thread.cpp rename to ospcommon/thread.cpp diff --git a/common/thread.h b/ospcommon/thread.h similarity index 100% rename from common/thread.h rename to ospcommon/thread.h diff --git a/common/vec.cpp b/ospcommon/vec.cpp similarity index 100% rename from common/vec.cpp rename to ospcommon/vec.cpp diff --git a/common/vec.h b/ospcommon/vec.h similarity index 95% rename from common/vec.h rename to ospcommon/vec.h index 32748ebc26..18f77f04c5 100644 --- a/common/vec.h +++ b/ospcommon/vec.h @@ -297,6 +297,7 @@ namespace ospcommon { inline bool operator!=(const vec_t &a, const vec_t &b) { return !(a==b); } + /*! comparison operators; we need those to be able to put vec's in std::map etc @{ */ template inline bool operator<(const vec_t &a, const vec_t &b) { @@ -321,6 +322,7 @@ namespace ospcommon { ((a.y==b.y) && ((a.z< b.z) || ((a.z==b.z) && (a.w < b.w)))))); } + /*! @} */ // 'anyLessThan' - return true if any component is less than the other vec's template @@ -394,18 +396,18 @@ namespace ospcommon { // ------------------------------------------------------- // binary functors // ------------------------------------------------------- -#define define_functor(f) \ - template \ - inline vec_t f(const vec_t &a, const vec_t &b) \ - { return vec_t(f(a.x,b.x),f(a.y,b.y)); } \ - \ - template \ - inline vec_t f(const vec_t &a, const vec_t &b) \ - { return vec_t(f(a.x,b.x),f(a.y,b.y),f(a.z,b.z)); } \ - \ +#define define_functor(f) \ template \ - inline vec_t f(const vec_t &a, const vec_t &b) \ - { return vec_t(f(a.x,b.x),f(a.y,b.y),f(a.z,b.z),f(a.w,b.w)); } \ + inline vec_t f(const vec_t &a, const vec_t &b) \ + { return vec_t(f(a.x,b.x),f(a.y,b.y)); } \ + \ + template \ + inline vec_t f(const vec_t &a, const vec_t &b) \ + { return vec_t(f(a.x,b.x),f(a.y,b.y),f(a.z,b.z)); } \ + \ + template \ + inline vec_t f(const vec_t &a, const vec_t &b) \ + { return vec_t(f(a.x,b.x),f(a.y,b.y),f(a.z,b.z),f(a.w,b.w)); } \ define_functor(min); define_functor(max); @@ -504,12 +506,12 @@ namespace ospcommon { // ------------------------------------------------------- // parsing from strings // ------------------------------------------------------- - vec2f toVec2f(const char *ptr); - vec3f toVec3f(const char *ptr); - vec4f toVec4f(const char *ptr); - vec2i toVec2i(const char *ptr); - vec3i toVec3i(const char *ptr); - vec4i toVec4i(const char *ptr); + OSPCOMMON_INTERFACE vec2f toVec2f(const char *ptr); + OSPCOMMON_INTERFACE vec3f toVec3f(const char *ptr); + OSPCOMMON_INTERFACE vec4f toVec4f(const char *ptr); + OSPCOMMON_INTERFACE vec2i toVec2i(const char *ptr); + OSPCOMMON_INTERFACE vec3i toVec3i(const char *ptr); + OSPCOMMON_INTERFACE vec4i toVec4i(const char *ptr); } // ::ospcommon diff --git a/ospray/CMakeLists.txt b/ospray/CMakeLists.txt index 2555698399..53b9b018ff 100644 --- a/ospray/CMakeLists.txt +++ b/ospray/CMakeLists.txt @@ -14,10 +14,8 @@ ## limitations under the License. ## ## ======================================================================== ## -CONFIGURE_OSPRAY() - IF(OSPRAY_BUILD_MPI_DEVICE) - OPTION(OSPRAY_EXP_DATA_PARALLEL "Experimental data-parallel compositing mode") + OPTION(OSPRAY_EXP_DATA_PARALLEL "Experimental data-parallel compositing mode" ON) OPTION(OSPRAY_PIN_ASYNC "Pin async mpi comm threads?" OFF) MARK_AS_ADVANCED(OSPRAY_PIN_ASYNC) ENDIF() @@ -35,6 +33,21 @@ FIND_PACKAGE(Threads REQUIRED) SET(REQUIRED_MINIMUM_EMBREE 2.7.1) +# Do a check to see if we can find the system Embree +IF(NOT DEFINED LAST_CONFIG_USED_EXTERNAL_EMBREE) + FIND_PACKAGE(embree ${REQUIRED_MINIMUM_EMBREE} QUIET) + IF(NOT DEFINED EMBREE_INCLUDE_DIRS) + MESSAGE(WARNING + "We did not find Embree installed on your system. If you would" + " like to use a newer version of Embree than the one in the" + " OSPRay source tree (v2.7.1), please download and extract or" + " compile Embree from source, set the 'embree_DIR' environment" + " variable to where it is installed, and re-enable the" + " OSPRAY_USE_EXTERNAL_EMBREE CMake option.") + SET(OSPRAY_USE_EXTERNAL_EMBREE OFF CACHE BOOL "" FORCE) + ENDIF() +ENDIF() + IF(OSPRAY_USE_EXTERNAL_EMBREE) # Clear out embree directories if they were previously populated by an # internal build @@ -89,8 +102,20 @@ ELSE() SET(LAST_CONFIG_USED_EXTERNAL_EMBREE OFF CACHE INTERNAL "" FORCE) ENDIF() -INCLUDE_DIRECTORIES(${EMBREE_INCLUDE_DIRS}) -INCLUDE_DIRECTORIES_ISPC(${EMBREE_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/ospray/include + ${CMAKE_SOURCE_DIR}/ospray + ${CMAKE_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + ${EMBREE_INCLUDE_DIRS} +) +INCLUDE_DIRECTORIES_ISPC( + ${CMAKE_SOURCE_DIR}/ospray/include + ${CMAKE_SOURCE_DIR}/ospray + ${CMAKE_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + ${EMBREE_INCLUDE_DIRS} +) SET(EMBREE_INCLUDE_DIRS ${EMBREE_INCLUDE_DIRS} PARENT_SCOPE) SET(EMBREE_LIBRARIES ${EMBREE_LIBRARIES} PARENT_SCOPE) @@ -133,6 +158,7 @@ SET(OSPRAY_SOURCES fb/LocalFB.ispc fb/LocalFB.cpp fb/PixelOp.cpp + fb/Tile.h camera/Camera.cpp camera/PerspectiveCamera.ispc @@ -242,7 +268,141 @@ SET(OSPRAY_SOURCES api/API.cpp api/Device.cpp api/LocalDevice.cpp - ) +) + +# ------------------------------------------------------- +# Install API headers +# ------------------------------------------------------- + +OSPRAY_INSTALL_SDK_HEADERS( + camera/Camera.h + camera/Camera.ih + camera/OrthographicCamera.h + camera/OrthographicCamera.ih + camera/PanoramicCamera.h + camera/PanoramicCamera.ih + camera/PerspectiveCamera.h + camera/PerspectiveCamera.ih + DESTINATION camera +) + +OSPRAY_INSTALL_SDK_HEADERS( + common/Core.h + common/Data.h + common/DifferentialGeometry.ih + common/Library.h + common/Managed.h + common/Material.h + common/Material.ih + common/Model.h + common/Model.ih + common/ObjectHandle.h + common/OSPCommon.h + common/OSPCommon.ih + common/Ray.h + common/Ray.ih + common/Texture.h + common/Thread.h + DESTINATION common +) + +OSPRAY_INSTALL_SDK_HEADERS( + common/tasking/async.h + common/tasking/parallel_for.h + common/tasking/TaskingTypeTraits.h + common/tasking/TaskSys.h + DESTINATION common/tasking +) + +OSPRAY_INSTALL_SDK_HEADERS( + fb/FrameBuffer.h + fb/FrameBuffer.ih + fb/PixelOp.h + fb/Tile.h + fb/Tile.ih + DESTINATION fb +) + +OSPRAY_INSTALL_SDK_HEADERS( + geometry/Cylinders.h + geometry/Geometry.h + geometry/Geometry.ih + geometry/Instance.h + geometry/Isosurfaces.h + geometry/Slices.h + geometry/Spheres.h + geometry/StreamLines.h + geometry/TriangleMesh.h + geometry/TriangleMesh.ih + DESTINATION geometry +) + +OSPRAY_INSTALL_SDK_HEADERS( + lights/AmbientLight.h + lights/DirectionalLight.h + lights/HDRILight.h + lights/Light.h + lights/Light.ih + lights/PointLight.h + lights/QuadLight.h + lights/SpotLight.h + DESTINATION lights +) + +OSPRAY_INSTALL_SDK_HEADERS( + #FIXME: check if all of these are used... + math/AffineSpace.ih + math/box.ih + math/Distribution2D.ih + math/LinearSpace.ih + math/math.ih + math/random.ih + math/region.ih + math/sampling.ih + math/vec.ih + DESTINATION math +) + +OSPRAY_INSTALL_SDK_HEADERS( + render/LoadBalancer.h + render/Renderer.h + render/Renderer.ih + render/util.h + render/util.ih + DESTINATION render +) + +#TODO: all the specific renderer headers... + +OSPRAY_INSTALL_SDK_HEADERS( + texture/Texture2D.h + texture/Texture2D.ih + texture/TextureParam.ih + DESTINATION texture +) + +OSPRAY_INSTALL_SDK_HEADERS( + transferFunction/LinearTransferFunction.h + transferFunction/LinearTransferFunction.ih + transferFunction/TransferFunction.h + transferFunction/TransferFunction.ih + DESTINATION transferFunction +) + +OSPRAY_INSTALL_SDK_HEADERS( + volume/BlockBrickedVolume.h + volume/BlockBrickedVolume.ih + volume/GridAccelerator.ih + volume/GhostBlockBrickedVolume.h + volume/GhostBlockBrickedVolume.ih + volume/SharedStructuredVolume.h + volume/SharedStructuredVolume.ih + volume/StructuredVolume.h + volume/StructuredVolume.ih + volume/Volume.h + volume/Volume.ih + DESTINATION volume +) # ------------------------------------------------------- # MPI components @@ -269,6 +429,7 @@ IF (OSPRAY_MPI) mpi/DistributedFrameBuffer.cpp mpi/DistributedFrameBuffer.ispc + mpi/DistributedFrameBuffer_TileTypes.cpp fb/DisplayWall.cpp ) @@ -364,13 +525,9 @@ ENDIF() ############################################################## IF (OSPRAY_MPI) IF (THIS_IS_MIC) - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray - ${MPI_LIBRARY_MIC} - ) + OSPRAY_LIBRARY_LINK_LIBRARIES(ospray ${MPI_LIBRARY_MIC}) ELSE() - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray - ${MPI_CXX_LIBRARIES} - ) + OSPRAY_LIBRARY_LINK_LIBRARIES(ospray ${MPI_CXX_LIBRARIES}) ENDIF() OSPRAY_ADD_EXECUTABLE(ospray_mpi_worker mpi/MPIWorker.cpp) @@ -387,9 +544,7 @@ IF (OSPRAY_BUILD_COI_DEVICE) # ------------------------------------------------------------ # dev-side of COI device: lib dev-side libospray to coi dev libs,... # ------------------------------------------------------------ - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray - ${LIBCOI_DEVICE} - ) + OSPRAY_LIBRARY_LINK_LIBRARIES(ospray ${LIBCOI_DEVICE}) # ... and add the coi worker executable OSPRAY_ADD_EXECUTABLE(ospray_coi_worker api/COIDeviceWorker.cpp) OSPRAY_EXE_LINK_LIBRARIES(ospray_coi_worker @@ -403,9 +558,7 @@ IF (OSPRAY_BUILD_COI_DEVICE) # ------------------------------------------------------------ # host-side of COI device: just link libospray to coi host libs # ------------------------------------------------------------ - OSPRAY_LIBRARY_LINK_LIBRARIES(ospray - ${LIBCOI_HOST} - ) + OSPRAY_LIBRARY_LINK_LIBRARIES(ospray ${LIBCOI_HOST}) # note: no need to add the host-side libcoi to the install # targets; it's already done above ENDIF() diff --git a/ospray/api/API.cpp b/ospray/api/API.cpp index 89850a8b80..6172704eab 100644 --- a/ospray/api/API.cpp +++ b/ospray/api/API.cpp @@ -14,15 +14,15 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/OSPCommon.h" -#include "ospray/include/ospray/ospray.h" -#include "ospray/render/Renderer.h" -#include "ospray/camera/Camera.h" -#include "ospray/common/Material.h" -#include "ospray/volume/Volume.h" -#include "ospray/transferFunction/TransferFunction.h" +#include "common/OSPCommon.h" +#include "include/ospray/ospray.h" +#include "render/Renderer.h" +#include "camera/Camera.h" +#include "common/Material.h" +#include "volume/Volume.h" +#include "transferFunction/TransferFunction.h" #include "LocalDevice.h" -#include "ospray/common/Core.h" +#include "common/Core.h" #ifdef _WIN32 # include // for getpid @@ -70,7 +70,7 @@ std::string getPidString() { return s; } -#define ASSERT_DEVICE() if (ospray::api::Device::current == NULL) \ +#define ASSERT_DEVICE() if (!ospray::api::Device::current) \ throw std::runtime_error("OSPRay not yet initialized " \ "(most likely this means you tried to " \ "call an ospray API function before " \ @@ -85,22 +85,24 @@ extern "C" void ospInit(int *_ac, const char **_av) "(did you call ospInit twice?)"); } - auto *nThreads = getenv("OSPRAY_THREADS"); - if (nThreads) { - numThreads = atoi(nThreads); + auto OSPRAY_THREADS = getEnvVar("OSPRAY_THREADS"); + if (OSPRAY_THREADS.first) { + numThreads = OSPRAY_THREADS.second; } /* call ospray::init to properly parse common args like --osp:verbose, --osp:debug etc */ ospray::init(_ac,&_av); - const char *OSP_MPI_LAUNCH_FROM_ENV = getenv("OSPRAY_MPI_LAUNCH"); + auto OSP_MPI_LAUNCH = getEnvVar("OSPRAY_MPI_LAUNCH"); - if (OSP_MPI_LAUNCH_FROM_ENV) { + if (OSP_MPI_LAUNCH.first) { #ifdef OSPRAY_MPI - std::cout << "#osp: launching ospray mpi ring - make sure that mpd is running" << std::endl; + std::cout << "#osp: launching ospray mpi ring -" + << " make sure that mpd is running" << std::endl; ospray::api::Device::current - = mpi::createMPI_LaunchWorkerGroup(_ac,_av,OSP_MPI_LAUNCH_FROM_ENV); + = mpi::createMPI_LaunchWorkerGroup(_ac,_av, + OSP_MPI_LAUNCH.second.c_str()); #else throw std::runtime_error("OSPRay MPI support not compiled in"); #endif @@ -172,7 +174,7 @@ extern "C" void ospInit(int *_ac, const char **_av) } // no device created on cmd line, yet, so default to localdevice - if (ospray::api::Device::current == NULL) { + if (!ospray::api::Device::current) { ospray::api::Device::current = new ospray::api::LocalDevice(_ac,_av); } } @@ -315,7 +317,7 @@ extern "C" OSPPixelOp ospNewPixelOp(const char *_type) Assert2(_type,"invalid render type identifier in ospNewPixelOp"); LOG("ospNewPixelOp(" << _type << ")"); int L = strlen(_type); - char *type = (char *)alloca(L+1); + char *type = STACK_BUFFER(char, L+1); for (int i=0;i<=L;i++) { char c = _type[i]; if (c == '-' || c == ':') @@ -424,7 +426,8 @@ extern "C" OSPVolume ospNewVolume(const char *type) OSPVolume volume = ospray::api::Device::current->newVolume(type); if (ospray::logLevel > 0) { if (volume) - cout << "ospNewVolume: " << ((ospray::Volume*)volume)->toString() << endl; + cout << "ospNewVolume: " << type << endl; + // cout << "ospNewVolume: " << ((ospray::Volume*)volume)->toString() << endl; else std::cerr << "#ospray: could not create volume '" << type << "'" << std::endl; } @@ -443,7 +446,7 @@ extern "C" OSPTransferFunction ospNewTransferFunction(const char *type) OSPTransferFunction transferFunction = ospray::api::Device::current->newTransferFunction(type); if(ospray::logLevel > 0) { if(transferFunction) - cout << "ospNewTransferFunction: " << ((ospray::TransferFunction*)transferFunction)->toString() << endl; + cout << "ospNewTransferFunction(" << type << ")" << endl; else std::cerr << "#ospray: could not create transfer function '" << type << "'" << std::endl; } @@ -659,87 +662,6 @@ extern "C" void ospSetMaterial(OSPGeometry geometry, OSPMaterial material) ospray::api::Device::current->setMaterial(geometry,material); } -//! Get the handle of the named data array associated with an object. -extern "C" int ospGetData(OSPObject object, const char *name, OSPData *value) { - ASSERT_DEVICE(); - return(ospray::api::Device::current->getData(object, name, value)); -} - -//! Get a copy of the data in an array (the application is responsible for freeing this pointer). -extern "C" int ospGetDataValues(OSPData object, void **pointer, size_t *count, OSPDataType *type) { - ASSERT_DEVICE(); - return(ospray::api::Device::current->getDataValues(object, pointer, count, type)); -} - -//! Get the named scalar floating point value associated with an object. -extern "C" int ospGetf(OSPObject object, const char *name, float *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getf(object, name, value)); -} - -//! Get the named scalar integer associated with an object. -extern "C" int ospGeti(OSPObject object, const char *name, int *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->geti(object, name, value)); -} - -//! Get the material associated with a geometry object. -extern "C" int ospGetMaterial(OSPGeometry geometry, OSPMaterial *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getMaterial(geometry, value)); -} - -//! Get the named object associated with an object. -extern "C" int ospGetObject(OSPObject object, const char *name, OSPObject *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getObject(object, name, value)); -} - -//! Retrieve a NULL-terminated list of the parameter names associated with an object. -extern "C" int ospGetParameters(OSPObject object, char ***value) { - ASSERT_DEVICE(); - return(ospray::api::Device::current->getParameters(object, value)); -} - -//! Get a pointer to a copy of the named character string associated with an object. -extern "C" int ospGetString(OSPObject object, const char *name, char **value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getString(object, name, value)); -} - -//! Get the type of the named parameter or the given object (if 'name' is NULL). -extern "C" int ospGetType(OSPObject object, const char *name, OSPDataType *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getType(object, name, value)); -} - -//! Get the named 2-vector floating point value associated with an object. -extern "C" int ospGetVec2f(OSPObject object, const char *name, osp::vec2f *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getVec2f(object, name, (vec2f *)value)); -} - -//! Get the named 3-vector floating point value associated with an object. -extern "C" int ospGetVec3f(OSPObject object, const char *name, osp::vec3f *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getVec3f(object, name, (vec3f *)value)); -} - -//! Get the named 3-vector integer value associated with an object. -extern "C" int ospGetVec3i(OSPObject object, const char *name, osp::vec3i *value) -{ - ASSERT_DEVICE(); - return(ospray::api::Device::current->getVec3i(object, name, (vec3i *)value)); -} - /*! \brief create a new instance geometry that instantiates another model. the resulting geometry still has to be added to another model via ospAddGeometry */ @@ -764,7 +686,8 @@ extern "C" void ospPick(OSPPickResult *result, ASSERT_DEVICE(); Assert2(renderer, "NULL renderer passed to ospPick"); if (!result) return; - *result = ospray::api::Device::current->pick(renderer, (const vec2f&)screenPos); + *result = ospray::api::Device::current->pick(renderer, + (const vec2f&)screenPos); } //! \brief allows for switching the MPI scope from "per rank" to "all ranks" @@ -780,8 +703,9 @@ extern "C" void ospPick(OSPPickResult *result, MPI_Init(), NOT "in addition to" */ extern "C" void ospdMpiInit(int *ac, char ***av, OSPDRenderMode mode) { - if (ospray::api::Device::current != NULL) + if (!ospray::api::Device::current) { throw std::runtime_error("#osp:mpi: OSPRay already initialized!?"); + } ospray::mpi::initDistributedAPI(ac,av,mode); } @@ -806,6 +730,7 @@ extern "C" void ospSampleVolume(float **results, return; } - ospray::api::Device::current->sampleVolume(results, volume, (vec3f*)&worldCoordinates, count); + ospray::api::Device::current->sampleVolume(results, volume, + (vec3f*)&worldCoordinates, count); } diff --git a/ospray/api/COIDeviceCommon.h b/ospray/api/COIDeviceCommon.h index 895c2fca86..4079f8ea53 100644 --- a/ospray/api/COIDeviceCommon.h +++ b/ospray/api/COIDeviceCommon.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.h" -#include "ospray/common/ObjectHandle.h" +#include "common/OSPCommon.h" +#include "common/ObjectHandle.h" namespace ospray { namespace coi { diff --git a/ospray/api/COIDeviceHost.cpp b/ospray/api/COIDeviceHost.cpp index 92cef5854f..f53f22f38e 100644 --- a/ospray/api/COIDeviceHost.cpp +++ b/ospray/api/COIDeviceHost.cpp @@ -19,7 +19,8 @@ // ospray #include "Device.h" #include "COIDeviceCommon.h" -#include "ospray/common/Data.h" +#include "common/Data.h" +#include "volume/Volume.h" // coi #include "common/COIResult_common.h" #include "source/COIEngine_source.h" @@ -47,11 +48,6 @@ namespace ospray { x(OSPCOI_NEW_DATA, "ospray_coi_new_data") \ x(OSPCOI_COMMIT, "ospray_coi_commit") \ x(OSPCOI_SET_VALUE, "ospray_coi_set_value") \ - x(OSPCOI_GET_DATA_PROPERTIES, "ospray_coi_get_data_properties") \ - x(OSPCOI_GET_DATA_VALUES, "ospray_coi_get_data_values") \ - x(OSPCOI_GET_PARAMETERS, "ospray_coi_get_parameters") \ - x(OSPCOI_GET_PARAMETERS_SIZE, "ospray_coi_get_parameters_size") \ - x(OSPCOI_GET_TYPE, "ospray_coi_get_type") \ x(OSPCOI_GET_VALUE, "ospray_coi_get_value") \ x(OSPCOI_NEW_MATERIAL, "ospray_coi_new_material") \ x(OSPCOI_SET_MATERIAL, "ospray_coi_set_material") \ @@ -275,69 +271,6 @@ namespace ospray { const char *bufName, void *v) override { NOTIMPLEMENTED; } - /*! Get the handle of the named data array associated with an object. */ - int getData(OSPObject object, - const char *name, - OSPData *value) override; - - /*! Get the type and count of the elements contained in the given array - * object.*/ - int getDataProperties(OSPData object, size_t *count, OSPDataType *type); - - /*! Get a copy of the data in an array (the application is responsible - * for freeing this pointer). */ - int getDataValues(OSPData object, void **pointer, - size_t *count, OSPDataType *type) override; - - /*! Get the named scalar floating point value associated with an object.*/ - int getf(OSPObject object, const char *name, float *value) override; - - /*! Get the named scalar integer associated with an object. */ - int geti(OSPObject object, const char *name, int *value) override; - - /*! Get the material associated with a geometry object. */ - int getMaterial(OSPGeometry geometry, OSPMaterial *value) override; - - /*! Get the named object associated with an object. */ - int getObject(OSPObject object, - const char *name, - OSPObject *value) override; - - /*! Retrieve a nullptr-terminated list of the parameter names associated - * with an object. */ - int getParameters(OSPObject object, char ***value) override; - - /*! Retrieve the total length of the names (with terminators) of the - * parameters associated with an object. */ - int getParametersSize(OSPObject object, int *value); - - /*! Get a pointer to a copy of the named character string associated - * with an object. */ - int getString(OSPObject object, - const char *name, - char **value) override; - - /*! Get the type of the named parameter or the given object (if 'name' - * is nullptr). */ - int getType(OSPObject object, - const char *name, - OSPDataType *value) override; - - /*! Get the named 2-vector floating point value associated with an - * object. */ - int getVec2f(OSPObject object, const char *name, vec2f *value) override; - - /*! Get the named 3-vector floating point value associated with an - * object. */ - int getVec3f(OSPObject object, const char *name, vec3f *value) override; - - /*! Get the named 4-vector floating point value associated with an - * object. */ - int getVec4f(OSPObject object, const char *name, vec4f *value) override; - - /*! Get the named 3-vector integer value associated with an object. */ - int getVec3i(OSPObject object, const char *name, vec3i *value) override; - /*! create a new renderer object (out of list of registered renderers) */ OSPRenderer newRenderer(const char *type) override; @@ -390,10 +323,14 @@ namespace ospray { OSPVolume volume, const vec3f *worldCoordinates, const size_t &count) override; + + private: + + /*! This only exists to support getting a volume type for setRegion */ + int getString(OSPObject object, const char *name, char **value); }; - /*! create this engine, initialize coi with this engine ID, print basic device info */ COIEngine::COIEngine(COIDevice *osprayDevice, size_t engineID) @@ -419,8 +356,9 @@ namespace ospray { void COIEngine::loadOSPRay() { COIRESULT result; - const char *coiWorker = getenv("OSPRAY_COI_WORKER"); - if (coiWorker == nullptr) { + auto OSPRAY_COI_WORKER = getEnvVar("OSPRAY_COI_WORKER"); + auto &coiWorker = OSPRAY_COI_WORKER.second; + if (!OSPRAY_COI_WORKER.first) { cerr << "Error: OSPRAY_COI_WORKER not defined." << endl; cerr << "Note: In order to run the OSPRay COI device on the Xeon" << " Phi(s) it needs to know the full path of the" @@ -431,8 +369,9 @@ namespace ospray { << " this executable." << endl; exit(1); } - const char *sinkLDPath = getenv("SINK_LD_LIBRARY_PATH"); - if (sinkLDPath == nullptr) { + auto SINK_LD_LIBRARY_PATH = + getEnvVar("SINK_LD_LIBRARY_PATH"); + if (!SINK_LD_LIBRARY_PATH.first) { cerr << "SINK_LD_LIBRARY_PATH not defined." << endl; cerr << "Note: In order for the COI version of OSPRay to find all" << " the shared libraries (ospray, plus whatever modules the" @@ -447,7 +386,7 @@ namespace ospray { } std::vector workerArgs; - workerArgs.push_back(coiWorker); + workerArgs.push_back(coiWorker.c_str()); if (ospray::debugMode) workerArgs.push_back("--osp:debug"); if (ospray::logLevel == 1) @@ -456,7 +395,7 @@ namespace ospray { workerArgs.push_back("--osp:vv"); result = COIProcessCreateFromFile(coiEngine, - coiWorker, + coiWorker.c_str(), workerArgs.size(), workerArgs.data(), false, nullptr, true,nullptr,/*proxy!*/ @@ -570,9 +509,9 @@ namespace ospray { cout << "#osp:coi: found " << numEngines << " COI engines" << endl; Assert(numEngines > 0); - char *maxEnginesFromEnv = getenv("OSPRAY_COI_MAX_ENGINES"); - if (maxEnginesFromEnv) { - numEngines = std::min((int)numEngines,(int)atoi(maxEnginesFromEnv)); + auto OSPRAY_COI_MAX_ENGINES = getEnvVar("OSPRAY_COI_MAX_ENGINES"); + if (OSPRAY_COI_MAX_ENGINES.first) { + numEngines = std::min((int)numEngines, OSPRAY_COI_MAX_ENGINES.second); cout << "#osp:coi: max engines after considering" << " 'OSPRAY_COI_MAX_ENGINES' : " << numEngines << endl; } @@ -1180,9 +1119,11 @@ namespace ospray { const vec3i &index, const vec3i &count) { Assert(object != nullptr && "invalid volume object handle"); + char *typeString = nullptr; getString(object, "voxelType", &typeString); OSPDataType type = typeForString(typeString); + Assert(type != OSP_UNKNOWN && "unknown volume element type"); OSPData data = newData(size_t(count.x) * count.y * count.z, type, (void*)source, OSP_DATA_SHARED_BUFFER); @@ -1273,219 +1214,17 @@ namespace ospray { callFunction(OSPCOI_SET_VALUE,args); } - /*! Get the handle of the named data array associated with an object. */ - int COIDevice::getData(OSPObject object, const char *name, OSPData *value) { - - struct ReturnValue { int success; ObjectHandle value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_DATA); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = (OSPData)(int64) result.value, true : false); - - } - - /*! Get the type and count of the elements contained in the given array - * object.*/ - int COIDevice::getDataProperties(OSPData object, - size_t *count, - OSPDataType *type) { - - struct ReturnValue - { - int success; - size_t count; - OSPDataType type; - } result; - - Assert(object != nullptr && "invalid data object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - callFunction(OSPCOI_GET_DATA_PROPERTIES, - stream, - &result, - sizeof(ReturnValue)); - - if (result.success) { - *count = result.count; - *type = result.type; - } - - return result.success; - } - - /*! Get a copy of the data in an array (the application is responsible - * for freeing this pointer). */ - int COIDevice::getDataValues(OSPData object, void **pointer, - size_t *count, OSPDataType *type) { - - if (getDataProperties(object, count, type) == false) return(false); - size_t size = *count * sizeOf(*type); - COIBUFFER coiBuffer = nullptr; - COIRESULT coiResult; - - coiResult = COIBufferCreate( - size, - COI_BUFFER_NORMAL, - size > 1024 * 1024 * 128 ? COI_OPTIMIZE_HUGE_PAGE_SIZE : 0, - nullptr, - 1, &engines[0]->coiProcess, - &coiBuffer - ); - - if (coiResult != COI_SUCCESS) { - coiError(coiResult, - "unable to create COI buffer in COIDevice::getDataValues"); - } - COI_ACCESS_FLAGS coiBufferFlags = COI_SINK_WRITE; - int result; - DataStream stream; - stream.write((ObjectHandle &) object); - - coiResult = COIPipelineRunFunction( - engines[0]->coiPipe, - engines[0]->coiFctHandle[OSPCOI_GET_DATA_VALUES], - 1, &coiBuffer, &coiBufferFlags, - 0, nullptr, - stream.buf, stream.ofs, - &result, sizeof(int), - nullptr - ); - - if (coiResult != COI_SUCCESS) { - coiError(coiResult, - "error during COIDevice::getDataValues run function"); - } - if (!result) { - COIBufferDestroy(coiBuffer); - return(false); - } - void *coiBufferPointer = nullptr; - *pointer = malloc(size); - COIMAPINSTANCE coiMapInstance; - - coiResult = COIBufferMap( - coiBuffer, - 0, 0, - COI_MAP_READ_ONLY, - 0, nullptr, - nullptr, - &coiMapInstance, - &coiBufferPointer - ); - - if (coiResult != COI_SUCCESS) { - coiError(coiResult, - "unable to map COI buffer in COIDevice::getDataValues"); - } - memcpy(*pointer, coiBufferPointer, size); - COIBufferUnmap(coiMapInstance, 0, nullptr, nullptr); - COIBufferDestroy(coiBuffer); return(true); - } - - /*! Get the named scalar floating point value associated with an object. */ - int COIDevice::getf(OSPObject object, const char *name, float *value) { - - struct ReturnValue { int success; float value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_FLOAT); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Get the named scalar integer associated with an object. */ - int COIDevice::geti(OSPObject object, const char *name, int *value) { - - struct ReturnValue { int success; int value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_INT); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Get the material associated with a geometry object. */ - int COIDevice::getMaterial(OSPGeometry object, OSPMaterial *value) { - - struct ReturnValue { int success; ObjectHandle value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write("\0"); - stream.write(OSP_MATERIAL); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = (OSPMaterial)(int64) result.value, true : false); - - } - - /*! Get the named object associated with an object. */ - int COIDevice::getObject(OSPObject object, - const char *name, - OSPObject *value) { - - struct ReturnValue { int success; ObjectHandle value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_OBJECT); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return result.success ? *value = (OSPObject)(int64)result.value, true : false; - } - - /*! Retrieve a nullptr-terminated list of the parameter names associated with an object. */ - int COIDevice::getParameters(OSPObject object, char ***value) { - - int size = 0; getParametersSize(object, &size); - struct ReturnValue { int success; int value; }; - ReturnValue *result = (ReturnValue *) malloc(size + sizeof(int)); - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - callFunction(OSPCOI_GET_PARAMETERS, stream, result, size + sizeof(int)); - - int count = 0; - for (size_t offset=0, length=0 ; offset < size ; offset += length + 1) { - - length = strlen(((char *) &result->value) + offset); - count++; - - } - - char **names = (char **) malloc((count + 1) * sizeof(char *)); - for (size_t i=0, offset=0 ; i < count ; i++) { - - names[i] = strdup(((char *) &result->value) + offset); - offset += strlen(((char *) &result->value) + offset) + 1; - - } - - names[count] = nullptr; - return(*value = names, free(result), true); - - } - - /*! Retrieve the total length of the names (with terminators) of the - * parameters associated with an object. */ - int COIDevice::getParametersSize(OSPObject object, int *value) { - - struct ReturnValue { int success; int value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - callFunction(OSPCOI_GET_PARAMETERS_SIZE, stream, - &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - + /*! Clear the specified channel(s) of the frame buffer specified in 'whichChannels'. + If whichChannel&OSP_FB_COLOR!=0, clear the color buffer to '0,0,0,0'. + If whichChannel&OSP_FB_DEPTH!=0, clear the depth buffer to +inf. + If whichChannel&OSP_FB_ACCUM!=0, clear the accum buffer to 0,0,0,0, and reset accumID. + */ + void COIDevice::frameBufferClear(OSPFrameBuffer _fb, const uint32 fbChannelFlags) + { + DataStream args; + args.write((ObjectHandle&)_fb); + args.write(fbChannelFlags); + callFunction(OSPCOI_FRAMEBUFFER_CLEAR,args); } /*! Get a pointer to a copy of the named character string associated with @@ -1503,88 +1242,5 @@ namespace ospray { } - /*! Get the type of the named parameter or the given object (if 'name' is - * nullptr). */ - int COIDevice::getType(OSPObject object, const char *name, OSPDataType *value) { - - struct ReturnValue { int success; OSPDataType type; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name ? name : "\0"); - callFunction(OSPCOI_GET_TYPE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.type, true : false); - - } - - /*! Get the named 2-vector floating point value associated with an object.*/ - int COIDevice::getVec2f(OSPObject object, const char *name, vec2f *value) { - - struct ReturnValue { int success; vec2f value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_FLOAT2); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Get the named 3-vector floating point value associated with an object.*/ - int COIDevice::getVec3f(OSPObject object, const char *name, vec3f *value) { - - struct ReturnValue { int success; vec3f value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_FLOAT3); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Get the named 4-vector floating point value associated with an object.*/ - int COIDevice::getVec4f(OSPObject object, const char *name, vec4f *value) { - - struct ReturnValue { int success; vec4f value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_FLOAT4); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Get the named 3-vector integer value associated with an object. */ - int COIDevice::getVec3i(OSPObject object, const char *name, vec3i *value) { - - struct ReturnValue { int success; vec3i value; } result; - Assert(object != nullptr && "invalid source object handle"); - DataStream stream; - stream.write((ObjectHandle &) object); - stream.write(name); - stream.write(OSP_INT3); - callFunction(OSPCOI_GET_VALUE, stream, &result, sizeof(ReturnValue)); - return(result.success ? *value = result.value, true : false); - - } - - /*! Clear the specified channel(s) of the frame buffer specified in 'whichChannels'. - If whichChannel&OSP_FB_COLOR!=0, clear the color buffer to '0,0,0,0'. - If whichChannel&OSP_FB_DEPTH!=0, clear the depth buffer to +inf. - If whichChannel&OSP_FB_ACCUM!=0, clear the accum buffer to 0,0,0,0, and reset accumID. - */ - void COIDevice::frameBufferClear(OSPFrameBuffer _fb, const uint32 fbChannelFlags) - { - DataStream args; - args.write((ObjectHandle&)_fb); - args.write(fbChannelFlags); - callFunction(OSPCOI_FRAMEBUFFER_CLEAR,args); - } - } // ::ospray::coi } // ::ospray diff --git a/ospray/api/COIDeviceWorker.cpp b/ospray/api/COIDeviceWorker.cpp index 944af3797a..e5747e2939 100644 --- a/ospray/api/COIDeviceWorker.cpp +++ b/ospray/api/COIDeviceWorker.cpp @@ -22,19 +22,19 @@ #include #include #include -#include "ospray/common/ObjectHandle.h" +#include "common/ObjectHandle.h" // ospray -#include "ospray/common/Model.h" -#include "ospray/common/Data.h" -#include "ospray/geometry/TriangleMesh.h" -#include "ospray/camera/Camera.h" -#include "ospray/volume/Volume.h" -#include "ospray/transferFunction/TransferFunction.h" -#include "ospray/render/Renderer.h" -#include "ospray/render/LoadBalancer.h" -#include "ospray/texture/Texture2D.h" -#include "ospray/lights/Light.h" -#include "ospray/fb/LocalFB.h" +#include "common/Model.h" +#include "common/Data.h" +#include "geometry/TriangleMesh.h" +#include "camera/Camera.h" +#include "volume/Volume.h" +#include "transferFunction/TransferFunction.h" +#include "render/Renderer.h" +#include "render/LoadBalancer.h" +#include "texture/Texture2D.h" +#include "lights/Light.h" +#include "fb/LocalFB.h" using namespace std; diff --git a/ospray/api/Device.cpp b/ospray/api/Device.cpp index 28aba9b098..374871b5dd 100644 --- a/ospray/api/Device.cpp +++ b/ospray/api/Device.cpp @@ -16,7 +16,7 @@ // ospray #include "Device.h" -#include "ospray/common/OSPCommon.h" +#include "common/OSPCommon.h" // embree #include "embree2/rtcore.h" @@ -26,7 +26,7 @@ namespace ospray { namespace api { - Device *Device::current = NULL; + Ref Device::current = nullptr; Device::Device() { // rtcSetErrorFunction(error_handler); need to call rtcInit first diff --git a/ospray/api/Device.h b/ospray/api/Device.h index bfcbdeae4c..0cb6126c15 100644 --- a/ospray/api/Device.h +++ b/ospray/api/Device.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.h" -#include "ospray/include/ospray/ospray.h" +#include "common/OSPCommon.h" +#include "ospray/ospray.h" /*! \file device.h Defines the abstract base class for OSPRay "devices" that implement the OSPRay API */ @@ -27,9 +27,9 @@ namespace ospray { namespace api { /*! abstract base class of all 'devices' that implement the ospray API */ - struct Device { + struct Device : public RefCount { /*! singleton that points to currently active device */ - static Device *current; + static Ref current; Device(); @@ -107,45 +107,6 @@ namespace ospray { /*! add untyped void pointer to object - this will *ONLY* work in local rendering! */ virtual void setVoidPtr(OSPObject object, const char *bufName, void *v) = 0; - /*! Get the handle of the named data array associated with an object. */ - virtual int getData(OSPObject object, const char *name, OSPData *value) = 0; - - /*! Get a copy of the data in an array (the application is responsible for freeing this pointer). */ - virtual int getDataValues(OSPData object, void **pointer, size_t *count, OSPDataType *type) = 0; - - /*! Get the named scalar floating point value associated with an object. */ - virtual int getf(OSPObject object, const char *name, float *value) = 0; - - /*! Get the named scalar integer associated with an object. */ - virtual int geti(OSPObject object, const char *name, int *value) = 0; - - /*! Get the material associated with a geometry object. */ - virtual int getMaterial(OSPGeometry geometry, OSPMaterial *value) = 0; - - /*! Get the named object associated with an object. */ - virtual int getObject(OSPObject object, const char *name, OSPObject *value) = 0; - - /*! Retrieve a NULL-terminated list of the parameter names associated with an object. */ - virtual int getParameters(OSPObject object, char ***value) = 0; - - /*! Get a pointer to a copy of the named character string associated with an object. */ - virtual int getString(OSPObject object, const char *name, char **value) = 0; - - /*! Get the type of the named parameter or the given object (if 'name' is NULL). */ - virtual int getType(OSPObject object, const char *name, OSPDataType *value) = 0; - - /*! Get the named 2-vector floating point value associated with an object. */ - virtual int getVec2f(OSPObject object, const char *name, vec2f *value) = 0; - - /*! Get the named 3-vector floating point value associated with an object. */ - virtual int getVec3f(OSPObject object, const char *name, vec3f *value) = 0; - - /*! Get the named 4-vector floating point value associated with an object. */ - virtual int getVec4f(OSPObject object, const char *name, vec4f *value) = 0; - - /*! Get the named 3-vector integer value associated with an object. */ - virtual int getVec3i(OSPObject object, const char *name, vec3i *value) = 0; - /*! create a new renderer object (out of list of registered renderers) */ virtual OSPRenderer newRenderer(const char *type) = 0; @@ -239,7 +200,6 @@ namespace ospray { { throw std::runtime_error("sampleVolume() not implemented for this device"); } - }; } // ::ospray::api } // ::ospray diff --git a/ospray/api/LocalDevice.cpp b/ospray/api/LocalDevice.cpp index d341bb7b46..1a1bac1a3b 100644 --- a/ospray/api/LocalDevice.cpp +++ b/ospray/api/LocalDevice.cpp @@ -15,19 +15,19 @@ // ======================================================================== // #include "LocalDevice.h" -#include "ospray/common/Model.h" -#include "ospray/common/Data.h" -#include "ospray/geometry/TriangleMesh.h" -#include "ospray/render/Renderer.h" -#include "ospray/camera/Camera.h" -#include "ospray/volume/Volume.h" -#include "ospray/transferFunction/TransferFunction.h" -#include "ospray/render/LoadBalancer.h" -#include "ospray/common/Material.h" -#include "ospray/common/Library.h" -#include "ospray/texture/Texture2D.h" -#include "ospray/lights/Light.h" -#include "ospray/fb/LocalFB.h" +#include "common/Model.h" +#include "common/Data.h" +#include "geometry/TriangleMesh.h" +#include "render/Renderer.h" +#include "camera/Camera.h" +#include "volume/Volume.h" +#include "transferFunction/TransferFunction.h" +#include "render/LoadBalancer.h" +#include "common/Material.h" +#include "common/Library.h" +#include "texture/Texture2D.h" +#include "lights/Light.h" +#include "fb/LocalFB.h" // stl #include @@ -44,11 +44,11 @@ namespace ospray { throw std::runtime_error("embree internal error '" +std::string(str)+"'"); } - LocalDevice::LocalDevice(int *_ac, const char **_av) + LocalDevice::LocalDevice(int */*_ac*/, const char **/*_av*/) { - char *logLevelFromEnv = getenv("OSPRAY_LOG_LEVEL"); - if (logLevelFromEnv && logLevel == 0) - logLevel = atoi(logLevelFromEnv); + auto logLevelFromEnv = getEnvVar("OSPRAY_LOG_LEVEL"); + if (logLevelFromEnv.first && logLevel == 0) + logLevel = logLevelFromEnv.second; // ------------------------------------------------------- // initialize embree. (we need to do this here rather than in @@ -85,7 +85,7 @@ namespace ospray { const OSPFrameBufferFormat mode, const uint32 channels) { - FrameBuffer::ColorBufferFormat colorBufferFormat = mode; //FrameBuffer::RGBA_UINT8;//FLOAT32; + FrameBuffer::ColorBufferFormat colorBufferFormat = mode; bool hasDepthBuffer = (channels & OSP_FB_DEPTH)!=0; bool hasAccumBuffer = (channels & OSP_FB_ACCUM)!=0; bool hasVarianceBuffer = (channels & OSP_FB_VARIANCE)!=0; @@ -98,7 +98,8 @@ namespace ospray { } - /*! clear the specified channel(s) of the frame buffer specified in 'whichChannels' + /*! clear the specified channel(s) of the frame buffer specified in + * 'whichChannels' if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to '0,0,0,0'. @@ -125,7 +126,7 @@ namespace ospray { switch (channel) { case OSP_FB_COLOR: return fb->mapColorBuffer(); case OSP_FB_DEPTH: return fb->mapDepthBuffer(); - default: return NULL; + default: return nullptr; } } @@ -133,7 +134,7 @@ namespace ospray { void LocalDevice::frameBufferUnmap(const void *mapped, OSPFrameBuffer _fb) { - Assert2(_fb != NULL, "invalid framebuffer"); + Assert2(_fb != nullptr, "invalid framebuffer"); FrameBuffer *fb = (FrameBuffer *)_fb; fb->unmap(mapped); } @@ -171,14 +172,6 @@ namespace ospray { model->geometry.push_back(geometry); } - /*! remove an existing geometry from a model */ - struct GeometryLocator { - bool operator()(const Ref &g) const { - return ptr == &*g; - } - Geometry *ptr; - }; - void LocalDevice::removeGeometry(OSPModel _model, OSPGeometry _geometry) { Model *model = (Model *)_model; @@ -187,9 +180,12 @@ namespace ospray { Geometry *geometry = (Geometry *)_geometry; Assert2(geometry, "null geometry in LocalDevice::removeGeometry"); - GeometryLocator locator; - locator.ptr = geometry; - Model::GeometryVector::iterator it = std::find_if(model->geometry.begin(), model->geometry.end(), locator); + auto it = std::find_if(model->geometry.begin(), + model->geometry.end(), + [&](const Ref &g) { + return geometry == &*g; + }); + if(it != model->geometry.end()) { model->geometry.erase(it); } @@ -207,14 +203,6 @@ namespace ospray { model->volume.push_back(volume); } - /*! remove an existing volume from a model */ - struct VolumeLocator { - bool operator()(const Ref &g) const { - return ptr == &*g; - } - Volume *ptr; - }; - void LocalDevice::removeVolume(OSPModel _model, OSPVolume _volume) { Model *model = (Model *)_model; @@ -223,16 +211,20 @@ namespace ospray { Volume *volume = (Volume *)_volume; Assert2(volume, "null volume in LocalDevice::removeVolume"); - VolumeLocator locator; - locator.ptr = volume; - Model::VolumeVector::iterator it = std::find_if(model->volume.begin(), model->volume.end(), locator); + auto it = std::find_if(model->volume.begin(), + model->volume.end(), + [&](const Ref &g) { + return volume == &*g; + }); + if(it != model->volume.end()) { model->volume.erase(it); } } /*! create a new data buffer */ - OSPData LocalDevice::newData(size_t nitems, OSPDataType format, void *init, int flags) + OSPData LocalDevice::newData(size_t nitems, OSPDataType format, + void *init, int flags) { Data *data = new Data(nitems,format,init,flags); data->refInc(); @@ -240,38 +232,46 @@ namespace ospray { } /*! assign (named) string parameter to an object */ - void LocalDevice::setString(OSPObject _object, const char *bufName, const char *s) + void LocalDevice::setString(OSPObject _object, + const char *bufName, + const char *s) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(s); } /*! assign (named) string parameter to an object */ - void LocalDevice::setVoidPtr(OSPObject _object, const char *bufName, void *v) + void LocalDevice::setVoidPtr(OSPObject _object, + const char *bufName, + void *v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(v); } /*! assign (named) int parameter to an object */ - void LocalDevice::setInt(OSPObject _object, const char *bufName, const int f) + void LocalDevice::setInt(OSPObject _object, + const char *bufName, + const int f) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(f); } /*! assign (named) float parameter to an object */ - void LocalDevice::setFloat(OSPObject _object, const char *bufName, const float f) + void LocalDevice::setFloat(OSPObject _object, + const char *bufName, + const float f) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); ManagedObject::Param *param = object->findParam(bufName,1); param->set(f); @@ -282,209 +282,96 @@ namespace ospray { const vec3i &index, const vec3i &count) { Volume *volume = (Volume *) handle; - Assert(volume != NULL && "invalid volume object handle"); + Assert(volume != nullptr && "invalid volume object handle"); return(volume->setRegion(source, index, count)); } /*! assign (named) vec2f parameter to an object */ - void LocalDevice::setVec2f(OSPObject _object, const char *bufName, const vec2f &v) + void LocalDevice::setVec2f(OSPObject _object, + const char *bufName, + const vec2f &v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName, 1)->set(v); } /*! assign (named) vec3f parameter to an object */ - void LocalDevice::setVec3f(OSPObject _object, const char *bufName, const vec3f &v) + void LocalDevice::setVec3f(OSPObject _object, + const char *bufName, + const vec3f &v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(v); } /*! assign (named) vec3f parameter to an object */ - void LocalDevice::setVec4f(OSPObject _object, const char *bufName, const vec4f &v) + void LocalDevice::setVec4f(OSPObject _object, + const char *bufName, + const vec4f &v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(v); } /*! assign (named) vec2f parameter to an object */ - void LocalDevice::setVec2i(OSPObject _object, const char *bufName, const vec2i &v) + void LocalDevice::setVec2i(OSPObject _object, + const char *bufName, + const vec2i &v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName, 1)->set(v); } /*! assign (named) vec3i parameter to an object */ - void LocalDevice::setVec3i(OSPObject _object, const char *bufName, const vec3i &v) + void LocalDevice::setVec3i(OSPObject _object, + const char *bufName, + const vec3i &v) { ManagedObject *object = (ManagedObject *)_object; - Assert(object != NULL && "invalid object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(object != nullptr && "invalid object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); object->findParam(bufName,1)->set(v); } /*! assign (named) data item as a parameter to an object */ - void LocalDevice::setObject(OSPObject _target, const char *bufName, OSPObject _value) + void LocalDevice::setObject(OSPObject _target, + const char *bufName, + OSPObject _value) { ManagedObject *target = (ManagedObject *)_target; ManagedObject *value = (ManagedObject *)_value; - Assert(target != NULL && "invalid target object handle"); - Assert(bufName != NULL && "invalid identifier for object parameter"); + Assert(target != nullptr && "invalid target object handle"); + Assert(bufName != nullptr && "invalid identifier for object parameter"); target->setParam(bufName,value); } - /*! Get the handle of the named data array associated with an object. */ - int LocalDevice::getData(OSPObject handle, const char *name, OSPData *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_OBJECT && param->ptr->managedObjectType == OSP_DATA ? *value = (OSPData) param->ptr, true : false); - } - - /*! Get a copy of the data in an array (the application is responsible for freeing this pointer). */ - int LocalDevice::getDataValues(OSPData handle, void **pointer, size_t *count, OSPDataType *type) - { - Data *data = (Data *) handle; - Assert(data != NULL && "invalid data object handle"); - *pointer = malloc(data->numBytes); if (pointer == NULL) return(false); - return(memcpy(*pointer, data->data, data->numBytes), *count = data->numItems, *type = data->type, true); - } - - /*! Get the named scalar floating point value associated with an object. */ - int LocalDevice::getf(OSPObject handle, const char *name, float *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_FLOAT ? *value = param->f[0], true : false); - } - - /*! Get the named scalar integer associated with an object. */ - int LocalDevice::geti(OSPObject handle, const char *name, int *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_INT ? *value = param->i[0], true : false); - } - - /*! Get the material associated with a geometry object. */ - int LocalDevice::getMaterial(OSPGeometry handle, OSPMaterial *value) - { - Geometry *geometry = (Geometry *) handle; - Assert(geometry != NULL && "invalid source geometry handle"); - return(*value = (OSPMaterial) geometry->getMaterial(), true); - } - - /*! Get the named object associated with an object. */ - int LocalDevice::getObject(OSPObject handle, const char *name, OSPObject *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_OBJECT ? *value = (OSPObject) param->ptr, true : false); - } - - /*! Retrieve a NULL-terminated list of the parameter names associated with an object. */ - int LocalDevice::getParameters(OSPObject handle, char ***value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - char **names = (char **) malloc((object->paramList.size() + 1) * sizeof(char *)); - - for (size_t i=0 ; i < object->paramList.size() ; i++) - names[i] = strdup(object->paramList[i]->name); - names[object->paramList.size()] = NULL; - return(*value = names, true); - - } - - /*! Get a pointer to a copy of the named character string associated with an object. */ - int LocalDevice::getString(OSPObject handle, const char *name, char **value) { - - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_STRING ? *value = strdup(param->s), true : false); - } - - /*! Get the type of the named parameter or the given object (if 'name' is NULL). */ - int LocalDevice::getType(OSPObject handle, const char *name, OSPDataType *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - if (name == NULL) return(*value = object->managedObjectType, true); - - ManagedObject::Param *param = object->findParam(name); - if (param == NULL) return(false); - return(*value - = (param->type == OSP_OBJECT) - ? param->ptr->managedObjectType - : param->type, true); - } - - /*! Get the named 2-vector floating point value associated with an object. */ - int LocalDevice::getVec2f(OSPObject handle, const char *name, vec2f *value) { - - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_FLOAT2 ? *value = ((vec2f *) param->f)[0], true : false); - } - - /*! Get the named 3-vector floating point value associated with an object. */ - int LocalDevice::getVec3f(OSPObject handle, const char *name, vec3f *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_FLOAT3 ? *value = ((vec3f *) param->f)[0], true : false); - } - - /*! Get the named 4-vector floating point value associated with an object. */ - int LocalDevice::getVec4f(OSPObject handle, const char *name, vec4f *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_FLOAT4 ? *value = ((vec4f *) param->f)[0], true : false); - } - - /*! Get the named 3-vector integer value associated with an object. */ - int LocalDevice::getVec3i(OSPObject handle, const char *name, vec3i *value) - { - ManagedObject *object = (ManagedObject *) handle; - Assert(object != NULL && "invalid source object handle"); - ManagedObject::Param *param = object->findParam(name); - return(param && param->type == OSP_INT3 ? *value = ((vec3i *) param->i)[0], true : false); - } - /*! create a new pixelOp object (out of list of registered pixelOps) */ OSPPixelOp LocalDevice::newPixelOp(const char *type) { - Assert(type != NULL && "invalid render type identifier"); + Assert(type != nullptr && "invalid render type identifier"); PixelOp *pixelOp = PixelOp::createPixelOp(type); if (!pixelOp) { - if (ospray::debugMode) - throw std::runtime_error("unknown pixelOp type '"+std::string(type)+"'"); + if (ospray::debugMode) { + throw std::runtime_error("unknown pixelOp type '" + + std::string(type) + "'"); + } else - return NULL; + return nullptr; } pixelOp->refInc(); return (OSPPixelOp)pixelOp; @@ -503,13 +390,15 @@ namespace ospray { /*! create a new renderer object (out of list of registered renderers) */ OSPRenderer LocalDevice::newRenderer(const char *type) { - Assert(type != NULL && "invalid render type identifier"); + Assert(type != nullptr && "invalid render type identifier"); Renderer *renderer = Renderer::createRenderer(type); if (!renderer) { - if (ospray::debugMode) - throw std::runtime_error("unknown renderer type '"+std::string(type)+"'"); + if (ospray::debugMode) { + throw std::runtime_error("unknown renderer type '" + + std::string(type) + "'"); + } else - return NULL; + return nullptr; } renderer->refInc(); return (OSPRenderer)renderer; @@ -518,20 +407,22 @@ namespace ospray { /*! create a new geometry object (out of list of registered geometrys) */ OSPGeometry LocalDevice::newGeometry(const char *type) { - Assert(type != NULL && "invalid render type identifier"); + Assert(type != nullptr && "invalid render type identifier"); Geometry *geometry = Geometry::createGeometry(type); - if (!geometry) return NULL; + if (!geometry) return nullptr; geometry->refInc(); return (OSPGeometry)geometry; } /*! have given renderer create a new material */ - OSPMaterial LocalDevice::newMaterial(OSPRenderer _renderer, const char *type) + OSPMaterial LocalDevice::newMaterial(OSPRenderer _renderer, + const char *type) { - Assert2(type != NULL, "invalid material type identifier"); + Assert2(type != nullptr, "invalid material type identifier"); // ------------------------------------------------------- - // first, check if there's a renderer that we can ask to create the material. + // first, check if there's a renderer that we can ask to create the + // material. // Renderer *renderer = (Renderer *)_renderer; if (renderer) { @@ -543,10 +434,11 @@ namespace ospray { } // ------------------------------------------------------- - // if there was no renderer, check if there's a loadable material by that name + // if there was no renderer, check if there's a loadable material by that + // name // Material *material = Material::createMaterial(type); - if (!material) return NULL; + if (!material) return nullptr; material->refInc(); return (OSPMaterial)material; } @@ -554,13 +446,15 @@ namespace ospray { /*! create a new camera object (out of list of registered cameras) */ OSPCamera LocalDevice::newCamera(const char *type) { - Assert(type != NULL && "invalid camera type identifier"); + Assert(type != nullptr && "invalid camera type identifier"); Camera *camera = Camera::createCamera(type); if (!camera) { - if (ospray::debugMode) - throw std::runtime_error("unknown camera type '"+std::string(type)+"'"); + if (ospray::debugMode) { + throw std::runtime_error("unknown camera type '" + + std::string(type) + "'"); + } else - return NULL; + return nullptr; } camera->refInc(); return (OSPCamera)camera; @@ -569,13 +463,15 @@ namespace ospray { /*! create a new volume object (out of list of registered volumes) */ OSPVolume LocalDevice::newVolume(const char *type) { - Assert(type != NULL && "invalid volume type identifier"); + Assert(type != nullptr && "invalid volume type identifier"); Volume *volume = Volume::createInstance(type); if (!volume) { - if (ospray::debugMode) - throw std::runtime_error("unknown volume type '"+std::string(type)+"'"); + if (ospray::debugMode) { + throw std::runtime_error("unknown volume type '" + + std::string(type) + "'"); + } else - return NULL; + return nullptr; } volume->refInc(); return (OSPVolume)volume; @@ -584,13 +480,15 @@ namespace ospray { /*! create a new volume object (out of list of registered volumes) */ OSPTransferFunction LocalDevice::newTransferFunction(const char *type) { - Assert(type != NULL && "invalid transfer function type identifier"); - TransferFunction *transferFunction = TransferFunction::createInstance(type); + Assert(type != nullptr && "invalid transfer function type identifier"); + auto *transferFunction = TransferFunction::createInstance(type); if (!transferFunction) { - if (ospray::debugMode) - throw std::runtime_error("unknown transfer function type '"+std::string(type)+"'"); + if (ospray::debugMode) { + throw std::runtime_error("unknown transfer function type '" + + std::string(type) + "'"); + } else - return NULL; + return nullptr; } transferFunction->refInc(); return (OSPTransferFunction)transferFunction; @@ -607,9 +505,10 @@ namespace ospray { } } - //If there was no renderer try to see if there is a loadable light by that name + // If there was no renderer try to see if there is a loadable light by + // that name Light *light = Light::createLight(type); - if (!light) return NULL; + if (!light) return nullptr; light->refInc(); return (OSPLight)light; } @@ -618,8 +517,10 @@ namespace ospray { OSPTexture2D LocalDevice::newTexture2D(const vec2i &size, const OSPTextureFormat type, void *data, const uint32 flags) { - Assert(size.x > 0 && "Width must be greater than 0 in LocalDevice::newTexture2D"); - Assert(size.y > 0 && "Height must be greater than 0 in LocalDevice::newTexture2D"); + Assert(size.x > 0 && + "Width must be greater than 0 in LocalDevice::newTexture2D"); + Assert(size.y > 0 && + "Height must be greater than 0 in LocalDevice::newTexture2D"); Texture2D *tx = Texture2D::createTexture(size, type, data, flags); if(tx) tx->refInc(); return (OSPTexture2D)tx; @@ -662,8 +563,8 @@ namespace ospray { FrameBuffer *fb = (FrameBuffer *)_fb; Renderer *renderer = (Renderer *)_renderer; - Assert(fb != NULL && "invalid frame buffer handle"); - Assert(renderer != NULL && "invalid renderer handle"); + Assert(fb != nullptr && "invalid frame buffer handle"); + Assert(renderer != nullptr && "invalid renderer handle"); try { return renderer->renderFrame(fb, fbChannelFlags); @@ -706,7 +607,7 @@ namespace ospray { OSPPickResult LocalDevice::pick(OSPRenderer _renderer, const vec2f &screenPos) { - Assert(_renderer != NULL && "invalid renderer handle"); + Assert(_renderer != nullptr && "invalid renderer handle"); Renderer *renderer = (Renderer*)_renderer; return renderer->pick(screenPos); diff --git a/ospray/api/LocalDevice.h b/ospray/api/LocalDevice.h index 38673bf23c..dff2473cef 100644 --- a/ospray/api/LocalDevice.h +++ b/ospray/api/LocalDevice.h @@ -131,52 +131,6 @@ namespace ospray { /*! add untyped void pointer to object - this will *ONLY* work in local rendering! */ void setVoidPtr(OSPObject object, const char *bufName, void *v) override; - /*! Get the handle of the named data array associated with an object. */ - int getData(OSPObject object, const char *name, OSPData *value) override; - - /*! Get a copy of the data in an array (the application is responsible for freeing this pointer). */ - int getDataValues(OSPData object, - void **pointer, - size_t *count, - OSPDataType *type) override; - - /*! Get the named scalar floating point value associated with an object. */ - int getf(OSPObject object, const char *name, float *value) override; - - /*! Get the named scalar integer associated with an object. */ - int geti(OSPObject object, const char *name, int *value) override; - - /*! Get the material associated with a geometry object. */ - int getMaterial(OSPGeometry geometry, OSPMaterial *value) override; - - /*! Get the named object associated with an object. */ - int getObject(OSPObject object, - const char *name, - OSPObject *value) override; - - /*! Retrieve a NULL-terminated list of the parameter names associated with an object. */ - int getParameters(OSPObject object, char ***value) override; - - /*! Get a pointer to a copy of the named character string associated with an object. */ - int getString(OSPObject object, const char *name, char **value) override; - - /*! Get the type of the named parameter or the given object (if 'name' is NULL). */ - int getType(OSPObject object, - const char *name, - OSPDataType *value) override; - - /*! Get the named 2-vector floating point value associated with an object. */ - int getVec2f(OSPObject object, const char *name, vec2f *value) override; - - /*! Get the named 3-vector floating point value associated with an object. */ - int getVec3f(OSPObject object, const char *name, vec3f *value) override; - - /*! Get the named 4-vector floating point value associated with an object. */ - int getVec4f(OSPObject object, const char *name, vec4f *value) override; - - /*! Get the named 3-vector integer value associated with an object. */ - int getVec3i(OSPObject object, const char *name, vec3i *value) override; - /*! create a new renderer object (out of list of registered renderers) */ OSPRenderer newRenderer(const char *type) override; diff --git a/ospray/camera/Camera.h b/ospray/camera/Camera.h index ea57fe51aa..d8d09fb7b1 100644 --- a/ospray/camera/Camera.h +++ b/ospray/camera/Camera.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/Managed.h" -#include "ospray/common/Ray.h" +#include "common/Managed.h" +#include "common/Ray.h" namespace ospray { diff --git a/ospray/camera/Camera.ih b/ospray/camera/Camera.ih index 5e6bd3f4dc..d25a52ccfa 100644 --- a/ospray/camera/Camera.ih +++ b/ospray/camera/Camera.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/Ray.ih" +#include "../common/Ray.ih" /*! \file camera.ih Defines the abstract base class of an ISPC-side camera */ diff --git a/ospray/camera/OrthographicCamera.h b/ospray/camera/OrthographicCamera.h index 13d9ab9c23..d20cb61bbb 100644 --- a/ospray/camera/OrthographicCamera.h +++ b/ospray/camera/OrthographicCamera.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/camera/Camera.h" +#include "camera/Camera.h" namespace ospray { diff --git a/ospray/camera/PanoramicCamera.h b/ospray/camera/PanoramicCamera.h index a1656ce3b8..4bf73f162b 100644 --- a/ospray/camera/PanoramicCamera.h +++ b/ospray/camera/PanoramicCamera.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/camera/Camera.h" +#include "camera/Camera.h" namespace ospray { diff --git a/ospray/camera/PanoramicCamera.ispc b/ospray/camera/PanoramicCamera.ispc index 306b3e0404..3c845532c0 100644 --- a/ospray/camera/PanoramicCamera.ispc +++ b/ospray/camera/PanoramicCamera.ispc @@ -15,7 +15,7 @@ // ======================================================================== // #include "PanoramicCamera.ih" -#include "ospray/math/sampling.ih" +#include "math/sampling.ih" void PanoramicCamera_initRay(uniform Camera *uniform _self, varying Ray &ray, diff --git a/ospray/camera/PerspectiveCamera.h b/ospray/camera/PerspectiveCamera.h index e8e74ca974..e98ce69ecb 100644 --- a/ospray/camera/PerspectiveCamera.h +++ b/ospray/camera/PerspectiveCamera.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/camera/Camera.h" +#include "camera/Camera.h" namespace ospray { diff --git a/ospray/camera/PerspectiveCamera.ispc b/ospray/camera/PerspectiveCamera.ispc index 1c79ee57e0..853ab66e17 100644 --- a/ospray/camera/PerspectiveCamera.ispc +++ b/ospray/camera/PerspectiveCamera.ispc @@ -15,7 +15,7 @@ // ======================================================================== // #include "PerspectiveCamera.ih" -#include "ospray/math/sampling.ih" +#include "math/sampling.ih" void PerspectiveCamera_initRay(uniform Camera *uniform _self, varying Ray &ray, diff --git a/ospray/common/Core.cpp b/ospray/common/Core.cpp index 43b812a7cb..3de2c5a731 100644 --- a/ospray/common/Core.cpp +++ b/ospray/common/Core.cpp @@ -25,9 +25,9 @@ something similarly */ -#include "ospray/common/Core.h" +#include "common/Core.h" #ifdef OSPRAY_MPI -# include "ospray/mpi/MPIDevice.h" +# include "mpi/MPIDevice.h" #endif namespace ospray { diff --git a/ospray/common/Core.h b/ospray/common/Core.h index ae9ff45c24..aa99781e77 100644 --- a/ospray/common/Core.h +++ b/ospray/common/Core.h @@ -27,7 +27,7 @@ something similarly */ -#include "ospray/common/OSPCommon.h" +#include "common/OSPCommon.h" namespace ospray { diff --git a/ospray/common/Data.cpp b/ospray/common/Data.cpp index a8862a56f9..18ad44d9b6 100644 --- a/ospray/common/Data.cpp +++ b/ospray/common/Data.cpp @@ -16,6 +16,7 @@ // ospray #include "Data.h" +#include "ospray/ospray.h" // stl #include diff --git a/ospray/common/DifferentialGeometry.ih b/ospray/common/DifferentialGeometry.ih index e45bf4de52..bb84c4d8e9 100644 --- a/ospray/common/DifferentialGeometry.ih +++ b/ospray/common/DifferentialGeometry.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/math/vec.ih" +#include "../math/vec.ih" struct Geometry; struct Model; diff --git a/ospray/common/Library.h b/ospray/common/Library.h index 4a383ce790..51f02cf877 100644 --- a/ospray/common/Library.h +++ b/ospray/common/Library.h @@ -16,7 +16,7 @@ #pragma once -#include "common/library.h" +#include "ospcommon/library.h" namespace ospray { using ospcommon::getSymbol; diff --git a/ospray/common/Managed.cpp b/ospray/common/Managed.cpp index 577a5758de..707f668adb 100644 --- a/ospray/common/Managed.cpp +++ b/ospray/common/Managed.cpp @@ -107,9 +107,9 @@ namespace ospray { f[2] = 0; f[3] = 0; if (name) this->name = strdup(name); - }; + } - void *ManagedObject::getVoidPtr(const char *name, void * valIfNotFound) + void *ManagedObject::getVoidPtr(const char *name, void * valIfNotFound) { Param *param = findParam(name); if (!param) return valIfNotFound; @@ -117,7 +117,8 @@ namespace ospray { return (void*)param->ptr; } - ManagedObject::Param *ManagedObject::findParam(const char *name, bool addIfNotExist) + ManagedObject::Param *ManagedObject::findParam(const char *name, + bool addIfNotExist) { for (size_t i=0 ; i < paramList.size() ; i++) { if (!strcmp(paramList[i]->name,name)) return paramList[i]; @@ -127,12 +128,13 @@ namespace ospray { return paramList[paramList.size()-1]; } -#define define_getparam(T,ABB,TARGETTYPE,FIELD) \ - T ManagedObject::getParam##ABB(const char *name, T valIfNotFound) { \ - Param *param = findParam(name); \ - if (!param) return valIfNotFound; \ - if (param->type != TARGETTYPE) return valIfNotFound; \ - return (T&)param->FIELD; \ +#define define_getparam(T,ABB,TARGETTYPE,FIELD) \ + T ManagedObject::getParam##ABB(const char *name, T valIfNotFound) \ + { \ + Param *param = findParam(name); \ + if (!param) return valIfNotFound; \ + if (param->type != TARGETTYPE) return valIfNotFound; \ + return (T&)param->FIELD; \ } define_getparam(ManagedObject *, Object, OSP_OBJECT, ptr); @@ -157,7 +159,7 @@ namespace ospray { /*!< call 'dependencyGotChanged' on each of the objects in 'objectsListeningForChanges' */ void ManagedObject::notifyListenersThatObjectGotChanged() { - for (std::set::iterator it = objectsListeningForChanges.begin(); + for (auto it = objectsListeningForChanges.begin(); it != objectsListeningForChanges.end(); it++) { ManagedObject *object = *it; object->dependencyGotChanged(this); diff --git a/ospray/common/Managed.h b/ospray/common/Managed.h index e6664e8f91..252eb68a6f 100644 --- a/ospray/common/Managed.h +++ b/ospray/common/Managed.h @@ -17,9 +17,9 @@ #pragma once // ospray -#include "OSPCommon.h" -#include "ospray/ospray.h" -#include "ospray/common/ObjectHandle.h" +#include "ospray/OSPDataType.h" +#include "common/OSPCommon.h" +#include "common/ObjectHandle.h" // stl #include #include @@ -58,7 +58,7 @@ namespace ospray { arrays etc being shared by common pointer). To do this any ospray object has an (optional) pointer to a possibly exisitng ISPC counterpart (its "ISPC equivalent", or IE). For objects that do not - have an IE, this value is NULL; typically, the IE will also have a + have an IE, this value is nullptr; typically, the IE will also have a pointer back to its "C equivalent" (CE); and typically, it will be the C side that is doing creation, destruction, etc. Since (due to some internal ISPC issues) many of the ISPC types we use cannot be @@ -114,7 +114,8 @@ namespace ospray { * not need an own destructor. */ virtual ~ManagedObject(); - /*! \brief commit the object's outstanding changes (such as changed parameters etc) */ + /*! \brief commit the object's outstanding changes (such as changed + * parameters etc) */ virtual void commit(); //! \brief common function to help printf-debugging @@ -134,14 +135,16 @@ namespace ospray { Param(const char *name); ~Param() { clear(); }; - /*! clear parameter to 'invalid type and value'; free/de-refcount data if reqd' */ + /*! clear parameter to 'invalid type and value'; free/de-refcount data if + * reqd' */ void clear(); /*! set parameter to a 'pointer to object' type, and given pointer */ void set(ManagedObject *ptr); //! set parameter to a 'c-string' type - /* \internal this function creates and keeps a *copy* of the passed string! */ + /* \internal this function creates and keeps a *copy* of the passed + * string! */ void set(const char *s); //! set parameter to a 'c-string' type @@ -184,17 +187,19 @@ namespace ospray { const char *name; }; - /*! \brief find a given parameter, or add it if not exists (and so specified) */ + /*! \brief find a given parameter, or add it if not exists (and so + * specified) */ Param *findParam(const char *name, bool addIfNotExist = false); /*! \brief check if a given parameter is available */ bool hasParam(const char *name) - { return findParam(name,false) != NULL; } + { return findParam(name,false) != nullptr; } /*! \brief set given parameter to given data array */ void setParam(const char *name, ManagedObject *data); - /*! set a parameter with given name to given value, create param if not existing */ + /*! set a parameter with given name to given value, create param if not + * existing */ template inline void set(const char *name, const T &t) { findParam(name,1)->set(t); } @@ -206,9 +211,10 @@ namespace ospray { have its refcount increased; it is up to the callee to properly do that (typically by assigning to a proper 'ref' instance */ - ManagedObject *getParamObject(const char *name, ManagedObject *valIfNotFound=NULL); + ManagedObject *getParamObject(const char *name, + ManagedObject *valIfNotFound = nullptr); - Data *getParamData(const char *name, Data *valIfNotFound=NULL) + Data *getParamData(const char *name, Data *valIfNotFound = nullptr) { return (Data*)getParamObject(name,(ManagedObject*)valIfNotFound); } vec4f getParam4f(const char *name, const vec4f valIfNotFound); @@ -220,8 +226,8 @@ namespace ospray { float getParam1f(const char *name, const float valIfNotFound); float getParamf (const char *name, const float valIfNotFound); - void *getVoidPtr(const char *name, void *valIfNotFound); - const char *getParamString(const char *name, const char *valIfNotFound); + void *getVoidPtr(const char *name, void *valIfNotFound); + const char *getParamString(const char *name, const char *valIfNotFound); /*! @} */ // ------------------------------------------------------------------ @@ -232,7 +238,8 @@ namespace ospray { // depdencies got changed/committed. // ------------------------------------------------------------------ - /*! \brief gets called whenever any of this node's dependencies got changed */ + /*! \brief gets called whenever any of this node's dependencies got + * changed */ virtual void dependencyGotChanged(ManagedObject *object); //! \brief Will notify all listeners that we got changed @@ -245,7 +252,7 @@ namespace ospray { void registerListener(ManagedObject *newListener); //! \brief un-register a listener - /*! \detailed this object will no longer get update notifications from us */ + /*! \detailed this object will no longer get update notifications from us */ void unregisterListener(ManagedObject *noLongerListening); @@ -278,10 +285,11 @@ namespace ospray { /*! \brief list of parameters attached to this object */ std::vector paramList; - /*! \brief a global ID that can be used for referencing an object remotely */ + /*! \brief a global ID that can be used for referencing an object remotely*/ id_t ID; - /*! \brief ISPC-side eqivalent of this C++-side class, if available (NULL if not) */ + /*! \brief ISPC-side eqivalent of this C++-side class, if available + * (nullptr if not) */ void *ispcEquivalent; /*! \brief subtype of this ManagedObject */ diff --git a/ospray/common/Material.cpp b/ospray/common/Material.cpp index 69e8d0df7a..b7a5595625 100644 --- a/ospray/common/Material.cpp +++ b/ospray/common/Material.cpp @@ -16,7 +16,7 @@ // ospray #include "Material.h" -#include "ospray/common/Library.h" +#include "common/Library.h" // stl #include diff --git a/ospray/common/Material.ih b/ospray/common/Material.ih index e0ea4c0f55..79358a5d9a 100644 --- a/ospray/common/Material.ih +++ b/ospray/common/Material.ih @@ -16,9 +16,10 @@ #pragma once -#include "ospray/common/OSPCommon.ih" +#include "../common/OSPCommon.ih" /*! ISPC-side abstraction for a material. */ struct Material { - void *uniform cppEquivalent; //! pointer back to the C++-equivalent of this class + //! pointer back to the C++-equivalent of this class + void *uniform cppEquivalent; }; diff --git a/ospray/common/Model.cpp b/ospray/common/Model.cpp index 7255131409..0b0257fda8 100644 --- a/ospray/common/Model.cpp +++ b/ospray/common/Model.cpp @@ -16,7 +16,7 @@ // ospray #include "Model.h" -#include "ospray/geometry/TriangleMesh.h" +#include "geometry/TriangleMesh.h" // embree #include "embree2/rtcore.h" #include "embree2/rtcore_scene.h" @@ -32,6 +32,8 @@ namespace ospray { extern RTCDevice g_embreeDevice; + extern "C" void *ospray_getEmbreeDevice() { return g_embreeDevice; } + Model::Model() { managedObjectType = OSP_MODEL; @@ -64,9 +66,6 @@ namespace ospray { bounds.extend(geometry[i]->bounds); ispc::Model_setGeometry(getIE(), i, geometry[i]->getIE()); } - if (geometry.size() > 10) { - PING; PRINT(geometry.size()); - } for (size_t i=0; igetIE()); diff --git a/ospray/common/Model.h b/ospray/common/Model.h index a4c473c5a0..59a4aed632 100644 --- a/ospray/common/Model.h +++ b/ospray/common/Model.h @@ -17,8 +17,8 @@ #pragma once // ospray stuff -#include "ospray/geometry/Geometry.h" -#include "ospray/volume/Volume.h" +#include "geometry/Geometry.h" +#include "volume/Volume.h" // stl stuff #include diff --git a/ospray/common/Model.ih b/ospray/common/Model.ih index fdd02d1586..5729a89d85 100644 --- a/ospray/common/Model.ih +++ b/ospray/common/Model.ih @@ -17,9 +17,9 @@ #pragma once // ospray stuff -#include "ospray/common/Ray.ih" -#include "ospray/geometry/Geometry.ih" -#include "ospray/volume/Volume.ih" +#include "../common/Ray.ih" +#include "../geometry/Geometry.ih" +#include "../volume/Volume.ih" // embree stuff #include "embree2/rtcore.isph" @@ -38,7 +38,7 @@ struct Model { uniform int32 geometryCount; //! volumes contained in the model - Volume **uniform volumes; + uniform Volume *uniform *uniform volumes; uniform int32 volumeCount; }; diff --git a/ospray/common/Model.ispc b/ospray/common/Model.ispc index 97a8065f0f..2db65c6069 100644 --- a/ospray/common/Model.ispc +++ b/ospray/common/Model.ispc @@ -81,7 +81,7 @@ export void Model_setVolume(void *uniform pointer, uniform int32 index, void *uniform volume) { - Model *uniform model = (Model *uniform) pointer; - model->volumes[index] = (Volume *uniform) volume; + uniform Model *uniform model = (uniform Model *uniform) pointer; + model->volumes[index] = (uniform Volume *uniform) volume; } diff --git a/ospray/common/OSPCommon.cpp b/ospray/common/OSPCommon.cpp index 19e1481f38..b182debd8c 100644 --- a/ospray/common/OSPCommon.cpp +++ b/ospray/common/OSPCommon.cpp @@ -16,12 +16,12 @@ #include "OSPCommon.h" #ifdef OSPRAY_USE_INTERNAL_TASKING -# include "ospray/common/tasking/TaskSys.h" +# include "common/tasking/TaskSys.h" #endif -#include "ospray/common/tasking/async.h" +#include "common/tasking/async.h" // embree #include "embree2/rtcore.h" -#include "common/sysinfo.h" +#include "ospcommon/sysinfo.h" //stl #include @@ -45,12 +45,14 @@ namespace ospray { numbers mean increasing verbosity of log messages */ uint32_t logLevel = 0; bool debugMode = false; - int32_t numThreads = -1; //!< for default (==maximum) number of OSPRay/Embree threads + int32_t numThreads = -1; //!< for default (==maximum) number of + // OSPRay/Embree threads WarnOnce::WarnOnce(const std::string &s) : s(s) { - std::cout << "Warning: " << s << " (only reporting first occurrence)" << std::endl; + std::cout << "Warning: " << s << " (only reporting first occurrence)" + << std::endl; } /*! for debugging. compute a checksum for given area range... */ @@ -61,21 +63,16 @@ namespace ospray { long sum = 0; long i = 0; - int nextPing = 1; while (mem < end) { sum += (i+13) * *mem; ++i; ++mem; - - // if (i==nextPing) { - // std::cout << "checksum after " << (i*8) << " bytes: " << (int*)sum << std::endl; - // nextPing += nextPing; - // } } return (void *)sum; } - void doAssertion(const char *file, int line, const char *expr, const char *expl) { + void doAssertion(const char *file, int line, + const char *expr, const char *expl) { if (expl) fprintf(stderr,"%s:%i: Assertion failed: \"%s\":\nAdditional Info: %s\n", file, line, expr, expl); @@ -94,10 +91,12 @@ namespace ospray { void init(int *_ac, const char ***_av) { #ifndef OSPRAY_TARGET_MIC - // If we're not on a MIC, check for SSE4.1 as minimum supported ISA. Will be increased to SSE4.2 in future. + // If we're not on a MIC, check for SSE4.1 as minimum supported ISA. Will + // be increased to SSE4.2 in future. int cpuFeatures = ospcommon::getCPUFeatures(); if ((cpuFeatures & ospcommon::CPU_FEATURE_SSE41) == 0) - throw std::runtime_error("Error. OSPRay only runs on CPUs that support at least SSE4.1."); + throw std::runtime_error("Error. OSPRay only runs on CPUs that support" + " at least SSE4.1."); #endif if (_ac && _av) { @@ -192,7 +191,8 @@ namespace ospray { }; std::stringstream error; - error << __FILE__ << ":" << __LINE__ << ": unknown OSPDataType " << (int)type; + error << __FILE__ << ":" << __LINE__ << ": unknown OSPDataType " + << (int)type; throw std::runtime_error(error.str()); } @@ -233,7 +233,8 @@ namespace ospray { } std::stringstream error; - error << __FILE__ << ":" << __LINE__ << ": unknown OSPTextureFormat " << (int)type; + error << __FILE__ << ":" << __LINE__ << ": unknown OSPTextureFormat " + << (int)type; throw std::runtime_error(error.str()); } diff --git a/ospray/common/OSPCommon.h b/ospray/common/OSPCommon.h index 2c7610975a..2fc1fd7bd1 100644 --- a/ospray/common/OSPCommon.h +++ b/ospray/common/OSPCommon.h @@ -41,10 +41,10 @@ typedef int ssize_t; #endif #if 1 -#include "../../common/AffineSpace.h" -#include "../../common/intrinsics.h" -#include "../../common/RefCount.h" -#include "../../common/malloc.h" +#include "ospcommon/AffineSpace.h" +#include "ospcommon/intrinsics.h" +#include "ospcommon/RefCount.h" +#include "ospcommon/malloc.h" #else // embree #include "common/math/vec2.h" @@ -61,6 +61,7 @@ typedef int ssize_t; #include #include #include +#include #if 1 // iw: remove this eventually, and replace all occurrences with actual @@ -211,6 +212,45 @@ namespace ospray { } return result; } + + template + inline std::pair getEnvVar(const std::string &/*var*/) + { + static_assert(!std::is_same::value && + !std::is_same::value && + !std::is_same::value, + "You can only get an int, float, or std::string " + "when using ospray::getEnvVar()!"); + return {false, {}}; + } + + template <> + inline std::pair + getEnvVar(const std::string &var) + { + auto *str = getenv(var.c_str()); + bool found = (str != nullptr); + return {found, found ? atof(str) : float{}}; + } + + template <> + inline std::pair + getEnvVar(const std::string &var) + { + auto *str = getenv(var.c_str()); + bool found = (str != nullptr); + return {found, found ? atoi(str) : int{}}; + } + + template <> + inline std::pair + getEnvVar(const std::string &var) + { + auto *str = getenv(var.c_str()); + bool found = (str != nullptr); + return {found, found ? std::string(str) : std::string{}}; + } + } // ::ospray #ifdef _WIN32 diff --git a/ospray/common/ObjectHandle.h b/ospray/common/ObjectHandle.h index e4b4c12e73..d58efd2d6e 100644 --- a/ospray/common/ObjectHandle.h +++ b/ospray/common/ObjectHandle.h @@ -18,8 +18,8 @@ /*! \todo move to api - it's also used in coi, and not mpi specific */ -#include "ospray/common/OSPCommon.h" -#include "ospray/common/Managed.h" +#include "common/OSPCommon.h" +#include "common/Managed.h" namespace ospray { diff --git a/ospray/common/ProducerConsumerQueue.h b/ospray/common/ProducerConsumerQueue.h index b8db9af28b..b97d2692de 100644 --- a/ospray/common/ProducerConsumerQueue.h +++ b/ospray/common/ProducerConsumerQueue.h @@ -51,8 +51,14 @@ namespace ospray { void getAll(std::vector &all); /*! get elements in the queue into the given vector, and clear the queue. - if the queue is currently empty, wait until at least one element is there */ + if the queue is currently empty, waiting until at least one element is there + */ size_t getSome(T *some, size_t maxSize); + /*! get elements in the queue into the given vector, and clear the queue. + if the queue is currently empty, wait until at least one element is there + or until the timeOut has elapsed, in which case the function may + return 0. */ + size_t getSomeFor(T *some, size_t maxSize, std::chrono::milliseconds timeOut); private: /*! the actual queue that holds the data */ @@ -142,6 +148,23 @@ namespace ospray { return num; } + template + size_t ProducerConsumerQueue::getSomeFor(T *some, size_t maxSize, std::chrono::milliseconds timeOut) + { + using namespace std::chrono; + std::unique_lock lock(mutex); + if (!notEmptyCond.wait_for(lock, timeOut, [&]{return !content.empty();})) { + return 0; + } + + size_t num = 0; + while (num < maxSize && !content.empty()) { + some[num++] = content.front(); + content.pop_front(); + } + return num; + } + } // ::ospray diff --git a/ospray/common/Ray.ih b/ospray/common/Ray.ih index 74fd2511b7..2c764c6258 100644 --- a/ospray/common/Ray.ih +++ b/ospray/common/Ray.ih @@ -17,8 +17,8 @@ #pragma once // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" +#include "../math/vec.ih" +#include "../math/box.ih" /*! \brief ospray ray class diff --git a/ospray/common/Thread.cpp b/ospray/common/Thread.cpp index f5440693ee..820116944f 100644 --- a/ospray/common/Thread.cpp +++ b/ospray/common/Thread.cpp @@ -18,7 +18,7 @@ #include "Thread.h" // embree -# include "common/thread.h" +# include "ospcommon/thread.h" namespace ospray { diff --git a/ospray/common/Thread.h b/ospray/common/Thread.h index 8c23707251..05c394e8d2 100644 --- a/ospray/common/Thread.h +++ b/ospray/common/Thread.h @@ -21,7 +21,7 @@ // ospray #include "OSPCommon.h" // ospcommon -#include "common/thread.h" +#include "ospcommon/thread.h" namespace ospray { diff --git a/ospray/common/tasking/TaskSys.cpp b/ospray/common/tasking/TaskSys.cpp index ba71864d81..29f4e5cdae 100644 --- a/ospray/common/tasking/TaskSys.cpp +++ b/ospray/common/tasking/TaskSys.cpp @@ -16,8 +16,8 @@ #include "TaskSys.h" //ospray -#include "common/sysinfo.h" -#include "common/thread.h" +#include "ospcommon/sysinfo.h" +#include "ospcommon/thread.h" //stl #include #include diff --git a/ospray/common/tasking/TaskSys.h b/ospray/common/tasking/TaskSys.h index bc13fb1ede..2ce9acc5f5 100644 --- a/ospray/common/tasking/TaskSys.h +++ b/ospray/common/tasking/TaskSys.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/OSPCommon.h" +#include "common/OSPCommon.h" namespace ospray { diff --git a/ospray/common/tasking/async.h b/ospray/common/tasking/async.h index b208602dc2..85dc0f29c1 100644 --- a/ospray/common/tasking/async.h +++ b/ospray/common/tasking/async.h @@ -23,7 +23,7 @@ #elif defined(OSPRAY_TASKING_CILK) # include #elif defined(OSPRAY_TASKING_INTERNAL) -# include "ospray/common/tasking/TaskSys.h" +# include "common/tasking/TaskSys.h" #endif namespace ospray { diff --git a/ospray/common/tasking/parallel_for.h b/ospray/common/tasking/parallel_for.h index 8869be195e..edab0007cd 100644 --- a/ospray/common/tasking/parallel_for.h +++ b/ospray/common/tasking/parallel_for.h @@ -23,7 +23,7 @@ #elif defined(OSPRAY_TASKING_CILK) # include #elif defined(OSPRAY_TASKING_INTERNAL) -# include "ospray/common/tasking/TaskSys.h" +# include "common/tasking/TaskSys.h" #endif namespace ospray { diff --git a/ospray/fb/DisplayWall.cpp b/ospray/fb/DisplayWall.cpp index 0608150942..2105ab8185 100644 --- a/ospray/fb/DisplayWall.cpp +++ b/ospray/fb/DisplayWall.cpp @@ -32,7 +32,7 @@ namespace ospray { const char *hostname = po->getParamString("hostname", "localhost"); streamName = po->getParamString("streamName", "ospray"); - std::cerr << "connecting to hostname " << hostname << " for stream name " << streamName << std::endl; + std::cerr << "connecting to host " << hostname << " for stream " << streamName << std::endl; // connect to DisplayCluster at given hostname. dcSocket = dcStreamConnect(hostname); diff --git a/ospray/fb/DisplayWall.h b/ospray/fb/DisplayWall.h index 00370b4e73..3f09e4c8f7 100644 --- a/ospray/fb/DisplayWall.h +++ b/ospray/fb/DisplayWall.h @@ -22,7 +22,7 @@ */ // ospray -#include "ospray/fb/PixelOp.h" +#include "fb/PixelOp.h" // DisplayCluster class DcSocket; diff --git a/ospray/fb/FrameBuffer.cpp b/ospray/fb/FrameBuffer.cpp index a563111c06..c3c0cbe3e8 100644 --- a/ospray/fb/FrameBuffer.cpp +++ b/ospray/fb/FrameBuffer.cpp @@ -26,6 +26,8 @@ namespace ospray { bool hasAccumBuffer, bool hasVarianceBuffer) : size(size), + numTiles(divRoundUp(size, getTileSize())), + maxValidPixelID(size-vec2i(1)), colorBufferFormat(colorBufferFormat), hasDepthBuffer(hasDepthBuffer), hasAccumBuffer(hasAccumBuffer), @@ -65,7 +67,7 @@ namespace ospray { return; } fprintf(file, "PF\n%i %i\n-1.0\n", size.x, size.y); - float *out = (float *)alloca(sizeof(float)*3*size.x); + float *out = STACK_BUFFER(float, 3*size.x); for (int y = 0; y < size.y; y++) { const float *in = (const float *)&pixel[(size.y-1-y)*size.x*channel]; for (int x = 0; x < size.x; x++) { diff --git a/ospray/fb/FrameBuffer.h b/ospray/fb/FrameBuffer.h index d5b9a5706b..50e0449720 100644 --- a/ospray/fb/FrameBuffer.h +++ b/ospray/fb/FrameBuffer.h @@ -16,11 +16,10 @@ #pragma once -// std -#include - // ospray -#include "ospray/fb/PixelOp.h" +#include "common/Managed.h" +#include "ospray/ospray.h" +#include "fb/PixelOp.h" namespace ospray { @@ -29,7 +28,7 @@ namespace ospray { /*! app-mappable format of the color buffer. make sure that this matches the definition on the ISPC side */ typedef OSPFrameBufferFormat ColorBufferFormat; - + const vec2i size; FrameBuffer(const vec2i &size, ColorBufferFormat colorBufferFormat, @@ -46,7 +45,21 @@ namespace ospray { /*! \brief clear (the specified channels of) this frame buffer */ virtual void clear(const uint32 fbChannelFlags) = 0; - //! \brief common function to help printf-debugging + //! get number of pixels per tile, in x and y direction + vec2i getTileSize() const { return vec2i(TILE_SIZE); } + + //! return number of tiles in x and y direction + vec2i getNumTiles() const { return numTiles; } + + int getTotalTiles() const { return numTiles.x * numTiles.y; } + + //! get number of pixels in x and y diretion + vec2i getNumPixels() const { return size; } + + vec2i numTiles; + vec2i maxValidPixelID; + + //! \brief common function to help printf-debugging /*! \detailed Every derived class should overrride this! */ virtual std::string toString() const { return "ospray::FrameBuffer"; } @@ -82,5 +95,5 @@ namespace ospray { //! helper function to write a (float) image as (flipped) PFM file void writePFM(const std::string &fileName, const vec2i &size, const int channel, const float *pixels); - + } // ::ospray diff --git a/ospray/fb/FrameBuffer.ispc b/ospray/fb/FrameBuffer.ispc index 761e979562..f41abf12bc 100644 --- a/ospray/fb/FrameBuffer.ispc +++ b/ospray/fb/FrameBuffer.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/fb/FrameBuffer.ih" +#include "fb/FrameBuffer.ih" void FrameBuffer_Constructor(FrameBuffer *uniform self, void *uniform cClassPtr) diff --git a/ospray/fb/LocalFB.cpp b/ospray/fb/LocalFB.cpp index f501981ca0..d9b90c8f40 100644 --- a/ospray/fb/LocalFB.cpp +++ b/ospray/fb/LocalFB.cpp @@ -60,16 +60,15 @@ namespace ospray { else accumBuffer = NULL; - tilesx = divRoundUp(size.x, TILE_SIZE); - tiles = tilesx * divRoundUp(size.y, TILE_SIZE); - tileAccumID = (int32*)alignedMalloc(sizeof(int32)*tiles); - memset(tileAccumID, 0, tiles*sizeof(int32)); + const size_t bytes = sizeof(int32)*getTotalTiles(); + tileAccumID = (int32*)alignedMalloc(bytes); + memset(tileAccumID, 0, bytes); if (hasVarianceBuffer) { varianceBuffer = (vec4f*)alignedMalloc(sizeof(vec4f)*size.x*size.y); - tileErrorBuffer = (float*)alignedMalloc(sizeof(float)*tiles); + tileErrorBuffer = (float*)alignedMalloc(sizeof(float)*getTotalTiles()); // maximum number of regions: all regions are of size 3 are split in half - errorRegion.reserve(divRoundUp(tiles*2, 3)); + errorRegion.reserve(divRoundUp(getTotalTiles()*2, 3)); } else { varianceBuffer = NULL; tileErrorBuffer = NULL; @@ -106,18 +105,16 @@ namespace ospray { // it is only necessary to reset the accumID, // LocalFrameBuffer_accumulateTile takes care of clearing the // accumulation buffers - memset(tileAccumID, 0, tiles*sizeof(int32)); + memset(tileAccumID, 0, getTotalTiles()*sizeof(int32)); // always also clear error buffer (if present) if (hasVarianceBuffer) { - for (int i = 0; i < tiles; i++) + for (int i = 0; i < getTotalTiles(); i++) tileErrorBuffer[i] = inf; errorRegion.clear(); // initially create one region covering the complete image - errorRegion.push_back(box2i(vec2i(0), - vec2i(tilesx, - divRoundUp(size.y, TILE_SIZE)))); + errorRegion.push_back(box2i(vec2i(0), getNumTiles())); } } } @@ -149,12 +146,12 @@ namespace ospray { int32 LocalFrameBuffer::accumID(const vec2i &tile) { - return tileAccumID[tile.y * tilesx + tile.x]; + return tileAccumID[tile.y * numTiles.x + tile.x]; } float LocalFrameBuffer::tileError(const vec2i &tile) { - const int idx = tile.y * tilesx + tile.x; + const int idx = tile.y * numTiles.x + tile.x; return hasVarianceBuffer ? tileErrorBuffer[idx] : inf; } @@ -169,7 +166,7 @@ namespace ospray { float maxErr = 0.0f; for (int y = region.lower.y; y < region.upper.y; y++) for (int x = region.lower.x; x < region.upper.x; x++) { - int idx = y * tilesx + x; + int idx = y * numTiles.x + x; err += tileErrorBuffer[idx]; maxErr = std::max(maxErr, tileErrorBuffer[idx]); } @@ -177,7 +174,7 @@ namespace ospray { // refinement as a group for (int y = region.lower.y; y < region.upper.y; y++) for (int x = region.lower.x; x < region.upper.x; x++) { - int idx = y * tilesx + x; + int idx = y * numTiles.x + x; tileErrorBuffer[idx] = maxErr; } vec2i size = region.size(); @@ -206,7 +203,7 @@ namespace ospray { } float maxErr = 0.0f; - for (int i = 0; i < tiles; i++) + for (int i = 0; i < getTotalTiles(); i++) maxErr = std::max(maxErr, tileErrorBuffer[i]); return maxErr; diff --git a/ospray/fb/LocalFB.h b/ospray/fb/LocalFB.h index 238fdd96ee..ce051ddd64 100644 --- a/ospray/fb/LocalFB.h +++ b/ospray/fb/LocalFB.h @@ -17,7 +17,7 @@ #pragma once // ospray -#include "ospray/fb/FrameBuffer.h" +#include "fb/FrameBuffer.h" namespace ospray { @@ -32,8 +32,6 @@ namespace ospray { int32 *tileAccumID; //< holds accumID per tile, for adaptive accumulation float *tileErrorBuffer; /*!< holds error per tile, for variance estimation / stopping */ std::vector errorRegion; // image regions (in #tiles) which do not yet estimate the error on tile base - int32 tilesx; - int32 tiles; LocalFrameBuffer(const vec2i &size, ColorBufferFormat colorBufferFormat, diff --git a/ospray/fb/LocalFB.ih b/ospray/fb/LocalFB.ih index a03fe5bc87..1b026e3063 100644 --- a/ospray/fb/LocalFB.ih +++ b/ospray/fb/LocalFB.ih @@ -16,9 +16,9 @@ #pragma once -#include "ospray/fb/Tile.ih" -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/render/util.ih" +#include "fb/Tile.ih" +#include "fb/FrameBuffer.ih" +#include "render/util.ih" /*! a Local FrameBuffer that stores all pixel values (color, depth, accum) in a plain 2D array of pixels (one array per component) */ diff --git a/ospray/fb/PixelOp.cpp b/ospray/fb/PixelOp.cpp index 7ce0a47e5b..443b79092a 100644 --- a/ospray/fb/PixelOp.cpp +++ b/ospray/fb/PixelOp.cpp @@ -16,7 +16,7 @@ // ospray #include "PixelOp.h" -#include "ospray/common/Library.h" +#include "common/Library.h" // stl #include @@ -34,9 +34,7 @@ namespace ospray { PixelOp *PixelOp::createPixelOp(const char *_type) { - PING; PRINT(_type); - - char *type = (char*)alloca(strlen(_type)+1); + char *type = STACK_BUFFER(char, strlen(_type)+1); strcpy(type,_type); char *atSign = strstr(type,"@"); char *libName = nullptr; @@ -60,7 +58,6 @@ namespace ospray { //dlsym(RTLD_DEFAULT,creatorName.c_str()); pixelOpRegistry[type] = creator; if (creator == nullptr) { - PING; if (ospray::logLevel >= 1) { std::cout << "#ospray: could not find pixelOp type '" << type << "'" << std::endl; @@ -68,9 +65,6 @@ namespace ospray { return nullptr; } PixelOp *pixelOp = (*creator)(); - PING; - PRINT(pixelOp); - PRINT(pixelOp->toString()); pixelOp->managedObjectType = OSP_PIXEL_OP; return(pixelOp); } diff --git a/ospray/fb/PixelOp.h b/ospray/fb/PixelOp.h index 0f77e71576..8fd2b2553f 100644 --- a/ospray/fb/PixelOp.h +++ b/ospray/fb/PixelOp.h @@ -17,8 +17,8 @@ #pragma once // ospray -#include "ospray/fb/Tile.h" -#include "ospray/common/Managed.h" +#include "fb/Tile.h" +#include "common/Managed.h" namespace ospray { diff --git a/ospray/fb/Tile.h b/ospray/fb/Tile.h index 81529d25b3..f41b4f32b2 100644 --- a/ospray/fb/Tile.h +++ b/ospray/fb/Tile.h @@ -16,11 +16,14 @@ #pragma once -#include "ospray/common/OSPCommon.h" -#include "ospray/render/util.h" +#include "common/OSPCommon.h" +#include "render/util.h" namespace ospray { + static_assert(TILE_SIZE > 0 && (TILE_SIZE & (TILE_SIZE - 1)) == 0, + "OSPRay config error: TILE_SIZE must be a positive power of two."); + //! a tile of pixels used by any tile-based renderer /*! pixels in the tile are in a row-major TILE_SIZE x TILE_SIZE pattern. the 'region' specifies which part of the screen this diff --git a/ospray/fb/Tile.ih b/ospray/fb/Tile.ih index 6b70ef10ea..8705e4ae5b 100644 --- a/ospray/fb/Tile.ih +++ b/ospray/fb/Tile.ih @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/math/region.ih" +#include "../common/OSPCommon.ih" +#include "../math/region.ih" /*! a screen tile. the memory layout of this class has to _exactly_ match the (C++-)one in tile.h */ @@ -36,8 +36,8 @@ struct Tile { }; struct VaryingTile { - varying float r[TILE_SIZE*TILE_SIZE/programCount]; /*! red */ - varying float g[TILE_SIZE*TILE_SIZE/programCount]; /*! green */ + varying float r[TILE_SIZE*TILE_SIZE/programCount]; /*!< red */ + varying float g[TILE_SIZE*TILE_SIZE/programCount]; /*!< green */ varying float b[TILE_SIZE*TILE_SIZE/programCount]; /*!< blue */ varying float a[TILE_SIZE*TILE_SIZE/programCount]; /*!< alpha */ varying float z[TILE_SIZE*TILE_SIZE/programCount]; /*!< depth */ diff --git a/ospray/geometry/Cylinders.cpp b/ospray/geometry/Cylinders.cpp index fd9f257650..5d4b00b3c8 100644 --- a/ospray/geometry/Cylinders.cpp +++ b/ospray/geometry/Cylinders.cpp @@ -18,8 +18,8 @@ // ospray #include "Cylinders.h" -#include "ospray/common/Data.h" -#include "ospray/common/Model.h" +#include "common/Data.h" +#include "common/Model.h" // ispc-generated files #include "Cylinders_ispc.h" diff --git a/ospray/geometry/Cylinders.ispc b/ospray/geometry/Cylinders.ispc index c431216f76..bbf8fbd318 100644 --- a/ospray/geometry/Cylinders.ispc +++ b/ospray/geometry/Cylinders.ispc @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/Geometry.cpp b/ospray/geometry/Geometry.cpp index a223124588..ca31613025 100644 --- a/ospray/geometry/Geometry.cpp +++ b/ospray/geometry/Geometry.cpp @@ -16,7 +16,7 @@ // ospray #include "Geometry.h" -#include "ospray/common/Library.h" +#include "common/Library.h" // stl #include // ISPC exports diff --git a/ospray/geometry/Geometry.h b/ospray/geometry/Geometry.h index 79578244a8..98a32ea911 100644 --- a/ospray/geometry/Geometry.h +++ b/ospray/geometry/Geometry.h @@ -16,9 +16,9 @@ #pragma once -#include "ospray/common/Managed.h" -#include "ospray/common/OSPCommon.h" -#include "ospray/common/Material.h" +#include "common/Managed.h" +#include "common/OSPCommon.h" +#include "common/Material.h" namespace ospray { diff --git a/ospray/geometry/Geometry.ih b/ospray/geometry/Geometry.ih index f9f1d2d3f6..005b94afe7 100644 --- a/ospray/geometry/Geometry.ih +++ b/ospray/geometry/Geometry.ih @@ -16,9 +16,9 @@ #pragma once -#include "ospray/common/Material.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/DifferentialGeometry.ih" +#include "../common/Material.ih" +#include "../common/Ray.ih" +#include "../common/DifferentialGeometry.ih" /*! Geometries are supposed to fill certain members of DifferentialGeometry: calculate Ng, Ns, st, color, and materialID if the respective bit DG_NG, diff --git a/ospray/geometry/Instance.cpp b/ospray/geometry/Instance.cpp index 10db1408de..d0b7429f17 100644 --- a/ospray/geometry/Instance.cpp +++ b/ospray/geometry/Instance.cpp @@ -18,7 +18,7 @@ // ospray #include "Instance.h" -#include "ospray/common/Model.h" +#include "common/Model.h" // ispc exports #include "Instance_ispc.h" diff --git a/ospray/geometry/Instance.h b/ospray/geometry/Instance.h index 3a919011bc..2707bb51d2 100644 --- a/ospray/geometry/Instance.h +++ b/ospray/geometry/Instance.h @@ -17,7 +17,7 @@ #pragma once #include "Geometry.h" -#include "ospray/common/Data.h" +#include "common/Data.h" namespace ospray { diff --git a/ospray/geometry/Instance.ispc b/ospray/geometry/Instance.ispc index 005c2f1f9c..07758044ad 100644 --- a/ospray/geometry/Instance.ispc +++ b/ospray/geometry/Instance.ispc @@ -15,12 +15,12 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/math/AffineSpace.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "math/AffineSpace.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/Isosurfaces.cpp b/ospray/geometry/Isosurfaces.cpp index 4165db9222..6aeb903d6a 100644 --- a/ospray/geometry/Isosurfaces.cpp +++ b/ospray/geometry/Isosurfaces.cpp @@ -18,8 +18,8 @@ // ospray #include "Isosurfaces.h" -#include "ospray/common/Data.h" -#include "ospray/common/Model.h" +#include "common/Data.h" +#include "common/Model.h" // ispc-generated files #include "Isosurfaces_ispc.h" diff --git a/ospray/geometry/Isosurfaces.h b/ospray/geometry/Isosurfaces.h index 7c7fe5a42f..a5d1885705 100644 --- a/ospray/geometry/Isosurfaces.h +++ b/ospray/geometry/Isosurfaces.h @@ -17,7 +17,7 @@ #pragma once #include "Geometry.h" -#include "ospray/volume/Volume.h" +#include "volume/Volume.h" namespace ospray { diff --git a/ospray/geometry/Isosurfaces.ispc b/ospray/geometry/Isosurfaces.ispc index 6f4ae17e4f..d5503cfcbc 100644 --- a/ospray/geometry/Isosurfaces.ispc +++ b/ospray/geometry/Isosurfaces.ispc @@ -15,12 +15,12 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" -#include "ospray/volume/Volume.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" +#include "volume/Volume.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/Slices.cpp b/ospray/geometry/Slices.cpp index 1d0aba25b2..4febbc393c 100644 --- a/ospray/geometry/Slices.cpp +++ b/ospray/geometry/Slices.cpp @@ -18,8 +18,8 @@ // ospray #include "Slices.h" -#include "ospray/common/Data.h" -#include "ospray/common/Model.h" +#include "common/Data.h" +#include "common/Model.h" // ispc-generated files #include "Slices_ispc.h" diff --git a/ospray/geometry/Slices.h b/ospray/geometry/Slices.h index cd2ae81baa..e3ba3790ec 100644 --- a/ospray/geometry/Slices.h +++ b/ospray/geometry/Slices.h @@ -17,7 +17,7 @@ #pragma once #include "Geometry.h" -#include "ospray/volume/Volume.h" +#include "volume/Volume.h" namespace ospray { diff --git a/ospray/geometry/Slices.ispc b/ospray/geometry/Slices.ispc index e81e956f57..3b20cf2772 100644 --- a/ospray/geometry/Slices.ispc +++ b/ospray/geometry/Slices.ispc @@ -15,12 +15,12 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" -#include "ospray/volume/Volume.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" +#include "volume/Volume.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/Spheres.cpp b/ospray/geometry/Spheres.cpp index 25642bbd6e..2f21a98827 100644 --- a/ospray/geometry/Spheres.cpp +++ b/ospray/geometry/Spheres.cpp @@ -18,8 +18,8 @@ // ospray #include "Spheres.h" -#include "ospray/common/Data.h" -#include "ospray/common/Model.h" +#include "common/Data.h" +#include "common/Model.h" // ispc-generated files #include "Spheres_ispc.h" diff --git a/ospray/geometry/Spheres.ispc b/ospray/geometry/Spheres.ispc index a38d3eaef6..144292c420 100644 --- a/ospray/geometry/Spheres.ispc +++ b/ospray/geometry/Spheres.ispc @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/StreamLines.cpp b/ospray/geometry/StreamLines.cpp index fa046d9db4..ce8e8a3f46 100644 --- a/ospray/geometry/StreamLines.cpp +++ b/ospray/geometry/StreamLines.cpp @@ -18,8 +18,8 @@ // ospray #include "StreamLines.h" -#include "ospray/common/Data.h" -#include "ospray/common/Model.h" +#include "common/Data.h" +#include "common/Model.h" // ispc-generated files #include "StreamLines_ispc.h" diff --git a/ospray/geometry/StreamLines.ispc b/ospray/geometry/StreamLines.ispc index 182ff8a034..c10517761a 100644 --- a/ospray/geometry/StreamLines.ispc +++ b/ospray/geometry/StreamLines.ispc @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/math/vec.ih" -#include "ospray/math/box.ih" -#include "ospray/common/Ray.ih" -#include "ospray/common/Model.ih" -#include "ospray/geometry/Geometry.ih" +#include "math/vec.ih" +#include "math/box.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "geometry/Geometry.ih" // embree #include "embree2/rtcore.isph" #include "embree2/rtcore_scene.isph" diff --git a/ospray/geometry/TriangleMesh.cpp b/ospray/geometry/TriangleMesh.cpp index e6931af209..735b7e2bc7 100644 --- a/ospray/geometry/TriangleMesh.cpp +++ b/ospray/geometry/TriangleMesh.cpp @@ -19,12 +19,12 @@ #endif #if OSP_COMPOSITING_TEST -# include "ospray/mpi/MPICommon.h" +# include "mpi/MPICommon.h" #endif // ospray #include "TriangleMesh.h" -#include "ospray/common/Model.h" +#include "common/Model.h" #include "../include/ospray/ospray.h" // embree #include "embree2/rtcore.h" diff --git a/ospray/geometry/TriangleMesh.h b/ospray/geometry/TriangleMesh.h index aff48377c1..5efa14a974 100644 --- a/ospray/geometry/TriangleMesh.h +++ b/ospray/geometry/TriangleMesh.h @@ -17,7 +17,7 @@ #pragma once #include "Geometry.h" -#include "ospray/common/Data.h" +#include "common/Data.h" namespace ospray { diff --git a/ospray/geometry/TriangleMesh.ispc b/ospray/geometry/TriangleMesh.ispc index bb8ec8aa94..e1bb1a49f5 100644 --- a/ospray/geometry/TriangleMesh.ispc +++ b/ospray/geometry/TriangleMesh.ispc @@ -16,8 +16,8 @@ // ospray #include "TriangleMesh.ih" -#include "ospray/common/Model.ih" -#include "ospray/math/LinearSpace.ih" +#include "common/Model.ih" +#include "math/LinearSpace.ih" // embree #include "embree2/rtcore.isph" @@ -243,17 +243,6 @@ void TriangleMesh_Constructor(uniform TriangleMesh *uniform mesh, mesh->geom_materialID = geom_materialID; } -#ifdef OSPRAY_INTERSECTION_FILTER -static void intersectionFilter(void* uniform ptr, /*!< pointer to user data */ - varying Ray &ray /*!< intersection to filter */) -{ - uniform Geometry *uniform geom = (uniform Geometry *uniform)ptr; - if (ray.intersectionFilter) { - ray.intersectionFilter(geom,(varying Ray &)ray); - } -} -#endif - export void *uniform TriangleMesh_create(void *uniform cppEquivalent) { uniform TriangleMesh *uniform mesh = uniform new uniform TriangleMesh; @@ -298,9 +287,4 @@ export void *uniform TriangleMesh_set(void *uniform _mesh, (Material*uniform)material, (Material*uniform*uniform)materialList, prim_materialID); -#ifdef OSPRAY_INTERSECTION_FILTER - rtcSetUserData(model->embreeSceneHandle,geomID,mesh); - rtcSetIntersectionFilterFunction(model->embreeSceneHandle,geomID, - (uniform RTCFilterFuncVarying)&intersectionFilter); -#endif } diff --git a/ospray/include/ospray/ospray.h b/ospray/include/ospray/ospray.h index 31673bd20b..888de11db5 100644 --- a/ospray/include/ospray/ospray.h +++ b/ospray/include/ospray/ospray.h @@ -41,8 +41,8 @@ #include #include -#include "ospray/OSPDataType.h" -#include "ospray/OSPTexture.h" +#include "OSPDataType.h" +#include "OSPTexture.h" #ifdef _WIN32 # ifdef ospray_EXPORTS @@ -494,11 +494,6 @@ extern "C" { /*! add a c-string (zero-terminated char *) parameter to another object */ OSPRAY_INTERFACE void ospSetString(OSPObject, const char *id, const char *s); - /*! add a object-typed parameter to another object - - \warning this call has been superseded by ospSetObject, and will eventually get removed */ - OSP_DEPRECATED OSPRAY_INTERFACE void ospSetParam(OSPObject, const char *id, OSPObject other); - /*! add a object-typed parameter to another object */ OSPRAY_INTERFACE void ospSetObject(OSPObject, const char *id, OSPObject other); @@ -548,70 +543,6 @@ extern "C" { OSPRAY_INTERFACE void ospSetVoidPtr(OSPObject, const char *id, void *v); #ifdef __cplusplus - /*! \brief Object and parameter introspection. These are all DEPRECATED. */ - /*! */ - /*! These functions are used to retrieve the type or handle of an object, */ - /*! or the name, type, or value of a parameter associated with an object. */ - /*! These functions can be expensive in cases where the host and compute */ - /*! device are separated by a PCI bus or interconnect. */ - /*! */ - /*! If the type of the requested parameter does not match that indicated */ - /*! by the function name, or the named parameter does not exist, then the */ - /*! functions return '0'. */ - - /*! \brief Get the handle of the named data array associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetData(OSPObject object, const char *name, OSPData *value); - - /*! \brief Get a copy of the data in an array (the application is responsible for freeing this pointer). */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetDataValues(OSPData object, - void **pointer, - size_t *count, - OSPDataType *type); - - /*! \brief Get the named scalar floating point value associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetf(OSPObject object, const char *name, float *value); - - /*! \brief Get the named scalar integer associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGeti(OSPObject object, const char *name, int *value); - - /*! \brief Get the material associated with a geometry object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetMaterial(OSPGeometry geometry, OSPMaterial *value); - - /*! \brief Get the named object associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetObject(OSPObject object, const char *name, OSPObject *value); - - /*! \brief Retrieve a NULL-terminated list of the parameter names associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetParameters(OSPObject object, char ***value); - - /*! \brief Get a pointer to a copy of the named character string associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetString(OSPObject object, const char *name, char **value); - - /*! \brief Get the type of the named parameter or the given object (if 'name' is NULL). */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetType(OSPObject object, const char *name, OSPDataType *value); - - /*! \brief Get the named 2-vector floating point value associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetVec2f(OSPObject object, const char *name, osp::vec2f *value); - - /*! \brief Get the named 3-vector floating point value associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetVec3f(OSPObject object, const char *name, osp::vec3f *value); - - /*! \brief Get the named 3-vector integer value associated with an object. */ - /*! \warning this call has been deprecated and will eventually be removed */ - OSP_DEPRECATED OSPRAY_INTERFACE int ospGetVec3i(OSPObject object, const char *name, osp::vec3i *value); - - // probably want to deprecate those as well: - /*! add 2-float parameter to given object */ OSPRAY_INTERFACE void ospSetVec2f(OSPObject, const char *id, const osp::vec2f &v); diff --git a/ospray/lights/AmbientLight.ispc b/ospray/lights/AmbientLight.ispc index 379acb4be9..2772a7581a 100644 --- a/ospray/lights/AmbientLight.ispc +++ b/ospray/lights/AmbientLight.ispc @@ -15,8 +15,8 @@ // ======================================================================== // #include "Light.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" struct AmbientLight { @@ -54,7 +54,7 @@ Light_EvalRes AmbientLight_eval(const uniform Light* uniform super, uniform AmbientLight* uniform self = (uniform AmbientLight* uniform)super; Light_EvalRes res; - res.value = self->radiance; + res.radiance = self->radiance; res.dist = inf; res.pdf = cosineSampleHemispherePDF(max(dot(dg.Ns, dir), 0.f)); diff --git a/ospray/lights/DirectionalLight.ispc b/ospray/lights/DirectionalLight.ispc index 2c516312d7..aa9f8300af 100644 --- a/ospray/lights/DirectionalLight.ispc +++ b/ospray/lights/DirectionalLight.ispc @@ -15,8 +15,8 @@ // ======================================================================== // #include "Light.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" struct DirectionalLight { @@ -63,10 +63,10 @@ Light_EvalRes DirectionalLight_eval(const uniform Light* uniform super, res.dist = inf; if (self->cosAngle < COS_ANGLE_MAX && dot(self->frame.vz, dir) > self->cosAngle) { - res.value = self->radiance * self->pdf; + res.radiance = self->radiance * self->pdf; res.pdf = self->pdf; } else { - res.value = make_vec3f(0.f); + res.radiance = make_vec3f(0.f); res.pdf = 0.f; } diff --git a/ospray/lights/HDRILight.h b/ospray/lights/HDRILight.h index 00e77583c1..2f2e422284 100644 --- a/ospray/lights/HDRILight.h +++ b/ospray/lights/HDRILight.h @@ -17,7 +17,7 @@ #pragma once #include "Light.h" -#include "ospray/texture/Texture2D.h" +#include "texture/Texture2D.h" namespace ospray { diff --git a/ospray/lights/HDRILight.ispc b/ospray/lights/HDRILight.ispc index e15d43621a..3e18938770 100644 --- a/ospray/lights/HDRILight.ispc +++ b/ospray/lights/HDRILight.ispc @@ -15,10 +15,10 @@ // ======================================================================== // #include "Light.ih" -#include "ospray/math/LinearSpace.ih" -#include "ospray/math/Distribution2D.ih" -#include "ospray/math/sampling.ih" -#include "ospray/texture/Texture2D.ih" +#include "math/LinearSpace.ih" +#include "math/Distribution2D.ih" +#include "math/sampling.ih" +#include "texture/Texture2D.ih" struct HDRILight { @@ -88,7 +88,7 @@ Light_EvalRes HDRILight_eval(const uniform Light* uniform super, const float v = acos(localDir.z) * one_over_pi; const vec2f uv = make_vec2f(u, v); - res.value = get3f(self->map, uv) * self->intensity; + res.radiance = get3f(self->map, uv) * self->intensity; res.dist = inf; // domain of Distribution2D is shifted by half a texel compared to texture diff --git a/ospray/lights/Light.cpp b/ospray/lights/Light.cpp index 4b422dce34..3fae01eea8 100644 --- a/ospray/lights/Light.cpp +++ b/ospray/lights/Light.cpp @@ -16,7 +16,7 @@ //ospray #include "Light.h" -#include "ospray/common/Library.h" +#include "common/Library.h" //system #include diff --git a/ospray/lights/Light.h b/ospray/lights/Light.h index 004ef9e1e4..d8238cdf4e 100644 --- a/ospray/lights/Light.h +++ b/ospray/lights/Light.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/Managed.h" +#include "common/Managed.h" namespace ospray { diff --git a/ospray/lights/Light.ih b/ospray/lights/Light.ih index d4ed16926d..fb07620072 100644 --- a/ospray/lights/Light.ih +++ b/ospray/lights/Light.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/DifferentialGeometry.ih" +#include "common/DifferentialGeometry.ih" struct Light; @@ -30,23 +30,23 @@ struct Light_SampleRes //! compute the weighted radiance at a point caused by a sample on the light source // by convention, giving (0, 0) as "random" numbers should sample the "center" -// of the light source (used by the raytracing renderers such as the OBJ renderer) -typedef Light_SampleRes (*Light_SampleFunc)(const uniform Light* uniform self, - const DifferentialGeometry& dg, /*! point to generate the sample for >*/ - const vec2f& s); /*! random numbers to generate the sample >*/ +// of the light source (used by the raytracing renderers such as the SciVis renderer) +typedef Light_SampleRes (*Light_SampleFunc)(const uniform Light* uniform, + const DifferentialGeometry&, /*! point to generate the sample for >*/ + const vec2f& s); /*! random numbers to generate the sample >*/ struct Light_EvalRes { - vec3f value; //!< radiance that arrives at the given point (not weighted by pdf) - float dist; + vec3f radiance; //!< radiance that arrives at the given point (not weighted by pdf) + float dist; //!< distance from the given point and direction to the light source float pdf; //!< probability density that the direction would have been sampled }; //! compute the radiance, distance and pdf caused by the light source (pointed to by the given direction) -typedef Light_EvalRes (*Light_EvalFunc)(const uniform Light* uniform self, - const DifferentialGeometry& dg, /*! point to evaluate illumination for >*/ - const vec3f& dir); /*! direction towards the light source >*/ +typedef Light_EvalRes (*Light_EvalFunc)(const uniform Light* uniform, + const DifferentialGeometry&, //! point to evaluate illumination for >*/ + const vec3f& dir); //! direction towards the light source, normalized >*/ struct Light diff --git a/ospray/lights/Light.ispc b/ospray/lights/Light.ispc index 24c8051bee..89b8aacbc6 100644 --- a/ospray/lights/Light.ispc +++ b/ospray/lights/Light.ispc @@ -21,7 +21,7 @@ Light_EvalRes Light_eval(const uniform Light* uniform, const vec3f&) { Light_EvalRes res; - res.value = make_vec3f(0.f); + res.radiance = make_vec3f(0.f); res.dist = inf; res.pdf = 0.f; return res; diff --git a/ospray/lights/PointLight.ispc b/ospray/lights/PointLight.ispc index 152e644b8f..50b3f56385 100644 --- a/ospray/lights/PointLight.ispc +++ b/ospray/lights/PointLight.ispc @@ -15,8 +15,8 @@ // ======================================================================== // #include "Light.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" struct PointLight { @@ -83,7 +83,7 @@ Light_EvalRes PointLight_eval(const uniform Light* uniform super, { const PointLight* uniform self = (PointLight* uniform)super; Light_EvalRes res; - res.value = make_vec3f(0.f); + res.radiance = make_vec3f(0.f); res.dist = inf; res.pdf = 0.f; @@ -106,7 +106,7 @@ Light_EvalRes PointLight_eval(const uniform Light* uniform super, const float cosTheta = sqrt(1.f - sinTheta2); res.pdf = uniformSampleConePDF(cosTheta); const float invdist = rcp(t_near); - res.value = self->power * res.pdf * sqr(invdist); + res.radiance = self->power * res.pdf * sqr(invdist); } } } diff --git a/ospray/lights/QuadLight.ispc b/ospray/lights/QuadLight.ispc index 20b9b8f06c..e55d3d1ac2 100644 --- a/ospray/lights/QuadLight.ispc +++ b/ospray/lights/QuadLight.ispc @@ -54,7 +54,7 @@ Light_SampleRes QuadLight_sample(const uniform Light* uniform super, // convert to pdf wrt. solid angle const float cosd = dot(self->nnormal, res.dir); - res.pdf = self->ppdf * (dist * dist) / abs(cosd); + res.pdf = self->ppdf * sqr(dist) / abs(cosd); // emit only to one side res.weight = cosd > 0.f ? self->radiance * rcp(res.pdf) : make_vec3f(0.f); @@ -62,6 +62,40 @@ Light_SampleRes QuadLight_sample(const uniform Light* uniform super, return res; } +Light_EvalRes QuadLight_eval(const uniform Light* uniform super, + const DifferentialGeometry& dg, + const vec3f& dir) +{ + uniform QuadLight* uniform self = (uniform QuadLight* uniform)super; + Light_EvalRes res; + res.radiance = make_vec3f(0.f); + res.pdf = 0.f; + res.dist = inf; + + // backfacing? + const float cosd = dot(self->nnormal, dir); + // denominator = dot(cross(edge1, edge2), dir) == cosd/ppdf + if (cosd <= 0.f) + return res; + + const vec3f c = self->position - dg.P; + const vec3f r = cross(c, dir); + const float u = dot(r, self->edge1); + const float v = -dot(r, self->edge2); + + // u/denominator > 1? + if (min(u, v) < 0.f | max(u, v)*self->ppdf > cosd) + return res; + + const float rcosd = rcp(cosd); + const float dist = dot(self->nnormal, c) * rcosd; + res.radiance = self->radiance; + res.pdf = self->ppdf * sqr(dist) * rcosd; + res.dist = dist; + + return res; +} + // Exports (called from C++) ////////////////////////////////////////////////////////////////////////////// @@ -80,8 +114,8 @@ export void QuadLight_set(void* uniform super, self->radiance = radiance; const uniform vec3f ndirection = cross(edge2, edge1); - self->ppdf = rcp(length(ndirection)); - self->nnormal = ndirection * self->ppdf; + self->ppdf = rcp(length(ndirection)); // 1/area + self->nnormal = ndirection * self->ppdf; // normalize } //! Create an ispc-side QuadLight object @@ -91,6 +125,7 @@ export void* uniform QuadLight_create() Light_Constructor(&self->super); self->super.sample = QuadLight_sample; + self->super.eval = QuadLight_eval; QuadLight_set(self, make_vec3f(0.f), diff --git a/ospray/lights/SpotLight.ispc b/ospray/lights/SpotLight.ispc index 10a249930a..eb802ed945 100644 --- a/ospray/lights/SpotLight.ispc +++ b/ospray/lights/SpotLight.ispc @@ -15,8 +15,8 @@ // ======================================================================== // #include "Light.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" struct SpotLight { @@ -76,7 +76,7 @@ Light_EvalRes SpotLight_eval(const uniform Light* uniform super, { const SpotLight* uniform self = (SpotLight* uniform)super; Light_EvalRes res; - res.value = make_vec3f(0.f); + res.radiance = make_vec3f(0.f); res.dist = inf; res.pdf = 0.f; @@ -92,7 +92,7 @@ Light_EvalRes SpotLight_eval(const uniform Light* uniform super, if (dot(vd, vd) < sqr(self->radius)) { // inside disk? const float angularAttenuation = min((cosAngle - self->cosAngleMax) * self->cosAngleScale, 1.f); const float pdf = self->diskPdf * cosAngle; - res.value = self->power * (angularAttenuation * pdf); // *sqr(t)/sqr(t) cancels + res.radiance = self->power * (angularAttenuation * pdf); // *sqr(t)/sqr(t) cancels res.dist = t; res.pdf = pdf * sqr(t); } diff --git a/ospray/math/Distribution2D.ih b/ospray/math/Distribution2D.ih index f55bf92c97..88e6c262d4 100644 --- a/ospray/math/Distribution2D.ih +++ b/ospray/math/Distribution2D.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/math/vec.ih" +#include "math/vec.ih" struct Distribution2D { diff --git a/ospray/math/box.ih b/ospray/math/box.ih index d641b3eeee..96da72f246 100644 --- a/ospray/math/box.ih +++ b/ospray/math/box.ih @@ -75,61 +75,13 @@ inline uniform box1f make_box1f(const uniform float f) inline uniform box1f make_box1f(const uniform float lo, const uniform float hi) { uniform box1f bb; bb.lower = lo; bb.upper = hi; return bb; } -inline uniform box1f box_union(const uniform box1f &a, const uniform box1f &b) +inline uniform box1f box_extend(const uniform box1f &a, const uniform box1f &b) { return make_box1f(min(a.lower,b.lower),max(a.upper,b.upper)); } // ------------------------------------------------------- // box3f 'constructors' // ------------------------------------------------------- -#if 0 -//! make a box with given lower and upper coordinates -inline uniform box3f make_box3f(const uniform vec3f lo, const uniform vec3f hi) -{ uniform box3f bb; bb.lower = lo; bb.upper = hi; return bb; } - -//! make a box with given lower and upper coordinates -inline box3f make_box3f(const vec3f lo, const vec3f hi) -{ box3f bb; bb.lower = lo; bb.upper = hi; return bb; } - -//! make a box with given lower and upper coordinates -inline box3fa make_box3f(const vec3fa lo, const vec3fa hi) -{ box3fa bb; bb.lower = lo; bb.upper = hi; return bb; } - -//! make a 3f-box out of given 3fa-box -inline box3f make_box3f(const box3fa &other) -{ box3f bb; bb.lower = make_vec3f(other.lower); bb.upper = make_vec3f(other.upper); return bb; } - -//! make a 3f-box out of given 3fa-box -inline uniform box3f make_box3f(const uniform box3fa &other) -{ uniform box3f bb; bb.lower = make_vec3f(other.lower); bb.upper = make_vec3f(other.upper); return bb; } - - -// ------------------------------------------------------- -// box3fa 'constructors' -// ------------------------------------------------------- - -//! make a box with given lower and upper coordinates -inline uniform box3fa make_box3fa(const uniform box3f &b) -{ uniform box3fa bb; bb.lower = make_vec3fa(b.lower); bb.upper = make_vec3fa(b.upper); return bb; } - -//! make a box with given lower and upper coordinates -inline box3fa make_box3fa(const vec3f lo, const vec3f hi) -{ box3fa bb; bb.lower = make_vec3fa(lo); bb.upper = make_vec3fa(hi); return bb; } - -//! make a box with given lower and upper coordinates -inline box3fa make_box3fa(const vec3fa lo, const vec3fa hi) -{ box3fa bb; bb.lower = lo; bb.upper = hi; return bb; } - -//! make a box with given lower and upper coordinates -inline uniform box3fa make_box3fa(const uniform vec3f lo, const uniform vec3f hi) -{ uniform box3fa bb; bb.lower = make_vec3fa(lo); bb.upper = make_vec3fa(hi); return bb; } - -//! make a box with given lower and upper coordinates -inline uniform box3fa make_box3fa(uniform const vec3fa lo, uniform const vec3fa hi) -{ uniform box3fa bb; bb.lower = lo; bb.upper = hi; return bb; } -#else - - #define MAKE_BOX_CONSTRUCTORS_uv_3T_fromVec3(univary,Tabb,otherT) \ inline univary box3##Tabb make_box3##Tabb(const univary vec3##otherT lower, \ const univary vec3##otherT upper) { \ @@ -155,6 +107,11 @@ inline uniform box3fa make_box3fa(uniform const vec3fa lo, uniform const vec3fa return bb; \ } +#define MAKE_BOX_CONSTRUCTORS_uv_3T_empty(Tabb) \ + inline uniform box3##Tabb make_box3##Tabb##_empty() { \ + return make_box3##Tabb(make_vec3##Tabb(inf), make_vec3##Tabb(neg_inf)); \ + } + #define MAKE_BOX_CONSTRUCTORS_uv_3T(univary,Tabb) \ MAKE_BOX_CONSTRUCTORS_uv_3T_fromVec3(univary,Tabb,f) \ MAKE_BOX_CONSTRUCTORS_uv_3T_fromVec3(univary,Tabb,fa) \ @@ -162,7 +119,7 @@ inline uniform box3fa make_box3fa(uniform const vec3fa lo, uniform const vec3fa \ MAKE_BOX_CONSTRUCTORS_uv_3T_fromBox3(univary,Tabb,f) \ MAKE_BOX_CONSTRUCTORS_uv_3T_fromBox3(univary,Tabb,fa) \ - MAKE_BOX_CONSTRUCTORS_uv_3T_fromBox3(univary,Tabb,i) + MAKE_BOX_CONSTRUCTORS_uv_3T_fromBox3(univary,Tabb,i) #define MAKE_BOX_CONSTRUCTORS_uv_3(univary) \ MAKE_BOX_CONSTRUCTORS_uv_3T(univary,i) \ @@ -172,12 +129,47 @@ inline uniform box3fa make_box3fa(uniform const vec3fa lo, uniform const vec3fa #define MAKE_BOX_CONSTRUCTORS_uv(univary) \ MAKE_BOX_CONSTRUCTORS_uv_3(univary) -MAKE_BOX_CONSTRUCTORS_uv(uniform); -MAKE_BOX_CONSTRUCTORS_uv(varying); +MAKE_BOX_CONSTRUCTORS_uv(uniform) +MAKE_BOX_CONSTRUCTORS_uv(varying) +MAKE_BOX_CONSTRUCTORS_uv_3T_empty(f) +MAKE_BOX_CONSTRUCTORS_uv_3T_empty(fa) + +#undef MAKE_BOX_CONSTRUCTORS_uv_3T_fromVec3 +#undef MAKE_BOX_CONSTRUCTORS_uv_3T_fromBox3 +#undef MAKE_BOX_CONSTRUCTORS_uv_3T_empty +#undef MAKE_BOX_CONSTRUCTORS_uv_3T +#undef MAKE_BOX_CONSTRUCTORS_uv_3 +#undef MAKE_BOX_CONSTRUCTORS_uv + +// ------------------------------------------------------- +// box3 'operations' +// ------------------------------------------------------- + +#define MAKE_BOX_OPERATIONS_uv_3T(univary,T) \ + inline univary vec3##T box_size(univary box3##T bb) { \ + return bb.upper - bb.lower; \ + } \ + \ + inline univary box3##T \ + box_extend(const univary box3##T bb, const univary vec3##T v) { \ + return make_box3##T(min(bb.lower,v), max(bb.upper,v)); \ + } \ + \ + inline univary box3##T \ + box_extend(const univary box3##T bb, const univary box3##T other) { \ + return make_box3##T(min(bb.lower,other.lower), max(bb.upper,other.upper)); \ + } +#define MAKE_BOX_OPERATIONS_uv(univary) \ + MAKE_BOX_OPERATIONS_uv_3T(univary,i) \ + MAKE_BOX_OPERATIONS_uv_3T(univary,f) \ + MAKE_BOX_OPERATIONS_uv_3T(univary,fa) +MAKE_BOX_OPERATIONS_uv(uniform) +MAKE_BOX_OPERATIONS_uv(varying) -#endif +#undef MAKE_BOX_OPERATIONS_uv_3T +#undef MAKE_BOX_OPERATIONS_uv //! print given box to stdout void print_box(const uniform box3f &bbox); diff --git a/ospray/math/math.ih b/ospray/math/math.ih index 52565fcf95..4b8ca2cd9e 100644 --- a/ospray/math/math.ih +++ b/ospray/math/math.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/OSPCommon.ih" +#include "../common/OSPCommon.ih" // ------------------------------------------------------------------ // Constants diff --git a/ospray/math/sampling.ih b/ospray/math/sampling.ih index 45f6f257e9..79d5f90940 100644 --- a/ospray/math/sampling.ih +++ b/ospray/math/sampling.ih @@ -22,8 +22,7 @@ // sampling functions often come in pairs: sample and pdf (needed later for MIS) // good reference is "Total Compendium" by Philip Dutre http://people.cs.kuleuven.be/~philip.dutre/GI/ -#include "ospray/math/vec.ih" - +#include "math/vec.ih" inline vec3f cartesian(const float phi, const float sinTheta, const float cosTheta) { diff --git a/ospray/math/vec.ih b/ospray/math/vec.ih index 5d124f489e..e35fc9d9d6 100644 --- a/ospray/math/vec.ih +++ b/ospray/math/vec.ih @@ -28,7 +28,7 @@ TYPE x,y,z; \ }; \ struct vec3##ABB##a { \ - TYPE x,y,z,a; \ + TYPE x,y,z,w; \ }; \ #define __define_ispc_vector4(TYPE,ABB) \ @@ -410,6 +410,28 @@ inline varying vec3f negate(const varying vec3f &a) inline vec3##abb opname (const type a, const vec3##abb b) { \ return make_vec3##abb(a op b.x, a op b.y, a op b.z); \ } \ + /* vec3##abb##a */ \ + inline uniform vec3##abb##a opname (const uniform vec3##abb##a a, \ + const uniform vec3##abb##a b) { \ + return make_vec3##abb##a(a.x op b.x, a.y op b.y, a.z op b.z); \ + } \ + inline vec3##abb##a opname (const vec3##abb##a a, const vec3##abb##a b) { \ + return make_vec3##abb##a(a.x op b.x, a.y op b.y, a.z op b.z); \ + } \ + inline uniform vec3##abb##a opname (const uniform vec3##abb##a a, \ + const uniform type b) { \ + return make_vec3##abb##a(a.x op b, a.y op b, a.z op b); \ + } \ + inline vec3##abb##a opname (const vec3##abb##a a, const type b) { \ + return make_vec3##abb##a(a.x op b, a.y op b, a.z op b); \ + } \ + inline uniform vec3##abb##a opname (const uniform type a, \ + const uniform vec3##abb##a b) { \ + return make_vec3##abb##a(a op b.x, a op b.y, a op b.z); \ + } \ + inline vec3##abb##a opname (const type a, const vec3##abb##a b) { \ + return make_vec3##abb##a(a op b.x, a op b.y, a op b.z); \ + } \ /* vec4##abb */ \ inline uniform vec4##abb opname (const uniform vec4##abb a, \ const uniform vec4##abb b) { \ @@ -436,7 +458,7 @@ inline varying vec3f negate(const varying vec3f &a) #define __define_binary_operator(opname,op) \ __define_binary_operator_typed(opname,op,f,float) \ __define_binary_operator_typed(opname,op,i,int32) \ - __define_binary_operator_typed(opname,op,ui,uint32) + __define_binary_operator_typed(opname,op,ui,uint32) // define 'regular' operators @@ -480,6 +502,10 @@ inline uniform bool eq(const uniform vec3f a, const uniform vec3f b) { return a.x==b.x && a.y==b.y && a.z==b.z; } inline bool eq(const vec3f a, const vec3f b) { return a.x==b.x & a.y==b.y & a.z==b.z; } +inline uniform bool eq(const uniform vec3fa a, const uniform vec3fa b) +{ return a.x==b.x && a.y==b.y && a.z==b.z; } +inline bool eq(const vec3fa a, const vec3fa b) +{ return a.x==b.x & a.y==b.y & a.z==b.z; } inline uniform bool ne(const uniform vec2f a, const uniform vec2f b) { return !eq(a,b); } @@ -489,6 +515,10 @@ inline uniform bool ne(const uniform vec3f a, const uniform vec3f b) { return !eq(a,b); } inline bool ne(const vec3f a, const vec3f b) { return !eq(a,b); } +inline uniform bool ne(const uniform vec3fa a, const uniform vec3fa b) +{ return !eq(a,b); } +inline bool ne(const vec3fa a, const vec3fa b) +{ return !eq(a,b); } // ------------------------------------------------------------------ @@ -508,6 +538,15 @@ inline uniform float distance(const uniform vec3f a, const uniform vec3f b) { re inline varying float distance(const varying vec3f a, const uniform vec3f b) { return length(sub(a,b)); } +inline uniform float dot(const uniform vec3fa a, const uniform vec3fa b) { return a.x*b.x+a.y*b.y+a.z*b.z; } +inline varying float dot(const varying vec3fa a, const varying vec3fa b) { return a.x*b.x+a.y*b.y+a.z*b.z; } + +inline uniform float length(const uniform vec3fa a) { return sqrtf(dot(a,a)); } +inline varying float length(const varying vec3fa a) { return sqrtf(dot(a,a)); } + +inline uniform float distance(const uniform vec3fa a, const uniform vec3fa b) { return length(sub(a,b)); } +inline varying float distance(const varying vec3fa a, const uniform vec3fa b) { return length(sub(a,b)); } + // ------------------------------------------------------------------ // cross product // ------------------------------------------------------------------ @@ -536,9 +575,28 @@ inline vec3f normalize(const vec3f v) inline vec3f normalize(const vec3f v, float &len) { len = sqrtf(dot(v,v)); return v * rcpf(len); } -inline vec3f safe_normalize(const vec3f v) +inline vec3f safe_normalize(const vec3f v) { return v * (1.f/sqrt(max(1e-6f,dot(v,v)))); } +/*! differentiated normalization */ +inline varying vec3f dnormalize(const varying vec3f& p, const varying vec3f& dp) +{ + const float pp = dot(p,p); + const float pdp = dot(p,dp); + return (pp*dp-pdp*p)*rcp(pp)*rsqrt(pp); +} + + +inline uniform vec3fa normalize(const uniform vec3fa &v) { return v * (1.f/sqrt(dot(v,v))); } +inline varying vec3fa normalize(const varying vec3fa v) { return v * (1.f/sqrt(dot(v,v))); } + +inline varying vec3fa dnormalize(const varying vec3fa& p, const varying vec3fa& dp) +{ + const float pp = dot(p,p); + const float pdp = dot(p,dp); + return (pp*dp-pdp*p)*rcp(pp)*rsqrt(pp); +} + #define __lift_unary_fct(F) \ inline uniform vec2f F(const uniform vec2f v) \ @@ -619,7 +677,7 @@ inline uniform vec4f make_vec4f(const uniform vec3f rgb, const uniform float a) #define __define_lerp3a(ABB) \ inline uniform vec3##ABB##a lerp(uniform float factor, const uniform vec3##ABB##a a, const uniform vec3##ABB##a b) \ { \ - return make_vec3##ABB##a(lerp(factor, a.x, b.x), lerp(factor, a.y, b.y), lerp(factor, a.z, b.z), lerp(factor, a.a, b.a)); \ + return make_vec3##ABB##a(lerp(factor, a.x, b.x), lerp(factor, a.y, b.y), lerp(factor, a.z, b.z), lerp(factor, a.w, b.w)); \ } \ inline vec3##ABB##a lerp(float factor, const vec3##ABB##a a, const vec3##ABB##a b) \ { \ @@ -816,14 +874,14 @@ inline vec4f linear_to_srgba(const vec4f c) return make_vec4f(linear_to_srgb(c.x), linear_to_srgb(c.y), linear_to_srgb(c.z), - max(c.w, 0f)); // alpha is never gamma-corrected + max(c.w, 0.f)); // alpha is never gamma-corrected } inline uint32 linear_to_srgba8(const vec4f c) { #if 1 vec4f l = 255.f * min(linear_to_srgba(c), make_vec4f(1.f)); - return + return ((uint32)l.x << 0) | ((uint32)l.y << 8) | ((uint32)l.z << 16) | diff --git a/ospray/mpi/DistributedFrameBuffer.cpp b/ospray/mpi/DistributedFrameBuffer.cpp index 36e502bbcf..9a16ad428f 100644 --- a/ospray/mpi/DistributedFrameBuffer.cpp +++ b/ospray/mpi/DistributedFrameBuffer.cpp @@ -15,206 +15,137 @@ // ======================================================================== // #include "DistributedFrameBuffer.h" +#include "DistributedFrameBuffer_TileTypes.h" #include "DistributedFrameBuffer_ispc.h" -#include "ospray/common/tasking/async.h" -#include "ospray/common/tasking/parallel_for.h" +#include "common/tasking/async.h" +#include "common/tasking/parallel_for.h" #ifdef _WIN32 -# define NOMINMAX # include // for Sleep #endif #define DBG(a) /* ignore */ -namespace ospray { - using std::cout; - using std::endl; - - inline int clientRank(int clientID) { return clientID+1; } - - typedef DistributedFrameBuffer DFB; - - DFB::TileDesc::TileDesc(DFB *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : tileID(tileID), ownerID(ownerID), dfb(dfb), begin(begin) - {} - - DFB::TileData::TileData(DFB *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileDesc(dfb,begin,tileID,ownerID) - {} - - - /*! called exactly once at the beginning of each frame */ - void DFB::AlphaBlendTile_simple::newFrame() - { - currentGeneration = 0; - expectedInNextGeneration = 0; - missingInCurrentGeneration = 1; +using std::cout; +using std::endl; - assert(bufferedTile.empty()); - } - - void computeSortOrder(DFB::AlphaBlendTile_simple::BufferedTile *t) - { - float z = std::numeric_limits::infinity(); - for (int iy=0;iytile.region.upper.y-t->tile.region.lower.y;iy++) - for (int ix=0;ixtile.region.upper.x-t->tile.region.lower.x;ix++) - z = std::min(z,t->tile.z[ix+TILE_SIZE*iy]); - t->sortOrder = z; - - // t->sortOrder = - t->tile.generation; - } +namespace ospray { - inline int compareBufferedTiles(const void *_a, - const void *_b) - { - const auto *a = *(const DFB::AlphaBlendTile_simple::BufferedTile **)_a; - const auto *b = *(const DFB::AlphaBlendTile_simple::BufferedTile **)_b; - if (a->sortOrder == b->sortOrder) return 0; - return a->sortOrder > b->sortOrder ? -1 : +1; - } + // Helper types ///////////////////////////////////////////////////////////// + + using DFB = DistributedFrameBuffer; + + struct MasterTileMessage : public mpi::async::CommLayer::Message { + vec2i coords; + float error; + }; + + /*! message sent to the master when a tile is finished. Todo: + compress the color data */ + template + struct MasterTileMessage_FB : public MasterTileMessage { + FBType color[TILE_SIZE][TILE_SIZE]; + }; + + using MasterTileMessage_RGBA_I8 = MasterTileMessage_FB; + using MasterTileMessage_RGBA_F32 = MasterTileMessage_FB; + using MasterTileMessage_NONE = MasterTileMessage; + + /*! message sent from one node's instance to another, to tell that + instance to write that tile */ + struct WriteTileMessage : public mpi::async::CommLayer::Message { + // TODO: add compression of pixels during transmission + vec2i coords; // XXX redundant: it's also in tile.region.lower + ospray::Tile tile; + }; + + /*! color buffer and depth buffer on master */ + + enum { + /*! command tag that identifies a CommLayer::message as a write + tile command. this is a command using for sending a tile of + new samples to another instance of the framebuffer (the one + that actually owns that tile) for processing and 'writing' of + that tile on that owner node. */ + WORKER_WRITE_TILE = 13, + /*! command tag used for sending 'final' tiles from the tile + owner to the master frame buffer. Note that we *do* send a + message back ot the master even in cases where the master + does not actually care about the pixel data - we still have + to let the master know when we're done. */ + MASTER_WRITE_TILE_I8, + MASTER_WRITE_TILE_F32, + /*! command tag used for sending 'final' tiles from the tile + owner to the master frame buffer. Note that we *do* send a + message back ot the master even in cases where the master + does not actually care about the pixel data - we still have + to let the master know when we're done. */ + MASTER_WRITE_TILE_NONE, + } COMMANDTAG; + + // Helper functions ///////////////////////////////////////////////////////// - Mutex gMutex; + inline int clientRank(int clientID) { return clientID+1; } - void DFB::TileData::accumulate(const ospray::Tile &tile) - { - switch(dfb->colorBufferFormat) { - case OSP_FB_RGBA8: - ispc::DFB_accumulate_RGBA8(dfb->ispcEquivalent, - (ispc::VaryingTile *)&tile, - (ispc::VaryingTile*)&this->final, - (ispc::VaryingTile*)&this->accum, - (ispc::VaryingRGBA_I8*)&this->color, - dfb->accumId, - dfb->hasAccumBuffer); - case OSP_FB_SRGBA: - ispc::DFB_accumulate_SRGBA(dfb->ispcEquivalent, - (ispc::VaryingTile *)&tile, - (ispc::VaryingTile*)&this->final, - (ispc::VaryingTile*)&this->accum, - (ispc::VaryingRGBA_I8*)&this->color, - dfb->accumId, - dfb->hasAccumBuffer); - } - } + // DistributedFrameBuffer definitions /////////////////////////////////////// - /*! called exactly once for each ospray::Tile that needs to get - written into / composited into this dfb tile */ - void DFB::AlphaBlendTile_simple::process(const ospray::Tile &tile) + DFB::DistributedFrameBuffer(mpi::async::CommLayer *comm, + const vec2i &numPixels, + size_t myID, + ColorBufferFormat colorBufferFormat, + bool hasDepthBuffer, + bool hasAccumBuffer, + bool hasVarianceBuffer) + : mpi::async::CommLayer::Object(comm,myID), + FrameBuffer(numPixels,colorBufferFormat,hasDepthBuffer, + hasAccumBuffer,hasVarianceBuffer), + accumId(0), + tileErrorBuffer(nullptr), + localFBonMaster(nullptr), + frameMode(WRITE_ONCE), + frameIsActive(false), + frameIsDone(false) { - BufferedTile *addTile = new BufferedTile; - memcpy(&addTile->tile,&tile,sizeof(tile)); - computeSortOrder(addTile); + assert(comm); + this->ispcEquivalent = ispc::DFB_create(this); + ispc::DFB_set(getIE(), numPixels.x, numPixels.y, colorBufferFormat); + comm->registerObject(this,myID); - this->final.region = tile.region; - this->final.fbSize = tile.fbSize; - this->final.rcp_fbSize = tile.rcp_fbSize; + createTiles(); - { - LockGuard lock(mutex); - bufferedTile.push_back(addTile); - - if (tile.generation == currentGeneration) { - --missingInCurrentGeneration; - expectedInNextGeneration += tile.children; - while (missingInCurrentGeneration == 0 && - expectedInNextGeneration > 0) { - currentGeneration++; - missingInCurrentGeneration = expectedInNextGeneration; - expectedInNextGeneration = 0; - for (int i=0;itile.generation == currentGeneration) { - --missingInCurrentGeneration; - expectedInNextGeneration += bt->tile.children; - } - } - } + if (comm->group->rank == 0) { + if (colorBufferFormat == OSP_FB_NONE) { + cout << "#osp:mpi:dfb: we're the master, but framebuffer has 'NONE' " + << "format; creating distributed frame buffer WITHOUT having a " + << "mappable copy on the master" << endl; + } else { + localFBonMaster = new LocalFrameBuffer(numPixels, + colorBufferFormat, + hasDepthBuffer, + false, + false); } - - if (missingInCurrentGeneration == 0) { - Tile **tileArray = (Tile**)alloca(sizeof(Tile*)*bufferedTile.size()); - for (int i=0;itile; - } - - ispc::DFB_sortAndBlendFragments((ispc::VaryingTile **)tileArray, - bufferedTile.size()); - - this->final.region = tile.region; - this->final.fbSize = tile.fbSize; - this->final.rcp_fbSize = tile.rcp_fbSize; - accumulate(bufferedTile[0]->tile); - dfb->tileIsCompleted(this); - for (int i=0;ifinal.region = tile.region; - this->final.fbSize = tile.fbSize; - this->final.rcp_fbSize = tile.rcp_fbSize; - accumulate(tile); - dfb->tileIsCompleted(this); - } - - void DFB::ZCompositeTile::newFrame() - { - numPartsComposited = 0; - } - - void DFB::ZCompositeTile::process(const ospray::Tile &tile) - { - bool done = false; - - { - SCOPED_LOCK(mutex); - if (numPartsComposited == 0) - memcpy(&compositedTileData,&tile,sizeof(tile)); - else - ispc::DFB_zComposite((ispc::VaryingTile*)&tile, - (ispc::VaryingTile*)&this->compositedTileData); - - done = (++numPartsComposited == dfb->comm->numWorkers()); - } - - if (done) { - accumulate(this->compositedTileData); - dfb->tileIsCompleted(this); - } + freeTiles(); + alignedFree(tileErrorBuffer); } void DFB::startNewFrame() { std::vector delayedMessage; - { - LockGuard lock(mutex); + SCOPED_LOCK(mutex); DBG(printf("rank %i starting new frame\n",mpi::world.rank)); assert(!frameIsActive); @@ -224,9 +155,8 @@ namespace ospray { if (pixelOp) pixelOp->beginFrame(); - for (int i=0;inewFrame(); - } + for (auto &tile : myTiles) + tile->newFrame(); // create a local copy of delayed tiles, so we can work on them outside // the mutex @@ -247,88 +177,61 @@ namespace ospray { } // might actually want to move this to a thread: - for (int i=0;iincoming(msg); - } } void DFB::freeTiles() { - for (int i=0;igroup->size-1); + size_t tileID = 0; + vec2i numPixels = getNumPixels(); + for (size_t y = 0; y < numPixels.y; y += TILE_SIZE) { + for (size_t x = 0; x < numPixels.x; x += TILE_SIZE, tileID++) { + size_t ownerID = tileID % (comm->group->size - 1); if (clientRank(ownerID) == comm->group->rank) { - TileData *td - = (frameMode == WRITE_ONCE) - ? (TileData*)new WriteOnlyOnceTile(this,vec2i(x,y),tileID,ownerID) -#if 1 - : (TileData*)new AlphaBlendTile_simple(this,vec2i(x,y), - tileID,ownerID); -#else - : (TileData*)new ZCompositeTile(this,vec2i(x,y),tileID,ownerID); -#endif + TileData *td = createTile(vec2i(x, y), tileID, ownerID); myTiles.push_back(td); allTiles.push_back(td); } else { - allTiles.push_back(new TileDesc(this,vec2i(x,y),tileID,ownerID)); + allTiles.push_back(new TileDesc(this, vec2i(x,y), tileID, ownerID)); } } - } - - DFB::DistributedFrameBuffer(mpi::async::CommLayer *comm, - const vec2i &numPixels, - size_t myID, - ColorBufferFormat colorBufferFormat, - bool hasDepthBuffer, - bool hasAccumBuffer) - : mpi::async::CommLayer::Object(comm,myID), - FrameBuffer(numPixels,colorBufferFormat,hasDepthBuffer,hasAccumBuffer), - numPixels(numPixels), - maxValidPixelID(numPixels-vec2i(1)), - numTiles((numPixels.x+TILE_SIZE-1)/TILE_SIZE, - (numPixels.y+TILE_SIZE-1)/TILE_SIZE), - frameIsActive(false), frameIsDone(false), localFBonMaster(NULL), - accumId(0), - frameMode(WRITE_ONCE) - { - assert(comm); - this->ispcEquivalent = ispc::DFB_create(this); - ispc::DFB_set(getIE(),numPixels.x,numPixels.y, colorBufferFormat); - comm->registerObject(this,myID); - - createTiles(); - - if (comm->group->rank == 0) { - if (colorBufferFormat == OSP_FB_NONE) { - cout << "#osp:mpi:dfb: we're the master, but framebuffer has 'NONE' " - << "format; creating distriubted frame buffer WITHOUT having a " - << "mappable copy on the master" << endl; - } else { - localFBonMaster = new LocalFrameBuffer(numPixels, - colorBufferFormat, - hasDepthBuffer, - false, - false); - } } - - ispc::DFB_set(getIE(),numPixels.x,numPixels.y, - colorBufferFormat); } void DFB::setFrameMode(FrameMode newFrameMode) { - if (frameMode == newFrameMode) return; + if (frameMode == newFrameMode) + return; freeTiles(); this->frameMode = newFrameMode; @@ -371,46 +274,27 @@ namespace ospray { void DFB::waitUntilFinished() { std::unique_lock lock(mutex); - doneCond.wait(lock, [&]{return frameIsDone;}); + frameDoneCond.wait(lock, [&]{return frameIsDone;}); } - void DFB::processMessage(DFB::WriteTileMessage *msg) - { - DFB::TileDesc *tileDesc = this->getTileDescFor(msg->coords); - // TODO: compress/decompress tile data - TileData *td = (TileData*)tileDesc; - td->process(msg->tile); - } - - void DFB::processMessage(MasterTileMessage_NONE *msg) + void DFB::processMessage(MasterTileMessage *msg) { { /* nothing to do for 'none' tiles */ } + if (hasVarianceBuffer && (accumId & 1) == 1) + tileErrorBuffer[getTileIDof(msg->coords)] = msg->error; // and finally, tell the master that this tile is done - DFB::TileDesc *tileDesc = this->getTileDescFor(msg->coords); + auto *tileDesc = this->getTileDescFor(msg->coords); TileData *td = (TileData*)tileDesc; this->tileIsCompleted(td); } - void DFB::processMessage(MasterTileMessage_RGBA_I8 *msg) + void DFB::processMessage(WriteTileMessage *msg) { - for (int iy=0;iycoords.y; - if (iiy >= numPixels.y) continue; - - for (int ix=0;ixcoords.x; - if (iix >= numPixels.x) continue; - - ((uint32*)localFBonMaster->colorBuffer)[iix + iiy*numPixels.x] - = msg->color[iy][ix]; - } - } - - // and finally, tell the master that this tile is done - DFB::TileDesc *tileDesc = this->getTileDescFor(msg->coords); + auto *tileDesc = this->getTileDescFor(msg->coords); + // TODO: compress/decompress tile data TileData *td = (TileData*)tileDesc; - this->tileIsCompleted(td); + td->process(msg->tile); } void DFB::tileIsCompleted(TileData *tile) @@ -442,6 +326,7 @@ namespace ospray { MasterTileMessage_NONE *mtm = new MasterTileMessage_NONE; mtm->command = MASTER_WRITE_TILE_NONE; mtm->coords = tile->begin; + mtm->error = tile->error; comm->sendTo(this->master,mtm,sizeof(*mtm)); } break; case OSP_FB_RGBA8: @@ -451,9 +336,20 @@ namespace ospray { MasterTileMessage_RGBA_I8 *mtm = new MasterTileMessage_RGBA_I8; mtm->command = MASTER_WRITE_TILE_I8; mtm->coords = tile->begin; + mtm->error = tile->error; memcpy(mtm->color,tile->color,TILE_SIZE*TILE_SIZE*sizeof(uint32)); comm->sendTo(this->master,mtm,sizeof(*mtm)); } break; + case OSP_FB_RGBA32F: { + /*! if the master has RGBA32F format, we're sending him a tile of the + proper data */ + MasterTileMessage_RGBA_F32 *mtm = new MasterTileMessage_RGBA_F32; + mtm->command = MASTER_WRITE_TILE_F32; + mtm->coords = tile->begin; + mtm->error = tile->error; + memcpy(mtm->color,tile->color,TILE_SIZE*TILE_SIZE*sizeof(vec4f)); + comm->sendTo(this->master,mtm,sizeof(*mtm)); + } break; default: throw std::runtime_error("#osp:mpi:dfb: color buffer format not " "implemented for distributed frame buffer"); @@ -469,8 +365,9 @@ namespace ospray { numTiles.x*numTiles.y)); } - if (numTilesCompletedByMe == myTiles.size()) + if (numTilesCompletedByMe == myTiles.size()) { closeCurrentFrame(); + } } } @@ -488,14 +385,17 @@ namespace ospray { async([=]() { switch (_msg->command) { - case DFB::MASTER_WRITE_TILE_NONE: - this->processMessage((DFB::MasterTileMessage_NONE*)_msg); + case MASTER_WRITE_TILE_NONE: + this->processMessage((MasterTileMessage_NONE*)_msg); + break; + case MASTER_WRITE_TILE_I8: + this->processMessage((MasterTileMessage_RGBA_I8*)_msg); break; - case DFB::MASTER_WRITE_TILE_I8: - this->processMessage((DFB::MasterTileMessage_RGBA_I8*)_msg); + case MASTER_WRITE_TILE_F32: + this->processMessage((MasterTileMessage_RGBA_F32*)_msg); break; - case DFB::WORKER_WRITE_TILE: - this->processMessage((DFB::WriteTileMessage*)_msg); + case WORKER_WRITE_TILE: + this->processMessage((WriteTileMessage*)_msg); break; default: assert(0); @@ -509,14 +409,23 @@ namespace ospray { DBG(printf("rank %i CLOSES frame\n",mpi::world.rank)); frameIsActive = false; frameIsDone = true; - doneCond.notify_all(); + + if (IamTheMaster()) { + /* do nothing */ + } else { + if (pixelOp) { + pixelOp->endFrame(); + } + } + + frameDoneCond.notify_all(); } //! write given tile data into the frame buffer, sending to remove owner if //! required void DFB::setTile(ospray::Tile &tile) { - DFB::TileDesc *tileDesc = this->getTileDescFor(tile.region.lower); + auto *tileDesc = this->getTileDescFor(tile.region.lower); if (!tileDesc->mine()) { // NOT my tile... @@ -526,8 +435,7 @@ namespace ospray { memcpy(&msg->tile,&tile,sizeof(ospray::Tile)); msg->command = WORKER_WRITE_TILE; - comm->sendTo(this->worker[tileDesc->ownerID], - msg,sizeof(*msg)); + comm->sendTo(this->worker[tileDesc->ownerID], msg,sizeof(*msg)); } else { // this is my tile... assert(frameIsActive); @@ -549,28 +457,57 @@ namespace ospray { { if (!myTiles.empty()) { parallel_for(myTiles.size(), [&](int taskIndex){ - DFB::TileData *td = this->myTiles[taskIndex]; + TileData *td = this->myTiles[taskIndex]; assert(td); if (fbChannelFlags & OSP_FB_ACCUM) { - for (int i=0;iaccum.r[i] = 0.f; - for (int i=0;iaccum.g[i] = 0.f; - for (int i=0;iaccum.b[i] = 0.f; - for (int i=0;iaccum.a[i] = 0.f; - for (int i=0;iaccum.z[i] = inf; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->accum.r[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->accum.g[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->accum.b[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->accum.a[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->accum.z[i] = inf; } if (fbChannelFlags & OSP_FB_DEPTH) - for (int i=0;ifinal.z[i] = inf; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->final.z[i] = inf; if (fbChannelFlags & OSP_FB_COLOR) { - for (int i=0;ifinal.r[i] = 0.f; - for (int i=0;ifinal.g[i] = 0.f; - for (int i=0;ifinal.b[i] = 0.f; - for (int i=0;ifinal.a[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->final.r[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->final.g[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->final.b[i] = 0.f; + for (int i = 0; i < TILE_SIZE*TILE_SIZE; i++) td->final.a[i] = 0.f; } - }); + }); + } - if (hasAccumBuffer && (fbChannelFlags & OSP_FB_ACCUM)) - accumId = -1; // we increment at the start of the frame + if (hasAccumBuffer && (fbChannelFlags & OSP_FB_ACCUM)) { + accumId = -1; // we increment at the start of the frame + + // always also clear error buffer (if present) + if (tileErrorBuffer) { + const int tiles = numTiles.x*numTiles.y; + for (int i = 0; i < tiles; i++) + tileErrorBuffer[i] = inf; + } } } + float DFB::tileError(const vec2i &tile) + { + if (tileErrorBuffer) + return tileErrorBuffer[getTileIDof(tile)]; + else + return inf; + } + + float DFB::endFrame(const float /*errorThreshold*/) + { + if (tileErrorBuffer) { + float maxErr = 0.0f; + const int tiles = numTiles.x*numTiles.y; + for (int i = 0; i < tiles; i++) + maxErr = std::max(maxErr, tileErrorBuffer[i]); + + return maxErr; + } else + return inf; + } + } // ::ospray diff --git a/ospray/mpi/DistributedFrameBuffer.h b/ospray/mpi/DistributedFrameBuffer.h index a454be268b..b2c6fe0da1 100644 --- a/ospray/mpi/DistributedFrameBuffer.h +++ b/ospray/mpi/DistributedFrameBuffer.h @@ -16,267 +16,41 @@ #pragma once -#include "ospray/mpi/async/CommLayer.h" -#include "ospray/fb/Tile.h" -#include "ospray/fb/LocalFB.h" -#include "ospray/common/Thread.h" +#include "mpi/async/CommLayer.h" +#include "fb/LocalFB.h" +#include "common/Thread.h" #include namespace ospray { using std::cout; using std::endl; + struct TileDesc; + struct TileData; + + struct MasterTileMessage; + template + struct MasterTileMessage_FB; + struct WriteTileMessage; + struct DistributedFrameBuffer : public mpi::async::CommLayer::Object, public virtual FrameBuffer { - //! get number of pixels per tile, in x and y direction - vec2i getTileSize() const { return vec2i(TILE_SIZE); } - - //! return number of tiles in x and y direction - vec2i getNumTiles() const { return numTiles; } - - //! get number of pixels in x and y diretion - vec2i getNumPixels() const { return size; } - - //! number of tiles that "I" own - size_t numMyTiles() const { return myTiles.size(); } - - int accumId; - int32 accumID(const vec2i &) override { return accumId; } - float tileError(const vec2i &) override { return inf; } - float endFrame(const float) override { return inf; } - - /*! color buffer and depth buffer on master */ - - enum { - /*! command tag that identifies a CommLayer::message as a write - tile command. this is a command using for sending a tile of - new samples to another instance of the framebuffer (the one - that actually owns that tile) for processing and 'writing' of - that tile on that owner node. */ - WORKER_WRITE_TILE = 13, - /*! command tag used for sending 'final' tiles from the tile - owner to the master frame buffer. Note that we *do* send a - message back ot the master even in cases where the master - does not actually care about the pixel data - we still have - to let the master know when we're done. */ - MASTER_WRITE_TILE_I8, - /*! command tag used for sending 'final' tiles from the tile - owner to the master frame buffer. Note that we *do* send a - message back ot the master even in cases where the master - does not actually care about the pixel data - we still have - to let the master know when we're done. */ - MASTER_WRITE_TILE_NONE, - } COMMANDTAG; - - // ------------------------------------------------------- - /*! keeps the book-keeping of one tile of the frame buffer. note - that 'size' is the tile size used by the frame buffer, _NOT_ - necessariy 'end-begin'. 'color' and 'depth' arrays are always - alloc'ed in TILE_SIZE pixels */ - struct TileDesc { - ALIGNED_STRUCT; - - /*! constructor */ - TileDesc(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID); - - /*! returns whether this tile is one of this particular - node's tiles */ - virtual bool mine() const { return false; } - - DistributedFrameBuffer *dfb; - vec2i begin; - size_t tileID,ownerID; - }; - - // ------------------------------------------------------- - /*! base class for a dfb tile. the only thing that all tiles have - in common is depth, and RGBA-float accumulated data. Note we - do not have a RGBA-I8 color field, because typically that'll - be done by the postop and send-to-master op, and not stored in - the DFB tile itself */ - struct TileData : public TileDesc { - TileData(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID); - - /*! called exactly once at the beginning of each frame */ - virtual void newFrame() = 0; - - /*! returns whether this tile is one of this particular - node's tiles */ - virtual bool mine() const { return true; } - - /*! called exactly once for each ospray::Tile that needs to get - written into / composited into this dfb tile */ - virtual void process(const ospray::Tile &tile) = 0; - - void accumulate(const ospray::Tile &tile); - - ospray::Tile __aligned(64) accum; - /* iw: TODO - have to change this. right now, to be able to give - the 'postaccum' pixel op a readily normalized tile we have to - create a local copy (the tile stores only the accum value, - and we cannot change this) */ - ospray::Tile __aligned(64) final; - - //! the rbga32-convoerted colors - uint32 __aligned(64) color[TILE_SIZE*TILE_SIZE]; - }; - - // ------------------------------------------------------- - /*! specialized tile for plain sort-first rendering, where each - tile is written only exactly once. */ - struct WriteOnlyOnceTile : public TileData { - - /*! constructor */ - WriteOnlyOnceTile(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileData(dfb,begin,tileID,ownerID) - {} - - /*! called exactly once at the beginning of each frame */ - virtual void newFrame(); - - /*! called exactly once for each ospray::Tile that needs to get - written into / composited into this dfb tile. - - for a write-once tile, we expect this to be called exactly - once per tile, so there's not a lot to do in here than - accumulating the tile data and telling the parent that we're - done. - */ - virtual void process(const ospray::Tile &tile); - }; - - // ------------------------------------------------------- - /*! specialized tile for doing Z-compositing. this does not have - additional data, but a different write op. */ - struct ZCompositeTile : public TileData { - - /*! constructor */ - ZCompositeTile(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileData(dfb,begin,tileID,ownerID) - {} - - /*! called exactly once at the beginning of each frame */ - virtual void newFrame(); - - /*! called exactly once for each ospray::Tile that needs to get - written into / composited into this dfb tile */ - virtual void process(const ospray::Tile &tile); - - /*! number of input tiles that have been composited into this - tile */ - size_t numPartsComposited; - - /*! since we do not want to mess up the existing accumulatation - buffer in the parent tile we temporarily composite into this - buffer until all the composites have been done. */ - ospray::Tile compositedTileData; - Mutex mutex; - }; - - /*! specialized tile implementation that first buffers all - ospray::Tile's until all input tiles are available, then sorts - them by closest z component per tile, and only tthen does - front-to-back compositing of those tiles */ - struct AlphaBlendTile_simple : public TileData { - - /*! constructor */ - AlphaBlendTile_simple(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileData(dfb,begin,tileID,ownerID) - {} - - /*! called exactly once at the beginning of each frame */ - virtual void newFrame(); - - /*! called exactly once for each ospray::Tile that needs to get - written into / composited into this dfb tile */ - virtual void process(const ospray::Tile &tile); - - struct BufferedTile { - ospray::Tile tile; - - /*! determines order of this tile relative to other tiles. - - Tiles will get blended with the 'over' operator in - increasing 'BufferedTile::sortOrder' value */ - float sortOrder; - }; - std::vector bufferedTile; - int currentGeneration; - int expectedInNextGeneration; - int missingInCurrentGeneration; - Mutex mutex; - }; - - /*! this function gets called whenever one of our tiles is done - writing/compositing/blending/etc; i.e., as soon as we know - that all the ingredient tile datas for that tile have been - received from the client(s) that generated them. By the time - the tile gets called we do know that 'accum' field of the tile - has been set; it is this function's job to make sure we - properly call the post-op(s), properly send final color data - to the master (if required), and properly do the bookkeeping - that this tile is now done. */ - virtual void tileIsCompleted(TileData *tile); - - - /*! message sent to the master when a tile is finished. Todo: - compress the color data */ - struct MasterTileMessage_RGBA_I8 : public mpi::async::CommLayer::Message { - vec2i coords; - uint32 color[TILE_SIZE][TILE_SIZE]; - }; - - /*! message sent to the master when a tile is finished. Todo: - compress the color data */ - struct MasterTileMessage_NONE : public mpi::async::CommLayer::Message { - vec2i coords; - }; - - /*! message sent from one node's instance to another, to tell that - instance to write that tile */ - struct WriteTileMessage : public mpi::async::CommLayer::Message { - // TODO: add compression of pixels during transmission - vec2i coords; - ospray::Tile tile; - }; - - /*! local frame buffer on the master used for storing the final - tiles. will be null on all workers, and _may_ be null on the - master if the master does not have a color buffer */ - Ref localFBonMaster; - - inline bool IamTheMaster() const { return comm->IamTheMaster(); } - //! constructor DistributedFrameBuffer(mpi::async::CommLayer *comm, const vec2i &numPixels, size_t myHandle, ColorBufferFormat colorBufferFormat, bool hasDepthBuffer, - bool hasAccumBuffer); - //! destructor - ~DistributedFrameBuffer() - { freeTiles(); } + bool hasAccumBuffer, + bool hasVarianceBuffer); + + ~DistributedFrameBuffer(); // ================================================================== // framebuffer / device interface // ================================================================== + const void *mapDepthBuffer() override; const void *mapColorBuffer() override; void unmap(const void *mappedMem) override; @@ -304,6 +78,13 @@ namespace ospray { void waitUntilFinished(); + // ================================================================== + // remaining framebuffer interface + // ================================================================== + + int32 accumID(const vec2i &) override { return accumId; } + float tileError(const vec2i &tile) override; + float endFrame(const float errorThreshold) override; // ================================================================== // interface for the comm layer, to enable communication between @@ -314,11 +95,12 @@ namespace ospray { //! recipient's job to properly delete the message. void incoming(mpi::async::CommLayer::Message *msg) override; - //! process a (non-empty) write tile message at the master - void processMessage(MasterTileMessage_RGBA_I8 *msg); + //! process a client-to-client write tile message */ + void processMessage(MasterTileMessage *msg); - //! process a (empty) write tile message at the master - void processMessage(MasterTileMessage_NONE *msg); + //! process a (non-empty) write tile message at the master + template + void processMessage(MasterTileMessage_FB *msg); //! process a client-to-client write tile message */ void processMessage(WriteTileMessage *msg); @@ -327,39 +109,54 @@ namespace ospray { // internal helper functions // ================================================================== + /*! this function gets called whenever one of our tiles is done + writing/compositing/blending/etc; i.e., as soon as we know + that all the ingredient tile datas for that tile have been + received from the client(s) that generated them. By the time + the tile gets called we do know that 'accum' field of the tile + has been set; it is this function's job to make sure we + properly call the post-op(s), properly send final color data + to the master (if required), and properly do the bookkeeping + that this tile is now done. */ + void tileIsCompleted(TileData *tile); + + //! number of tiles that "I" own + inline size_t numMyTiles() const { return myTiles.size(); } + inline bool IamTheMaster() const { return comm->IamTheMaster(); } + /*! return tile descriptor for given pixel coordinates. this tile ! may or may not belong to current instance */ inline TileDesc *getTileDescFor(const vec2i &coords) const - { return allTiles[getTileIDof(coords.x,coords.y)]; } + { return allTiles[getTileIDof(coords)]; } /*! return the tile ID for given pair of coordinates. this tile may or may not belong to current instance */ - inline size_t getTileIDof(size_t x, size_t y) const - { return (x/TILE_SIZE)+(y/TILE_SIZE)*numTiles.x; } + inline size_t getTileIDof(const vec2i &c) const + { return (c.x/TILE_SIZE)+(c.y/TILE_SIZE)*numTiles.x; } //! \brief common function to help printf-debugging /*! \detailed Every derived class should overrride this! */ std::string toString() const override { return "ospray::DistributedFrameBuffer"; } + typedef enum { + WRITE_ONCE, ALPHA_BLEND, Z_COMPOSITE + } FrameMode; - /*! the number of pixels in the (whole) frame buffer (independent - of which tiles we have). + int accumId; - Note this number should ALWAYS be equal to FrameBuffer::size; - but we use a more explicit name in this class to avoid confusion - when also iterating over numTiles, numDisplays, etc */ - vec2i numPixels; - vec2i maxValidPixelID; - vec2i numTiles; + //! holds error per tile, for variance estimation / stopping + float *tileErrorBuffer; - typedef enum { - WRITE_ONCE, ALPHA_BLENDING - } FrameMode; + /*! local frame buffer on the master used for storing the final + tiles. will be null on all workers, and _may_ be null on the + master if the master does not have a color buffer */ + Ref localFBonMaster; FrameMode frameMode; void setFrameMode(FrameMode newFrameMode) ; void createTiles(); + TileData *createTile(const vec2i &xy, size_t tileID, size_t ownerID); void freeTiles(); /*! number of tiles written this frame */ @@ -381,8 +178,8 @@ namespace ospray { /*! mutex used to protect all threading-sensitive data in this object */ - Mutex mutex; + //! set to true when the frame becomes 'active', and write tile //! messages can be consumed. bool frameIsActive; @@ -392,7 +189,7 @@ namespace ospray { bool frameIsDone; //! condition that gets triggered when the frame is done - Condition doneCond; + Condition frameDoneCond; /*! a vector of async messages for the *current* frame that got received before that frame actually started, and that we have @@ -403,4 +200,34 @@ namespace ospray { std::vector delayedMessage; }; + // Inlined definitions ////////////////////////////////////////////////////// + + template + inline void + DistributedFrameBuffer::processMessage(MasterTileMessage_FB *msg) + { + if (hasVarianceBuffer && (accumId & 1) == 1) + tileErrorBuffer[getTileIDof(msg->coords)] = msg->error; + + vec2i numPixels = getNumPixels(); + + for (int iy = 0; iy < TILE_SIZE; iy++) { + int iiy = iy+msg->coords.y; + if (iiy >= numPixels.y) continue; + + for (int ix = 0; ix < TILE_SIZE; ix++) { + int iix = ix+msg->coords.x; + if (iix >= numPixels.x) continue; + + ((FBType*)localFBonMaster->colorBuffer)[iix + iiy*numPixels.x] + = msg->color[iy][ix]; + } + } + + // and finally, tell the master that this tile is done + auto *tileDesc = this->getTileDescFor(msg->coords); + TileData *td = (TileData*)tileDesc; + this->tileIsCompleted(td); + } + } // ::ospray diff --git a/ospray/mpi/DistributedFrameBuffer.ispc b/ospray/mpi/DistributedFrameBuffer.ispc index 6a3ed10ca9..f21163160a 100644 --- a/ospray/mpi/DistributedFrameBuffer.ispc +++ b/ospray/mpi/DistributedFrameBuffer.ispc @@ -14,17 +14,13 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/math/vec.ih" +#include "fb/FrameBuffer.ih" +#include "math/vec.ih" struct DistributedFrameBuffer { FrameBuffer super; }; -struct VaryingRGBA_I8 { - varying uint32 color[TILE_SIZE*TILE_SIZE/programCount]; -}; - export void DFB_alphaBlendBackground(VaryingTile *uniform firstTile, const uniform vec4f &bgColor) { @@ -56,16 +52,21 @@ export void DFB_alphaBlendTiles(VaryingTile *uniform prev, } } -#define template_accumulate(name, cvt) \ -export void DFB_accumulate_##name(void *uniform _self, \ +#define template_accumulate(name, dst_fmt, cvt) \ +export uniform float DFB_accumulate_##name(void *uniform _self, \ VaryingTile *uniform tile, \ VaryingTile *uniform final, \ VaryingTile *uniform accum, \ - VaryingRGBA_I8 *uniform color, \ + VaryingTile *uniform variance, \ + void *uniform _color, \ + uniform float cntu, \ uniform int accumID, \ - uniform bool hasAccumBuffer) \ + uniform bool hasAccumBuffer, \ + uniform bool hasVarianceBuffer) \ { \ DistributedFrameBuffer *uniform self = (DistributedFrameBuffer*)_self; \ + dst_fmt *uniform color = (dst_fmt*uniform)_color; \ + uniform float errf = inf; \ if (!hasAccumBuffer || accumID < 1) { \ for (uniform int i=0;ir[i], tile->g[i], tile->b[i], tile->a[i]);\ @@ -78,12 +79,15 @@ export void DFB_accumulate_##name(void *uniform _self, \ final->b[i] = col.z; \ final->a[i] = col.w; \ \ - color->color[i] = cvt(col); \ + color[i] = cvt(col); \ } \ } else { \ - const uniform float rcpAccumID = 1.f/(accumID+1); \ + const uniform float rcpAccumID = rcpf(accumID+1); \ + const uniform float accHalfScale = rcpf(accumID/2+1); \ + float err = 0.f; \ for (uniform int i=0;ir[i], tile->g[i], tile->b[i], tile->a[i]) \ + vec3f col3 = make_vec3f(tile->r[i], tile->g[i], tile->b[i]); \ + vec4f col = make_vec4f(col3, tile->a[i]) \ + make_vec4f(accum->r[i], accum->g[i], accum->b[i], accum->a[i]); \ \ accum->r[i] = col.x; \ @@ -93,18 +97,49 @@ export void DFB_accumulate_##name(void *uniform _self, \ \ col = col * rcpAccumID; \ \ + /* variance buffer accumulates every other frame */ \ + if (hasVarianceBuffer && (accumID & 1) == 1) { \ + varying vec3f vari = make_vec3f(0.f); \ + if (accumID > 1) \ + vari = make_vec3f(variance->r[i], variance->g[i], variance->b[i]); \ + vari = vari + col3; \ + variance->r[i] = vari.x; \ + variance->g[i] = vari.y; \ + variance->b[i] = vari.z; \ + \ + const vec3f acc3 = make_vec3f(col); \ + const float den2 = reduce_add(acc3); \ + if (den2 > 0.0f) { \ + const vec3f diff = absf(acc3 - accHalfScale * vari); \ + err += reduce_add(diff) * rsqrtf(den2); \ + } \ + } \ + \ final->r[i] = col.x; \ final->g[i] = col.y; \ final->b[i] = col.z; \ final->a[i] = col.w; \ \ - color->color[i] = cvt(col); \ + color[i] = cvt(col); \ } \ + /* error is also only updated every other frame to avoid alternating \ + * error (get a monotone sequence) */ \ + if (hasVarianceBuffer && (accumID & 1) == 1) \ + errf = reduce_add(err) * rsqrtf(cntu); \ } \ + return errf; \ +} + +inline vec4f soa_to_aos4f(const vec4f s) +{ + vec4f a; + soa_to_aos4(s.x, s.y, s.z, s.w, (uniform float*uniform)&a); + return a; } -template_accumulate(RGBA8, cvt_uint32); -template_accumulate(SRGBA, linear_to_srgba8); +template_accumulate(RGBA8, varying uint32, cvt_uint32); +template_accumulate(SRGBA, varying uint32, linear_to_srgba8); +template_accumulate(RGBA32F, varying vec4f, soa_to_aos4f); #undef template_accumulate diff --git a/ospray/mpi/DistributedFrameBuffer_TileTypes.cpp b/ospray/mpi/DistributedFrameBuffer_TileTypes.cpp new file mode 100644 index 0000000000..005cd15e35 --- /dev/null +++ b/ospray/mpi/DistributedFrameBuffer_TileTypes.cpp @@ -0,0 +1,207 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "DistributedFrameBuffer.h" +#include "DistributedFrameBuffer_TileTypes.h" +#include "DistributedFrameBuffer_ispc.h" + +namespace ospray { + + using DFB = DistributedFrameBuffer; + + TileDesc::TileDesc(DFB *dfb, const vec2i &begin, + size_t tileID, size_t ownerID) + : tileID(tileID), ownerID(ownerID), dfb(dfb), begin(begin) + {} + + TileData::TileData(DFB *dfb, const vec2i &begin, + size_t tileID, size_t ownerID) + : TileDesc(dfb,begin,tileID,ownerID) + {} + + /*! called exactly once at the beginning of each frame */ + void AlphaBlendTile_simple::newFrame() + { + currentGeneration = 0; + expectedInNextGeneration = 0; + missingInCurrentGeneration = 1; + + assert(bufferedTile.empty()); + } + + void computeSortOrder(AlphaBlendTile_simple::BufferedTile *t) + { + float z = std::numeric_limits::infinity(); + for (int iy=0;iytile.region.upper.y-t->tile.region.lower.y;iy++) + for (int ix=0;ixtile.region.upper.x-t->tile.region.lower.x;ix++) + z = std::min(z,t->tile.z[ix+TILE_SIZE*iy]); + t->sortOrder = z; + } + + void TileData::accumulate(const ospray::Tile &tile) + { + vec2i dia = tile.region.upper - tile.region.lower; + float pixelsf = (float)dia.x * dia.y; + switch(dfb->colorBufferFormat) { + case OSP_FB_RGBA8: + error = ispc::DFB_accumulate_RGBA8(dfb->ispcEquivalent, + (ispc::VaryingTile*)&tile, + (ispc::VaryingTile*)&this->final, + (ispc::VaryingTile*)&this->accum, + (ispc::VaryingTile*)&this->variance, + &this->color, + pixelsf, + dfb->accumId, + dfb->hasAccumBuffer, + dfb->hasVarianceBuffer); + break; + case OSP_FB_SRGBA: + error = ispc::DFB_accumulate_SRGBA(dfb->ispcEquivalent, + (ispc::VaryingTile*)&tile, + (ispc::VaryingTile*)&this->final, + (ispc::VaryingTile*)&this->accum, + (ispc::VaryingTile*)&this->variance, + &this->color, + pixelsf, + dfb->accumId, + dfb->hasAccumBuffer, + dfb->hasVarianceBuffer); + break; + case OSP_FB_NONE:// NOTE(jda) - We accumulate here to enable PixelOps + // working correctly here...this needs a + // better solution! + case OSP_FB_RGBA32F: + error = ispc::DFB_accumulate_RGBA32F(dfb->ispcEquivalent, + (ispc::VaryingTile*)&tile, + (ispc::VaryingTile*)&this->final, + (ispc::VaryingTile*)&this->accum, + (ispc::VaryingTile*)&this->variance, + &this->color, + pixelsf, + dfb->accumId, + dfb->hasAccumBuffer, + dfb->hasVarianceBuffer); + break; + default: + break; + } + } + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile */ + void AlphaBlendTile_simple::process(const ospray::Tile &tile) + { + BufferedTile *addTile = new BufferedTile; + memcpy(&addTile->tile,&tile,sizeof(tile)); + computeSortOrder(addTile); + + this->final.region = tile.region; + this->final.fbSize = tile.fbSize; + this->final.rcp_fbSize = tile.rcp_fbSize; + + { + SCOPED_LOCK(mutex); + bufferedTile.push_back(addTile); + + if (tile.generation == currentGeneration) { + --missingInCurrentGeneration; + expectedInNextGeneration += tile.children; + while (missingInCurrentGeneration == 0 && + expectedInNextGeneration > 0) { + currentGeneration++; + missingInCurrentGeneration = expectedInNextGeneration; + expectedInNextGeneration = 0; + for (int i=0;itile.generation == currentGeneration) { + --missingInCurrentGeneration; + expectedInNextGeneration += bt->tile.children; + } + } + } + } + + if (missingInCurrentGeneration == 0) { + Tile **tileArray = STACK_BUFFER(Tile*, bufferedTile.size()); + for (int i = 0; i < bufferedTile.size(); i++) { + tileArray[i] = &bufferedTile[i]->tile; + } + + ispc::DFB_sortAndBlendFragments((ispc::VaryingTile **)tileArray, + bufferedTile.size()); + + this->final.region = tile.region; + this->final.fbSize = tile.fbSize; + this->final.rcp_fbSize = tile.rcp_fbSize; + accumulate(bufferedTile[0]->tile); + dfb->tileIsCompleted(this); + for (auto &tile : bufferedTile) + delete tile; + bufferedTile.clear(); + } + } + } + + /*! called exactly once at the beginning of each frame */ + void WriteOnlyOnceTile::newFrame() + { + /* nothing to do for write-once tile - we *know* it'll get written + only once */ + } + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile. + + for a write-once tile, we expect this to be called exactly once + per tile, so there's not a lot to do in here than accumulating the + tile data and telling the parent that we're done. + */ + void WriteOnlyOnceTile::process(const ospray::Tile &tile) + { + this->final.region = tile.region; + this->final.fbSize = tile.fbSize; + this->final.rcp_fbSize = tile.rcp_fbSize; + accumulate(tile); + dfb->tileIsCompleted(this); + } + + void ZCompositeTile::newFrame() + { + numPartsComposited = 0; + } + + void ZCompositeTile::process(const ospray::Tile &tile) + { + bool done = false; + + { + SCOPED_LOCK(mutex); + if (numPartsComposited == 0) + memcpy(&compositedTileData,&tile,sizeof(tile)); + else + ispc::DFB_zComposite((ispc::VaryingTile*)&tile, + (ispc::VaryingTile*)&this->compositedTileData); + + done = (++numPartsComposited == dfb->comm->numWorkers()); + } + + if (done) { + accumulate(this->compositedTileData); + dfb->tileIsCompleted(this); + } + } + +}// namespace ospray diff --git a/ospray/mpi/DistributedFrameBuffer_TileTypes.h b/ospray/mpi/DistributedFrameBuffer_TileTypes.h new file mode 100644 index 0000000000..b741dc6c39 --- /dev/null +++ b/ospray/mpi/DistributedFrameBuffer_TileTypes.h @@ -0,0 +1,185 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "fb/Tile.h" + +#include + +namespace ospray { + + class DistributedFrameBuffer; + + // ------------------------------------------------------- + /*! keeps the book-keeping of one tile of the frame buffer. note + that 'size' is the tile size used by the frame buffer, _NOT_ + necessariy 'end-begin'. 'color' and 'depth' arrays are always + alloc'ed in TILE_SIZE pixels */ + struct TileDesc { + ALIGNED_STRUCT + + /*! constructor */ + TileDesc(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID); + + /*! returns whether this tile is one of this particular + node's tiles */ + virtual bool mine() const { return false; } + + DistributedFrameBuffer *dfb; + vec2i begin; + size_t tileID,ownerID; + }; + + // ------------------------------------------------------- + /*! base class for a dfb tile. the only thing that all tiles have + in common is depth, and RGBA-float accumulated data. Note we + do not have a RGBA-I8 color field, because typically that'll + be done by the postop and send-to-master op, and not stored in + the DFB tile itself */ + struct TileData : public TileDesc { + TileData(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID); + + /*! called exactly once at the beginning of each frame */ + virtual void newFrame() = 0; + + /*! returns whether this tile is one of this particular + node's tiles */ + bool mine() const override { return true; } + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile */ + virtual void process(const ospray::Tile &tile) = 0; + + void accumulate(const ospray::Tile &tile); + + float error; // estimated variance of this tile + // TODO: dynamically allocate to save memory when no ACCUM or VARIANCE + ospray::Tile __aligned(64) accum; + ospray::Tile __aligned(64) variance; + /* iw: TODO - have to change this. right now, to be able to give + the 'postaccum' pixel op a readily normalized tile we have to + create a local copy (the tile stores only the accum value, + and we cannot change this) */ + ospray::Tile __aligned(64) final; + + //! the rbga32-converted colors + // TODO: dynamically allocate to save memory when only uint32 / I8 colors + vec4f __aligned(64) color[TILE_SIZE*TILE_SIZE]; + }; + + // ------------------------------------------------------- + /*! specialized tile for plain sort-first rendering, where each + tile is written only exactly once. */ + struct WriteOnlyOnceTile : public TileData { + + /*! constructor */ + WriteOnlyOnceTile(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID) + : TileData(dfb,begin,tileID,ownerID) + {} + + /*! called exactly once at the beginning of each frame */ + void newFrame() override; + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile. + + for a write-once tile, we expect this to be called exactly + once per tile, so there's not a lot to do in here than + accumulating the tile data and telling the parent that we're + done. + */ + void process(const ospray::Tile &tile) override; + }; + + // ------------------------------------------------------- + /*! specialized tile for doing Z-compositing. this does not have + additional data, but a different write op. */ + struct ZCompositeTile : public TileData { + + /*! constructor */ + ZCompositeTile(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID) + : TileData(dfb,begin,tileID,ownerID) + {} + + /*! called exactly once at the beginning of each frame */ + void newFrame() override; + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile */ + void process(const ospray::Tile &tile) override; + + /*! number of input tiles that have been composited into this + tile */ + size_t numPartsComposited; + + /*! since we do not want to mess up the existing accumulatation + buffer in the parent tile we temporarily composite into this + buffer until all the composites have been done. */ + ospray::Tile compositedTileData; + Mutex mutex; + }; + + /*! specialized tile implementation that first buffers all + ospray::Tile's until all input tiles are available, then sorts + them by closest z component per tile, and only tthen does + front-to-back compositing of those tiles */ + struct AlphaBlendTile_simple : public TileData { + + /*! constructor */ + AlphaBlendTile_simple(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID) + : TileData(dfb,begin,tileID,ownerID) + {} + + /*! called exactly once at the beginning of each frame */ + void newFrame() override; + + /*! called exactly once for each ospray::Tile that needs to get + written into / composited into this dfb tile */ + void process(const ospray::Tile &tile) override; + + struct BufferedTile { + ospray::Tile tile; + + /*! determines order of this tile relative to other tiles. + + Tiles will get blended with the 'over' operator in + increasing 'BufferedTile::sortOrder' value */ + float sortOrder; + }; + std::vector bufferedTile; + int currentGeneration; + int expectedInNextGeneration; + int missingInCurrentGeneration; + Mutex mutex; + }; + +} // namespace ospray diff --git a/ospray/mpi/MPICommon.cpp b/ospray/mpi/MPICommon.cpp index 5b9ffffd78..7ef51f6938 100644 --- a/ospray/mpi/MPICommon.cpp +++ b/ospray/mpi/MPICommon.cpp @@ -14,15 +14,15 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/mpi/MPICommon.h" -#include "ospray/mpi/async/CommLayer.h" +#include "mpi/MPICommon.h" +#include "mpi/async/CommLayer.h" namespace ospray { namespace mpi { - Group world; - Group app; - Group worker; + OSPRAY_INTERFACE Group world; + OSPRAY_INTERFACE Group app; + OSPRAY_INTERFACE Group worker; void init(int *ac, const char **av) { diff --git a/ospray/mpi/MPICommon.h b/ospray/mpi/MPICommon.h index 0c58c1624c..b986beaec9 100644 --- a/ospray/mpi/MPICommon.h +++ b/ospray/mpi/MPICommon.h @@ -17,7 +17,7 @@ #pragma once #include -#include "ospray/common/OSPCommon.h" +#include "common/OSPCommon.h" // IMPI on Windows defines MPI_CALL already, erroneously #ifdef MPI_CALL @@ -73,16 +73,16 @@ namespace ospray { void barrier() { MPI_CALL(Barrier(comm)); } }; - extern Group world; //! MPI_COMM_WORLD - extern Group app; /*! for workers: intracommunicator to app + OSPRAY_INTERFACE extern Group world; //! MPI_COMM_WORLD + OSPRAY_INTERFACE extern Group app; /*! for workers: intracommunicator to app for app: intercommunicator among app processes */ - extern Group worker; /*!< group of all ospray workers (often the + OSPRAY_INTERFACE extern Group worker; /*!< group of all ospray workers (often the world root is reserved for either app or load balancing, and not part of the worker group */ - void init(int *ac, const char **av); + OSPRAY_INTERFACE void init(int *ac, const char **av); } } // ::ospray diff --git a/ospray/mpi/MPIDevice.cpp b/ospray/mpi/MPIDevice.cpp index 0b0df119c7..645955ce0d 100644 --- a/ospray/mpi/MPIDevice.cpp +++ b/ospray/mpi/MPIDevice.cpp @@ -16,19 +16,19 @@ #undef NDEBUG // do all assertions in this file -#include "ospray/mpi/MPICommon.h" -#include "ospray/mpi/MPIDevice.h" -#include "ospray/common/Model.h" -#include "ospray/common/Data.h" -#include "ospray/common/Library.h" -#include "ospray/geometry/TriangleMesh.h" -#include "ospray/render/Renderer.h" -#include "ospray/camera/Camera.h" -#include "ospray/volume/Volume.h" -#include "ospray/mpi/MPILoadBalancer.h" -#include "ospray/fb/LocalFB.h" -#include "ospray/mpi/async/CommLayer.h" -#include "ospray/mpi/DistributedFrameBuffer.h" +#include "mpi/MPICommon.h" +#include "mpi/MPIDevice.h" +#include "common/Model.h" +#include "common/Data.h" +#include "common/Library.h" +#include "geometry/TriangleMesh.h" +#include "render/Renderer.h" +#include "camera/Camera.h" +#include "volume/Volume.h" +#include "mpi/MPILoadBalancer.h" +#include "fb/LocalFB.h" +#include "mpi/async/CommLayer.h" +#include "mpi/DistributedFrameBuffer.h" // std #ifndef _WIN32 # include // for fork() @@ -44,14 +44,15 @@ namespace ospray { /*! it's up to the proper init routine to decide which processes call this function and which ones don't. This function will not return. */ - void runWorker(int *_ac, const char **_av); + OSPRAY_INTERFACE void runWorker(); /*! in this mode ("ospray on ranks" mode, or "ranks" mode), the user has launched the app across all ranks using mpirun " "; no new processes need to get launched. - Based on the 'startworkers' flag, this function can set up ospray in one of two modes: + Based on the 'startworkers' flag, this function can set up ospray in + one of two modes: in "workers" mode (startworkes=true) all ranks > 0 become workers, and will NOT return to the application; rank 0 is the @@ -87,19 +88,20 @@ namespace ospray { MPI_Barrier(MPI_COMM_WORLD); if (world.size <= 1) { - throw std::runtime_error("No MPI workers found.\n#osp:mpi: Fatal Error - OSPRay told to run in MPI mode, but there seems to be no MPI peers!?\n#osp:mpi: (Did you forget an 'mpirun' in front of your application?)"); + throw std::runtime_error("No MPI workers found.\n#osp:mpi: Fatal Error " + "- OSPRay told to run in MPI mode, but there " + "seems to be no MPI peers!?\n#osp:mpi: (Did " + "you forget an 'mpirun' in front of your " + "application?)"); } if (world.rank == 0) { -#if 0 - ospray::Task::initTaskSystem(debugMode ? 0 : numThreads); -#endif - // we're the root MPI_Comm_split(mpi::world.comm,1,mpi::world.rank,&app.comm); app.makeIntraComm(); // app.makeIntercomm(); - printf("#w: app process %i/%i (global %i/%i)\n",app.rank,app.size,world.rank,world.size); + printf("#w: app process %i/%i (global %i/%i)\n", + app.rank,app.size,world.rank,world.size); MPI_Intercomm_create(app.comm, 0, world.comm, 1, 1, &worker.comm); // worker.makeIntracomm(); @@ -116,19 +118,23 @@ namespace ospray { MPI_Barrier(MPI_COMM_WORLD); // ------------------------------------------------------- - // at this point, all processes should be set up and synced. in particular + // at this point, all processes should be set up and synced. in + // particular: // - app has intracommunicator to all workers (and vica versa) - // - app process(es) are in one intercomm ("app"); workers all in another ("worker") + // - app process(es) are in one intercomm ("app"); workers all in + // another ("worker") // - all processes (incl app) have barrier'ed, and thus now in sync. - // now, root proc(s) will return, initialize the MPI device, then return to the app + // now, root proc(s) will return, initialize the MPI device, then + // return to the app return new mpi::MPIDevice(ac,av); } else { // we're the workers MPI_Comm_split(mpi::world.comm,0,mpi::world.rank,&worker.comm); worker.makeIntraComm(); // worker.makeIntercomm(); - printf("#w: worker process %i/%i (global %i/%i)\n",worker.rank,worker.size,world.rank,world.size); + printf("#w: worker process %i/%i (global %i/%i)\n", + worker.rank,worker.size,world.rank,world.size); MPI_Intercomm_create(worker.comm, 0, world.comm, 0, 1, &app.comm); app.makeInterComm(); @@ -137,26 +143,31 @@ namespace ospray { // app.containsMe = false; // replying to test-message - printf("#w: worker %i trying to receive tag %i...\n",worker.rank,worker.rank); + printf("#w: worker %i trying to receive tag %i...\n", + worker.rank,worker.rank); int reply; MPI_Recv(&reply,1,MPI_INT,0,worker.rank,app.comm,&status); MPI_Send(&reply,1,MPI_INT,0,worker.rank,app.comm); MPI_Barrier(MPI_COMM_WORLD); // ------------------------------------------------------- - // at this point, all processes should be set up and synced. in particular + // at this point, all processes should be set up and synced. in + // particular: // - app has intracommunicator to all workers (and vica versa) - // - app process(es) are in one intercomm ("app"); workers all in another ("worker") + // - app process(es) are in one intercomm ("app"); workers all in + // another ("worker") // - all processes (incl app) have barrier'ed, and thus now in sync. - // now, all workers will enter their worker loop (ie, they will *not* return + // now, all workers will enter their worker loop (ie, they will *not* + // return) if (ranksBecomeWorkers) { cout << "RUNNING WORKER W/O RETURNING!" << endl; - mpi::runWorker(ac,av); + mpi::runWorker(); throw std::runtime_error("should never reach here!"); /* no return here - 'runWorker' will never return */ } else { - cout << "#osp:mpi: distributed mode detected, returning device on all ranks!" << endl << std::flush; + cout << "#osp:mpi: distributed mode detected, " + << "returning device on all ranks!" << endl; return new mpi::MPIDevice(ac,av); } } @@ -168,21 +179,28 @@ namespace ospray { /*! in this mode ("separate worker group" mode) - the user may or may not have launched MPI explicitly for his app - the app may or may not be running distributed - - the ospray frontend (the part linked to the app) will wait for a remote MPI gruop of + - the ospray frontend (the part linked to the app) will wait for a remote + MPI gruop of workers to connect to this app - - the ospray frontend will store the port its waiting on connections for in the - filename passed to this function; or will print it to stdout if this is NULL + - the ospray frontend will store the port its waiting on connections for + in the + filename passed to this function; or will print it to stdout if this is + NULL */ - ospray::api::Device *createMPI_ListenForWorkers(int *ac, const char **av, - const char *fileNameToStorePortIn) + ospray::api::Device * + createMPI_ListenForWorkers(int *ac, const char **av, + const char *fileNameToStorePortIn) { ospray::init(ac,&av); mpi::init(ac,av); if (world.rank < 1) { - cout << "=======================================================" << endl; - cout << "initializing OSPRay MPI in 'listening for workers' mode" << endl; - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; + cout << "initializing OSPRay MPI in 'listening for workers' mode" + << endl; + cout << "=======================================================" + << endl; } int rc; @@ -195,7 +213,8 @@ namespace ospray { rc = MPI_Open_port(MPI_INFO_NULL, appPortName); Assert(rc == MPI_SUCCESS); - // fix port name: replace all '$'s by '%'s to allow using it on the cmdline... + // fix port name: replace all '$'s by '%'s to allow using it on the + // cmdline... char *fixedPortName = strdup(appPortName); for (char *s = fixedPortName; *s; ++s) if (*s == '$') *s = '%'; @@ -218,16 +237,20 @@ namespace ospray { // worker.makeIntracomm(); if (app.rank == 0) { - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; cout << "OSPRAY Worker ring connected" << endl; - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; } MPI_Barrier(app.comm); if (app.rank == 1) { - cout << "WARNING: you're trying to run an mpi-parallel app with ospray; " << endl + cout << "WARNING: you're trying to run an mpi-parallel app with ospray;" + << endl << " only the root rank is allowed to issue ospray calls" << endl; - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; } MPI_Barrier(app.comm); @@ -261,16 +284,20 @@ namespace ospray { char appPortName[MPI_MAX_PORT_NAME]; if (app.rank == 0 || app.size == -1) { - cout << "=======================================================" << endl; - cout << "initializing OSPRay MPI in 'launching new workers' mode" << endl; + cout << "=======================================================" + << endl; + cout << "initializing OSPRay MPI in 'launching new workers' mode" + << endl; cout << "using launch script '" << launchCommand << "'" << endl; rc = MPI_Open_port(MPI_INFO_NULL, appPortName); Assert(rc == MPI_SUCCESS); - // fix port name: replace all '$'s by '%'s to allow using it on the cmdline... + // fix port name: replace all '$'s by '%'s to allow using it on the + // cmdline... char *fixedPortName = strdup(appPortName); cout << "with port " << fixedPortName << endl; - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; for (char *s = fixedPortName; *s; ++s) if (*s == '$') *s = '%'; @@ -281,7 +308,8 @@ namespace ospray { #else if (fork()) { system(systemCommand); - cout << "OSPRay worker process has died - killing application" << endl; + cout << "OSPRay worker process has died - killing application" + << endl; exit(0); } #endif @@ -294,13 +322,17 @@ namespace ospray { if (app.rank == 0 || app.size == -1) { cout << "OSPRay MPI Worker ring successfully connected." << endl; cout << "found " << worker.size << " workers." << endl; - cout << "=======================================================" << endl; + cout << "=======================================================" + << endl; } if (app.size > 1) { if (app.rank == 1) { - cout << "ospray:WARNING: you're trying to run an mpi-parallel app with ospray\n" - << "(only the root node is allowed to issue ospray api calls right now)\n"; - cout << "=======================================================" << endl; + cout << "ospray:WARNING: you're trying to run an mpi-parallel app " + << "with ospray\n" + << "(only the root node is allowed to issue ospray api " + << "calls right now)\n"; + cout << "=======================================================" + << endl; } MPI_Barrier(app.comm); return NULL; @@ -314,37 +346,49 @@ namespace ospray { int initialized = false; MPI_CALL(Initialized(&initialized)); if (initialized) - throw std::runtime_error("OSPRay MPI Error: MPI already Initialized when calling ospMpiInit()"); + throw std::runtime_error("OSPRay MPI Error: MPI already Initialized " + "when calling ospMpiInit()"); ospray::mpi::init(ac,(const char **)*av); if (mpi::world.size < 2) { - throw std::runtime_error("#osp:mpi: trying to run distributed API mode with a single rank? (did you forget the 'mpirun'?)"); + throw std::runtime_error("#osp:mpi: trying to run distributed API mode" + " with a single rank? (did you forget the " + "'mpirun'?)"); } ospray::api::Device::current - = ospray::mpi::createMPI_runOnExistingRanks(ac,(const char**)*av,false); + = ospray::mpi::createMPI_runOnExistingRanks(ac, + (const char**)*av, + false); } MPIDevice::MPIDevice(// AppMode appMode, OSPMode ospMode, int *_ac, const char **_av) : currentApiMode(OSPD_MODE_MASTERED) { - char *logLevelFromEnv = getenv("OSPRAY_LOG_LEVEL"); - if (logLevelFromEnv && logLevel == 0) - logLevel = atoi(logLevelFromEnv); - - ospray::init(_ac,&_av); + auto logLevelFromEnv = getEnvVar("OSPRAY_LOG_LEVEL"); + if (logLevelFromEnv.first && logLevel == 0) + logLevel = logLevelFromEnv.second; - if (mpi::world.size !=1) { + if (mpi::world.size != 1) { if (mpi::world.rank < 0) { PRINT(mpi::world.rank); PRINT(mpi::world.size); - throw std::runtime_error("OSPRay MPI startup error. Use \"mpirun -n 1 \" when calling an application that tries to spawnto start the application you were trying to start."); + throw std::runtime_error("OSPRay MPI startup error. Use \"mpirun " + "-n 1 \" when calling an " + "application that tries to spawn to start " + "the application you were trying to " + "start."); } } TiledLoadBalancer::instance = new mpi::staticLoadBalancer::Master; } + MPIDevice::~MPIDevice() { + cmd.newCommand(CMD_FINALIZE); + cmd.flush(); + async::shutdown(); + } OSPFrameBuffer @@ -352,17 +396,18 @@ namespace ospray { const OSPFrameBufferFormat mode, const uint32 channels) { - FrameBuffer::ColorBufferFormat colorBufferFormat = mode; //FrameBuffer::RGBA_UINT8;//FLOAT32; + FrameBuffer::ColorBufferFormat colorBufferFormat = mode; bool hasDepthBuffer = (channels & OSP_FB_DEPTH)!=0; bool hasAccumBuffer = (channels & OSP_FB_ACCUM)!=0; + bool hasVarianceBuffer = (channels & OSP_FB_VARIANCE)!=0; ObjectHandle handle = ObjectHandle::alloc(); - FrameBuffer *fb = new DistributedFrameBuffer(ospray::mpi::async::CommLayer::WORLD, - size, - handle, - colorBufferFormat, - hasDepthBuffer,hasAccumBuffer); + FrameBuffer *fb = + new DistributedFrameBuffer(ospray::mpi::async::CommLayer::WORLD, + size, handle, colorBufferFormat, + hasDepthBuffer,hasAccumBuffer, + hasVarianceBuffer); fb->refInc(); ObjectHandle::assign(handle,fb); cmd.newCommand(CMD_FRAMEBUFFER_CREATE); @@ -384,8 +429,6 @@ namespace ospray { ObjectHandle handle = (const ObjectHandle &)_fb; FrameBuffer *fb = (FrameBuffer *)handle.lookup(); - LocalFrameBuffer *lfb = (LocalFrameBuffer*)fb; - switch (channel) { case OSP_FB_COLOR: return fb->mapColorBuffer(); case OSP_FB_DEPTH: return fb->mapDepthBuffer(); @@ -447,7 +490,8 @@ namespace ospray { } /*! create a new data buffer */ - OSPData MPIDevice::newData(size_t nitems, OSPDataType format, void *init, int flags) + OSPData MPIDevice::newData(size_t nitems, OSPDataType format, + void *init, int flags) { ObjectHandle handle = ObjectHandle::alloc(); cmd.newCommand(CMD_NEW_DATA); @@ -476,7 +520,8 @@ namespace ospray { /*! assign (named) string parameter to an object */ void MPIDevice::setVoidPtr(OSPObject _object, const char *bufName, void *v) { - throw std::runtime_error("setting a void pointer as parameter to an object is not allowed in MPI mode"); + throw std::runtime_error("setting a void pointer as parameter to an " + "object is not allowed in MPI mode"); } /*! Copy data into the given object. */ @@ -486,15 +531,22 @@ namespace ospray { Assert(_volume); Assert(source); - char *typeString = NULL; + char *typeString = nullptr; getString(_volume, "voxelType", &typeString); OSPDataType type = typeForString(typeString); + Assert(type != OSP_UNKNOWN && "unknown volume voxel type"); int typeSize = sizeOf(type); - size_t size = typeSize * size_t(count.x) * size_t(count.y) * size_t(count.z); - if (size > 1000000000LL) - throw std::runtime_error("setregion does not currently work for region sizes >= 2GB"); + size_t size = + typeSize * size_t(count.x) * size_t(count.y) * size_t(count.z); + // This size restriction is imposed by MPI_Bcast which indexes into the + // buffer with an int + // limiting us to a max size of 2^31 bytes, a bit more than 2GB + if (size > 2000000000LL) { + throw std::runtime_error("setregion does not currently work for " + "region sizes > 2GB"); + } cmd.newCommand(CMD_SET_REGION); cmd.send((const ObjectHandle &)_volume); @@ -515,7 +567,9 @@ namespace ospray { } /*! assign (named) string parameter to an object */ - void MPIDevice::setString(OSPObject _object, const char *bufName, const char *s) + void MPIDevice::setString(OSPObject _object, + const char *bufName, + const char *s) { Assert(_object); Assert(bufName); @@ -541,7 +595,8 @@ namespace ospray { std::string initSymName = "ospray_init_module_"+std::string(name); void*initSym = getSymbol(initSymName); if (!initSym) - throw std::runtime_error("#osp:mpi:mpidevice: could not find module initializer "+initSymName); + throw std::runtime_error("#osp:mpi:mpidevice: could not find module " + "initializer " + initSymName); void (*initMethod)() = (void(*)())initSym; initMethod(); @@ -553,7 +608,9 @@ namespace ospray { } /*! assign (named) float parameter to an object */ - void MPIDevice::setFloat(OSPObject _object, const char *bufName, const float f) + void MPIDevice::setFloat(OSPObject _object, + const char *bufName, + const float f) { Assert(_object); Assert(bufName); @@ -577,7 +634,9 @@ namespace ospray { } /*! assign (named) vec2f parameter to an object */ - void MPIDevice::setVec2f(OSPObject _object, const char *bufName, const vec2f &v) + void MPIDevice::setVec2f(OSPObject _object, + const char *bufName, + const vec2f &v) { Assert(_object); Assert(bufName); @@ -589,7 +648,9 @@ namespace ospray { } /*! assign (named) vec3f parameter to an object */ - void MPIDevice::setVec3f(OSPObject _object, const char *bufName, const vec3f &v) + void MPIDevice::setVec3f(OSPObject _object, + const char *bufName, + const vec3f &v) { Assert(_object); Assert(bufName); @@ -601,7 +662,9 @@ namespace ospray { } /*! assign (named) vec4f parameter to an object */ - void MPIDevice::setVec4f(OSPObject _object, const char *bufName, const vec4f &v) + void MPIDevice::setVec4f(OSPObject _object, + const char *bufName, + const vec4f &v) { Assert(_object); Assert(bufName); @@ -613,7 +676,9 @@ namespace ospray { } /*! assign (named) vec2i parameter to an object */ - void MPIDevice::setVec2i(OSPObject _object, const char *bufName, const vec2i &v) + void MPIDevice::setVec2i(OSPObject _object, + const char *bufName, + const vec2i &v) { Assert(_object); Assert(bufName); @@ -625,7 +690,9 @@ namespace ospray { } /*! assign (named) vec3i parameter to an object */ - void MPIDevice::setVec3i(OSPObject _object, const char *bufName, const vec3i &v) + void MPIDevice::setVec3i(OSPObject _object, + const char *bufName, + const vec3i &v) { Assert(_object); Assert(bufName); @@ -637,7 +704,9 @@ namespace ospray { } /*! assign (named) data item as a parameter to an object */ - void MPIDevice::setObject(OSPObject _target, const char *bufName, OSPObject _value) + void MPIDevice::setObject(OSPObject _target, + const char *bufName, + OSPObject _value) { Assert(_target != NULL); Assert(bufName != NULL); @@ -651,192 +720,6 @@ namespace ospray { cmd.flush(); } - /*! Get the handle of the named data array associated with an object. */ - int MPIDevice::getData(OSPObject object, const char *name, OSPData *value) - { - // Not yet implemented. - return false; - } - - /*! Get a copy of the data in an array (the application is - responsible for freeing this pointer). */ - int MPIDevice::getDataValues(OSPData object, void **pointer, - size_t *count, OSPDataType *type) - { - // Not yet implemented. - return false; - } - - /*! Get the named scalar floating point value associated with an object. */ - int MPIDevice::getf(OSPObject object, const char *name, float *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_FLOAT); - cmd.flush(); - - struct ReturnValue { int success; float value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get the named scalar integer associated with an object. */ - int MPIDevice::geti(OSPObject object, const char *name, int *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_INT); - cmd.flush(); - - struct ReturnValue { int success; int value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get the material associated with a geometry object. */ - int MPIDevice::getMaterial(OSPGeometry geometry, OSPMaterial *value) - { - // Not yet implemented. - return false; - } - - /*! Get the named object associated with an object. */ - int MPIDevice::getObject(OSPObject object, const char *name, OSPObject *value) - { - // Not yet implemented. - return false; - } - - /*! Retrieve a NULL-terminated list of the parameter names associated with an object. */ - int MPIDevice::getParameters(OSPObject object, char ***value) - { - // Not yet implemented. - return false; - } - - /*! Retrieve the total length of the names (with terminators) of the parameters associated with an object. */ - int MPIDevice::getParametersSize(OSPObject object, int *value) - { - // Not yet implemented. - return false; - } - - /*! Get the type of the named parameter or the given object (if 'name' is NULL). */ - int MPIDevice::getType(OSPObject object, const char *name, OSPDataType *value) - { - Assert(object); - - cmd.newCommand(CMD_GET_TYPE); - cmd.send((const ObjectHandle &) object); - cmd.send(name ? name : "\0"); - cmd.flush(); - - struct ReturnValue { int success; OSPDataType value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get a pointer to a copy of the named character string associated with an object. */ - int MPIDevice::getString(OSPObject object, const char *name, char **value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_STRING); - cmd.flush(); - - struct ReturnValue { int success; char value[2048]; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = strdup(result.value), true : false; - } - - /*! Get the named 2-vector floating point value associated with an object. */ - int MPIDevice::getVec2f(OSPObject object, const char *name, vec2f *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_FLOAT2); - cmd.flush(); - - struct ReturnValue { int success; vec2f value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get the named 3-vector floating point value associated with an object. */ - int MPIDevice::getVec3f(OSPObject object, const char *name, vec3f *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_FLOAT3); - cmd.flush(); - - struct ReturnValue { int success; vec3f value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get the named 4-vector floating point value associated with an object. */ - int MPIDevice::getVec4f(OSPObject object, const char *name, vec4f *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_FLOAT4); - cmd.flush(); - - struct ReturnValue { int success; vec4f value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - - /*! Get the named 3-vector integer value associated with an object. */ - int MPIDevice::getVec3i(OSPObject object, const char *name, vec3i *value) - { - Assert(object); - Assert(name); - - cmd.newCommand(CMD_GET_VALUE); - cmd.send((const ObjectHandle &) object); - cmd.send(name); - cmd.send(OSP_INT3); - cmd.flush(); - - struct ReturnValue { int success; vec3i value; } result; - cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); - - return result.success ? *value = result.value, true : false; - } - /*! create a new pixelOp object (out of list of registered pixelOps) */ OSPPixelOp MPIDevice::newPixelOp(const char *type) { @@ -994,7 +877,8 @@ namespace ospray { return NULL; } - /*! clear the specified channel(s) of the frame buffer specified in 'whichChannels' + /*! clear the specified channel(s) of the frame buffer specified in + 'whichChannels' if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to '0,0,0,0'. @@ -1008,10 +892,16 @@ namespace ospray { void MPIDevice::frameBufferClear(OSPFrameBuffer _fb, const uint32 fbChannelFlags) { + ObjectHandle handle = (const ObjectHandle &)_fb; cmd.newCommand(CMD_FRAMEBUFFER_CLEAR); - cmd.send((const ObjectHandle&)_fb); + cmd.send(handle); cmd.send((int32)fbChannelFlags); cmd.flush(); + + // also clear FB on master, i.e. clear error buffer for variance + // estimation + FrameBuffer *fb = (FrameBuffer *)handle.lookup(); + fb->clear(fbChannelFlags); } /*! remove an existing geometry from a model */ @@ -1038,7 +928,6 @@ namespace ospray { OSPRenderer _renderer, const uint32 fbChannelFlags) { - double T0 = getSysTime(); const ObjectHandle handle = (const ObjectHandle&)_fb; // const ObjectHandle handle = (const ObjectHandle&)_sc; // SwapChain *sc = (SwapChain *)handle.lookup(); @@ -1130,7 +1019,8 @@ namespace ospray { NOTIMPLEMENTED; } break; // ================================================================== - // currently in default (mastered) mode where master tells workers what to do + // currently in default (mastered) mode where master tells workers what + // to do // ================================================================== case OSPD_MODE_MASTERED: { // first: tell workers to switch to new mode: they're in @@ -1144,12 +1034,14 @@ namespace ospray { } break; case OSPD_MODE_INDEPENDENT: case OSPD_MODE_COLLABORATIVE: { - printf("rank %i telling clients to switch to %s mode.\n",mpi::world.rank,apiModeName(newMode)); + printf("rank %i telling clients to switch to %s mode.\n", + mpi::world.rank,apiModeName(newMode)); cmd.newCommand(CMD_API_MODE); cmd.send((int32)newMode); cmd.flush(); currentApiMode = newMode; - // and just to be sure, do a barrier here -- not acutally needed AFAICT. + // and just to be sure, do a barrier here -- not acutally needed + // AFAICT. MPI_Barrier(MPI_COMM_WORLD); } break; default: @@ -1168,11 +1060,14 @@ namespace ospray { default: NOTIMPLEMENTED; }; - throw std::runtime_error("Distributed API not available on this device (when calling ospApiMode())"); + throw std::runtime_error("Distributed API not available on this device " + "(when calling ospApiMode())"); } - void MPIDevice::sampleVolume(float **results, OSPVolume volume, - const vec3f *worldCoordinates, const size_t &count) + void MPIDevice::sampleVolume(float **results, + OSPVolume volume, + const vec3f *worldCoordinates, + const size_t &count) { Assert2(volume, "invalid volume handle"); Assert2(worldCoordinates, "invalid worldCoordinates"); @@ -1190,6 +1085,23 @@ namespace ospray { cmd.get_data(count * sizeof(float), *results, 0, mpi::worker.comm); } + int MPIDevice::getString(OSPObject object, const char *name, char **value) + { + Assert(object); + Assert(name); + + cmd.newCommand(CMD_GET_VALUE); + cmd.send((const ObjectHandle &) object); + cmd.send(name); + cmd.send(OSP_STRING); + cmd.flush(); + + struct ReturnValue { int success; char value[2048]; } result; + cmd.get_data(sizeof(ReturnValue), &result, 0, mpi::worker.comm); + + return result.success ? *value = strdup(result.value), true : false; + } + } // ::ospray::mpi } // ::ospray diff --git a/ospray/mpi/MPIDevice.h b/ospray/mpi/MPIDevice.h index 38dd596172..7897c762dc 100644 --- a/ospray/mpi/MPIDevice.h +++ b/ospray/mpi/MPIDevice.h @@ -17,10 +17,10 @@ #pragma once #include "MPICommon.h" -#include "ospray/api/Device.h" -#include "ospray/mpi/command.h" +#include "api/Device.h" +#include "mpi/command.h" #include "CommandStream.h" -#include "ospray/common/Managed.h" +#include "common/Managed.h" /*! \file mpidevice.h Implements the "mpi" device for mpi rendering */ @@ -37,6 +37,7 @@ namespace ospray { /*! constructor */ MPIDevice(// AppMode appMode, OSPMode ospMode, int *_ac=NULL, const char **_av=NULL); + virtual ~MPIDevice(); /*! create a new frame buffer */ OSPFrameBuffer @@ -154,55 +155,6 @@ namespace ospray { /*! add untyped void pointer to object - this will *ONLY* work in local rendering! */ void setVoidPtr(OSPObject object, const char *bufName, void *v) override; - /*! Get the handle of the named data array associated with an object. */ - int getData(OSPObject object, const char *name, OSPData *value) override; - - /*! Get a copy of the data in an array (the application is responsible for freeing this pointer). */ - int getDataValues(OSPData object, void **pointer, - size_t *count, OSPDataType *type) override; - - /*! Get the named scalar floating point value associated with an object. */ - int getf(OSPObject object, const char *name, float *value) override; - - /*! Get the named scalar integer associated with an object. */ - int geti(OSPObject object, const char *name, int *value) override; - - /*! Get the material associated with a geometry object. */ - int getMaterial(OSPGeometry geometry, OSPMaterial *value) override; - - /*! Get the named object associated with an object. */ - int getObject(OSPObject object, - const char *name, - OSPObject *value) override; - - /*! Retrieve a NULL-terminated list of the parameter names associated with an object. */ - int getParameters(OSPObject object, char ***value) override; - - /*! Retrieve the total length of the names (with terminators) of the parameters associated with an object. */ - int getParametersSize(OSPObject object, int *value); - - /*! Get a pointer to a copy of the named character string associated with an object. */ - int getString(OSPObject object, - const char *name, - char **value) override; - - /*! Get the type of the named parameter or the given object (if 'name' is NULL). */ - int getType(OSPObject object, - const char *name, - OSPDataType *value) override; - - /*! Get the named 2-vector floating point value associated with an object. */ - int getVec2f(OSPObject object, const char *name, vec2f *value) override; - - /*! Get the named 3-vector floating point value associated with an object. */ - int getVec3f(OSPObject object, const char *name, vec3f *value) override; - - /*! Get the named 4-vector floating point value associated with an object. */ - int getVec4f(OSPObject object, const char *name, vec4f *value) override; - - /*! Get the named 3-vector integer value associated with an object. */ - int getVec3i(OSPObject object, const char *name, vec3i *value) override; - /*! create a new renderer object (out of list of registered renderers) */ OSPRenderer newRenderer(const char *type) override; @@ -256,6 +208,11 @@ namespace ospray { const vec3f *worldCoordinates, const size_t &count) override; + private: + + /*! This only exists to support getting the voxel type for setRegion */ + int getString(OSPObject object, const char *name, char **value); + }; // ================================================================== diff --git a/ospray/mpi/MPILoadBalancer.cpp b/ospray/mpi/MPILoadBalancer.cpp index aed01191a6..cc07814aa7 100644 --- a/ospray/mpi/MPILoadBalancer.cpp +++ b/ospray/mpi/MPILoadBalancer.cpp @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/mpi/MPILoadBalancer.h" -#include "ospray/render/Renderer.h" -#include "ospray/fb/LocalFB.h" -#include "ospray/mpi/DistributedFrameBuffer.h" -#include "ospray/common/tasking/parallel_for.h" +#include "mpi/MPILoadBalancer.h" +#include "render/Renderer.h" +#include "fb/LocalFB.h" +#include "mpi/DistributedFrameBuffer.h" +#include "common/tasking/parallel_for.h" #include @@ -70,15 +70,20 @@ namespace ospray { void *perFrameData = tiledRenderer->beginFrame(fb); - int numTiles_x = divRoundUp(fb->size.x,TILE_SIZE); - int numTiles_y = divRoundUp(fb->size.y,TILE_SIZE); + const int ALLTASKS = fb->getTotalTiles(); + int NTASKS = ALLTASKS / worker.size; + + // NOTE(jda) - If all tiles do not divide evenly among all worker ranks + // (a.k.a. ALLTASKS / worker.size has a remainder), then + // some ranks will have one extra tile to do. Thus NTASKS + // is incremented if we are one of those ranks. + if ((ALLTASKS % worker.size) > worker.rank) + NTASKS++; - const int NTASKS = numTiles_x * numTiles_y; // serial_for(NTASKS, [&](int taskIndex){ parallel_for(NTASKS, [&](int taskIndex){ - const size_t tileID = taskIndex; - if ((tileID % worker.size) != worker.rank) return; - + const size_t tileID = taskIndex * worker.size + worker.rank; + const size_t numTiles_x = fb->getNumTiles().x; const size_t tile_y = tileID / numTiles_x; const size_t tile_x = tileID - tile_y*numTiles_x; const vec2i tileId(tile_x, tile_y); @@ -97,9 +102,9 @@ namespace ospray { Tile __aligned(64) tile(tileId, fb->size, accumID); #endif - // serial_for(numJobs(tiledRenderer->spp, accumID), [&](int taskIndex){ - parallel_for(numJobs(tiledRenderer->spp, accumID), [&](int taskIndex){ - tiledRenderer->renderTile(perFrameData, tile, taskIndex); + // serial_for(numJobs(tiledRenderer->spp, accumID), [&](int tid){ + parallel_for(numJobs(tiledRenderer->spp, accumID), [&](int tid){ + tiledRenderer->renderTile(perFrameData, tile, tid); }); fb->setTile(tile); @@ -113,7 +118,7 @@ namespace ospray { async_endFrame(); - return dfb->endFrame(0.f); + return inf; // irrelevant on slave } std::string Slave::toString() const diff --git a/ospray/mpi/MPILoadBalancer.h b/ospray/mpi/MPILoadBalancer.h index b87b87b015..071ba19d25 100644 --- a/ospray/mpi/MPILoadBalancer.h +++ b/ospray/mpi/MPILoadBalancer.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/mpi/MPICommon.h" -#include "ospray/render/LoadBalancer.h" +#include "mpi/MPICommon.h" +#include "render/LoadBalancer.h" namespace ospray { namespace mpi { diff --git a/ospray/mpi/MPIWorker.cpp b/ospray/mpi/MPIWorker.cpp index 0399c430ab..5c202f708f 100644 --- a/ospray/mpi/MPIWorker.cpp +++ b/ospray/mpi/MPIWorker.cpp @@ -14,8 +14,8 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/mpi/MPICommon.h" -#include "ospray/mpi/MPIDevice.h" +#include "mpi/MPICommon.h" +#include "mpi/MPIDevice.h" #include "ospray/ospray.h" @@ -25,7 +25,7 @@ namespace ospray { using std::cout; using std::endl; - void runWorker(int *_ac, const char **_av); + OSPRAY_INTERFACE void runWorker(); void workerMain(int ac, const char **av) { @@ -41,42 +41,44 @@ namespace ospray { mpi::init(&ac,av); worker.comm = world.comm; worker.makeIntraComm(); - // worker.makeIntercomm(); if (ac == 3 && !strcmp(av[1],"--osp:connect")) { - // if (worker.rank == 0) { char *appPortName = strdup(av[2]); // de-fix port name - 'real' port name has '$'s, not '%'s for (char *s = appPortName; *s; ++s) if (*s == '%') *s = '$'; cout << "#w: trying to connect to port " << appPortName << endl; - rc = MPI_Comm_connect(appPortName,MPI_INFO_NULL,0,worker.comm,&app.comm); + rc = MPI_Comm_connect(appPortName,MPI_INFO_NULL,0, + worker.comm,&app.comm); Assert(rc == MPI_SUCCESS); cout << "#w: ospray started with " << worker.size << " workers, " << "#w: and connected to app at " << appPortName << endl; - // } app.makeInterComm(); - // app.makeIntracomm(); } else { char servicePortName[MPI_MAX_PORT_NAME]; if (world.rank == 0) { rc = MPI_Open_port(MPI_INFO_NULL, servicePortName); - cout << "#osp:ospray_mpi_worker opened port: " << servicePortName << endl; - for (char *s = servicePortName;*s;s++) + cout << "#osp:ospray_mpi_worker opened port: " << servicePortName + << endl; + for (char *s = servicePortName; *s ; s++) if (*s == '$') *s = '%'; + Assert(rc == MPI_SUCCESS); - cout << "------------------------------------------------------" << endl; - cout << "ospray service started with " << worker.size << " workers" << endl; + cout << "------------------------------------------------------" + << endl; + cout << "ospray service started with " << worker.size + << " workers" << endl; cout << "OSPRAY_SERVICE_PORT:" << servicePortName << endl; } - cout << "#osp:ospray_mpi_worker trying to accept from '" << servicePortName << "'" << endl; - rc = MPI_Comm_accept(servicePortName,MPI_INFO_NULL,0,MPI_COMM_WORLD,&app.comm); + cout << "#osp:ospray_mpi_worker trying to accept from '" + << servicePortName << "'" << endl; + rc = MPI_Comm_accept(servicePortName,MPI_INFO_NULL, + 0,MPI_COMM_WORLD,&app.comm); Assert(rc == MPI_SUCCESS); app.makeInterComm(); - // app.makeIntracomm(); } MPI_Barrier(world.comm); - runWorker(&ac,av); // this fct will not return + runWorker(); // this fct will not return } } // ::ospray::api diff --git a/ospray/mpi/async/BatchedIsendIrecvMessaging.cpp b/ospray/mpi/async/BatchedIsendIrecvMessaging.cpp index 17059b62d9..17a8154109 100644 --- a/ospray/mpi/async/BatchedIsendIrecvMessaging.cpp +++ b/ospray/mpi/async/BatchedIsendIrecvMessaging.cpp @@ -14,6 +14,15 @@ // limitations under the License. // // ======================================================================== // +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include // for Sleep +#else +# include +#endif +#include +#include +#include #include "BatchedIsendIrecvMessaging.h" namespace ospray { @@ -27,7 +36,7 @@ namespace ospray { BatchedIsendIrecvImpl::Group::Group(const std::string &name, MPI_Comm comm, Consumer *consumer, int32 tag) : async::Group(comm,consumer,tag), - sendThread(this), recvThread(this), procThread(this) + sendThread(this), recvThread(this), procThread(this), shouldExit(false) { recvThread.start(); sendThread.start(); @@ -41,13 +50,22 @@ namespace ospray { MPI_Request request[SEND_WINDOW_SIZE]; while (1) { // usleep(80); - size_t numActions = g->sendQueue.getSome(actions,SEND_WINDOW_SIZE); + size_t numActions = 0; + while (numActions == 0){ + numActions = g->sendQueue.getSomeFor(actions,SEND_WINDOW_SIZE, + std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } for (int i=0;idata,action->size,MPI_BYTE, action->addr.rank,g->tag,g->comm,&request[i])); } - + + // TODO: Is it ok to wait even if we're exiting? Maybe we'd just get send + // failed statuses back? MPI_CALL(Waitall(numActions,request,MPI_STATUSES_IGNORE)); for (int i=0;i much longer than 150us +#else + const timespec sleep_time = timespec{0, 150000}; +#endif + while (1) { numRequests = 0; // wait for first message { // usleep(280); - MPI_CALL(Probe(MPI_ANY_SOURCE,g->tag,g->comm,&status)); + int msgAvail = 0; + while (!msgAvail) { + MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); + if (g->shouldExit.load()){ + return; + } + if (msgAvail){ + break; + } +#ifdef _WIN32 + Sleep(sleep_time); +#else + // TODO: Can we do a CMake feature test for this_thread::sleep_for and + // conditionally use nanosleep? + nanosleep(&sleep_time, NULL); +#endif + } Action *action = new Action; action->addr = Address(g,status.MPI_SOURCE); MPI_CALL(Get_count(&status,MPI_BYTE,&action->size)); @@ -104,6 +143,8 @@ namespace ospray { actions[numRequests++] = action; } + // TODO: Is it ok to wait even if we're exiting? Maybe we'd just get send + // failed statuses back? // now, have certain number of messages available... MPI_CALL(Waitall(numRequests,request,MPI_STATUSES_IGNORE)); @@ -117,7 +158,14 @@ namespace ospray { Group *g = (Group *)this->group; while (1) { Action *actions[PROC_WINDOW_SIZE]; - size_t numActions = g->recvQueue.getSome(actions,PROC_WINDOW_SIZE); + size_t numActions = 0; + while (numActions == 0){ + numActions = g->recvQueue.getSomeFor(actions,PROC_WINDOW_SIZE, + std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } for (int i=0;iconsumer->process(action->addr,action->data,action->size); @@ -128,8 +176,11 @@ namespace ospray { void BatchedIsendIrecvImpl::Group::shutdown() { - std::cout << "shutdown() not implemented for this messaging ..." - << std::endl; + std::cout << "#osp:mpi:BatchIsendIrecvMessaging:Group shutting down" << std::endl; + shouldExit.store(true); + sendThread.join(); + recvThread.join(); + procThread.join(); } void BatchedIsendIrecvImpl::init() diff --git a/ospray/mpi/async/BatchedIsendIrecvMessaging.h b/ospray/mpi/async/BatchedIsendIrecvMessaging.h index d32b028f3b..b60b15ae0d 100644 --- a/ospray/mpi/async/BatchedIsendIrecvMessaging.h +++ b/ospray/mpi/async/BatchedIsendIrecvMessaging.h @@ -18,8 +18,8 @@ // ospray #include "Messaging.h" -#include "ospray/common/Thread.h" -#include "ospray/common/ProducerConsumerQueue.h" +#include "common/Thread.h" +#include "common/ProducerConsumerQueue.h" // stl #include #include @@ -95,6 +95,7 @@ namespace ospray { SendThread sendThread; ProcThread procThread; RecvThread recvThread; + std::atomic shouldExit; }; virtual void init(); diff --git a/ospray/mpi/async/Messaging.cpp b/ospray/mpi/async/Messaging.cpp index e336b96e71..55dd7c838d 100644 --- a/ospray/mpi/async/Messaging.cpp +++ b/ospray/mpi/async/Messaging.cpp @@ -14,10 +14,10 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/mpi/async/Messaging.h" -#include "ospray/mpi/async/SimpleSendRecvMessaging.h" -#include "ospray/mpi/async/MultiIsendIrecvMessaging.h" -#include "ospray/mpi/async/BatchedIsendIrecvMessaging.h" +#include "mpi/async/Messaging.h" +#include "mpi/async/SimpleSendRecvMessaging.h" +#include "mpi/async/MultiIsendIrecvMessaging.h" +#include "mpi/async/BatchedIsendIrecvMessaging.h" namespace ospray { namespace mpi { diff --git a/ospray/mpi/async/Messaging.h b/ospray/mpi/async/Messaging.h index 54be512893..d8ba415319 100644 --- a/ospray/mpi/async/Messaging.h +++ b/ospray/mpi/async/Messaging.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/mpi/MPICommon.h" -#include "ospray/common/Thread.h" +#include "mpi/MPICommon.h" +#include "common/Thread.h" namespace ospray { namespace mpi { diff --git a/ospray/mpi/async/MultiIsendIrecvMessaging.cpp b/ospray/mpi/async/MultiIsendIrecvMessaging.cpp index 68b49d6c83..3d1a37c55e 100644 --- a/ospray/mpi/async/MultiIsendIrecvMessaging.cpp +++ b/ospray/mpi/async/MultiIsendIrecvMessaging.cpp @@ -14,6 +14,15 @@ // limitations under the License. // // ======================================================================== // +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include // for Sleep +#else +# include +#endif +#include +#include +#include #include "MultiIsendIrecvMessaging.h" namespace ospray { @@ -34,7 +43,14 @@ namespace ospray { { Group *g = this->group; while (1) { - Action *action = g->sendQueue.get(); + Action *action = nullptr; + size_t numActions = 0; + while (numActions == 0){ + numActions = g->sendQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } // note we're not using any window here; we just pull them // in order. this is OK because they key is to have mulitple // sends going in parallel - which we do because each @@ -51,10 +67,31 @@ namespace ospray { // note this thread not only _probes_ for new receives, it // also immediately starts the receive operation using Irecv() Group *g = (Group *)this->group; - +#ifdef _WIN32 + const DWORD sleep_time = 1; // ms --> much longer than 150us +#else + const timespec sleep_time = timespec{ 0, 150000 }; +#endif + while (1) { MPI_Status status; - MPI_CALL(Probe(MPI_ANY_SOURCE,g->tag,g->comm,&status)); + int msgAvail = 0; + while (!msgAvail) { + MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); + if (g->shouldExit.load()){ + return; + } + if (msgAvail){ + break; + } +#ifdef _WIN32 + Sleep(sleep_time); +#else + // TODO: Can we do a CMake feature test for this_thread::sleep_for and + // conditionally use nanosleep? + nanosleep(&sleep_time, NULL); +#endif + } Action *action = new Action; action->addr = Address(g,status.MPI_SOURCE); @@ -71,7 +108,14 @@ namespace ospray { { Group *g = (Group *)this->group; while (1) { - Action *action = g->recvQueue.get(); + Action *action = nullptr; + size_t numActions = 0; + while (numActions == 0){ + numActions = g->recvQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } MPI_CALL(Wait(&action->request,MPI_STATUS_IGNORE)); g->consumer->process(action->addr,action->data,action->size); delete action; @@ -80,7 +124,11 @@ namespace ospray { void MultiIsendIrecvImpl::Group::shutdown() { - std::cout << "shutdown() not implemented for this messaging ..." << std::endl; + std::cout << "#osp:mpi:MultiIsendIrecvImpl:Group shutting down" << std::endl; + shouldExit.store(true); + sendThread.join(); + recvThread.join(); + procThread.join(); } void MultiIsendIrecvImpl::init() diff --git a/ospray/mpi/async/MultiIsendIrecvMessaging.h b/ospray/mpi/async/MultiIsendIrecvMessaging.h index 855afbcc5a..3a3cf42961 100644 --- a/ospray/mpi/async/MultiIsendIrecvMessaging.h +++ b/ospray/mpi/async/MultiIsendIrecvMessaging.h @@ -18,11 +18,12 @@ // ospray #include "Messaging.h" -#include "ospray/common/Thread.h" -#include "ospray/common/ProducerConsumerQueue.h" +#include "common/Thread.h" +#include "common/ProducerConsumerQueue.h" // stl #include #include +#include namespace ospray { namespace mpi { @@ -83,6 +84,7 @@ namespace ospray { SendThread sendThread; ProcThread procThread; RecvThread recvThread; + std::atomic shouldExit; }; virtual void init(); diff --git a/ospray/mpi/async/SimpleSendRecvMessaging.cpp b/ospray/mpi/async/SimpleSendRecvMessaging.cpp index 93fd01e259..4a5b3a4e71 100644 --- a/ospray/mpi/async/SimpleSendRecvMessaging.cpp +++ b/ospray/mpi/async/SimpleSendRecvMessaging.cpp @@ -16,6 +16,15 @@ //#define PROFILE_MPI 1 +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include // for Sleep +#else +# include +#endif +#include +#include +#include #include "SimpleSendRecvMessaging.h" namespace ospray { @@ -87,7 +96,14 @@ namespace ospray { Group *g = this->group; while (1) { - Action *action = g->sendQueue.get(); + Action *action = nullptr; + size_t numActions = 0; + while (numActions == 0){ + numActions = g->sendQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } double t0 = getSysTime(); MPI_CALL(Send(action->data,action->size,MPI_BYTE, action->addr.rank,g->tag,action->addr.group->comm)); @@ -113,11 +129,31 @@ namespace ospray { void SimpleSendRecvImpl::RecvThread::run() { Group *g = (Group *)this->group; +#ifdef _WIN32 + const DWORD sleep_time = 1; // ms --> much longer than 150us +#else + const timespec sleep_time = timespec{ 0, 150000 }; +#endif while (1) { MPI_Status status; - // PING;fflush(0); - MPI_CALL(Probe(MPI_ANY_SOURCE,g->tag,g->comm,&status)); + int msgAvail = 0; + while (!msgAvail) { + MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); + if (g->shouldExit.load()){ + return; + } + if (msgAvail){ + break; + } +#ifdef _WIN32 + Sleep(sleep_time); +#else + // TODO: Can we do a CMake feature test for this_thread::sleep_for and + // conditionally use nanosleep? + nanosleep(&sleep_time, NULL); +#endif + } Action *action = new Action; action->addr = Address(g,status.MPI_SOURCE); @@ -149,7 +185,14 @@ namespace ospray { { Group *g = (Group *)this->group; while (1) { - Action *action = g->procQueue.get(); + Action *action = nullptr; + size_t numActions = 0; + while (numActions == 0){ + numActions = g->procQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); + if (g->shouldExit.load()){ + return; + } + } g->consumer->process(action->addr,action->data,action->size); delete action; } @@ -157,7 +200,11 @@ namespace ospray { void SimpleSendRecvImpl::Group::shutdown() { - std::cout << "shutdown() not implemented for this messaging ..." << std::endl; + std::cout << "#osp:mpi:SimpleSendRecvImpl:Group shutting down" << std::endl; + shouldExit.store(true); + sendThread.join(); + recvThread.join(); + procThread.join(); } void SimpleSendRecvImpl::init() diff --git a/ospray/mpi/async/SimpleSendRecvMessaging.h b/ospray/mpi/async/SimpleSendRecvMessaging.h index 582b1d4a83..b62b04adc2 100644 --- a/ospray/mpi/async/SimpleSendRecvMessaging.h +++ b/ospray/mpi/async/SimpleSendRecvMessaging.h @@ -18,11 +18,12 @@ // ospray #include "Messaging.h" -#include "ospray/common/Thread.h" -#include "ospray/common/ProducerConsumerQueue.h" +#include "common/Thread.h" +#include "common/ProducerConsumerQueue.h" // stl #include #include +#include namespace ospray { namespace mpi { @@ -77,6 +78,7 @@ namespace ospray { SendThread sendThread; RecvThread recvThread; ProcThread procThread; + std::atomic shouldExit; }; virtual void init(); diff --git a/ospray/mpi/buffers.h b/ospray/mpi/buffers.h index 281c352212..94f5ca2126 100644 --- a/ospray/mpi/buffers.h +++ b/ospray/mpi/buffers.h @@ -20,7 +20,7 @@ buffers that can be used to send data across all kinds of network/communication devices */ -#include "ospray/common/OSPCommon.h" +#include "common/OSPCommon.h" namespace ospray { namespace nwlayer { diff --git a/ospray/mpi/command.h b/ospray/mpi/command.h index d3c4df8a06..48ffa70c28 100644 --- a/ospray/mpi/command.h +++ b/ospray/mpi/command.h @@ -18,8 +18,8 @@ /*! \file ospray/device/nwlayer.h \brief Defines the basic network layer abstraction */ -#include "ospray/include/ospray/ospray.h" -#include "ospray/mpi/buffers.h" +#include "include/ospray/ospray.h" +#include "mpi/buffers.h" namespace ospray { // namespace nwlayer { @@ -75,6 +75,8 @@ namespace ospray { CMD_API_MODE, CMD_SAMPLE_VOLUME, + CMD_FINALIZE, + CMD_USER } CommandTag; diff --git a/ospray/mpi/worker.cpp b/ospray/mpi/worker.cpp index cb2dee6519..a4634c5d0f 100644 --- a/ospray/mpi/worker.cpp +++ b/ospray/mpi/worker.cpp @@ -14,26 +14,26 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/mpi/MPICommon.h" -#include "ospray/mpi/MPIDevice.h" -#include "ospray/mpi/CommandStream.h" -#include "ospray/common/Model.h" -#include "ospray/common/Data.h" -#include "ospray/common/Library.h" -#include "ospray/common/Model.h" -#include "ospray/geometry/TriangleMesh.h" -#include "ospray/render/Renderer.h" -#include "ospray/camera/Camera.h" -#include "ospray/volume/Volume.h" -#include "ospray/lights/Light.h" -#include "ospray/texture/Texture2D.h" -#include "ospray/fb/LocalFB.h" -#include "ospray/mpi/async/CommLayer.h" -#include "ospray/mpi/DistributedFrameBuffer.h" -#include "ospray/mpi/MPILoadBalancer.h" -#include "ospray/transferFunction/TransferFunction.h" - -#include "ospray/mpi/MPIDevice.h" +#include "mpi/MPICommon.h" +#include "mpi/MPIDevice.h" +#include "mpi/CommandStream.h" +#include "common/Model.h" +#include "common/Data.h" +#include "common/Library.h" +#include "common/Model.h" +#include "geometry/TriangleMesh.h" +#include "render/Renderer.h" +#include "camera/Camera.h" +#include "volume/Volume.h" +#include "lights/Light.h" +#include "texture/Texture2D.h" +#include "fb/LocalFB.h" +#include "mpi/async/CommLayer.h" +#include "mpi/DistributedFrameBuffer.h" +#include "mpi/MPILoadBalancer.h" +#include "transferFunction/TransferFunction.h" + +#include "mpi/MPIDevice.h" // std #include @@ -63,23 +63,12 @@ namespace ospray { using std::cout; using std::endl; - struct GeometryLocator { - bool operator()(const Ref &g) const { - return ptr == &*g; - } - Geometry *ptr; - }; - - struct VolumeLocator { - bool operator()(const Ref &g) const { - return ptr == &*g; - } - Volume *ptr; - }; + OSPRAY_INTERFACE void runWorker(); void embreeErrorFunc(const RTCError code, const char* str) { - std::cerr << "#osp: embree internal error " << code << " : " << str << std::endl; + std::cerr << "#osp: embree internal error " << code << " : " + << str << std::endl; throw std::runtime_error("embree internal error '"+std::string(str)+"'"); } @@ -90,10 +79,9 @@ namespace ospray { \internal We ssume that mpi::worker and mpi::app have already been set up */ - void runWorker(int *_ac, const char **_av) + void runWorker() { - mpi::MPIDevice *device = (mpi::MPIDevice *)ospray::api::Device::current; - ospray::init(_ac,&_av); + Ref device = ospray::api::Device::current.dynamicCast(); // initialize embree. (we need to do this here rather than in // ospray::init() because in mpi-mode the latter is also called @@ -124,13 +112,6 @@ namespace ospray { << (int)rtcDeviceGetError(embreeDevice) << std::endl; } - // ------------------------------------------------------- - // initialize our task system - // ------------------------------------------------------- -#if 0 - ospray::Task::initTaskSystem(debugMode ? 0 : numThreads); -#endif - CommandStream cmd; char hostname[HOST_NAME_MAX]; @@ -139,21 +120,20 @@ namespace ospray { worker.rank,worker.size,getpid(),hostname); int rc; - // TiledLoadBalancer::instance = new mpi::DynamicLoadBalancer_Slave; TiledLoadBalancer::instance = new mpi::staticLoadBalancer::Slave; try { while (1) { const int command = cmd.get_int32(); - // printf("worker: got command %i\n",command); fflush(0); switch (command) { case ospray::CMD_NEW_PIXELOP: { const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new pixelOp \"" << type << "\" ID " << handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new pixelOp \"" << type << "\" ID " + << handle << endl; + } PixelOp *pixelOp = PixelOp::createPixelOp(type); cmd.free(type); Assert(pixelOp); @@ -162,9 +142,10 @@ namespace ospray { case ospray::CMD_NEW_RENDERER: { const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new renderer \"" << type << "\" ID " << handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new renderer \"" << type << "\" ID " + << handle << endl; + } Renderer *renderer = Renderer::createRenderer(type); cmd.free(type); Assert(renderer); @@ -173,40 +154,45 @@ namespace ospray { case ospray::CMD_NEW_CAMERA: { const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new camera \"" << type << "\" ID " << (void*)(int64)handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new camera \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } Camera *camera = Camera::createCamera(type); cmd.free(type); Assert(camera); handle.assign(camera); - // cout << "#w: new camera " << handle << endl; } break; case ospray::CMD_NEW_VOLUME: { // Assert(type != NULL && "invalid volume type identifier"); const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new volume \"" << type << "\" ID " << (void*)(int64)handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new volume \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } Volume *volume = Volume::createInstance(type); - if (!volume) - throw std::runtime_error("unknown volume type '"+std::string(type)+"'"); + if (!volume) { + throw std::runtime_error("unknown volume type '" + + std::string(type) + "'"); + } volume->refInc(); cmd.free(type); Assert(volume); handle.assign(volume); - // cout << "#w: new volume " << handle << endl; } break; case ospray::CMD_NEW_TRANSFERFUNCTION: { const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new transfer function \"" << type << "\" ID " << (void*)(int64)handle << endl; - TransferFunction *transferFunction = TransferFunction::createInstance(type); + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new transfer function \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } + TransferFunction *transferFunction = + TransferFunction::createInstance(type); if (!transferFunction) { - throw std::runtime_error("unknown transfer function type '"+std::string(type)+"'"); + throw std::runtime_error("unknown transfer function type '" + + std::string(type) + "'"); } transferFunction->refInc(); cmd.free(type); @@ -217,9 +203,10 @@ namespace ospray { const ObjectHandle rendererHandle = cmd.get_handle(); const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new material \"" << type << "\" ID " << (void*)(int64)handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new material \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } Renderer *renderer = (Renderer *)rendererHandle.lookup(); Material *material = NULL; @@ -263,9 +250,10 @@ namespace ospray { const ObjectHandle rendererHandle = cmd.get_handle(); const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new light \"" << type << "\" ID " << (void*)(int64)handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new light \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } Renderer *renderer = (Renderer *)rendererHandle.lookup(); Light *light = NULL; @@ -309,78 +297,65 @@ namespace ospray { // Assert(type != NULL && "invalid volume type identifier"); const ObjectHandle handle = cmd.get_handle(); const char *type = cmd.get_charPtr(); - if (worker.rank == 0) - if (logLevel > 2) - cout << "creating new geometry \"" << type << "\" ID " << (void*)(int64)handle << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "creating new geometry \"" << type << "\" ID " + << (void*)(int64)handle << endl; + } Geometry *geometry = Geometry::createGeometry(type); - if (!geometry) - throw std::runtime_error("unknown geometry type '"+std::string(type)+"'"); + if (!geometry) { + throw std::runtime_error("unknown geometry type '" + + std::string(type) + "'"); + } geometry->refInc(); cmd.free(type); Assert(geometry); handle.assign(geometry); - if (worker.rank == 0) - if (logLevel > 2) - cout << "#w: new geometry " << handle << " " << geometry->toString() << endl; + if (worker.rank == 0 && logLevel > 2) { + cout << "#w: new geometry " << handle << " " + << geometry->toString() << endl; + } } break; case ospray::CMD_FRAMEBUFFER_CREATE: { const ObjectHandle handle = cmd.get_handle(); const vec2i size = cmd.get_vec2i(); - const OSPFrameBufferFormat mode = (OSPFrameBufferFormat)cmd.get_int32(); + const OSPFrameBufferFormat mode = + (OSPFrameBufferFormat)cmd.get_int32(); const uint32 channelFlags = cmd.get_int32(); const bool hasDepthBuffer = (channelFlags & OSP_FB_DEPTH); const bool hasAccumBuffer = (channelFlags & OSP_FB_ACCUM); -// #if USE_DFB - FrameBuffer *fb = new DistributedFrameBuffer(ospray::mpi::async::CommLayer::WORLD, - size,handle,mode, - hasDepthBuffer,hasAccumBuffer); - -// #else -// FrameBuffer *fb = new LocalFrameBuffer(size,mode,hasDepthBuffer,hasAccumBuffer); -// #endif + const bool hasVarianceBuffer = (channelFlags & OSP_FB_VARIANCE); + FrameBuffer *fb = + new DistributedFrameBuffer(ospray::mpi::async::CommLayer::WORLD, + size, handle, mode, hasDepthBuffer, + hasAccumBuffer, hasVarianceBuffer); + handle.assign(fb); } break; case ospray::CMD_FRAMEBUFFER_CLEAR: { const ObjectHandle handle = cmd.get_handle(); - const uint32 channelFlags = cmd.get_int32(); + const uint32 channelFlags = cmd.get_int32(); FrameBuffer *fb = (FrameBuffer*)handle.lookup(); assert(fb); fb->clear(channelFlags); } break; case ospray::CMD_RENDER_FRAME: { const ObjectHandle fbHandle = cmd.get_handle(); - // const ObjectHandle swapChainHandle = cmd.get_handle(); const ObjectHandle rendererHandle = cmd.get_handle(); const uint32 channelFlags = cmd.get_int32(); FrameBuffer *fb = (FrameBuffer*)fbHandle.lookup(); - // SwapChain *sc = (SwapChain*)swapChainHandle.lookup(); - // Assert(sc); Renderer *renderer = (Renderer*)rendererHandle.lookup(); Assert(renderer); // double before = getSysTime(); renderer->renderFrame(fb,channelFlags); //sc->getBackBuffer()); // double after = getSysTime(); // float T = after - before; - // printf("#rank %i: pure time to render %f, theo fps %f\n",mpi::worker.rank,T,1.f/T); + // printf("#rank %i: pure time to render %f, theo fps %f\n", + // mpi::worker.rank,T,1.f/T); // fflush(0); - - // sc->advance(); } break; case ospray::CMD_FRAMEBUFFER_MAP: { FATAL("should never get called on worker!?"); - // const ObjectHandle handle = cmd.get_handle(); - // FrameBuffer *fb = (FrameBuffer*)handle.lookup(); - // // SwapChain *sc = (SwapChain*)handle.lookup(); - // // Assert(sc); - // if (worker.rank == 0) { - // // FrameBuffer *fb = sc->getBackBuffer(); - // void *ptr = (void*)fb->map(); - // // void *ptr = (void*)sc->map(); - // rc = MPI_Send(ptr,fb->size.x*fb->size.y,MPI_INT,0,0,mpi::app.comm); - // Assert(rc == MPI_SUCCESS); - // fb->unmap(ptr); - // } } break; case ospray::CMD_NEW_MODEL: { const ObjectHandle handle = cmd.get_handle(); @@ -464,9 +439,12 @@ namespace ospray { Geometry *geom = (Geometry*)geomHandle.lookup(); Assert(geom); - GeometryLocator locator; - locator.ptr = geom; - Model::GeometryVector::iterator it = std::find_if(model->geometry.begin(), model->geometry.end(), locator); + auto it = std::find_if(model->geometry.begin(), + model->geometry.end(), + [&](const Ref &g) { + return geom == &*g; + }); + if(it != model->geometry.end()) { model->geometry.erase(it); } @@ -480,9 +458,12 @@ namespace ospray { Volume *geom = (Volume*)geomHandle.lookup(); Assert(geom); - VolumeLocator locator; - locator.ptr = geom; - Model::VolumeVector::iterator it = std::find_if(model->volume.begin(), model->volume.end(), locator); + auto it = std::find_if(model->volume.begin(), + model->volume.end(), + [&](const Ref &g) { + return geom == &*g; + }); + if(it != model->volume.end()) { model->volume.erase(it); } @@ -502,9 +483,10 @@ namespace ospray { const ObjectHandle handle = cmd.get_handle(); ManagedObject *obj = handle.lookup(); Assert(obj); - // printf("#w%i:c%i obj %lx\n",worker.rank,(int)handle,obj); - if (logLevel > 2) - cout << "#w: committing " << handle << " " << obj->toString() << endl; + if (logLevel > 2) { + cout << "#w: committing " << handle << " " << obj->toString() + << endl; + } obj->commit(); // hack, to stay compatible with earlier version @@ -567,7 +549,8 @@ namespace ospray { ManagedObject::Param *param = object->findParam(name); bool foundParameter = (param != NULL); - result.success = foundParameter ? result.value = param->type, 1 : 0; + result.success = + foundParameter ? result.value = param->type, 1 : 0; } cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); @@ -584,7 +567,8 @@ namespace ospray { Assert(object); ManagedObject::Param *param = object->findParam(name); - bool foundParameter = (param == NULL || param->type != type) ? false : true; + bool foundParameter = + (param == NULL || param->type != type) ? false : true; switch (type) { case OSP_STRING: { struct ReturnValue { int success; char value[2048]; } result; @@ -595,33 +579,9 @@ namespace ospray { result.success = 0; cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); } break; - case OSP_FLOAT: { - struct ReturnValue { int success; float value; } result; - result.success = foundParameter ? result.value = ((float *) param->f)[0], 1 : 0; - cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); - } break; - case OSP_FLOAT2: { - struct ReturnValue { int success; vec2f value; } result; - result.success = foundParameter ? result.value = ((vec2f *) param->f)[0], 1 : 0; - cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); - } break; - case OSP_FLOAT3: { - struct ReturnValue { int success; vec3f value; } result; - result.success = foundParameter ? result.value = ((vec3f *) param->f)[0], 1 : 0; - cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); - } break; - case OSP_INT: { - struct ReturnValue { int success; int value; } result; - result.success = foundParameter ? result.value = ((int *) param->i)[0], 1 : 0; - cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); - } break; - case OSP_INT3: { - struct ReturnValue { int success; vec3i value; } result; - result.success = foundParameter ? result.value = ((vec3i *) param->i)[0], 1 : 0; - cmd.send(&result, sizeof(ReturnValue), 0, mpi::app.comm); - } break; default: { - throw std::runtime_error("CMD_GET_VALUE not implemented for type"); + throw std::runtime_error("CMD_GET_VALUE not implemented for " + "type"); } } cmd.flush(); @@ -645,6 +605,8 @@ namespace ospray { assert(fb); assert(po); fb->pixelOp = po->createInstance(fb,fb->pixelOp.ptr); + if (!fb->pixelOp) + std::cout << "#osp:mpi: WARNING. display op did not create an instance!" << std::endl; } break; case ospray::CMD_SET_REGION: { @@ -663,15 +625,6 @@ namespace ospray { Assert(volume); int success = volume->setRegion(mem, index, count); - // double t1 = getSysTime(); - // static long num = 0; - // static long bytes = 0; - // bytes += size; - // static double sum = 0.f; - // sum += (t1-t0); - // num++; - // if (mpi::worker.rank == 0) - // printf("time for get_data(%li bytes so far) is %fs, that's %f Mbytes/sec\n",bytes,sum,bytes/sum/1e6f); int myFail = (success == 0); int sumFail = 0; @@ -788,14 +741,16 @@ namespace ospray { // embree automatically puts this into "lib.so" format std::string libName = "ospray_module_"+std::string(name)+"_mic"; #else - std::string libName = "ospray_module_"+std::string(name)+""; + std::string libName = "ospray_module_" + std::string(name) + ""; #endif loadLibrary(libName); std::string initSymName = "ospray_init_module_"+std::string(name); void*initSym = getSymbol(initSymName); - if (!initSym) - throw std::runtime_error("could not find module initializer "+initSymName); + if (!initSym) { + throw std::runtime_error("could not find module initializer " + + initSymName); + } void (*initMethod)() = (void(*)())initSym; initMethod(); @@ -813,7 +768,9 @@ namespace ospray { const OSPDApiMode newMode = (OSPDApiMode)cmd.get_int32(); assert(device); assert(device->currentApiMode == OSPD_MODE_MASTERED); - printf("rank %i: master telling me to switch to %s mode.\n",mpi::world.rank,apiModeName(newMode)); + printf("rank %i: master telling me to switch to %s mode.\n", + mpi::world.rank, + apiModeName(newMode)); switch (newMode) { case OSPD_MODE_COLLABORATIVE: { PING; @@ -828,6 +785,12 @@ namespace ospray { NOTIMPLEMENTED; } break; + // ================================================================== + case ospray::CMD_FINALIZE: { + // ================================================================== + async::shutdown(); + std::exit(0); + } break; // ================================================================== default: // ================================================================== diff --git a/ospray/render/LoadBalancer.cpp b/ospray/render/LoadBalancer.cpp index 8fa7819a26..aacbbf81a8 100644 --- a/ospray/render/LoadBalancer.cpp +++ b/ospray/render/LoadBalancer.cpp @@ -17,9 +17,9 @@ // own #include "LoadBalancer.h" #include "Renderer.h" -#include "ospray/common/tasking/parallel_for.h" +#include "common/tasking/parallel_for.h" // ospc -#include "common/sysinfo.h" +#include "ospcommon/sysinfo.h" // stl #include @@ -47,12 +47,8 @@ namespace ospray { void *perFrameData = renderer->beginFrame(fb); - int numTiles_x = divRoundUp(fb->size.x,TILE_SIZE); - int numTiles_y = divRoundUp(fb->size.y,TILE_SIZE); - - const int NTASKS = numTiles_x * numTiles_y; - - parallel_for(NTASKS, [&](int taskIndex) { + parallel_for(fb->getTotalTiles(), [&](int taskIndex) { + const size_t numTiles_x = fb->getNumTiles().x; const size_t tile_y = taskIndex / numTiles_x; const size_t tile_x = taskIndex - tile_y*numTiles_x; const vec2i tileID(tile_x, tile_y); @@ -95,15 +91,14 @@ namespace ospray { void *perFrameData = renderer->beginFrame(fb); - int numTiles_x = divRoundUp(fb->size.x,TILE_SIZE); - int numTiles_y = divRoundUp(fb->size.y,TILE_SIZE); - int numTiles_total = numTiles_x * numTiles_y; + int numTiles_total = fb->getTotalTiles(); const int NTASKS = (numTiles_total / numDevices) + (numTiles_total % numDevices > deviceID); parallel_for(NTASKS, [&](int taskIndex) { int tileIndex = deviceID + numDevices * taskIndex; + const size_t numTiles_x = fb->getNumTiles().x; const size_t tile_y = tileIndex / numTiles_x; const size_t tile_x = tileIndex - tile_y*numTiles_x; const vec2i tileID(tile_x, tile_y); diff --git a/ospray/render/LoadBalancer.h b/ospray/render/LoadBalancer.h index a314bd1dfe..5aaf70c39a 100644 --- a/ospray/render/LoadBalancer.h +++ b/ospray/render/LoadBalancer.h @@ -19,9 +19,9 @@ /*! \file LoadBalancer.h Implements the abstracion layer for a (tiled) load balancer */ // ospray -#include "ospray/common/OSPCommon.h" -#include "ospray/fb/FrameBuffer.h" -#include "ospray/render/Renderer.h" +#include "common/OSPCommon.h" +#include "fb/FrameBuffer.h" +#include "render/Renderer.h" // tbb #ifdef OSPRAY_TASKING_TBB diff --git a/ospray/render/Renderer.h b/ospray/render/Renderer.h index 6b4fd8edc0..4bf03e02e9 100644 --- a/ospray/render/Renderer.h +++ b/ospray/render/Renderer.h @@ -18,9 +18,9 @@ /*! \file Renderer.h Defines the base renderer class */ -#include "ospray/common/Model.h" -#include "ospray/fb/FrameBuffer.h" -#include "ospray/texture/Texture2D.h" +#include "common/Model.h" +#include "fb/FrameBuffer.h" +#include "texture/Texture2D.h" namespace ospray { diff --git a/ospray/render/Renderer.ih b/ospray/render/Renderer.ih index d3283cf4ab..198b9ccc67 100644 --- a/ospray/render/Renderer.ih +++ b/ospray/render/Renderer.ih @@ -16,11 +16,11 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/fb/Tile.ih" -#include "ospray/common/Ray.ih" -#include "ospray/texture/Texture2D.ih" +#include "../common/OSPCommon.ih" +#include "../fb/FrameBuffer.ih" +#include "../fb/Tile.ih" +#include "../common/Ray.ih" +#include "../texture/Texture2D.ih" struct Renderer; struct Model; diff --git a/ospray/render/Renderer.ispc b/ospray/render/Renderer.ispc index aa713b82f7..273cfdb646 100644 --- a/ospray/render/Renderer.ispc +++ b/ospray/render/Renderer.ispc @@ -15,9 +15,9 @@ // ======================================================================== // #include "Renderer.ih" -#include "ospray/render/util.ih" -#include "ospray/camera/Camera.ih" -#include "ospray/common/Model.ih" +#include "render/util.ih" +#include "camera/Camera.ih" +#include "common/Model.ih" // The layout of this struct must match that of OSPPickData in ospray.h void Renderer_default_renderSample(uniform Renderer *uniform self, diff --git a/ospray/render/pathtracer/PathTracer.cpp b/ospray/render/pathtracer/PathTracer.cpp index 2af527299c..4bbc0b7d92 100644 --- a/ospray/render/pathtracer/PathTracer.cpp +++ b/ospray/render/pathtracer/PathTracer.cpp @@ -18,8 +18,8 @@ #include "PathTracer.h" // ospray -#include "ospray/common/Data.h" -#include "ospray/lights/Light.h" +#include "common/Data.h" +#include "lights/Light.h" // ispc exports #include "PathTracer_ispc.h" // std diff --git a/ospray/render/pathtracer/PathTracer.h b/ospray/render/pathtracer/PathTracer.h index f3ee9cdec5..fb61fecce5 100644 --- a/ospray/render/pathtracer/PathTracer.h +++ b/ospray/render/pathtracer/PathTracer.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/render/Renderer.h" -#include "ospray/common/Material.h" +#include "render/Renderer.h" +#include "common/Material.h" namespace ospray { struct PathTracer : public Renderer { diff --git a/ospray/render/pathtracer/PathTracer.ih b/ospray/render/pathtracer/PathTracer.ih index 61ebcf7e44..3f22b22f86 100644 --- a/ospray/render/pathtracer/PathTracer.ih +++ b/ospray/render/pathtracer/PathTracer.ih @@ -16,12 +16,12 @@ #pragma once -#include "ospray/math/vec.ih" -#include "ospray/common/Model.ih" -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/render/util.ih" -#include "ospray/lights/Light.ih" -#include "ospray/render/Renderer.ih" +#include "math/vec.ih" +#include "common/Model.ih" +#include "fb/FrameBuffer.ih" +#include "render/util.ih" +#include "lights/Light.ih" +#include "render/Renderer.ih" #define MAX_LIGHTS 1000 diff --git a/ospray/render/pathtracer/PathTracer.ispc b/ospray/render/pathtracer/PathTracer.ispc index 7ae21ce5c9..4af410023d 100644 --- a/ospray/render/pathtracer/PathTracer.ispc +++ b/ospray/render/pathtracer/PathTracer.ispc @@ -15,10 +15,10 @@ // ======================================================================== // #include "PathTracer.ih" -#include "ospray/camera/Camera.ih" +#include "camera/Camera.ih" -#include "materials/Medium.ih" -#include "materials/Material.ih" +#include "render/pathtracer/materials/Medium.ih" +#include "render/pathtracer/materials/Material.ih" #include "math/random.ih" #include "fb/LocalFB.ih" @@ -110,6 +110,10 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, lastDg.Ns = ray.dir; lastDg.Ng = ray.dir; + DifferentialGeometry previousDg; + + bool insideSolid = false;// Quick fix for glass absorption. + do { traceRay(self->super.model, ray); @@ -119,16 +123,17 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, const vec3f wo = neg(ray.dir); - float maxLightDist = ray.t; // per default virtual lights are occluded by hit geometry + float maxLightDist = ray.t; // per default, virtual lights are occluded by hit geometry // environment shading when nothing hit if (noHit(ray)) { maxLightDist = inf; // also include envLights (i.e. the ones in infinity) - if (straightPath) + if (straightPath) { sample.alpha = 1.0f - luminance(Lw); + } if ((bool)self->backplate & straightPath) { L = L + Lw * get3f(self->backplate, clamp2edge(self->backplate, pixel)); - maxLightDist = 1e38; // backplate hides envLights (i.e. the ones in infinity) + maxLightDist = 1e38; // backplate hides envLights (i.e. the ones at infinity) } } @@ -137,7 +142,7 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, const uniform Light *uniform l = self->lights[i]; Light_EvalRes le = l->eval(l, lastDg, ray.dir); if (le.dist <= maxLightDist) - L = L + Lw * le.value * misHeuristic(lastBSDFPdf, le.pdf); + L = L + Lw * le.radiance * misHeuristic(lastBSDFPdf, le.pdf); } if (noHit(ray)) @@ -167,7 +172,7 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, bsdf = mm->getBSDF(mm, &ctx, dg, ray, currentMedium); // direct lighting including shadows and MIS - if (bsdf->type & BSDF_SMOOTH) + if (bsdf && (bsdf->type & BSDF_SMOOTH)) { uniform int numLights = self->lights ? min(MAX_LIGHTS, self->numLights) : 0; for (uniform int i = 0; i < numLights; i++) @@ -225,15 +230,32 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, // compute simple volumetric effect const vec3f transmission = currentMedium.transmission; - if (ne(transmission,make_vec3f(1.f))) + if (ne(transmission,make_vec3f(1.f))) { Lw = Lw * powf(transmission, ray.t); + } - // update currentMedium if we hit a medium interface TODO: support nested dielectrics + if (insideSolid) {// For absorbing translucent materials. + foreach_unique(uniMat in dg.material) { + uniform PathTraceMaterial* uniform m = (uniform PathTraceMaterial *)uniMat; + if (m != NULL) { + Lw = Lw * m->getAbsorption(m, previousDg, ray.t); + } + } + } + + // update currentMedium if we hit a medium interface + // TODO: support nested dielectrics if (fs.type & BSDF_TRANSMISSION) { + + // This is a quick fix for glass. This only works if all solid objects are disjoint + // (some space is between them and they don't overlap). + insideSolid = !insideSolid; + foreach_unique(uniMat in dg.material) { uniform PathTraceMaterial* uniform m = (uniform PathTraceMaterial *)uniMat; - if (m != NULL) + if (m != NULL) { m->selectNextMedium(m, currentMedium); + } } } @@ -244,6 +266,8 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, lastDg = dg; } + previousDg = dg; + // continue the path straightPath &= eq(ray.dir, fs.wi); setRay(ray, dg.P, fs.wi, self->super.epsilon, inf); @@ -251,6 +275,10 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, } while (reduce_max(Lw) > self->minContribution); sample.rgb = L; + if (isnan(L.x) || isnan(L.y) || isnan(L.z)){ + sample.rgb = make_vec3f(0.0f,0.0f,0.0f); + sample.alpha = 1.0f; + } return sample; } @@ -308,7 +336,6 @@ unmasked void *uniform PathTracer_beginFrame(uniform Renderer *uniform _self, uniform FrameBuffer *uniform fb) { _self->fb = fb; - uniform PathTracer *uniform self = (uniform PathTracer *uniform)_self; return NULL; } diff --git a/ospray/render/pathtracer/bsdfs/Dielectric.ih b/ospray/render/pathtracer/bsdfs/Dielectric.ih index 930497d72b..7e3ae2ce8c 100644 --- a/ospray/render/pathtracer/bsdfs/Dielectric.ih +++ b/ospray/render/pathtracer/bsdfs/Dielectric.ih @@ -23,6 +23,7 @@ struct Dielectric BSDF super; float eta; + DifferentialGeometry dgeom; }; inline BSDF_EvalRes Dielectric_eval(const varying BSDF* uniform super, @@ -31,48 +32,179 @@ inline BSDF_EvalRes Dielectric_eval(const varying BSDF* uniform super, return make_BSDF_EvalRes_zero(); } +inline vec3f RefractionDirection(const vec3f & d, const vec3f & N, const float eta, + varying float & FresnelR, varying bool & mustReflect) +{ + mustReflect = false; + + float cos1 = dot(d, N); + float cos1_abs = cos1; + if (cos1_abs < 0.0) { + cos1_abs = -cos1_abs; + } + bool headOn = (1.0-1.0e-6 < cos1_abs && cos1_abs < 1.0-1.0e-6); + vec3f dir; + if (headOn) { + FresnelR = (eta - 1.0)/(eta + 1.0); + FresnelR = FresnelR*FresnelR; + dir = d; + } else { + vec3f dt = cos1*N; + vec3f dp = d - dt; + vec3f b = normalize(dp); + float sin1 = dot(dp,b); + float sin2 = sin1/eta; + if (sin2 > 1.0) {// total internal reflection. + dir = make_vec3f(0.0f);// Can't refract. + FresnelR = 1.0; + mustReflect = true; + } else { + float k = 1.0 - sin2*sin2; + if (k < 0.0) { + k = 0.0; + } + if (k > 1.0) { + k = 1.0; + } + float cos2 = sqrt(k); + vec3f N2 = normalize(dt); + dir = N2*cos2 + b*sin2; + float c1 = cos1_abs; + float c2 = cos2; + float r1 = (eta*c1 - c2) /(eta*c1 + c2); + float r2 = (c1 - eta*c2)/(c1 + eta*c2); + FresnelR = 0.5*(r1*r1 + r2*r2); + } + } + if (FresnelR < 0) {// Clamp in case of roundoff error. + FresnelR = 0; + } + if (FresnelR > 1.0) { + FresnelR = 1.0; + } + + return dir; +} + +inline vec3f ReflectionDirection(const vec3f & d, const vec3f & N) +{ + float cos1 = dot(d, N); + vec3f dt = cos1*N; + vec3f dp = d - dt; + vec3f dir = dp - dt; + return dir; +} + inline BSDF_SampleRes Dielectric_sample(const varying BSDF* uniform super, const vec3f& wo, const vec2f& s, float ss) { const varying Dielectric* uniform self = (const varying Dielectric* uniform)super; BSDF_SampleRes res; + res.pdf = inf; - float cosThetaO = max(dot(wo, getN(super)), 0.0f); + float glass_epsilon = 1.0e-6; + // We want the actual ray direction into the surface. + vec3f d = make_vec3f(-wo.x, -wo.y, -wo.z); + vec3f N = self->dgeom.Ns; //getN(super); + vec3f shapeN = self->dgeom.Ng;// For testing if the ray entered/exited the geometry. + float eta = 1.0 / self->eta;// ior(outside) / ior(inside) + bool canReflect = (eta < 1.0-glass_epsilon || eta > 1.0+glass_epsilon); + bool mustReflect = false; - // Fresnel term - float cosThetaT; // positive - float F = fresnelDielectricEx(cosThetaO, cosThetaT, self->eta); - res.pdf = inf; + bool be_careful = true;// Extra test for edge cases when the shading normal is different from the geometry normal. + bool nudge = true;// When we have grazing angles. Only used if be_careful==true. + + if (be_careful) { + if (dot(d,N) > 0) { + N = make_vec3f(-N.x, -N.y, -N.z); + } + if (dot(d,shapeN) > 0) { + shapeN = make_vec3f(-shapeN.x, -shapeN.y, -shapeN.z); + } + } + + float FresnelR, geometricCosine=0; + vec3f dirR = ReflectionDirection(d,N), dirT = RefractionDirection(d, N, eta, FresnelR, mustReflect); // Sample the reflection or the transmission - if (ss <= F) { - // Reflection - res.wi = reflect(wo, getN(super), cosThetaO); + if ((ss < FresnelR && canReflect) || mustReflect) {// Reflection + res.wi = dirR; res.type = BSDF_SPECULAR_REFLECTION; res.weight = make_vec3f(1.0f); - } else { - // Transmission - res.wi = refract(wo, getN(super), cosThetaO, cosThetaT, self->eta); + + if (be_careful) { + geometricCosine = dot(res.wi,shapeN); + if (geometricCosine >= 0) { + // reflecting as expected. + if (nudge && (geometricCosine < glass_epsilon)) {// Grazing angle. + res.wi = res.wi + (glass_epsilon)*shapeN; + res.wi = normalize(res.wi); + } + } else {// geometricCosine < 0 + // transmitting instead. + if (nudge && (geometricCosine > -glass_epsilon)) {// Grazing angle. + res.wi = res.wi - (glass_epsilon)*shapeN; + res.wi = normalize(res.wi); + } + res.type = BSDF_SPECULAR_TRANSMISSION; + res.weight = make_vec3f(rsqrt(self->eta)); // solid angle compression + } + } + } else {// Transmission + res.wi = dirT; res.type = BSDF_SPECULAR_TRANSMISSION; res.weight = make_vec3f(rsqrt(self->eta)); // solid angle compression + + if (be_careful) { + geometricCosine = dot(res.wi,shapeN); + if (geometricCosine <= 0) { + // transmitting as expected. + if (nudge && (geometricCosine > -glass_epsilon)) {// Grazing angle. + res.wi = res.wi - (glass_epsilon)*shapeN; + res.wi = normalize(res.wi); + } + } else {// geometricCosine > 0 + // reflecting instead. + if (nudge && (geometricCosine < glass_epsilon)) {// Grazing angle. + res.wi = res.wi + (glass_epsilon)*shapeN; + res.wi = normalize(res.wi); + } + res.type = BSDF_SPECULAR_REFLECTION; + res.weight = make_vec3f(1.0f); + } + } } return res; } -inline void Dielectric_Constructor(varying Dielectric* uniform self, const varying linear3f* uniform frame, +//inline void Dielectric_Constructor(varying Dielectric* uniform self, const varying linear3f* uniform frame, +// float eta) +inline void Dielectric_Constructor(varying Dielectric* uniform self, uniform ShadingContext* uniform ctx, const DifferentialGeometry& dg, float eta) { + + const varying linear3f* uniform frame = LinearSpace3f_create(ctx, frame(dg.Ns)); + BSDF_Constructor(&self->super, BSDF_SPECULAR, Dielectric_eval, Dielectric_sample, frame); self->eta = eta; + self->dgeom = dg; } -inline varying BSDF* uniform Dielectric_create(uniform ShadingContext* uniform ctx, const varying linear3f* uniform frame, +//inline varying BSDF* uniform Dielectric_create(uniform ShadingContext* uniform ctx, const varying linear3f* uniform frame, +// float eta) + +inline varying BSDF* uniform Dielectric_create(uniform ShadingContext* uniform ctx, const DifferentialGeometry& dg, float eta) + { varying Dielectric* uniform self = (varying Dielectric* uniform)ShadingContext_alloc(ctx, sizeof(Dielectric)); - Dielectric_Constructor(self, frame, eta); + + + + Dielectric_Constructor(self, ctx, dg, eta); + //Dielectric_Constructor(self, frame, eta); return &self->super; } diff --git a/ospray/render/pathtracer/bsdfs/GGXDistribution.ih b/ospray/render/pathtracer/bsdfs/GGXDistribution.ih index 6c2617530b..f2856ca754 100644 --- a/ospray/render/pathtracer/bsdfs/GGXDistribution.ih +++ b/ospray/render/pathtracer/bsdfs/GGXDistribution.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/math/math.ih" +#include "math/math.ih" struct GGXDistribution { diff --git a/ospray/render/pathtracer/bsdfs/Optics.ih b/ospray/render/pathtracer/bsdfs/Optics.ih index e3d4f2e393..95ea1613ad 100644 --- a/ospray/render/pathtracer/bsdfs/Optics.ih +++ b/ospray/render/pathtracer/bsdfs/Optics.ih @@ -16,9 +16,9 @@ #pragma once -#include "../samplers/Sample.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" +#include "render/pathtracer/samplers/Sample.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" /*! Reflects a viewing vector I at a normal N. Cosine between I * and N is given as input. */ diff --git a/ospray/render/pathtracer/materials/Glass.cpp b/ospray/render/pathtracer/materials/Glass.cpp index eecd01d2b9..38a43c2ec4 100644 --- a/ospray/render/pathtracer/materials/Glass.cpp +++ b/ospray/render/pathtracer/materials/Glass.cpp @@ -14,7 +14,8 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" +#include "texture/Texture2D.h" #include "Glass_ispc.h" namespace ospray { @@ -29,16 +30,32 @@ namespace ospray { if (getIE() != NULL) return; const vec3f& transmissionInside - = getParam3f("transmission",getParam3f("color",vec3f(1.f))); + = getParam3f("transmissionInside",vec3f(1.f)); const vec3f& transmissionOutside = getParam3f("transmissionOutside",vec3f(1.f)); - const float etaInside = getParamf("etaInside", getParamf("eta", 1.5f)); - const float etaOutside = getParamf("etaOutside", 1.f); + const float etaOutside = getParamf("etaOutside", 1.f);; + const float absorptionDistance = getParamf("absorptionDistance", 0.0f); + // Why is "absorptionColor" not found, while "color" is found??? + const vec3f& absorptionColor = getParam3f("color",vec3f(1.f)); + + + Texture2D *map_Kd = (Texture2D*)getParamObject("map_Kd", getParamObject("map_kd", getParamObject("colorMap", NULL))); + affine2f xform_Kd = getTextureTransform("map_kd") * getTextureTransform("colorMap"); + + + ispcEquivalent = ispc::PathTracer_Glass_create(); - ispcEquivalent = ispc::PathTracer_Glass_create - (etaInside, (const ispc::vec3f&)transmissionInside, - etaOutside, (const ispc::vec3f&)transmissionOutside); + ispc::PathTracer_Glass_set( + ispcEquivalent, + etaInside, + (const ispc::vec3f&)transmissionInside, + etaOutside, + (const ispc::vec3f&)transmissionOutside, + absorptionDistance, + (const ispc::vec3f&)absorptionColor, + map_Kd ? map_Kd->getIE() : NULL, + (const ispc::AffineSpace2f&)xform_Kd); } }; diff --git a/ospray/render/pathtracer/materials/Glass.ispc b/ospray/render/pathtracer/materials/Glass.ispc index eb070bfae5..3a4ccdcc87 100644 --- a/ospray/render/pathtracer/materials/Glass.ispc +++ b/ospray/render/pathtracer/materials/Glass.ispc @@ -15,7 +15,8 @@ // ======================================================================== // #include "Material.ih" -#include "../bsdfs/Dielectric.ih" +#include "render/pathtracer/bsdfs/Dielectric.ih" +#include "texture/TextureParam.ih" struct Glass { @@ -23,6 +24,12 @@ struct Glass Medium mediumInside; Medium mediumOutside; + + float absorptionDistance; + vec3f absorptionColor; + vec3f absorption_k; + + TextureParam colorMap; }; /////////////////////////////////////////////////////////////////////////////// @@ -34,12 +41,13 @@ const varying BSDF* uniform Glass_getBSDF(const uniform PathTraceMaterial* unifo const Ray& ray, const Medium& currentMedium) { - const uniform Glass* uniform self = (const uniform Glass* uniform)super; + uniform const Glass* uniform self = (uniform const Glass* uniform)super; float eta = eq(currentMedium, self->mediumOutside) ? self->mediumOutside.ior*rcp(self->mediumInside.ior) : self->mediumInside.ior*rcp(self->mediumOutside.ior); - return Dielectric_create(ctx, LinearSpace3f_create(ctx, frame(dg.Ns)), eta); + varying BSDF* uniform bsdf = Dielectric_create(ctx, dg, eta); + return bsdf; } vec3f Glass_getTransparency(const uniform PathTraceMaterial* uniform material, @@ -67,28 +75,102 @@ void Glass_selectNextMedium(const uniform PathTraceMaterial* uniform super, currentMedium = self->mediumOutside; } -void Glass_Constructor(uniform Glass* uniform self, - uniform float iorInside, - const uniform vec3f& transmissionInside, - uniform float iorOutside, - const uniform vec3f& transmissionOutside) +vec3f Glass_getAbsorption(const uniform PathTraceMaterial* uniform material, + const DifferentialGeometry& dg, + const float & distance) { - PathTraceMaterial_Constructor(&self->super, Glass_getBSDF, Glass_getTransparency, Glass_selectNextMedium); + const uniform Glass* uniform self = (const uniform Glass* uniform)material; + + vec3f r = make_vec3f(1.0f); + + // Beer's law for glass absorption. + + vec3f k = make_vec3f(0.0f); + + if (self->colorMap.map != NULL) { + // Using a 2D texture for absorption color will cause the glass color + // to be view-dependent. As you move the camera the colors + // will change dynamically. This is fun, but entirely not physically correct. + // The correct thing is to have a 3D texture with ray-marching through + // the solid to integrate the absorption, and be view-independent. + + vec3f color = get3f(self->colorMap, dg.st); + + if (self->absorptionDistance > 0) { + if (color.x > 0) + k.x = log(color.x)/self->absorptionDistance; + + if (color.y > 0) + k.y = log(color.y)/self->absorptionDistance; + + if (color.z > 0) + k.z = log(color.z)/self->absorptionDistance; + } + } else { + k = self->absorption_k; + } + + if (k.x != 0) + r.x = expf(k.x * distance); + if (k.y != 0) + r.y = expf(k.y * distance); + if (k.z != 0) + r.z = expf(k.z * distance); + + return r; +} + +/////////////////////////////////////////////////////////////////////////////// +// External API + +export void PathTracer_Glass_set( + void* uniform _self, + const uniform float iorInside, + const uniform vec3f & transmissionInside, + const uniform float iorOutside, + const uniform vec3f & transmissionOutside, + const uniform float absDistance, + const uniform vec3f & absColor, + void* uniform map_Kd, + const uniform affine2f & xform_Kd) +{ + uniform Glass* uniform self = (uniform Glass* uniform)_self; + self->mediumInside.ior = iorInside; self->mediumInside.transmission = transmissionInside; self->mediumOutside.ior = iorOutside; self->mediumOutside.transmission = transmissionOutside; + self->absorptionDistance = absDistance; + self->absorptionColor = absColor; + self->colorMap = make_TextureParam((uniform Texture2D*)map_Kd, xform_Kd); + + if (absDistance > 0) { + if (absColor.x > 0) + self->absorption_k.x = log(absColor.x)/absDistance; + else + self->absorption_k.x = 0; + + if (absColor.y > 0) + self->absorption_k.y = log(absColor.y)/absDistance; + else + self->absorption_k.y = 0; + + if (absColor.z > 0) + self->absorption_k.z = log(absColor.z)/absDistance; + else + self->absorption_k.z = 0; + } } -/////////////////////////////////////////////////////////////////////////////// -// External API +void Glass_Constructor(uniform Glass* uniform self) +{ + PathTraceMaterial_Constructor(&self->super, Glass_getBSDF, + Glass_getTransparency, Glass_selectNextMedium, Glass_getAbsorption); +} -export void* uniform PathTracer_Glass_create(uniform float iorInside, - const uniform vec3f& transmissionInside, - uniform float iorOutside, - const uniform vec3f& transmissionOutside) +export void* uniform PathTracer_Glass_create() { uniform Glass* uniform self = uniform new uniform Glass; - Glass_Constructor(self, iorInside, transmissionInside, iorOutside, transmissionOutside); + Glass_Constructor(self); return self; } diff --git a/ospray/render/pathtracer/materials/Material.ih b/ospray/render/pathtracer/materials/Material.ih index 876d6e122f..ba074c65fb 100644 --- a/ospray/render/pathtracer/materials/Material.ih +++ b/ospray/render/pathtracer/materials/Material.ih @@ -16,13 +16,13 @@ #pragma once -#include "ospray/common/Material.ih" -#include "ospray/common/DifferentialGeometry.ih" -#include "ospray/common/Ray.ih" +#include "common/Material.ih" +#include "common/DifferentialGeometry.ih" +#include "common/Ray.ih" #include "Medium.ih" -#include "../samplers/Sample.ih" -#include "../bsdfs/BSDF.ih" -#include "../bsdfs/ShadingContext.ih" +#include "render/pathtracer/samplers/Sample.ih" +#include "render/pathtracer/bsdfs/BSDF.ih" +#include "render/pathtracer/bsdfs/ShadingContext.ih" struct PathTraceMaterial; @@ -46,15 +46,22 @@ typedef vec3f (*PathTraceMaterial_GetTransparencyFunc)(const uniform PathTraceMa typedef void (*PathTraceMaterial_SelectNextMediumFunc)(const uniform PathTraceMaterial* uniform self, Medium& currentMedium); +typedef vec3f (*PathTraceMaterial_GetAbsorptionFunc)( + const uniform PathTraceMaterial* uniform self, + const DifferentialGeometry& dg, + const float & distance); + struct PathTraceMaterial { Material material; PathTraceMaterial_GetBSDFFunc getBSDF; PathTraceMaterial_GetTransparencyFunc getTransparency; PathTraceMaterial_SelectNextMediumFunc selectNextMedium; + PathTraceMaterial_GetAbsorptionFunc getAbsorption; }; void PathTraceMaterial_Constructor(uniform PathTraceMaterial* uniform self, uniform PathTraceMaterial_GetBSDFFunc getBSDF, uniform PathTraceMaterial_GetTransparencyFunc getTransparency, - uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium); + uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium, + uniform PathTraceMaterial_GetAbsorptionFunc getAbsorption); diff --git a/ospray/render/pathtracer/materials/Material.ispc b/ospray/render/pathtracer/materials/Material.ispc index e8df0cbf26..f4b6a4a88c 100644 --- a/ospray/render/pathtracer/materials/Material.ispc +++ b/ospray/render/pathtracer/materials/Material.ispc @@ -28,12 +28,22 @@ void PathTraceMaterial_selectNextMedium(const uniform PathTraceMaterial* uniform Medium& currentMedium) { /* do nothing by default */ } +vec3f PathTraceMaterial_getAbsorption( + const uniform PathTraceMaterial* uniform self, + const DifferentialGeometry& dg, + const float & distance) +{ + return make_vec3f(1.0f); +} + void PathTraceMaterial_Constructor(uniform PathTraceMaterial* uniform self, uniform PathTraceMaterial_GetBSDFFunc getBSDF, uniform PathTraceMaterial_GetTransparencyFunc getTransparency, - uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium) + uniform PathTraceMaterial_SelectNextMediumFunc selectNextMedium, + uniform PathTraceMaterial_GetAbsorptionFunc getAbsorption) { self->getBSDF = getBSDF; self->getTransparency = getTransparency ? getTransparency : PathTraceMaterial_getTransparency; self->selectNextMedium = selectNextMedium ? selectNextMedium : PathTraceMaterial_selectNextMedium; + self->getAbsorption = getAbsorption ? getAbsorption : PathTraceMaterial_getAbsorption; } diff --git a/ospray/render/pathtracer/materials/Matte.cpp b/ospray/render/pathtracer/materials/Matte.cpp index df8bf5c109..3ba94b1040 100644 --- a/ospray/render/pathtracer/materials/Matte.cpp +++ b/ospray/render/pathtracer/materials/Matte.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "Matte_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/Matte.ispc b/ospray/render/pathtracer/materials/Matte.ispc index 2bcc74b1f6..211729cce0 100644 --- a/ospray/render/pathtracer/materials/Matte.ispc +++ b/ospray/render/pathtracer/materials/Matte.ispc @@ -40,7 +40,7 @@ const varying BSDF* uniform Matte_getBSDF(const uniform PathTraceMaterial* unifo inline void Matte_Constructor(uniform Matte* uniform self, const uniform vec3f& reflectance) { - PathTraceMaterial_Constructor(&self->super, Matte_getBSDF, NULL, NULL); + PathTraceMaterial_Constructor(&self->super, Matte_getBSDF, NULL, NULL, NULL); self->reflectance = reflectance; } diff --git a/ospray/render/pathtracer/materials/Metal.cpp b/ospray/render/pathtracer/materials/Metal.cpp index 0f29a4eb2f..c710291698 100644 --- a/ospray/render/pathtracer/materials/Metal.cpp +++ b/ospray/render/pathtracer/materials/Metal.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "Metal_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/Metal.ispc b/ospray/render/pathtracer/materials/Metal.ispc index 9714f80e0d..3618832943 100644 --- a/ospray/render/pathtracer/materials/Metal.ispc +++ b/ospray/render/pathtracer/materials/Metal.ispc @@ -49,7 +49,7 @@ void Metal_Constructor(uniform Metal* uniform self, const uniform vec3f& k, uniform float roughness) { - PathTraceMaterial_Constructor(&self->super, Metal_getBSDF, NULL, NULL); + PathTraceMaterial_Constructor(&self->super, Metal_getBSDF, NULL, NULL, NULL); self->reflectance = reflectance; self->eta = eta; self->k = k; diff --git a/ospray/render/pathtracer/materials/MetallicPaint.cpp b/ospray/render/pathtracer/materials/MetallicPaint.cpp index 6c66039128..17e9a28f07 100644 --- a/ospray/render/pathtracer/materials/MetallicPaint.cpp +++ b/ospray/render/pathtracer/materials/MetallicPaint.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "MetallicPaint_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/MetallicPaint.ispc b/ospray/render/pathtracer/materials/MetallicPaint.ispc index 76ad683949..05dca17df0 100644 --- a/ospray/render/pathtracer/materials/MetallicPaint.ispc +++ b/ospray/render/pathtracer/materials/MetallicPaint.ispc @@ -63,7 +63,7 @@ inline void MetallicPaint_Constructor(uniform MetallicPaint* uniform self, const uniform float glitterSpread, const uniform float ior) { - PathTraceMaterial_Constructor(&self->super, MetallicPaint_getBSDF, NULL, NULL); + PathTraceMaterial_Constructor(&self->super, MetallicPaint_getBSDF, NULL, NULL, NULL); self->shadeColor = shadeColor; self->glitterColor = glitterColor; self->glitterSpread = glitterSpread; diff --git a/ospray/render/pathtracer/materials/OBJ.cpp b/ospray/render/pathtracer/materials/OBJ.cpp index 294513ad14..3271ef05b0 100644 --- a/ospray/render/pathtracer/materials/OBJ.cpp +++ b/ospray/render/pathtracer/materials/OBJ.cpp @@ -14,8 +14,8 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" -#include "ospray/texture/Texture2D.h" +#include "common/Material.h" +#include "texture/Texture2D.h" #include "OBJ_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/OBJ.ispc b/ospray/render/pathtracer/materials/OBJ.ispc index 560d1eed8a..80f51268b0 100644 --- a/ospray/render/pathtracer/materials/OBJ.ispc +++ b/ospray/render/pathtracer/materials/OBJ.ispc @@ -14,12 +14,12 @@ // limitations under the License. // // ======================================================================== // -#include "./Material.ih" -#include "ospray/texture/TextureParam.ih" -#include "../bsdfs/MultiBSDF.ih" -#include "../bsdfs/Transmission.ih" -#include "../bsdfs/Lambert.ih" -#include "../bsdfs/Specular.ih" +#include "Material.ih" +#include "texture/TextureParam.ih" +#include "render/pathtracer/bsdfs/MultiBSDF.ih" +#include "render/pathtracer/bsdfs/Transmission.ih" +#include "render/pathtracer/bsdfs/Lambert.ih" +#include "render/pathtracer/bsdfs/Specular.ih" struct OBJ { @@ -135,7 +135,7 @@ vec3f OBJ_getTransparency(const uniform PathTraceMaterial* uniform super, void OBJ_Constructor(uniform OBJ* uniform self) { - PathTraceMaterial_Constructor(&self->super, OBJ_getBSDF, OBJ_getTransparency, NULL); + PathTraceMaterial_Constructor(&self->super, OBJ_getBSDF, OBJ_getTransparency, NULL, NULL); uniform affine2f xform = make_AffineSpace2f_identity(); diff --git a/ospray/render/pathtracer/materials/Plastic.cpp b/ospray/render/pathtracer/materials/Plastic.cpp index 6d1eb22b18..c26cc24c45 100644 --- a/ospray/render/pathtracer/materials/Plastic.cpp +++ b/ospray/render/pathtracer/materials/Plastic.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "Plastic_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/Plastic.ispc b/ospray/render/pathtracer/materials/Plastic.ispc index 551a8a8487..8a1ef20e71 100644 --- a/ospray/render/pathtracer/materials/Plastic.ispc +++ b/ospray/render/pathtracer/materials/Plastic.ispc @@ -56,7 +56,7 @@ void Plastic_Constructor(uniform Plastic* uniform self, uniform float roughness, uniform float thickness) { - PathTraceMaterial_Constructor(&self->super, Plastic_getBSDF, NULL, NULL); + PathTraceMaterial_Constructor(&self->super, Plastic_getBSDF, NULL, NULL, NULL); self->pigmentColor = pigmentColor; self->eta = rcp(ior); self->roughness = roughness; diff --git a/ospray/render/pathtracer/materials/ThinGlass.cpp b/ospray/render/pathtracer/materials/ThinGlass.cpp index abf23b9b9e..2300021fa1 100644 --- a/ospray/render/pathtracer/materials/ThinGlass.cpp +++ b/ospray/render/pathtracer/materials/ThinGlass.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "ThinGlass_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/ThinGlass.ispc b/ospray/render/pathtracer/materials/ThinGlass.ispc index f6bf8ef2ba..b176764b7a 100644 --- a/ospray/render/pathtracer/materials/ThinGlass.ispc +++ b/ospray/render/pathtracer/materials/ThinGlass.ispc @@ -57,7 +57,7 @@ void ThinGlass_Constructor(uniform ThinGlass* uniform self, const uniform vec3f& transmission, uniform float thickness) { - PathTraceMaterial_Constructor(&self->super, ThinGlass_getBSDF, ThinGlass_getTransparency, NULL); + PathTraceMaterial_Constructor(&self->super, ThinGlass_getBSDF, ThinGlass_getTransparency, NULL, NULL); self->eta = rcp(ior); self->transmission = transmission; self->thickness = thickness; diff --git a/ospray/render/pathtracer/materials/Velvet.cpp b/ospray/render/pathtracer/materials/Velvet.cpp index c6bd8b7218..3673cad1e0 100644 --- a/ospray/render/pathtracer/materials/Velvet.cpp +++ b/ospray/render/pathtracer/materials/Velvet.cpp @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Material.h" +#include "common/Material.h" #include "Velvet_ispc.h" namespace ospray { diff --git a/ospray/render/pathtracer/materials/Velvet.ispc b/ospray/render/pathtracer/materials/Velvet.ispc index 99b751e985..8c6a9b116d 100644 --- a/ospray/render/pathtracer/materials/Velvet.ispc +++ b/ospray/render/pathtracer/materials/Velvet.ispc @@ -64,7 +64,7 @@ void Velvet_Constructor(uniform Velvet* uniform self, uniform float horizonScatteringFallOff, uniform float backScattering) { - PathTraceMaterial_Constructor(&self->super, Velvet_getBSDF, NULL, NULL); + PathTraceMaterial_Constructor(&self->super, Velvet_getBSDF, NULL, NULL, NULL); self->reflectance = reflectance; self->horizonScatteringColor = horizonScatteringColor; self->horizonScatteringFallOff = horizonScatteringFallOff; diff --git a/ospray/render/pathtracer/samplers/Sample.ih b/ospray/render/pathtracer/samplers/Sample.ih index d1ba21021f..d88b28f4b1 100644 --- a/ospray/render/pathtracer/samplers/Sample.ih +++ b/ospray/render/pathtracer/samplers/Sample.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/math/AffineSpace.ih" +#include "math/AffineSpace.ih" struct Sample1f { float v; diff --git a/ospray/render/raycast/RaycastRenderer.cpp b/ospray/render/raycast/RaycastRenderer.cpp index 159208cf33..7d73f13983 100644 --- a/ospray/render/raycast/RaycastRenderer.cpp +++ b/ospray/render/raycast/RaycastRenderer.cpp @@ -18,7 +18,7 @@ // ospray #include "RaycastRenderer.h" -#include "ospray/camera/PerspectiveCamera.h" +#include "camera/PerspectiveCamera.h" // ispc exports #include "RaycastRenderer_ispc.h" diff --git a/ospray/render/raycast/RaycastRenderer.h b/ospray/render/raycast/RaycastRenderer.h index 1cb06f2bbb..67ce76e849 100644 --- a/ospray/render/raycast/RaycastRenderer.h +++ b/ospray/render/raycast/RaycastRenderer.h @@ -47,7 +47,7 @@ */ // ospray -#include "ospray/render/Renderer.h" +#include "render/Renderer.h" namespace ospray { diff --git a/ospray/render/raycast/RaycastRenderer.ispc b/ospray/render/raycast/RaycastRenderer.ispc index e0d35a7944..fd91077360 100644 --- a/ospray/render/raycast/RaycastRenderer.ispc +++ b/ospray/render/raycast/RaycastRenderer.ispc @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/camera/PerspectiveCamera.ih" -#include "ospray/common/Model.ih" -#include "ospray/render/util.ih" -#include "ospray/render/Renderer.ih" +#include "fb/FrameBuffer.ih" +#include "camera/PerspectiveCamera.ih" +#include "common/Model.ih" +#include "render/util.ih" +#include "render/Renderer.ih" /*! \brief a simple, primary-ray-only ray cast render that diff --git a/ospray/render/scivis/SciVisMaterial.cpp b/ospray/render/scivis/SciVisMaterial.cpp index 2a5100ed64..15efe8b02d 100644 --- a/ospray/render/scivis/SciVisMaterial.cpp +++ b/ospray/render/scivis/SciVisMaterial.cpp @@ -16,7 +16,7 @@ #include "SciVisMaterial.h" #include "SciVisMaterial_ispc.h" -#include "ospray/common/Data.h" +#include "common/Data.h" namespace ospray { namespace scivis { diff --git a/ospray/render/scivis/SciVisMaterial.h b/ospray/render/scivis/SciVisMaterial.h index cf2acde4bb..4e6ef8094d 100644 --- a/ospray/render/scivis/SciVisMaterial.h +++ b/ospray/render/scivis/SciVisMaterial.h @@ -16,10 +16,10 @@ #pragma once -#include "ospray/common/Material.h" -#include "ospray/texture/Texture2D.h" +#include "common/Material.h" +#include "texture/Texture2D.h" -#include "ospray/volume/Volume.h" +#include "volume/Volume.h" namespace ospray { namespace scivis { diff --git a/ospray/render/scivis/SciVisMaterial.ih b/ospray/render/scivis/SciVisMaterial.ih index f3318c9628..fd37e9f6a6 100644 --- a/ospray/render/scivis/SciVisMaterial.ih +++ b/ospray/render/scivis/SciVisMaterial.ih @@ -16,10 +16,10 @@ #pragma once -#include "ospray/math/vec.ih" -#include "ospray/common/Material.ih" -#include "ospray/texture/Texture2D.ih" -#include "ospray/volume/Volume.ih" +#include "math/vec.ih" +#include "common/Material.ih" +#include "texture/Texture2D.ih" +#include "volume/Volume.ih" //! ispc-equivalent of ospray::raytracer::SciVisMaterial struct SciVisMaterial diff --git a/ospray/render/scivis/SciVisRenderer.cpp b/ospray/render/scivis/SciVisRenderer.cpp index 26b6078bfa..fdf3aff99b 100644 --- a/ospray/render/scivis/SciVisRenderer.cpp +++ b/ospray/render/scivis/SciVisRenderer.cpp @@ -18,8 +18,8 @@ #include "SciVisRenderer.h" #include "SciVisMaterial.h" // ospray -#include "ospray/common/Data.h" -#include "ospray/lights/Light.h" +#include "common/Data.h" +#include "lights/Light.h" //sys #include // ispc exports diff --git a/ospray/render/scivis/SciVisRenderer.h b/ospray/render/scivis/SciVisRenderer.h index 7013e53615..3b29ee682e 100644 --- a/ospray/render/scivis/SciVisRenderer.h +++ b/ospray/render/scivis/SciVisRenderer.h @@ -36,8 +36,8 @@ */ // ospray -#include "ospray/render/Renderer.h" -#include "ospray/common/Material.h" +#include "render/Renderer.h" +#include "common/Material.h" // system #include diff --git a/ospray/render/scivis/SciVisRenderer.ispc b/ospray/render/scivis/SciVisRenderer.ispc index 01f03017a6..c9189b9f33 100644 --- a/ospray/render/scivis/SciVisRenderer.ispc +++ b/ospray/render/scivis/SciVisRenderer.ispc @@ -14,17 +14,17 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/render/util.ih" -#include "ospray/common/Model.ih" -#include "ospray/texture/Texture2D.ih" -#include "ospray/lights/Light.ih" -#include "ospray/render/Renderer.ih" -#include "ospray/math/random.ih" -#include "ospray/math/sampling.ih" -#include "ospray/math/LinearSpace.ih" - -#include "ospray/volume/DataDistributedBlockedVolume.ih" +#include "fb/FrameBuffer.ih" +#include "render/util.ih" +#include "common/Model.ih" +#include "texture/Texture2D.ih" +#include "lights/Light.ih" +#include "render/Renderer.ih" +#include "math/random.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" + +#include "volume/DataDistributedBlockedVolume.ih" #include "SciVisMaterial.ih" @@ -35,6 +35,7 @@ struct SciVisRenderer { Renderer super; + float volumeEpsilon; const uniform Light *uniform *uniform lights; uint32 numLights; @@ -256,10 +257,9 @@ inline void shadeLights(const uniform SciVisRenderer *uniform self, // Volume shading // -inline void SciVisRenderer_computeVolumeSample(SciVisRenderer *uniform renderer, +inline vec4f SciVisRenderer_computeVolumeSample(SciVisRenderer *uniform renderer, Volume *uniform volume, - varying Ray &ray, - varying vec4f &color) + varying Ray &ray) { // Sample the volume at the hit point in world coordinates. const vec3f coordinates = ray.org + ray.t0 * ray.dir; @@ -306,24 +306,23 @@ inline void SciVisRenderer_computeVolumeSample(SciVisRenderer *uniform renderer, volume->transferFunction->getOpacityForValue(volume->transferFunction, sample); - // Set the color contribution for this sample only (do not accumulate). - color = clamp(sampleOpacity / volume->samplingRate) - * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); - // Advance the ray for the next sample. volume->intersect(volume, ray); + + // return the color contribution for this sample only (do not accumulate) + return clamp(sampleOpacity / volume->samplingRate) + * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); } inline -void SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, +vec4f SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, const varying vec3i &sampleID, - varying Ray &ray, - varying vec4f &out_color) + varying Ray &ray) { vec3f color = make_vec3f(0.f); float path_opacity = 1.f; int path_depth = 0; - float ray_t_depth = 0.f; + float first_hit_dist = 0.f; while (1) { @@ -331,13 +330,12 @@ void SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, // Check if we missed, if so we are done // if (ray.geomID < 0) { - out_color = make_vec4f(color.x, color.y, color.z, 1.f - path_opacity); - return; + return make_vec4f(color, 1.f - path_opacity); } // Record depth of first hit for depth output // if (path_depth == 0) { - ray_t_depth = ray.t; + first_hit_dist = ray.t; } // Start shading // @@ -366,9 +364,8 @@ void SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, path_opacity = path_opacity * (1.f - info.d); path_depth++; if (path_depth >= self->maxDepth || path_opacity < 0.01f ) { - out_color = make_vec4f(color.x, color.y, color.z, 1.f - path_opacity); - ray.t = ray_t_depth; - return; + ray.t = first_hit_dist; + return make_vec4f(color, 1.f - path_opacity); } // Reset ray @@ -446,24 +443,6 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, // Original tMax for ray interval const float tMax = ray.t; - // Ray epsilon based on bounding box of all volumes. - uniform box3f boundingBox = renderer->super.model->volumeCount - ? renderer->super.model->volumes[0]->boundingBox - : make_box3f(make_vec3f(0.f), make_vec3f(0.f)); - - Model *uniform model = renderer->super.model; - for (uniform int32 i=1; isuper.model->volumeCount; i++) { - boundingBox.lower = min(boundingBox.lower, - model->volumes[i]->boundingBox.lower); - boundingBox.upper = max(boundingBox.upper, - model->volumes[i]->boundingBox.upper); - } - - const uniform float epsilon - = renderer->super.model->volumeCount - ? 1e-4f * distance(boundingBox.lower, boundingBox.upper) - : 1e-4f; - // Copy of the ray for geometry intersection. The original ray is // used for volume intersection. Ray geometryRay = ray; @@ -479,14 +458,9 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, if (volume != NULL) geometryRay.time = -rayOffset * volume->samplingStep; - // Separate color contributions for the volume and geometries. Initialize to - // provided color in case of no contribution... - vec4f volumeColor = color; - vec4f geometryColor = color; - // Initial trace through geometries. - SciVisRenderer_computeGeometrySample(renderer, sampleID, - geometryRay, geometryColor); + vec4f geometryColor = SciVisRenderer_computeGeometrySample(renderer, + sampleID, geometryRay); // Depth is the first volume bounding box or geometry hit depth = min(ray.t0, geometryRay.t); @@ -496,7 +470,7 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, if(geometryRay.t < ray.t0 || volume == NULL) { // Geometry contribution. - color = color + (1.0f - color.w) * geometryColor; + color = geometryColor; } while ((firstHit = min(ray.t0, geometryRay.t)) < tMax @@ -508,7 +482,7 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, // Check to see if we've exited the current volume. if (ray.t0 >= ray.t) { - ray.t0 = ray.t + epsilon; + ray.t0 = ray.t + renderer->volumeEpsilon; ray.t = tMax; volume = SciVisRenderer_intersectVolumes(renderer, ray, rayOffset); @@ -517,10 +491,10 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, if (any(volume == NULL)) print("ACCESSING NULL VOLUME HERE!\n"); - // Compute the volume sample at the current position and advance the - // ray. + // Compute the volume sample at the current position and advance the ray + vec4f volumeColor; foreach_unique (v in volume) - SciVisRenderer_computeVolumeSample(renderer, v, ray, volumeColor); + volumeColor = SciVisRenderer_computeVolumeSample(renderer, v, ray); // Volume contribution. color = color + (1.0f - color.w) * volumeColor; @@ -534,7 +508,7 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, if (color.w < 0.99f) { // Reset geometry ray. - geometryRay.t0 = geometryRay.t + epsilon; + geometryRay.t0 = geometryRay.t + renderer->volumeEpsilon; geometryRay.t = tMax; //!< end of valid ray interval for traceRay() geometryRay.primID = -1; geometryRay.geomID = -1; @@ -545,8 +519,8 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, geometryRay.time = volume ? -rayOffset * volume->samplingStep : 0.f; // Trace next geometry ray. - SciVisRenderer_computeGeometrySample(renderer, sampleID, - geometryRay, geometryColor); + geometryColor = SciVisRenderer_computeGeometrySample(renderer, + sampleID, geometryRay); } } @@ -577,10 +551,9 @@ void SciVisRenderer_renderSample(uniform Renderer *uniform _self, SciVisRenderer_intersect(renderer, sample.ray, rayOffset, sample.sampleID, color, depth); - // Attenuate the foreground and background colors by the opacity. + // blend with background if (renderer->super.backgroundEnabled) { - const vec4f background = make_vec4f(renderer->super.bgColor, 1.f); - color = color.w * color + (1.0f - color.w) * background; + color = color + (1.0f - color.w) * make_vec4f(renderer->super.bgColor, 0.f); } // Store the result in the sample. @@ -591,6 +564,27 @@ void SciVisRenderer_renderSample(uniform Renderer *uniform _self, sample.z = depth; } +static unmasked void *uniform +SciVisRenderer_beginFrame(uniform Renderer *uniform _self, + uniform FrameBuffer *uniform fb) +{ + uniform SciVisRenderer *uniform self = (uniform SciVisRenderer *uniform)_self; + self->super.fb = fb; + Model *uniform model = self->super.model; + self->volumeEpsilon = self->super.epsilon; + + if (model->volumeCount) { + // Ray epsilon based on bounding box of all volumes. + uniform box3f boundingBox = make_box3f_empty(); + for (uniform int32 i = 0; i < model->volumeCount; i++) + boundingBox = box_extend(boundingBox, model->volumes[i]->boundingBox); + + self->volumeEpsilon = 1e-4f * length(box_size(boundingBox)); + } + + return NULL; +} + // Exports (called from C++) ////////////////////////////////////////////////////////////////////////////// @@ -603,8 +597,7 @@ export void SciVisRenderer_set(void *uniform _self, void **uniform lights, const uniform uint32 numLights) { - uniform SciVisRenderer *uniform self = - (uniform SciVisRenderer *uniform)_self; + uniform SciVisRenderer *uniform self = (uniform SciVisRenderer *uniform)_self; self->shadowsEnabled = shadowsEnabled; self->maxDepth = maxDepth; @@ -623,6 +616,7 @@ export void *uniform SciVisRenderer_create(void *uniform cppE) uniform SciVisRenderer *uniform self = uniform new uniform SciVisRenderer; Renderer_Constructor(&self->super,cppE); self->super.renderSample = SciVisRenderer_renderSample; + self->super.beginFrame = SciVisRenderer_beginFrame; SciVisRenderer_set(self, true, 10, 4, infinity, 0.25f, NULL, 0); return self; diff --git a/ospray/render/simpleAO/SimpleAO.cpp b/ospray/render/simpleAO/SimpleAO.cpp index 4ad33173de..bd7d0b92a9 100644 --- a/ospray/render/simpleAO/SimpleAO.cpp +++ b/ospray/render/simpleAO/SimpleAO.cpp @@ -17,8 +17,8 @@ // ospray #include "SimpleAO.h" #include "SimpleAOMaterial.h" -#include "ospray/camera/Camera.h" -#include "ospray/texture/Texture2D.h" +#include "camera/Camera.h" +#include "texture/Texture2D.h" // ispc exports #include "SimpleAO_ispc.h" diff --git a/ospray/render/simpleAO/SimpleAO.h b/ospray/render/simpleAO/SimpleAO.h index 212d102cf3..85a37a7b0b 100644 --- a/ospray/render/simpleAO/SimpleAO.h +++ b/ospray/render/simpleAO/SimpleAO.h @@ -32,8 +32,8 @@ */ // ospray -#include "ospray/render/Renderer.h" -#include "ospray/texture/Texture2D.h" +#include "render/Renderer.h" +#include "texture/Texture2D.h" namespace ospray { diff --git a/ospray/render/simpleAO/SimpleAO.ispc b/ospray/render/simpleAO/SimpleAO.ispc index c1fad7ba46..f9331b916a 100644 --- a/ospray/render/simpleAO/SimpleAO.ispc +++ b/ospray/render/simpleAO/SimpleAO.ispc @@ -14,15 +14,15 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/camera/Camera.ih" -#include "ospray/common/Ray.ih" -#include "ospray/render/util.ih" -#include "ospray/math/math.ih" -#include "ospray/math/random.ih" -#include "ospray/common/Model.ih" -#include "ospray/render/Renderer.ih" -#include "ospray/render/simpleAO/SimpleAOMaterial.ih" +#include "fb/FrameBuffer.ih" +#include "camera/Camera.ih" +#include "common/Ray.ih" +#include "render/util.ih" +#include "math/math.ih" +#include "math/random.ih" +#include "common/Model.ih" +#include "render/Renderer.ih" +#include "render/simpleAO/SimpleAOMaterial.ih" struct SimpleAO { uniform Renderer super; diff --git a/ospray/render/simpleAO/SimpleAOMaterial.h b/ospray/render/simpleAO/SimpleAOMaterial.h index 5e0944d509..66431f43d6 100644 --- a/ospray/render/simpleAO/SimpleAOMaterial.h +++ b/ospray/render/simpleAO/SimpleAOMaterial.h @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/Material.h" -#include "ospray/texture/Texture2D.h" +#include "common/Material.h" +#include "texture/Texture2D.h" namespace ospray { namespace simpleao { diff --git a/ospray/render/simpleAO/SimpleAOMaterial.ih b/ospray/render/simpleAO/SimpleAOMaterial.ih index 479a719c2a..519e4c00d3 100644 --- a/ospray/render/simpleAO/SimpleAOMaterial.ih +++ b/ospray/render/simpleAO/SimpleAOMaterial.ih @@ -16,9 +16,9 @@ #pragma once -#include "ospray/math/vec.ih" -#include "ospray/common/Material.ih" -#include "ospray/texture/Texture2D.ih" +#include "math/vec.ih" +#include "common/Material.ih" +#include "texture/Texture2D.ih" struct SimpleAOMaterial { Material super; diff --git a/ospray/render/util.ih b/ospray/render/util.ih index 2d4ed645fd..09b7b0d525 100644 --- a/ospray/render/util.ih +++ b/ospray/render/util.ih @@ -16,8 +16,7 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/math/vec.ih" +#include "../math/vec.ih" /*! \file ospray/render/util.ih \brief Utility-functions for shaders */ @@ -97,6 +96,35 @@ inline uint32 getZOrderY(const uint32 &xs16_ys16) { return xs16_ys16 >> 16; } extern uniform z_order_t z_order; extern uniform bool z_order_initialized; +inline uniform uint32 partitionZOrder(uniform uint32 n) { + n &= 0x0000FFFF; + n = (n | (n << 8)) & 0x00FF00FF; + n = (n | (n << 4)) & 0x0F0F0F0F; + n = (n | (n << 2)) & 0x33333333; + n = (n | (n << 1)) & 0x55555555; + return n; +} + +inline uniform uint32 unpartitionZOrder(uniform uint32 n) { + n &= 0x55555555; + n = (n ^ (n >> 1)) & 0x33333333; + n = (n ^ (n >> 2)) & 0x0F0F0F0F; + n = (n ^ (n >> 4)) & 0x00FF00FF; + n = (n ^ (n >> 8)) & 0x0000FFFF; + return n; +} + +inline uniform uint32 interleaveZOrder(uniform uint32 x, uniform uint32 y) { + return partitionZOrder(x) | (partitionZOrder(y) << 1); +} + +inline void deinterleaveZOrder(uniform uint32 z, uniform uint32 *uniform x, uniform uint32 *uniform y) { + assert(x); assert(y); + *x = *y = 0; + *x = unpartitionZOrder(z); + *y = unpartitionZOrder(z >> 1); +} + /*! precompute a per-pixel z-order to be used within a tile */ extern void precomputedZOrder_create(); @@ -104,5 +132,3 @@ extern void precomputedZOrder_create(); inline void precomputeZOrder() { if (!z_order_initialized) precomputedZOrder_create(); } - - diff --git a/ospray/render/util.ispc b/ospray/render/util.ispc index 753e27d64c..322dd181fa 100644 --- a/ospray/render/util.ispc +++ b/ospray/render/util.ispc @@ -14,8 +14,8 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/render/util.ih" -#include "ospray/math/random.ih" +#include "render/util.ih" +#include "math/random.ih" uniform float precomputedHalton[3][NUM_PRECOMPUTED_HALTON_VALUES]; uniform bool precomputedHalton_initialized = false; @@ -36,38 +36,9 @@ static void precomputedHalton_create() { uniform bool z_order_initialized = false; -inline uniform uint32 partition(uniform uint32 n) { - n &= 0x0000FFFF; - n = (n | (n << 8)) & 0x00FF00FF; - n = (n | (n << 4)) & 0x0F0F0F0F; - n = (n | (n << 2)) & 0x33333333; - n = (n | (n << 1)) & 0x55555555; - return n; -} - -inline uniform uint32 unpartition(uniform uint32 n) { - n &= 0x55555555; - n = (n ^ (n >> 1)) & 0x33333333; - n = (n ^ (n >> 2)) & 0x0F0F0F0F; - n = (n ^ (n >> 4)) & 0x00FF00FF; - n = (n ^ (n >> 8)) & 0x0000FFFF; - return n; -} - -inline uniform uint32 interleave(uniform uint32 x, uniform uint32 y) { - return partition(x) | (partition(y) << 1); -} - -inline void deinterleave(uniform uint32 z, uniform uint32 *uniform x, uniform uint32 *uniform y) { - assert(x); assert(y); - *x = *y = 0; - *x = unpartition(z); - *y = unpartition(z >> 1); -} - void precomputedZOrder_create() { for(uniform uint32 i = 0; i < TILE_SIZE*TILE_SIZE; i++) { - deinterleave(i, &z_order.xs[i], &z_order.ys[i]); + deinterleaveZOrder(i, &z_order.xs[i], &z_order.ys[i]); z_order.xyIdx[i] = z_order.xs[i] | (z_order.ys[i] << 16); } diff --git a/ospray/render/volume/RaycastVolumeMaterial.cpp b/ospray/render/volume/RaycastVolumeMaterial.cpp index 3e9f1d88fa..b99a03906f 100644 --- a/ospray/render/volume/RaycastVolumeMaterial.cpp +++ b/ospray/render/volume/RaycastVolumeMaterial.cpp @@ -16,7 +16,7 @@ #include "RaycastVolumeMaterial.h" #include "RaycastVolumeMaterial_ispc.h" -#include "ospray/common/Data.h" +#include "common/Data.h" namespace ospray { diff --git a/ospray/render/volume/RaycastVolumeMaterial.h b/ospray/render/volume/RaycastVolumeMaterial.h index 2c3380ff9c..91e366ad9c 100644 --- a/ospray/render/volume/RaycastVolumeMaterial.h +++ b/ospray/render/volume/RaycastVolumeMaterial.h @@ -16,10 +16,10 @@ #pragma once -#include "ospray/common/Material.h" -#include "ospray/texture/Texture2D.h" +#include "common/Material.h" +#include "texture/Texture2D.h" -#include "ospray/volume/Volume.h" +#include "volume/Volume.h" namespace ospray { diff --git a/ospray/render/volume/RaycastVolumeMaterial.ih b/ospray/render/volume/RaycastVolumeMaterial.ih index ba89ae6037..db47c7477f 100644 --- a/ospray/render/volume/RaycastVolumeMaterial.ih +++ b/ospray/render/volume/RaycastVolumeMaterial.ih @@ -16,10 +16,10 @@ #pragma once -#include "ospray/common/Material.ih" -#include "ospray/math/vec.ih" -#include "ospray/texture/Texture2D.ih" -#include "ospray/volume/Volume.ih" +#include "common/Material.ih" +#include "math/vec.ih" +#include "texture/Texture2D.ih" +#include "volume/Volume.ih" struct RaycastVolumeRendererMaterial { Material super; diff --git a/ospray/render/volume/RaycastVolumeMaterial.ispc b/ospray/render/volume/RaycastVolumeMaterial.ispc index de31cdd948..076555e396 100644 --- a/ospray/render/volume/RaycastVolumeMaterial.ispc +++ b/ospray/render/volume/RaycastVolumeMaterial.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/render/volume/RaycastVolumeMaterial.ih" +#include "render/volume/RaycastVolumeMaterial.ih" export void *uniform RaycastVolumeMaterial_create(void *uniform cppE) { diff --git a/ospray/render/volume/RaycastVolumeRenderer.cpp b/ospray/render/volume/RaycastVolumeRenderer.cpp index 35056d5f6f..485b29c4fc 100644 --- a/ospray/render/volume/RaycastVolumeRenderer.cpp +++ b/ospray/render/volume/RaycastVolumeRenderer.cpp @@ -15,19 +15,19 @@ // ======================================================================== // // ospray -#include "ospray/lights/Light.h" -#include "ospray/common/Data.h" -#include "ospray/common/Core.h" -#include "ospray/common/tasking/parallel_for.h" -#include "ospray/render/volume/RaycastVolumeRenderer.h" +#include "lights/Light.h" +#include "common/Data.h" +#include "common/Core.h" +#include "common/tasking/parallel_for.h" +#include "render/volume/RaycastVolumeRenderer.h" #include "RaycastVolumeMaterial.h" // ispc exports #include "RaycastVolumeRenderer_ispc.h" #if EXP_DATA_PARALLEL -# include "ospray/mpi/DistributedFrameBuffer.h" -# include "ospray/volume/DataDistributedBlockedVolume.h" -# include "ospray/render/LoadBalancer.h" +# include "mpi/DistributedFrameBuffer.h" +# include "volume/DataDistributedBlockedVolume.h" +# include "render/LoadBalancer.h" #endif #define TILE_CACHE_SAFE_MUTEX 0 @@ -112,19 +112,11 @@ namespace ospray { size_t numTiles_x; size_t numTiles_y; uint32 channelFlags; - int32 workerRank; const DataDistributedBlockedVolume *dpv; - DPRenderTask(int workerRank); - void operator()(int taskIndex) const; }; - DPRenderTask::DPRenderTask(int workerRank) - : workerRank(workerRank) - { - } - void DPRenderTask::operator()(int taskIndex) const { const size_t tileID = taskIndex; @@ -137,9 +129,7 @@ namespace ospray { size_t numBlocks = dpv->numDDBlocks; CacheForBlockTiles blockTileCache(numBlocks); - // for (int i=0;iddBlock[i].bounds); - bool *blockWasVisible = (bool*)alloca(numBlocks*sizeof(bool)); + bool *blockWasVisible = STACK_BUFFER(bool, numBlocks); for (int i = 0; i < numBlocks; i++) blockWasVisible[i] = false; @@ -149,7 +139,7 @@ namespace ospray { const int numJobs = (TILE_SIZE*TILE_SIZE)/RENDERTILE_PIXELS_PER_JOB; - parallel_for(numJobs, [&](int taskIndex){ + parallel_for(numJobs, [&](int tid){ ispc::DDDVRRenderer_renderTile(renderer->getIE(), (ispc::Tile&)fgTile, (ispc::Tile&)bgTile, @@ -160,7 +150,7 @@ namespace ospray { tileID, ospray::core::getWorkerRank(), renderForeAndBackground, - taskIndex); + tid); }); @@ -169,7 +159,7 @@ namespace ospray { // generaition #0, and telling the fb how many more tiles will // be coming in generation #1 - size_t totalBlocksInTile=0; + size_t totalBlocksInTile = 0; for (int blockID = 0; blockID < numBlocks; blockID++) { if (blockWasVisible[blockID]) totalBlocksInTile++; @@ -185,12 +175,12 @@ namespace ospray { // set background tile bgTile.generation = 0; - bgTile.children = nextGenTiles; + bgTile.children = nextGenTiles; fb->setTile(bgTile); // set foreground tile fgTile.generation = 1; - fgTile.children = 0; //nextGenTiles-1; + fgTile.children = 0; //nextGenTiles-1; fb->setTile(fgTile); // all other tiles for gen #1 will be set below, no matter whether // it's mine or not @@ -212,7 +202,7 @@ namespace ospray { tile->fbSize = bgTile.fbSize; tile->rcp_fbSize = bgTile.rcp_fbSize; tile->generation = 1; - tile->children = 0; //nextGenTile-1; + tile->children = 0; //nextGenTile-1; fb->setTile(*tile); } @@ -272,7 +262,7 @@ namespace ospray { "not using the distributed frame buffer!?"); } - dfb->setFrameMode(DistributedFrameBuffer::ALPHA_BLENDING); + dfb->setFrameMode(DistributedFrameBuffer::ALPHA_BLEND); // note: we can NEVER be the master, since the master doesn't even // have an instance of this renderer class - @@ -294,7 +284,7 @@ namespace ospray { } // create the render task - DPRenderTask renderTask(workerRank); + DPRenderTask renderTask; renderTask.fb = fb; renderTask.renderer = this; renderTask.numTiles_x = divRoundUp(dfb->size.x, TILE_SIZE); diff --git a/ospray/render/volume/RaycastVolumeRenderer.h b/ospray/render/volume/RaycastVolumeRenderer.h index ca7791df21..6347b7daba 100644 --- a/ospray/render/volume/RaycastVolumeRenderer.h +++ b/ospray/render/volume/RaycastVolumeRenderer.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/render/Renderer.h" +#include "render/Renderer.h" namespace ospray { diff --git a/ospray/render/volume/RaycastVolumeRenderer.ih b/ospray/render/volume/RaycastVolumeRenderer.ih index f3c8645b3f..c7f76f958d 100644 --- a/ospray/render/volume/RaycastVolumeRenderer.ih +++ b/ospray/render/volume/RaycastVolumeRenderer.ih @@ -16,10 +16,10 @@ #pragma once -#include "ospray/common/Ray.ih" -#include "ospray/lights/Light.ih" -#include "ospray/math/box.ih" -#include "ospray/render/Renderer.ih" +#include "common/Ray.ih" +#include "lights/Light.ih" +#include "math/box.ih" +#include "render/Renderer.ih" //! \brief ISPC variables and functions for the RaycastVolumeRenderer //! class, a concrete subtype of the Renderer class for rendering diff --git a/ospray/render/volume/RaycastVolumeRenderer.ispc b/ospray/render/volume/RaycastVolumeRenderer.ispc index c97cf1cf36..2467b02bf7 100644 --- a/ospray/render/volume/RaycastVolumeRenderer.ispc +++ b/ospray/render/volume/RaycastVolumeRenderer.ispc @@ -14,16 +14,16 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/fb/FrameBuffer.ih" -#include "ospray/common/Model.ih" -#include "ospray/common/Ray.ih" -#include "ospray/render/util.ih" -#include "ospray/render/volume/RaycastVolumeRenderer.ih" -#include "ospray/render/volume/RaycastVolumeMaterial.ih" -#include "ospray/volume/DataDistributedBlockedVolume.ih" -#include "ospray/render/util.ih" -#include "ospray/camera/Camera.ih" -#include "ospray/common/Model.ih" +#include "fb/FrameBuffer.ih" +#include "common/Model.ih" +#include "common/Ray.ih" +#include "render/util.ih" +#include "render/volume/RaycastVolumeRenderer.ih" +#include "render/volume/RaycastVolumeMaterial.ih" +#include "volume/DataDistributedBlockedVolume.ih" +#include "render/util.ih" +#include "camera/Camera.ih" +#include "common/Model.ih" struct PassInfo { // region to integrate over in this pass @@ -34,10 +34,11 @@ struct PassInfo { uniform bool useBG; }; -inline void RaycastVolumeRenderer_computeVolumeSample(RaycastVolumeRenderer *uniform self, - Volume *uniform volume, - varying Ray &ray, - varying vec4f &color) +inline void +RaycastVolumeRenderer_computeVolumeSample(RaycastVolumeRenderer *uniform self, + Volume *uniform volume, + varying Ray &ray, + varying vec4f &color) { // Sample the volume at the hit point in world coordinates. const vec3f coordinates = ray.org + ray.t0 * ray.dir; @@ -52,7 +53,8 @@ inline void RaycastVolumeRenderer_computeVolumeSample(RaycastVolumeRenderer *uni if(volume->gradientShadingEnabled) { // Use volume gradient as the normal. - const vec3f gradient = safe_normalize(volume->computeGradient(volume, coordinates)); + const vec3f gradient = safe_normalize(volume->computeGradient(volume, + coordinates)); // Setup differential geometry for the volume sample point. DifferentialGeometry dg; @@ -64,9 +66,11 @@ inline void RaycastVolumeRenderer_computeVolumeSample(RaycastVolumeRenderer *uni const vec2f s = make_vec2f(0.5f); for (uniform uint32 i=0; inumLights; i++) { - const Light_SampleRes light = self->lights[i]->sample(self->lights[i], dg, s); - const float cosNL = (gradient.x == 0.f && gradient.y == 0.f && gradient.z == 0.f) ? - 1.f : abs(dot(safe_normalize(light.dir), gradient)); + const Light_SampleRes light = + self->lights[i]->sample(self->lights[i], dg, s); + const float cosNL = + (gradient.x == 0.f && gradient.y == 0.f && gradient.z == 0.f) ? + 1.f : abs(dot(safe_normalize(light.dir), gradient)); shadedColor = shadedColor + sampleColor * cosNL * light.weight; } @@ -86,12 +90,14 @@ inline void RaycastVolumeRenderer_computeVolumeSample(RaycastVolumeRenderer *uni volume->intersect(volume, ray); } -inline void RaycastVolumeRenderer_computeGeometrySample(RaycastVolumeRenderer *uniform self, - varying Ray &ray, - const region1f &clipRange, - varying vec4f &color) +inline void +RaycastVolumeRenderer_computeGeometrySample(RaycastVolumeRenderer *uniform self, + varying Ray &ray, + const region1f &clipRange, + varying vec4f &color) { - // We compute intersections on the model and provide the contribution for the closest hit. + // We compute intersections on the model and provide the contribution for the + // closest hit. traceRay(self->super.model, ray); // No hit found. @@ -132,7 +138,10 @@ inline void RaycastVolumeRenderer_computeGeometrySample(RaycastVolumeRenderer *u if (m->volume) { const float sample = m->volume->computeSample(m->volume, dg.P); - geometryColor = geometryColor * m->volume->transferFunction->getColorForValue(m->volume->transferFunction, sample); + geometryColor = + geometryColor * + m->volume->transferFunction->getColorForValue( + m->volume->transferFunction, sample); } } } @@ -145,19 +154,24 @@ inline void RaycastVolumeRenderer_computeGeometrySample(RaycastVolumeRenderer *u const vec2f s = make_vec2f(0.5f); for (uniform uint32 i=0; inumLights; i++) { - const Light_SampleRes light = self->lights[i]->sample(self->lights[i], dg, s); + const Light_SampleRes light = self->lights[i]->sample(self->lights[i], + dg, + s); const float cosNL = abs(dot(safe_normalize(light.dir), dg.Ns)); shadedColor = shadedColor + geometryColor * cosNL * light.weight; } // Set the color contribution for this sample only (do not accumulate). - color = geometryOpacity * make_vec4f(shadedColor.x, shadedColor.y, shadedColor.z, 1.f); + color = geometryOpacity * make_vec4f(shadedColor.x, + shadedColor.y, + shadedColor.z, + 1.f); } -/*! Returns the first hit volume for the provided ray and sets the ray bounds t0 and t, - considering the provided ray offset and any clipping. If no volume is found, the - returned volume is NULL and ray.t0 will be set to infinity. */ +/*! Returns the first hit volume for the provided ray and sets the ray bounds t0 + and t, considering the provided ray offset and any clipping. If no volume is + found, the returned volume is NULL and ray.t0 will be set to infinity. */ inline Volume * RaycastVolumeRenderer_intersectVolumes(uniform RaycastVolumeRenderer *uniform self, varying Ray &ray, @@ -188,9 +202,18 @@ RaycastVolumeRenderer_intersectVolumes(uniform RaycastVolumeRenderer *uniform se // Update the provided ray. ray = volumeRay; - // If we intersected a volume, offset ray by a fraction of the nominal ray step. - - if (volume) ray.t0 += rayOffset * volume->samplingStep * rcpf(volume->samplingRate); + // If we hit a volume, offset ray by a fraction of the nominal ray step. + /* make sure that we place the first sample in this block exactly + where it would have been if this was not its own block, but + rather a region in a larger volume; else we get artifacts */ + if (volume) { + float dt = volume->samplingStep * rcpf(volume->samplingRate); + float t0 = ray.t0; + int i0 = (int)(ray.t0 / dt); + ray.t0 = (i0 + rayOffset)*dt; + if (ray.t0 < t0) ray.t0 += dt; + } + // if (volume) ray.t0 += rayOffset * volume->samplingStep * rcpf(volume->samplingRate); // Return the first intersected volume. return volume; @@ -220,7 +243,8 @@ RaycastVolumeRenderer_intersectVolumes(uniform RaycastVolumeRenderer *uniform se #endif // Clip against volume clipping box (if specified). - if(ne(volume_i->volumeClippingBox.lower, volume_i->volumeClippingBox.upper)) { + if(ne(volume_i->volumeClippingBox.lower, + volume_i->volumeClippingBox.upper)) { float tClip0, tClip1; intersectBox(ray, volume_i->volumeClippingBox, tClip0, tClip1); @@ -249,12 +273,13 @@ RaycastVolumeRenderer_intersectVolumes(uniform RaycastVolumeRenderer *uniform se } /*! This function intersects the volume and geometries. */ -void RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self, - varying Ray &ray, - const uniform PassInfo &passInfo, - const varying float &rayOffset, - varying vec4f &color, - varying float &depth) +void +RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self, + varying Ray &ray, + const uniform PassInfo &passInfo, + const varying float &rayOffset, + varying vec4f &color, + varying float &depth) { const region1f clipRange = passInfo.region; @@ -272,9 +297,11 @@ void RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self : make_box3f(make_vec3f(0.f), make_vec3f(0.f)); Model *uniform model = self->super.model; - for (uniform int32 i=1; isuper.model->volumeCount; i++) { - boundingBox.lower = min(boundingBox.lower, model->volumes[i]->boundingBox.lower); - boundingBox.upper = max(boundingBox.upper, model->volumes[i]->boundingBox.upper); + for (uniform int32 i = 1; i < self->super.model->volumeCount; i++) { + boundingBox.lower = + min(boundingBox.lower, model->volumes[i]->boundingBox.lower); + boundingBox.upper = + max(boundingBox.upper, model->volumes[i]->boundingBox.upper); } const uniform float epsilon @@ -298,7 +325,8 @@ void RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self if (volume != NULL) geometryRay.time = -rayOffset * volume->samplingStep; - // Separate color contributions for the volume and geometries. Initialize to provided color in case of no contribution... + // Separate color contributions for the volume and geometries. Initialize + // to provided color in case of no contribution... vec4f volumeColor = color; vec4f geometryColor = color; @@ -321,14 +349,17 @@ void RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self ray.t0 = ray.t + epsilon; ray.t = tMax; - volume = RaycastVolumeRenderer_intersectVolumes(self, ray, passInfo, rayOffset); + volume = RaycastVolumeRenderer_intersectVolumes(self, + ray, + passInfo, + rayOffset); } else { if (any(volume == NULL)) print("ACCESSING NULL VOLUME HERE!\n"); - // Compute the volume sample at the current position and advance the ray. + // Compute the volume sample at the current position and advance the ray foreach_unique (v in volume) RaycastVolumeRenderer_computeVolumeSample(self, v, ray, volumeColor); @@ -348,7 +379,8 @@ void RaycastVolumeRenderer_intersect(uniform RaycastVolumeRenderer *uniform self geometryRay.geomID = -1; geometryRay.instID = -1; - // Update ray offset for use with isosurface geometries based on current volume (this value ignored elsewhere). + // Update ray offset for use with isosurface geometries based on + // current volume (this value ignored elsewhere). geometryRay.time = volume ? -rayOffset * volume->samplingStep : 0.f; // Trace next geometry ray. @@ -414,7 +446,8 @@ void RaycastVolumeRenderer_renderSample(Renderer *uniform pointer, export void *uniform RaycastVolumeRenderer_createInstance() { - RaycastVolumeRenderer *uniform self = uniform new uniform RaycastVolumeRenderer; + RaycastVolumeRenderer *uniform self = + uniform new uniform RaycastVolumeRenderer; Renderer_Constructor(&self->super, NULL); self->super.renderSample = RaycastVolumeRenderer_renderSample; @@ -427,7 +460,8 @@ export void RaycastVolumeRenderer_setLights(void *uniform _self, const uniform uint32 numLights) { // Cast to the actual Renderer subtype. - uniform RaycastVolumeRenderer *uniform self = (uniform RaycastVolumeRenderer *uniform)_self; + uniform RaycastVolumeRenderer *uniform self = + (uniform RaycastVolumeRenderer *uniform)_self; // Set the light sources. self->lights = (Light **uniform)lights; @@ -435,8 +469,9 @@ export void RaycastVolumeRenderer_setLights(void *uniform _self, } #if EXP_DATA_PARALLEL -extern "C" Tile *uniform CacheForBlockTiles_getTileForBlock(void *uniform _tileCache, - uniform uint32 blockID); +extern "C" Tile * +uniform CacheForBlockTiles_getTileForBlock(void *uniform _tileCache, + uniform uint32 blockID); inline uniform bool itIsIThatHasToRenderThisBlock(DDBVolumeBlock *uniform block, uniform int32 tileID, @@ -478,18 +513,21 @@ inline void DDVR_renderSample(RaycastVolumeRenderer *uniform self, // now, render this block if it's one of ours if (!block[blockID].mine) continue; if (itIsIThatHasToRenderThisBlock(&block[blockID],tileID,myRank)) { - Tile *uniform blockTile = CacheForBlockTiles_getTileForBlock(_tileCache,blockID); + Tile *uniform blockTile = + CacheForBlockTiles_getTileForBlock(_tileCache,blockID); // ------------------------------------------------------- // set up pass for 'one particular block' // ------------------------------------------------------- uniform PassInfo passInfo; - // set integration pass up for region from (at most) ray origin to start of blocks + // set integration pass up for region from (at most) ray origin to + // start of blocks passInfo.region = make_region1f(t0,t1); // do not use background color in this pass passInfo.useBG = false; // do not use any block in this pass passInfo.block = &block[blockID]; - RaycastVolumeRenderer_renderSample((Renderer*uniform)self,&passInfo,fgSample); + RaycastVolumeRenderer_renderSample((Renderer*uniform)self, + &passInfo,fgSample); fgSample.ray.t0 = org_ray_t0; fgSample.ray.t = org_ray_t1; setRGBAZ(*blockTile,pixelID,fgSample.rgb,fgSample.alpha,fgSample.z); @@ -497,7 +535,7 @@ inline void DDVR_renderSample(RaycastVolumeRenderer *uniform self, } } -if (!isMyTile) return; + if (!isMyTile) return; if (blockRegion.lower >= blockRegion.upper) { // ------------------------------------------------------------------ @@ -511,7 +549,8 @@ if (!isMyTile) return; // ------------------------------------------------------------------ uniform PassInfo passInfo; - // set integration pass up for region from (at most) ray origin to start of blocks + // set integration pass up for region from (at most) ray origin to start + // of blocks passInfo.region = make_region1f(fgSample.ray.t0,fgSample.ray.t); // do not use background color in this pass passInfo.useBG = true; @@ -519,7 +558,9 @@ if (!isMyTile) return; passInfo.block = NULL; // ray does not overlap _any_ block; render entire ray segment in one sweep - RaycastVolumeRenderer_renderSample((Renderer*uniform)self,&passInfo,fgSample); + RaycastVolumeRenderer_renderSample((Renderer*uniform)self, + &passInfo, + fgSample); bgSample.rgb = make_vec3f(0.f,0.f,0.f); bgSample.z = inf; @@ -532,24 +573,30 @@ if (!isMyTile) return; // set up pass for 'before any blocks' // ------------------------------------------------------- uniform PassInfo passInfo; - // set integration pass up for region from (at most) ray origin to start of blocks + // set integration pass up for region from (at most) ray origin to start + // of blocks passInfo.region = make_region1f(fgSample.ray.t0,blockRegion.lower); // do not use background color in this pass passInfo.useBG = false; // do not use any block in this pass passInfo.block = NULL; - RaycastVolumeRenderer_renderSample((Renderer*uniform)self,&passInfo,fgSample); + RaycastVolumeRenderer_renderSample((Renderer*uniform)self, + &passInfo, + fgSample); // ------------------------------------------------------- // set up pass for 'after any blocks' // ------------------------------------------------------- - // set integration pass up for region from (at most) ray origin to start of blocks + // set integration pass up for region from (at most) ray origin to start + // of blocks passInfo.region = make_region1f(blockRegion.upper,bgSample.ray.t); // do not use background color in this pass passInfo.useBG = true; // do not use any block in this pass passInfo.block = NULL; - RaycastVolumeRenderer_renderSample((Renderer*uniform)self,&passInfo,bgSample); + RaycastVolumeRenderer_renderSample((Renderer*uniform)self, + &passInfo, + bgSample); } } @@ -604,7 +651,7 @@ export void DDDVRRenderer_renderTile(void*uniform _self, camera->initRay(camera,fgSample.ray,cameraSample); - // set ray t value for early ray termination if we have a maximum depth texture + // set ray t value for early ray termination if we have a max depth texture if (self->maxDepthTexture) { // always sample center of pixel vec2f depthTexCoord; diff --git a/ospray/texture/Texture2D.h b/ospray/texture/Texture2D.h index 6862ffc5a2..8597a5702b 100644 --- a/ospray/texture/Texture2D.h +++ b/ospray/texture/Texture2D.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/Managed.h" +#include "common/Managed.h" #include "ospray/OSPTexture.h" namespace ospray { diff --git a/ospray/texture/Texture2D.ih b/ospray/texture/Texture2D.ih index e9102755a4..459ba0b86b 100644 --- a/ospray/texture/Texture2D.ih +++ b/ospray/texture/Texture2D.ih @@ -17,7 +17,7 @@ #pragma once #include "ospray/OSPTexture.h" -#include "ospray/math/vec.ih" +#include "math/vec.ih" struct Texture2D; diff --git a/ospray/texture/Texture2D.ispc b/ospray/texture/Texture2D.ispc index 61e40769ec..6571b4daa0 100644 --- a/ospray/texture/Texture2D.ispc +++ b/ospray/texture/Texture2D.ispc @@ -30,7 +30,7 @@ inline vec4f getTexel_RGBA8(const uniform Texture2D *uniform self, const vec2i i const uint32 g = (c >> 8) & 0xff; const uint32 b = (c >> 16) & 0xff; const uint32 a = c >> 24; - return make_vec4f(r, g, b, a)*(1.f/255.f); + return make_vec4f((float)r, (float)g, (float)b, (float)a)*(1.f/255.f); } inline vec4f getTexel_RGB8(const uniform Texture2D *uniform self, const vec2i i) @@ -41,7 +41,7 @@ inline vec4f getTexel_RGB8(const uniform Texture2D *uniform self, const vec2i i) const uint32 r = texel[texelOfs]; const uint32 g = texel[texelOfs+1]; const uint32 b = texel[texelOfs+2]; - return make_vec4f(make_vec3f(r, g, b)*(1.f/255.f), 1.f); + return make_vec4f(make_vec3f((float)r, (float)g, (float)b)*(1.f/255.f), 1.f); } inline vec4f getTexel_R8(const uniform Texture2D *uniform self, const vec2i i) @@ -182,6 +182,7 @@ static uniform Texture2D_get Texture2D_get_addr(const uniform uint32 type, switch (type) { __foreach_fetcher(__define_tex_get_case) } + return 0; }; #undef __define_tex_get @@ -202,7 +203,7 @@ export void *uniform Texture2D_create(uniform vec2i &size, void *uniform data, // negative x), although it should be strictly smaller than 1.0f. We handle // this case by having sizef slightly smaller than size, such that // frac(x)*sizef is always < size. - self->sizef = make_vec2f(nextafter(size.x, -1.0f), nextafter(size.y, -1.0f)); + self->sizef = make_vec2f(nextafter((float)size.x, -1.0f), nextafter((float)size.y, -1.0f)); self->halfTexel = make_vec2f(0.5f/size.x, 0.5f/size.y); self->data = data; self->get = Texture2D_get_addr(type, flags & OSP_TEXTURE_FILTER_NEAREST); diff --git a/ospray/texture/TextureParam.ih b/ospray/texture/TextureParam.ih index cbbb1f65f6..9508ff7013 100644 --- a/ospray/texture/TextureParam.ih +++ b/ospray/texture/TextureParam.ih @@ -17,7 +17,7 @@ #pragma once #include "Texture2D.ih" -#include "ospray/math/AffineSpace.ih" +#include "math/AffineSpace.ih" //! Texture2D including coordinate transformation, plus helpers diff --git a/ospray/transferFunction/LinearTransferFunction.cpp b/ospray/transferFunction/LinearTransferFunction.cpp index 6d113c8312..8d77926ea5 100644 --- a/ospray/transferFunction/LinearTransferFunction.cpp +++ b/ospray/transferFunction/LinearTransferFunction.cpp @@ -14,9 +14,9 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/common/Data.h" -#include "ospray/common/OSPCommon.h" -#include "ospray/transferFunction/LinearTransferFunction.h" +#include "common/Data.h" +#include "common/OSPCommon.h" +#include "transferFunction/LinearTransferFunction.h" #include "TransferFunction_ispc.h" namespace ospray { diff --git a/ospray/transferFunction/LinearTransferFunction.h b/ospray/transferFunction/LinearTransferFunction.h index ac890c2a10..0faa0b3648 100644 --- a/ospray/transferFunction/LinearTransferFunction.h +++ b/ospray/transferFunction/LinearTransferFunction.h @@ -17,8 +17,8 @@ #pragma once // ospray -#include "ospray/common/Data.h" -#include "ospray/transferFunction/TransferFunction.h" +#include "common/Data.h" +#include "transferFunction/TransferFunction.h" #include "LinearTransferFunction_ispc.h" // std #include diff --git a/ospray/transferFunction/LinearTransferFunction.ih b/ospray/transferFunction/LinearTransferFunction.ih index 40971cfa0b..6400a95a5b 100644 --- a/ospray/transferFunction/LinearTransferFunction.ih +++ b/ospray/transferFunction/LinearTransferFunction.ih @@ -16,7 +16,7 @@ #pragma once -#include "ospray/transferFunction/TransferFunction.ih" +#include "../transferFunction/TransferFunction.ih" #define PRECOMPUTED_OPACITY_SUBRANGE_COUNT 32 diff --git a/ospray/transferFunction/LinearTransferFunction.ispc b/ospray/transferFunction/LinearTransferFunction.ispc index b2f4dcd6d8..a842fe6f26 100644 --- a/ospray/transferFunction/LinearTransferFunction.ispc +++ b/ospray/transferFunction/LinearTransferFunction.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/transferFunction/LinearTransferFunction.ih" +#include "transferFunction/LinearTransferFunction.ih" inline varying vec3f LinearTransferFunction_getColorForValue(const void *uniform _self, diff --git a/ospray/transferFunction/TransferFunction.cpp b/ospray/transferFunction/TransferFunction.cpp index 4c2b92b64b..12d06419e3 100644 --- a/ospray/transferFunction/TransferFunction.cpp +++ b/ospray/transferFunction/TransferFunction.cpp @@ -15,8 +15,8 @@ // ======================================================================== // // ospray -#include "ospray/common/Library.h" -#include "ospray/transferFunction/TransferFunction.h" +#include "common/Library.h" +#include "transferFunction/TransferFunction.h" // std #include diff --git a/ospray/transferFunction/TransferFunction.h b/ospray/transferFunction/TransferFunction.h index 98d12ea333..532d283989 100644 --- a/ospray/transferFunction/TransferFunction.h +++ b/ospray/transferFunction/TransferFunction.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/Managed.h" +#include "common/Managed.h" /*! \brief Define a function to create an instance of the InternalClass associated with ExternalName. diff --git a/ospray/transferFunction/TransferFunction.ih b/ospray/transferFunction/TransferFunction.ih index 66616f4319..b090661e77 100644 --- a/ospray/transferFunction/TransferFunction.ih +++ b/ospray/transferFunction/TransferFunction.ih @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/math/vec.ih" +#include "../common/OSPCommon.ih" +#include "../math/vec.ih" //! Abstract base class for any kind of transfer function. struct TransferFunction { diff --git a/ospray/transferFunction/TransferFunction.ispc b/ospray/transferFunction/TransferFunction.ispc index a3f6caad07..35e145b839 100644 --- a/ospray/transferFunction/TransferFunction.ispc +++ b/ospray/transferFunction/TransferFunction.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/transferFunction/TransferFunction.ih" +#include "transferFunction/TransferFunction.ih" export void TransferFunction_setValueRange(void *uniform pointer, const uniform vec2f &value) diff --git a/ospray/version.h.in b/ospray/version.h.in index 49829bc365..446b41f755 100644 --- a/ospray/version.h.in +++ b/ospray/version.h.in @@ -17,4 +17,5 @@ #define OSPRAY_VERSION_MAJOR @OSPRAY_VERSION_MAJOR@ #define OSPRAY_VERSION_MINOR @OSPRAY_VERSION_MINOR@ #define OSPRAY_VERSION_PATCH @OSPRAY_VERSION_PATCH@ +#define OSPRAY_VERSION_GITHASH "@OSPRAY_VERSION_GITHASH@" #define OSPRAY_VERSION "@OSPRAY_VERSION@" diff --git a/ospray/volume/BlockBrickedVolume.cpp b/ospray/volume/BlockBrickedVolume.cpp index 391257cab9..bee7a2b829 100644 --- a/ospray/volume/BlockBrickedVolume.cpp +++ b/ospray/volume/BlockBrickedVolume.cpp @@ -15,8 +15,8 @@ // ======================================================================== // //ospray -#include "ospray/volume/BlockBrickedVolume.h" -#include "ospray/common/tasking/parallel_for.h" +#include "volume/BlockBrickedVolume.h" +#include "common/tasking/parallel_for.h" #include "BlockBrickedVolume_ispc.h" namespace ospray { @@ -92,19 +92,27 @@ namespace ospray { throw std::runtime_error("invalid voxelType in " "BlockBrickedVolume::setRegion()"); } + set("voxelRange", voxelRange); } #endif + vec3i finalRegionSize = regionSize; + vec3i finalRegionCoords = regionCoords; + void *finalSource = const_cast(source); + const bool upsampling = scaleRegion(source, finalSource, finalRegionSize, finalRegionCoords); // Copy voxel data into the volume. - const int NTASKS = regionSize.y * regionSize.z; + const int NTASKS = finalRegionSize.y * finalRegionSize.z; parallel_for(NTASKS, [&](int taskIndex){ - ispc::BlockBrickedVolume_setRegion(ispcEquivalent, - source, - (const ispc::vec3i &)regionCoords, - (const ispc::vec3i &)regionSize, - taskIndex); + ispc::BlockBrickedVolume_setRegion(ispcEquivalent, finalSource, (const ispc::vec3i&)finalRegionCoords, + (const ispc::vec3i&)finalRegionSize, taskIndex); }); + // If we're upsampling finalSource points at the chunk of data allocated by scaleRegion + // to hold the upsampled volume data and we must free it. + if (upsampling) { + free(finalSource); + } + return true; } diff --git a/ospray/volume/BlockBrickedVolume.h b/ospray/volume/BlockBrickedVolume.h index b8e7896a39..0089bae0b5 100644 --- a/ospray/volume/BlockBrickedVolume.h +++ b/ospray/volume/BlockBrickedVolume.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/volume/StructuredVolume.h" +#include "volume/StructuredVolume.h" namespace ospray { diff --git a/ospray/volume/BlockBrickedVolume.ih b/ospray/volume/BlockBrickedVolume.ih index e6f12286c2..1e9b98c8f9 100644 --- a/ospray/volume/BlockBrickedVolume.ih +++ b/ospray/volume/BlockBrickedVolume.ih @@ -17,7 +17,7 @@ #pragma once #include "ospray/OSPDataType.h" -#include "ospray/volume/StructuredVolume.ih" +#include "../volume/StructuredVolume.ih" //! \brief ISPC variables and functions for the BlockBrickedVolume class /*! \detailed ISPC variables and functions for the BlockBrickedVolume class, diff --git a/ospray/volume/BlockBrickedVolume.ispc b/ospray/volume/BlockBrickedVolume.ispc index ddba87a98b..a82e599496 100644 --- a/ospray/volume/BlockBrickedVolume.ispc +++ b/ospray/volume/BlockBrickedVolume.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/BlockBrickedVolume.ih" +#include "volume/BlockBrickedVolume.ih" //! The number of bits used to represent the width of a Block in voxels. #define BLOCK_VOXEL_WIDTH_BITCOUNT (6) diff --git a/ospray/volume/DataDistributedBlockedVolume.cpp b/ospray/volume/DataDistributedBlockedVolume.cpp index b43b61acaa..fb04b5bb27 100644 --- a/ospray/volume/DataDistributedBlockedVolume.cpp +++ b/ospray/volume/DataDistributedBlockedVolume.cpp @@ -14,10 +14,16 @@ // limitations under the License. // // ======================================================================== // +#include // ospray -#include "ospray/volume/DataDistributedBlockedVolume.h" -#include "ospray/common/Core.h" -#include "ospray/transferFunction/TransferFunction.h" +#include "volume/DataDistributedBlockedVolume.h" +#include "volume/GhostBlockBrickedVolume.h" +#include "common/Core.h" +#include "transferFunction/TransferFunction.h" +#include "volume/GhostBlockBrickedVolume.h" +#if EXP_DATA_PARALLEL +#include "mpi/MPICommon.h" +#endif // ispc exports: #include "DataDistributedBlockedVolume_ispc.h" @@ -113,27 +119,59 @@ namespace ospray { // Create the equivalent ISPC volume container and allocate memory for voxel // data. if (ispcEquivalent == NULL) createEquivalentISPC(); - + + // The block domains are in terms of the scaled regions so we must check + // if the data is for the block in the scaled volume + vec3i finalRegionCoords(vec3f(regionCoords) * scaleFactor); + vec3i finalRegionSize(vec3f(regionSize) * scaleFactor); + if (scaleFactor != vec3f(-1.f)) { + finalRegionCoords = vec3i(vec3f(regionCoords) * scaleFactor); + finalRegionSize = vec3i(vec3f(regionSize) * scaleFactor); + } else { + finalRegionCoords = regionCoords; + finalRegionSize = regionSize; + } + for (int i=0;i= regionCoords.x+regionSize.x) continue; - if (ddBlock[i].domain.lower.y >= regionCoords.y+regionSize.y) continue; - if (ddBlock[i].domain.lower.z >= regionCoords.z+regionSize.z) continue; + if (ddBlock[i].domain.lower.x >= finalRegionCoords.x+finalRegionSize.x) continue; + if (ddBlock[i].domain.lower.y >= finalRegionCoords.y+finalRegionSize.y) continue; + if (ddBlock[i].domain.lower.z >= finalRegionCoords.z+finalRegionSize.z) continue; - if (ddBlock[i].domain.upper.x+regionSize.x < regionCoords.x) continue; - if (ddBlock[i].domain.upper.y+regionSize.y < regionCoords.y) continue; - if (ddBlock[i].domain.upper.z+regionSize.z < regionCoords.z) continue; + if (ddBlock[i].domain.upper.x+finalRegionSize.x < finalRegionCoords.x) continue; + if (ddBlock[i].domain.upper.y+finalRegionSize.y < finalRegionCoords.y) continue; + if (ddBlock[i].domain.upper.z+finalRegionSize.z < finalRegionCoords.z) continue; + + vec3i setRegionCoords = finalRegionCoords - ddBlock[i].domain.lower; + if (scaleFactor != vec3f(-1.f)) { + setRegionCoords = vec3i(vec3f(setRegionCoords) / scaleFactor); + } - ddBlock[i].cppVolume->setRegion(source, - regionCoords-ddBlock[i].domain.lower, - regionSize); + ddBlock[i].cppVolume->setRegion(source, setRegionCoords, regionSize); + ddBlock[i].ispcVolume = ddBlock[i].cppVolume->getIE(); + +#ifndef OSPRAY_VOLUME_VOXELRANGE_IN_APP + ManagedObject::Param *param = ddBlock[i].cppVolume->findParam("voxelRange"); + if (param != NULL && param->type == OSP_FLOAT2){ + vec2f blockRange = ((vec2f*)param->f)[0]; + voxelRange.x = std::min(voxelRange.x, blockRange.x); + voxelRange.y = std::max(voxelRange.y, blockRange.y); + } +#endif } +#ifndef OSPRAY_VOLUME_VOXELRANGE_IN_APP + // Do a reduction here to worker 0 since it will be queried for the voxel range by the display node + vec2f globalVoxelRange = voxelRange; + MPI_CALL(Reduce(&voxelRange.x, &globalVoxelRange.x, 1, MPI_FLOAT, MPI_MIN, 0, mpi::worker.comm)); + MPI_CALL(Reduce(&voxelRange.y, &globalVoxelRange.y, 1, MPI_FLOAT, MPI_MAX, 0, mpi::worker.comm)); + set("voxelRange", globalVoxelRange); +#endif return 0; } @@ -169,6 +207,8 @@ namespace ospray { // Set the grid spacing, default to (1,1,1). this->gridSpacing = getParam3f("gridSpacing", vec3f(1.f)); + this->scaleFactor = getParam3f("scaleFactor", vec3f(-1.f)); + numDDBlocks = ospcommon::reduce_mul(ddBlocks); ddBlock = new DDBlock[numDDBlocks]; @@ -221,12 +261,19 @@ namespace ospray { if (block->isMine) { +#ifdef EXP_NEW_BB_VOLUME_KERNELS + Ref volume = new GhostBlockBrickedVolume; +#else Ref volume = new BlockBrickedVolume; +#endif vec3i blockDims = block->domain.upper - block->domain.lower; volume->findParam("dimensions",1)->set(blockDims); volume->findParam("gridOrigin",1)->set(block->bounds.lower); volume->findParam("gridSpacing",1)->set(gridSpacing); volume->findParam("voxelType",1)->set(voxelType.c_str()); + if (scaleFactor != vec3f(-1.f)) { + volume->findParam("scaleFactor",1)->set(scaleFactor); + } printf("rank %li owns block %i,%i,%i (ID %i), dims %i %i %i\n", (size_t)core::getWorkerRank(),ix,iy,iz, diff --git a/ospray/volume/DataDistributedBlockedVolume.h b/ospray/volume/DataDistributedBlockedVolume.h index f1a3a197b4..b4acc74626 100644 --- a/ospray/volume/DataDistributedBlockedVolume.h +++ b/ospray/volume/DataDistributedBlockedVolume.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/volume/BlockBrickedVolume.h" +#include "volume/BlockBrickedVolume.h" namespace ospray { diff --git a/ospray/volume/DataDistributedBlockedVolume.ih b/ospray/volume/DataDistributedBlockedVolume.ih index 2ab56a962b..1784669657 100644 --- a/ospray/volume/DataDistributedBlockedVolume.ih +++ b/ospray/volume/DataDistributedBlockedVolume.ih @@ -16,9 +16,7 @@ #pragma once -#include "ospray/volume/StructuredVolume.ih" - - +#include "../volume/StructuredVolume.ih" struct DDBVolumeBlock { diff --git a/ospray/volume/DataDistributedBlockedVolume.ispc b/ospray/volume/DataDistributedBlockedVolume.ispc index 963ff590e9..0992b8abc2 100644 --- a/ospray/volume/DataDistributedBlockedVolume.ispc +++ b/ospray/volume/DataDistributedBlockedVolume.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/DataDistributedBlockedVolume.ih" +#include "volume/DataDistributedBlockedVolume.ih" void DDBVolume_Constructor(DDBVolume *uniform self, void *uniform cppEquivalent, diff --git a/ospray/volume/GhostBlockBrickedVolume.cpp b/ospray/volume/GhostBlockBrickedVolume.cpp index 982cc0e70a..ada926c99c 100644 --- a/ospray/volume/GhostBlockBrickedVolume.cpp +++ b/ospray/volume/GhostBlockBrickedVolume.cpp @@ -15,8 +15,8 @@ // ======================================================================== // //ospray -#include "ospray/volume/GhostBlockBrickedVolume.h" -#include "ospray/common/tasking/parallel_for.h" +#include "volume/GhostBlockBrickedVolume.h" +#include "common/tasking/parallel_for.h" #include "GhostBlockBrickedVolume_ispc.h" namespace ospray { @@ -94,17 +94,24 @@ namespace ospray { } } #endif - // Copy voxel data into the volume. - const int NTASKS = regionSize.y * regionSize.z; + vec3i finalRegionSize = regionSize; + vec3i finalRegionCoords = regionCoords; + void *finalSource = const_cast(source); + const bool upsampling = scaleRegion(source, finalSource, finalRegionSize, finalRegionCoords); + // Copy voxel data into the volume. + const int NTASKS = finalRegionSize.y * finalRegionSize.z; parallel_for(NTASKS, [&](int taskIndex){ - ispc::GBBV_setRegion(ispcEquivalent, - source, - (const ispc::vec3i &)regionCoords, - (const ispc::vec3i &)regionSize, - taskIndex); + ispc::GBBV_setRegion(ispcEquivalent, finalSource, (const ispc::vec3i&)finalRegionCoords, + (const ispc::vec3i&)finalRegionSize, taskIndex); }); + // If we're upsampling finalSource points at the chunk of data allocated by scaleRegion + // to hold the upsampled volume data and we must free it. + if (upsampling) { + free(finalSource); + } + return true; } diff --git a/ospray/volume/GhostBlockBrickedVolume.h b/ospray/volume/GhostBlockBrickedVolume.h index d2965d84e1..0150ba7456 100644 --- a/ospray/volume/GhostBlockBrickedVolume.h +++ b/ospray/volume/GhostBlockBrickedVolume.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/volume/StructuredVolume.h" +#include "volume/StructuredVolume.h" namespace ospray { diff --git a/ospray/volume/GhostBlockBrickedVolume.ih b/ospray/volume/GhostBlockBrickedVolume.ih index f5b9b3889f..a79395a432 100644 --- a/ospray/volume/GhostBlockBrickedVolume.ih +++ b/ospray/volume/GhostBlockBrickedVolume.ih @@ -17,7 +17,7 @@ #pragma once #include "ospray/OSPDataType.h" -#include "ospray/volume/StructuredVolume.ih" +#include "../volume/StructuredVolume.ih" //! \brief ISPC variables and functions for the GhostBlockBrickedVolume class /*! \detailed ISPC variables and functions for the GhostBlockBrickedVolume class, diff --git a/ospray/volume/GhostBlockBrickedVolume.ispc b/ospray/volume/GhostBlockBrickedVolume.ispc index 54b706f78a..39e62c6aeb 100644 --- a/ospray/volume/GhostBlockBrickedVolume.ispc +++ b/ospray/volume/GhostBlockBrickedVolume.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/GhostBlockBrickedVolume.ih" +#include "volume/GhostBlockBrickedVolume.ih" /*! total number of bits per block dimension. '6' would mean 18 bits = 1/4million voxels per block, which for alots would be 1MB, so should diff --git a/ospray/volume/GridAccelerator.ih b/ospray/volume/GridAccelerator.ih index 4229a47e9a..6848991183 100644 --- a/ospray/volume/GridAccelerator.ih +++ b/ospray/volume/GridAccelerator.ih @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/common/Ray.ih" +#include "common/OSPCommon.ih" +#include "common/Ray.ih" //! \brief A spatial acceleration structure over a BlockBrickedVolume, used //! for opacity and variance based space skipping. diff --git a/ospray/volume/GridAccelerator.ispc b/ospray/volume/GridAccelerator.ispc index 209d21cfe4..9334cfbc54 100644 --- a/ospray/volume/GridAccelerator.ispc +++ b/ospray/volume/GridAccelerator.ispc @@ -15,9 +15,9 @@ // ======================================================================== // // ospray -#include "ospray/volume/GridAccelerator.ih" -#include "ospray/volume/StructuredVolume.ih" -#include "ospray/math/box.ih" +#include "volume/GridAccelerator.ih" +#include "volume/StructuredVolume.ih" +#include "math/box.ih" //! Bit count used to represent the brick width. #define BRICK_WIDTH_BITCOUNT (4) diff --git a/ospray/volume/SharedStructuredVolume.cpp b/ospray/volume/SharedStructuredVolume.cpp index c198c88b53..0988be3128 100644 --- a/ospray/volume/SharedStructuredVolume.cpp +++ b/ospray/volume/SharedStructuredVolume.cpp @@ -15,10 +15,11 @@ // ======================================================================== // //ospray -#include "ospray/volume/SharedStructuredVolume.h" +#include "volume/SharedStructuredVolume.h" #include "SharedStructuredVolume_ispc.h" #include "StructuredVolume_ispc.h" -#include "ospray/common/Data.h" +#include "common/Data.h" +#include "ospray/ospray.h" namespace ospray { diff --git a/ospray/volume/SharedStructuredVolume.h b/ospray/volume/SharedStructuredVolume.h index e28ec635b0..8b736208bd 100644 --- a/ospray/volume/SharedStructuredVolume.h +++ b/ospray/volume/SharedStructuredVolume.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/volume/StructuredVolume.h" +#include "volume/StructuredVolume.h" namespace ospray { diff --git a/ospray/volume/SharedStructuredVolume.ih b/ospray/volume/SharedStructuredVolume.ih index a1f77667d5..a328bd7719 100644 --- a/ospray/volume/SharedStructuredVolume.ih +++ b/ospray/volume/SharedStructuredVolume.ih @@ -17,7 +17,7 @@ #pragma once #include "ospray/OSPDataType.h" -#include "ospray/volume/StructuredVolume.ih" +#include "../volume/StructuredVolume.ih" //! \brief ISPC variables and functions for the SharedStructuredVolume class /*! \detailed The SharedStructuredVolume is a concrete implementation diff --git a/ospray/volume/SharedStructuredVolume.ispc b/ospray/volume/SharedStructuredVolume.ispc index a9b6ea2e15..77ca4106e2 100644 --- a/ospray/volume/SharedStructuredVolume.ispc +++ b/ospray/volume/SharedStructuredVolume.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/SharedStructuredVolume.ih" +#include "volume/SharedStructuredVolume.ih" /*! get a voxel from given volume type */ diff --git a/ospray/volume/StructuredVolume.cpp b/ospray/volume/StructuredVolume.cpp index 1a957a76ef..7b193164be 100644 --- a/ospray/volume/StructuredVolume.cpp +++ b/ospray/volume/StructuredVolume.cpp @@ -15,10 +15,10 @@ // ======================================================================== // //ospray -#include "ospray/common/Data.h" -#include "ospray/common/Core.h" -#include "ospray/common/Library.h" -#include "ospray/volume/StructuredVolume.h" +#include "common/Data.h" +#include "common/Core.h" +#include "common/Library.h" +#include "volume/StructuredVolume.h" #include "GridAccelerator_ispc.h" #include "StructuredVolume_ispc.h" @@ -58,6 +58,8 @@ namespace ospray { this->gridSpacing = getParam3f("gridSpacing", vec3f(1.f)); + this->scaleFactor = getParam3f("scaleFactor", vec3f(-1.f)); + ispc::StructuredVolume_setGridOrigin(ispcEquivalent, (const ispc::vec3f&)this->gridOrigin); ispc::StructuredVolume_setGridSpacing(ispcEquivalent, @@ -70,6 +72,39 @@ namespace ospray { } } + bool StructuredVolume::scaleRegion(const void *source, void *&out, vec3i ®ionSize, vec3i ®ionCoords){ + this->scaleFactor = getParam3f("scaleFactor", vec3f(-1.f)); + const bool upsampling = scaleFactor.x > 0 && scaleFactor.y > 0 && scaleFactor.z > 0; + vec3i scaledRegionSize = vec3i(scaleFactor * vec3f(regionSize)); + vec3i scaledRegionCoords = vec3i(scaleFactor * vec3f(regionCoords)); + + if (upsampling) { + if (voxelType == "uchar") { + out = malloc(sizeof(unsigned char) * size_t(scaledRegionSize.x) * + size_t(scaledRegionSize.y) * size_t(scaledRegionSize.z)); + upsampleRegion((unsigned char *)source, (unsigned char *)out, regionSize, scaledRegionSize); + } + else if (voxelType == "ushort") { + out = malloc(sizeof(unsigned short) * size_t(scaledRegionSize.x) * + size_t(scaledRegionSize.y) * size_t(scaledRegionSize.z)); + upsampleRegion((unsigned short *)source, (unsigned short *)out, regionSize, scaledRegionSize); + } + else if (voxelType == "float") { + out = malloc(sizeof(float) * size_t(scaledRegionSize.x) * + size_t(scaledRegionSize.y) * size_t(scaledRegionSize.z)); + upsampleRegion((float *)source, (float *)out, regionSize, scaledRegionSize); + } + else if (voxelType == "double") { + out = malloc(sizeof(double) * size_t(scaledRegionSize.x) * + size_t(scaledRegionSize.y) * size_t(scaledRegionSize.z)); + upsampleRegion((double *)source, (double *)out, regionSize, scaledRegionSize); + } + regionSize = scaledRegionSize; + regionCoords = scaledRegionCoords; + } + return upsampling; + } + void StructuredVolume::buildAccelerator() { // Create instance of volume accelerator. @@ -101,32 +136,10 @@ namespace ospray { Volume::finish(); } - OSPDataType StructuredVolume::getVoxelType() const + OSPDataType StructuredVolume::getVoxelType() { - // Separate out the base type and vector width. - char* kind = (char*)alloca(voxelType.size()); - unsigned int width = 1; - sscanf(voxelType.c_str(), "%[^0-9]%u", kind, &width); - - OSPDataType res = OSP_UNKNOWN; - - // Unsigned 8-bit scalar integer. - if (!strcmp(kind, "uchar") && width == 1) - res = OSP_UCHAR; - - // Unsigned 16-bit scalar integer. - if (!strcmp(kind, "ushort") && width == 1) - res = OSP_USHORT; - - // Single precision scalar floating point. - if (!strcmp(kind, "float") && width == 1) - res = OSP_FLOAT; - - // Double precision scalar floating point. - if (!strcmp(kind, "double") && width == 1) - res = OSP_DOUBLE; - - return res; + return finished ? typeForString(getParamString("voxelType", "unspecified")): + typeForString(voxelType.c_str()); } } // ::ospray diff --git a/ospray/volume/StructuredVolume.h b/ospray/volume/StructuredVolume.h index e3d3608cd6..ac1b2f7740 100644 --- a/ospray/volume/StructuredVolume.h +++ b/ospray/volume/StructuredVolume.h @@ -17,8 +17,8 @@ #pragma once // ospray -#include "ospray/common/tasking/parallel_for.h" -#include "ospray/volume/Volume.h" +#include "common/tasking/parallel_for.h" +#include "volume/Volume.h" // stl #include #include @@ -33,7 +33,8 @@ namespace ospray { type string passed to Volume::createInstance() specifies a particular concrete implementation. This type string must be registered in OSPRay proper, or in a loaded module via - OSP_REGISTER_VOLUME. + OSP_REGISTER_VOLUME. To place the volume in world coordinates, + modify the gridOrigin and gridSpacing to translate and scale the volume. */ class StructuredVolume : public Volume { public: @@ -64,18 +65,28 @@ namespace ospray { //! Complete volume initialization (only on first commit). virtual void finish() override; - //! Get the OSPDataType enum corresponding to the voxel type string. - OSPDataType getVoxelType() const; - #ifndef OSPRAY_VOLUME_VOXELRANGE_IN_APP template void computeVoxelRange(const T* source, const size_t &count); #endif + template + void upsampleRegion(const T *source, T *out, const vec3i ®ionSize, const vec3i &scaledRegionSize); + + /*! Scale up the region we're setting in ospSetRegion. Will return the scaled region size and coordinates + through the regionSize and regionCoords passed for the unscaled region (from ospSetRegion), + and return true if we actually upsampled the data. If we upsample the data the caller + is responsible for calling free on the out data parameter to release the scaled volume data. + */ + bool scaleRegion(const void *source, void *&out, vec3i ®ionSize, vec3i ®ionCoords); + //! build the accelerator - allows child class (data distributed) to avoid //! building.. virtual void buildAccelerator(); + //! Get the OSPDataType enum corresponding to the voxel type string. + OSPDataType getVoxelType(); + //! Volume size in voxels per dimension. vec3i dimensions; @@ -93,6 +104,12 @@ namespace ospray { //! Voxel type. std::string voxelType; + + /*! Scale factor for the volume, mostly for internal use or data scaling benchmarking. + Note that this must be set **before** calling 'ospSetRegion' on the volume as the + scaling is applied in that function. + */ + vec3f scaleFactor; }; // Inlined member functions /////////////////////////////////////////////////// @@ -104,7 +121,7 @@ namespace ospray { { const size_t blockSize = 1000000; int numBlocks = divRoundUp(count, blockSize); - vec2f* blockRange = (vec2f*)alloca(numBlocks*sizeof(vec2f)); + vec2f* blockRange = STACK_BUFFER(vec2f, numBlocks); //NOTE(jda) - shouldn't this be a simple reduction (TBB/OMP)? parallel_for(numBlocks, [&](int taskIndex){ @@ -126,6 +143,17 @@ namespace ospray { } } #endif - + template + void StructuredVolume::upsampleRegion(const T *source, T *out, const vec3i ®ionSize, const vec3i &scaledRegionSize){ + for (size_t z = 0; z < scaledRegionSize.z; ++z){ + parallel_for(scaledRegionSize.x * scaledRegionSize.y, [&](int taskID){ + int x = taskID % scaledRegionSize.x; + int y = taskID / scaledRegionSize.x; + const int idx = static_cast(z / scaleFactor.z) * regionSize.x * regionSize.y + + static_cast(y / scaleFactor.y) * regionSize.x + static_cast(x / scaleFactor.x); + out[z * scaledRegionSize.y * scaledRegionSize.x + y * scaledRegionSize.x + x] = source[idx]; + }); + } + } } // ::ospray diff --git a/ospray/volume/StructuredVolume.ih b/ospray/volume/StructuredVolume.ih index 478bbdd725..215ab66b0b 100644 --- a/ospray/volume/StructuredVolume.ih +++ b/ospray/volume/StructuredVolume.ih @@ -16,8 +16,8 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/volume/Volume.ih" +#include "common/OSPCommon.ih" +#include "volume/Volume.ih" struct GridAccelerator; diff --git a/ospray/volume/StructuredVolume.ispc b/ospray/volume/StructuredVolume.ispc index 4213a073d9..ca9ee1656b 100644 --- a/ospray/volume/StructuredVolume.ispc +++ b/ospray/volume/StructuredVolume.ispc @@ -14,8 +14,8 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/StructuredVolume.ih" -#include "ospray/volume/GridAccelerator.ih" +#include "volume/StructuredVolume.ih" +#include "volume/GridAccelerator.ih" inline varying float StructuredVolume_computeSample(void *uniform _volume, const varying vec3f &worldCoordinates) { diff --git a/ospray/volume/Volume.cpp b/ospray/volume/Volume.cpp index 0069cc6db3..68b28334bc 100644 --- a/ospray/volume/Volume.cpp +++ b/ospray/volume/Volume.cpp @@ -15,11 +15,11 @@ // ======================================================================== // // ospray -#include "ospray/common/Library.h" -#include "ospray/volume/Volume.h" +#include "common/Library.h" +#include "volume/Volume.h" #include "Volume_ispc.h" -#include "ospray/transferFunction/TransferFunction.h" -#include "ospray/common/Data.h" +#include "transferFunction/TransferFunction.h" +#include "common/Data.h" // stl #include diff --git a/ospray/volume/Volume.h b/ospray/volume/Volume.h index ed711d26b3..6f8f885fd5 100644 --- a/ospray/volume/Volume.h +++ b/ospray/volume/Volume.h @@ -16,7 +16,7 @@ #pragma once -#include "ospray/common/Managed.h" +#include "common/Managed.h" /*! \brief Define a function to create an instance of the InternalClass associated with ExternalName. diff --git a/ospray/volume/Volume.ih b/ospray/volume/Volume.ih index c933c84620..55f363f894 100644 --- a/ospray/volume/Volume.ih +++ b/ospray/volume/Volume.ih @@ -16,10 +16,9 @@ #pragma once -#include "ospray/common/OSPCommon.ih" -#include "ospray/common/Ray.ih" -#include "ospray/transferFunction/TransferFunction.ih" -#include "ospray/math/box.ih" +#include "../common/Ray.ih" +#include "../transferFunction/TransferFunction.ih" +#include "../math/box.ih" // #if EXP_DATA_PARALLEL // struct DataParallelInfo { @@ -55,9 +54,6 @@ struct Volume { //! Clipping box for the volume (applies to volume rendering only). An empty clipping box is ignored. uniform box3f volumeClippingBox; - //! Bounding box for the volume in world coordinates. - uniform box3f boundingBox; - //! The value at the given sample location in world coordinates. varying float (*uniform computeSample)(void *uniform _self, const varying vec3f &worldCoordinates); @@ -74,6 +70,9 @@ struct Volume { uniform float *uniform isovalues, uniform int numIsovalues, varying Ray &ray); + + //! Bounding box for the volume in world coordinates. This is an internal derived parameter and not meant to be redefined externally. + uniform box3f boundingBox; }; void Volume_Constructor(Volume *uniform volume, diff --git a/ospray/volume/Volume.ispc b/ospray/volume/Volume.ispc index fd182b9828..f3a5f7c924 100644 --- a/ospray/volume/Volume.ispc +++ b/ospray/volume/Volume.ispc @@ -14,7 +14,7 @@ // limitations under the License. // // ======================================================================== // -#include "ospray/volume/Volume.ih" +#include "volume/Volume.ih" void Volume_Constructor(Volume *uniform self, /*! pointer to the c++-equivalent class of this entity */ diff --git a/scripts/build_gitlab/linux.sh b/scripts/build_gitlab/linux.sh index 86d4d92ed4..04cc7f01e8 100755 --- a/scripts/build_gitlab/linux.sh +++ b/scripts/build_gitlab/linux.sh @@ -21,8 +21,6 @@ cd build cmake \ -D OSPRAY_BUILD_ISA=ALL \ - -D OSPRAY_APPS_PARTICLEVIEWER=ON \ - -D OSPRAY_MODULE_TACHYON=ON \ .. make -j`nproc` diff --git a/scripts/build_gitlab/win.bat b/scripts/build_gitlab/win.bat index e7ccf0a98b..f0709ec108 100755 --- a/scripts/build_gitlab/win.bat +++ b/scripts/build_gitlab/win.bat @@ -21,10 +21,11 @@ md build cd build cmake -L ^ --G "Visual Studio 12 2013 Win64" ^ --T "Intel C++ Compiler 16.0" ^ +-G "%~1" ^ +-T "%~2" ^ -D OSPRAY_BUILD_ISA=ALL ^ -D OSPRAY_BUILD_MIC_SUPPORT=OFF ^ +-D OSPRAY_BUILD_MPI_DEVICE=ON ^ -D OSPRAY_USE_EXTERNAL_EMBREE=ON ^ -D embree_DIR=..\..\embree\lib\cmake\embree-2.9.0 ^ -D USE_IMAGE_MAGICK=OFF ^ diff --git a/scripts/release/linux.sh b/scripts/release/linux.sh index d1e7e42f96..6b0ab33af0 100755 --- a/scripts/release/linux.sh +++ b/scripts/release/linux.sh @@ -94,7 +94,6 @@ rm -rf * # set release and RPM settings cmake \ -D OSPRAY_BUILD_ISA=ALL \ --D OSPRAY_BUILD_ENABLE_KNL=ON \ -D OSPRAY_BUILD_MIC_SUPPORT=OFF \ -D OSPRAY_BUILD_COI_DEVICE=OFF \ -D OSPRAY_BUILD_MPI_DEVICE=OFF \