From 29d04f3f366ee90a71f864214a5835cb8bd8b4fb Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 16 Feb 2022 15:01:13 -0600 Subject: [PATCH 01/78] Update CHANGELOG.md for release v.0.10.0 --- CHANGELOG.md | 40 +++++++++++++++++++++++++++++++++++++++- README.md | 2 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 877531de..25380537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,45 @@ Version History --------------- -### Changes in OSPRay Studio v0.10.0 (devel) +### Changes in OSPRay Studio v0.10.0 + +- Compatible with OSPRay release v2.9.0 + +- Features and Improvements + - UI + - Improved color picker widget so that color value are consistent whether + viewing frame as sRGB or linear. + - Added widget to change file selection on HDRI textures and photometric lights + - Added renderer setting to set and adjust OSPRay's shadowCatcherPlane + - Light + - Add support for new OSPRay `cylinder` light-type + - Photometric intensity distribution now supported on point, quad, and spot + light types. + - Lights in glTF scene are now instanced and can be used with motion blur + - Camera + - Custom support for "panoramic" camera type in glTF files + - All glTF cameras are instanced to allow for motion blur + - Batch + - Improved batch rendering flexibility with params for cameraType, bgColor, + frameStep and denoiser. + - Added final frame denoising to batch mode - only final accumulation is + denoised. + - Added glTF support for OSPRay's new multi-segment deformation motion blur + - Added python bindings for affine3f and affine3f::translate + - Added tasking/scheduling system. Currently used to greatly improve loading + of raw volumes. Will be expanded to other asynchronous operations. + +
+ +- Bug Fixes: + - Fixed lights editor interaction with group lights loaded in a glTF file. UI + now allows correct editing/removal of existing lights and addition new lights + - Added full path name and extension to importer and texture cache entried to + prevent collision with similar files. + - Better handling of failed texture load across glTF and OBJ materials, as well + as HDRI, background texture, and material editor. + - Correctly enable to/from_json for LinearSpace2f texture transform + - Improved saving of multi-layer EXR images ### Changes in OSPRay Studio v0.9.1 diff --git a/README.md b/README.md index 1344e00f..80685e0b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ support. The scenes can then be rendered either with OSPRay's pathtracer or scivis renderer. More information can be found in the [**high-level feature -description**](https://github.com/ospray/ospray_studio/blob/master/FEATURE.md). +description**](https://github.com/ospray/ospray_studio/blob/master/FEATURES.md). ## Building OSPRay Studio From a7dda14ea1f7be5aa7f7b4dfa041a041609e154c Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 16 Feb 2022 17:57:34 -0600 Subject: [PATCH 02/78] Bump devel to v0.11.0 (devel) --- CMakeLists.txt | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b907c9f..f6e8c449 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.15) -project(ospray_studio VERSION 0.10.0 LANGUAGES CXX) +project(ospray_studio VERSION 0.11.0 LANGUAGES CXX) include(GNUInstallDirs) include(ProcessorCount) diff --git a/README.md b/README.md index 80685e0b..d554f006 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OSPRay Studio -This is release v0.10.0 of Intel® OSPRay Studio. It is released under the +This is release v0.11.0 (devel) of Intel® OSPRay Studio. It is released under the Apache 2.0 license. Visit [**OSPRay Studio**](http://www.ospray.org/ospray_studio) From 523c62d8c53d2f4ee38cf9acde2a108088841fa3 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Tue, 22 Feb 2022 10:31:32 +0100 Subject: [PATCH 03/78] print list of cameras with indices and names --- app/Batch.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 748cc384..c1d287bf 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -34,7 +34,7 @@ void BatchContext::start() { std::cerr << "Batch mode\n"; - // load plugins + // load plugins for (auto &p : studioCommon.pluginsToLoad) pluginManager->loadPlugin(p); @@ -42,19 +42,27 @@ void BatchContext::start() std::cout << "...importing files!" << std::endl; refreshRenderer(); refreshScene(true); - for (int cameraIdx = cameraRange.lower; cameraIdx <= cameraRange.upper; - ++cameraIdx) { - resetFileId = true; - bool useCamera = refreshCamera(cameraIdx, true); - if (useCamera) { - render(); - if (fps) { - std::cout << "..rendering animation!" << std::endl; - renderAnimation(); - } else - renderFrame(); - } + if (cameras.size()) + std::cout << "List of imported scene cameras:\n"; + for (int c = 1; c <= cameras.size(); ++c) { + std::cout + << c << ": " + << cameras[c - 1]->child("uniqueCameraName").valueAs() + << std::endl; + } + for (int cameraIdx = cameraRange.lower; cameraIdx <= cameraRange.upper; + ++cameraIdx) { + resetFileId = true; + bool useCamera = refreshCamera(cameraIdx, true); + if (useCamera) { + render(); + if (fps) { + std::cout << "..rendering animation!" << std::endl; + renderAnimation(); + } else + renderFrame(); } + } std::cout << "...finished!" << std::endl; sg::clearAssets(); } @@ -310,8 +318,7 @@ bool BatchContext::refreshCamera(int cameraIdx, bool resetArcball) } } else { - std::cout << "No cameras imported or invalid camera index specified" - << std::endl; + std::cout << "No scene camera is selected." << std::endl; if (optCameraTypeStr != "perspective") { auto optCamera = createNode("camera", "camera_" + optCameraTypeStr); frame->remove("camera"); @@ -322,7 +329,7 @@ bool BatchContext::refreshCamera(int cameraIdx, bool resetArcball) reshape(); // resets aspect - // if imported cameras don't have parent transform then use Arcball properties + // if imported cameras don't have parent transform then use Arcball properties if (!hasParents) useArcball = true; updateCamera(); From b1d3b03168e5296e076c7fce9ebc5e6467991d2a Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 2 Mar 2022 18:56:22 -0600 Subject: [PATCH 04/78] More robust determination of glTF img.name for texture cache --- sg/importer/glTF.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index fd5dd28f..2d0dc0a2 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -1588,16 +1588,18 @@ NodePtr GLTFData::createOSPTexture(const std::string &texParam, // The uri can be a stream of base64-encoded data! If it is not, it // contains the base filename, whereas sometimes the img.name is less // descriptive. - if (img.uri.length() < 256) { + if (img.uri.length() > 0 && img.uri.length() < 256) { // XXX should decode the uri before using it as a filename. Otherwise // FileName::canonical() returns "" for names containing '%20', etc. std::string fileName = FileName(img.uri).canonical(); img.name = fileName != "" ? fileName : img.uri; } - // If the texture comes from a bufferView give it that name. - if (img.bufferView >= 0) - img.name = "texture_" + pad(std::to_string(img.bufferView)); + // Last try, if the texture comes from a bufferView and we have no other name + // give it the _texture_ + if (img.name.empty() && img.bufferView >= 0) + img.name = + fileName.name() + "_texture_" + pad(std::to_string(img.bufferView)); #if 0 DEBUG << pad("", '.', 9) << "image name: |" << img.name << "|\n"; From 6103ac36a92925c4fd27ae543ad45768b12a423d Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 23 Feb 2022 08:25:50 -0600 Subject: [PATCH 05/78] Add VolumetricModel params densityScale and anisotropy --- sg/scene/volume/Volume.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sg/scene/volume/Volume.cpp b/sg/scene/volume/Volume.cpp index 0d82be63..86c89fab 100644 --- a/sg/scene/volume/Volume.cpp +++ b/sg/scene/volume/Volume.cpp @@ -12,6 +12,17 @@ Volume::Volume(const std::string &osp_type) createChild("visible", "bool", true); createChild("filter", "int", "0 = nearest, 100 = trilinear", 0); + createChild("densityScale", + "float", + "makes volumes uniformly thinner or thicker", + 1.f) + .setMinMax(0.f, 1.f); + createChild("anisotropy", + "float", + "anisotropy of the (Henyey-Greenstein) phase function in [-1–1] (path tracer only), default to isotropic scattering", + 0.f) + .setMinMax(-1.f, 1.f); + // All volumes track their valueRange createChild("valueRange", "range1f", range1f(0.f, 1.f)); child("valueRange").setSGOnly(); From 7a0403fa283f290573599059ad029d2093d78d4f Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Thu, 3 Mar 2022 12:19:52 -0600 Subject: [PATCH 06/78] Update volume opacity/density range --- app/widgets/TransferFunctionWidget.cpp | 8 +++++--- sg/scene/volume/Volume.cpp | 11 +++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/widgets/TransferFunctionWidget.cpp b/app/widgets/TransferFunctionWidget.cpp index a026be91..0531070e 100644 --- a/app/widgets/TransferFunctionWidget.cpp +++ b/app/widgets/TransferFunctionWidget.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "TransferFunctionWidget.h" @@ -99,9 +99,11 @@ void TransferFunctionWidget::updateUI() ImGui::Text("Opacity scale"); ImGui::SameLine(); - if (ImGui::SliderFloat("##OpacityScale", &globalOpacityScale, 0.f, 10.f)) { + if (ImGui::SliderFloat("##OpacityScale", &globalOpacityScale, 0.f, 10.f)) tfnChanged = true; - } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("(value range is unbounded, slider is bounded for\n" + "convenience. shift-LMB to enter larger values manually)"); ImGui::Separator(); diff --git a/sg/scene/volume/Volume.cpp b/sg/scene/volume/Volume.cpp index 86c89fab..a34f77c3 100644 --- a/sg/scene/volume/Volume.cpp +++ b/sg/scene/volume/Volume.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Volume.h" @@ -14,12 +14,15 @@ Volume::Volume(const std::string &osp_type) createChild("densityScale", "float", - "makes volumes uniformly thinner or thicker", + "makes volumes uniformly thinner or thicker\n" + "(value range is unbounded, slider is bounded for convenience.\n" + "shift-LMB to enter larger values manually)", 1.f) - .setMinMax(0.f, 1.f); + .setMinMax(0.f, 100.f); createChild("anisotropy", "float", - "anisotropy of the (Henyey-Greenstein) phase function in [-1–1] (path tracer only), default to isotropic scattering", + "anisotropy of the (Henyey-Greenstein) phase function in [-1–1]\n" + "(path tracer only), default to isotropic scattering", 0.f) .setMinMax(-1.f, 1.f); From a4cd758d11f19c2fa4b31ce9e93f3802683ef73d Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Mon, 7 Mar 2022 12:18:53 -0600 Subject: [PATCH 07/78] Enable benchmarking support by default --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6e8c449..3758eafe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright 2009-2021 Intel Corporation +## Copyright 2009-2022 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.15) @@ -67,7 +67,7 @@ find_package(OpenGL 2 REQUIRED) include(glfw) include(ospray) -option(USE_BENCHMARK "Build benchmarking support into ospStudio" OFF) +option(USE_BENCHMARK "Build benchmarking support into ospStudio" ON) if(USE_BENCHMARK) include(benchmark) From 29b119aa3f60e2570cd9fe596c41a511f479098e Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Tue, 8 Mar 2022 16:26:51 +0100 Subject: [PATCH 08/78] use baseColorfactor alpha for opacity --- sg/importer/glTF.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 2d0dc0a2..94584558 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -1121,6 +1121,7 @@ NodePtr GLTFData::createOSPMaterial(const tinygltf::Material &mat) auto ospMat = createNode(matName, "principled"); ospMat->createChild("baseColor", "rgb") = rgb( pbr.baseColorFactor[0], pbr.baseColorFactor[1], pbr.baseColorFactor[2]); + auto alpha = (float)pbr.baseColorFactor[3]; ospMat->createChild("metallic", "float") = (float)pbr.metallicFactor; ospMat->createChild("roughness", "float") = (float)pbr.roughnessFactor; @@ -1134,7 +1135,7 @@ NodePtr GLTFData::createOSPMaterial(const tinygltf::Material &mat) if (mat.alphaMode == "OPAQUE") ospMat->createChild("opacity", "float") = 1.f; else if (mat.alphaMode == "BLEND") - ospMat->createChild("opacity", "float") = 1.f; + ospMat->createChild("opacity", "float") = alpha; else if (mat.alphaMode == "MASK") ospMat->createChild("opacity", "float") = 1.f - (float)mat.alphaCutoff; From b149b61b4685ad5736f706f78f19c4522f31b1d2 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Thu, 3 Mar 2022 17:33:23 +0100 Subject: [PATCH 09/78] parse INTEL_lights_sunsky ext, light templates --- external/tiny_gltf/tiny_gltf.h | 75 ------------------------- sg/importer/glTF.cpp | 100 +++++++++++++++++++++------------ sg/scene/lights/SunSky.cpp | 4 +- 3 files changed, 67 insertions(+), 112 deletions(-) diff --git a/external/tiny_gltf/tiny_gltf.h b/external/tiny_gltf/tiny_gltf.h index 979f3bc6..5242d428 100644 --- a/external/tiny_gltf/tiny_gltf.h +++ b/external/tiny_gltf/tiny_gltf.h @@ -1119,24 +1119,6 @@ struct SpotLight { std::string extensions_json_string; }; -struct SunSkyLight { - std::vector up; - double turbidity; - double albedo; - double horizonExtension; - - SunSkyLight() : turbidity(3.0), albedo(0.3), horizonExtension(0.01) {} - DEFAULT_METHODS(SunSkyLight) - bool operator==(const SunSkyLight &) const; - - ExtensionMap extensions; - Value extras; - - // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. - std::string extras_json_string; - std::string extensions_json_string; -}; - struct Light { std::string name; std::vector color; @@ -1144,7 +1126,6 @@ struct Light { std::string type; double range{0.0}; // 0.0 = inifinite SpotLight spot; - SunSkyLight sunSky; Light() : intensity(1.0), range(0.0) {} DEFAULT_METHODS(Light) @@ -5263,37 +5244,6 @@ static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, return true; } -static bool ParseSunSkyLight(SunSkyLight *light, std::string *err, const json &o, - bool store_original_json_for_extras_and_extensions) { - - ParseNumberArrayProperty(&light->up, err, o, "up", false); - ParseNumberProperty(&light->turbidity, err, o, "turbidity", false); - ParseNumberProperty(&light->albedo, err, o, "albedo", false); - ParseNumberProperty(&light->horizonExtension, err, o, "horizonExtension", false); - - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&light->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - light->extensions_json_string = JsonToString(GetValue(it)); - } - } - { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - light->extras_json_string = JsonToString(GetValue(it)); - } - } - } - - // TODO(syoyo): Validate parameter values. - - return true; -} - static bool ParseOrthographicCamera( OrthographicCamera *camera, std::string *err, const json &o, bool store_original_json_for_extras_and_extensions) { @@ -5469,31 +5419,6 @@ static bool ParseLight(Light *light, std::string *err, const json &o, store_original_json_for_extras_and_extensions)) { return false; } - } else if (light->type == "sunSky") { - json_const_iterator sunSkyIt; - if (!FindMember(o, "sunSky", sunSkyIt)) { - if (err) { - std::stringstream ss; - ss << "sunSky light description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const json &v = GetValue(sunSkyIt); - if (!IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"sunSky\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParseSunSkyLight(&light->sunSky, err, v, - store_original_json_for_extras_and_extensions)) { - return false; - } } ParseStringProperty(&light->name, err, o, "name", false); diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 94584558..c3095e3f 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -68,13 +68,14 @@ struct GLTFData void finalizeSkins(); void createGeometries(); void createCameraTemplates(); - void createLights(); + void createLightTemplates(); void buildScene(); void loadNodeInfo(const int nid, NodePtr sgNode); // load animations AFTER loading scene nodes and their transforms void createAnimations(std::vector &); void applySceneBackground(NodePtr bgXfm); std::vector lights; + std::vector lightTemplates; std::string geomId{""}; private: @@ -306,8 +307,39 @@ void GLTFData::loadNodeInfo(const int nid, NodePtr sgNode) } } -void GLTFData::createLights() +void GLTFData::createLightTemplates() { + // INTEL_lights_sunsky extension + if (model.extensions.find("INTEL_lights_sunsky") != model.extensions.end()) { + auto lightsSunSky = + model.extensions.find("INTEL_lights_sunsky")->second.Get("lights"); + int arrayLen = 0; + if (lightsSunSky.IsArray()) + arrayLen = lightsSunSky.ArrayLen(); + + for (int i = 0; i < arrayLen; i++) { + auto lightName = "sunSkyLight_" + std::to_string(i); + auto sunSky = lightsSunSky.Get(i); + float intensity = sunSky.Get("intensity").Get(); + float elevation = (float)sunSky.Get("elevation").Get() + * (180.f / (float)pi); // degrees + float azimuth = (float)sunSky.Get("azimuth").Get() + * (180.f / (float)pi); // degrees + float turbidity = sunSky.Get("turbidity").Get(); + float albedo = sunSky.Get("albedo").Get(); + float horizonExtension = sunSky.Get("horizonExtension").Get(); + auto sunSkyLight = createNode(lightName, "sunSky"); + sunSkyLight->child("intensity") = intensity; + sunSkyLight->child("elevation") = elevation; + sunSkyLight->child("azimuth") = azimuth; + sunSkyLight->child("turbidity") = turbidity; + sunSkyLight->child("albedo") = albedo; + sunSkyLight->child("horizonExtension") = horizonExtension; + + lightTemplates.push_back(sunSkyLight); + } + } + // KHR_lights_punctual for (auto &l : model.lights) { static auto nLight = 0; auto lightName = @@ -330,27 +362,6 @@ void GLTFData::createLights() newLight = createNode(lightName, l.type); auto hdrFileName = l.extras.Get("map").Get(); newLight->child("filename") = fileName.path() + hdrFileName; - } else if (l.type == "sunSky") { - newLight = createNode(lightName, l.type); - if (l.sunSky.up.size()) { - auto up = vec3f{(float)l.sunSky.up[0], - (float)l.sunSky.up[1], - (float)l.sunSky.up[2]}; - newLight->child("up") = up; - } - if (l.sunSky.turbidity) { - auto turbidity = (float)l.sunSky.turbidity; - newLight->child("turbidity") = turbidity; - } - if (l.sunSky.albedo) { - auto albedo = (float)l.sunSky.albedo; - newLight->child("albedo") = albedo; - } - - if (l.sunSky.horizonExtension) { - auto horizonExtension = (float)l.sunSky.horizonExtension; - newLight->child("horizonExtension") = horizonExtension; - } } else newLight = createNode(lightName, l.type); @@ -362,8 +373,7 @@ void GLTFData::createLights() // Color is optional, default:[1.0,1.0,1.0] auto lightColor = rgb(1.f); if (!l.color.empty()) - lightColor = - rgb{(float)l.color[0], (float)l.color[1], (float)l.color[2]}; + lightColor = rgb{(float)l.color[0], (float)l.color[1], (float)l.color[2]}; newLight->child("color") = lightColor; if (l.intensity) @@ -373,7 +383,7 @@ void GLTFData::createLights() // TODO:: Address extras property on lights - lights.push_back(newLight); + lightTemplates.push_back(newLight); } } @@ -759,14 +769,34 @@ void GLTFData::visitNode(NodePtr sgNode, if (n.extensions.find("BIT_node_info") != n.extensions.end()) loadNodeInfo(nid, sgNode); - // KHR_lights_punctual extension info on nodes - if (n.extensions.find("KHR_lights_punctual") != n.extensions.end()) { - // defines light orientation - auto lightIndex = n.extensions.find("KHR_lights_punctual") - ->second.Get("light") - .GetNumberAsInt(); - auto lightNode = lights[lightIndex]; - sgNode->add(lightNode); + tinygltf::Value lightJson; + // instantiate lights for extensions: INTEL_lights_sunsky and + // KHR_lights_punctual + if (n.extensions.find("KHR_lights_punctual") != n.extensions.end()) + lightJson = n.extensions.find("KHR_lights_punctual")->second.Get("light"); + else if (n.extensions.find("INTEL_lights_sunsky") != n.extensions.end()) + lightJson = n.extensions.find("INTEL_lights_sunsky")->second.Get("light"); + + if (lightJson.IsInt()) { + auto lightIdx = lightJson.GetNumberAsInt(); + auto lightTemplate = lightTemplates[lightIdx]; + static int lightCounter = 0; + // instantiate SG light nodes + auto uniqueLightName = n.name != "" + ? n.name + std::to_string(lightCounter++) + : lightTemplate->name() + std::to_string(lightCounter++); + auto light = createNode(uniqueLightName, lightTemplate->subType()); + for (auto &c : lightTemplate->children()) { + if (light->hasChild(c.first)) + light->child(c.first) = c.second->value(); + else { + light->createChild(c.first, c.second->subType(), c.second->value()); + if (lightTemplate->child(c.first).sgOnly()) + light->child(c.first).setSGOnly(); + } + } + lights.push_back(light); + sgNode->add(light); } // recursively process children nodes @@ -1768,7 +1798,7 @@ void glTFImporter::importScene() return; gltf.createMaterials(); - gltf.createLights(); + gltf.createLightTemplates(); if (importCameras) gltf.createCameraTemplates(); diff --git a/sg/scene/lights/SunSky.cpp b/sg/scene/lights/SunSky.cpp index abdb7cb8..893d21fd 100644 --- a/sg/scene/lights/SunSky.cpp +++ b/sg/scene/lights/SunSky.cpp @@ -26,7 +26,7 @@ SunSky::SunSky() : Light("sunSky") createChild("right", "vec3f", "right-pointing vector, such that up cross right = direction", - vec3f(0.f, 0.f, 1.f)); + vec3f(0.f, 0.f, -1.f)); createChild("azimuth", "float", "sun's angular distance from North", 0.f); createChild( "elevation", "float", "sun's angle relative to the horizon", 90.f); @@ -50,7 +50,7 @@ SunSky::SunSky() : Light("sunSky") // Set reasonable limits, this will set slider range child("albedo").setMinMax(0.f, 1.f); - child("azimuth").setMinMax(-180.f, 180.f); + child("azimuth").setMinMax(0.f, 360.f); child("elevation").setMinMax(-90.f, 90.f); child("turbidity").setMinMax(0.f, 10.f); child("horizonExtension").setMinMax(0.f, 1.f); From dd534c1e22081e05860aefa3c90996a2c966515e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Wed, 9 Mar 2022 11:24:57 +0100 Subject: [PATCH 10/78] Make sure imported quaternions are unit-size --- sg/importer/glTF.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index c3095e3f..15395eae 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -240,10 +240,10 @@ void GLTFData::applySceneBackground(NodePtr bgXfm) if (background.Has("rotation")) { const auto &r = background.Get("rotation").Get(); auto &rot = bgXfm->nodeAs()->child("rotation"); - rot = quaternionf(r[3].Get(), + rot = normalize(quaternionf(r[3].Get(), r[0].Get(), r[1].Get(), - r[2].Get()); + r[2].Get())); } lights.push_back(bgNode); @@ -627,7 +627,7 @@ void GLTFData::createAnimations(std::vector &animations) t->values.reserve(value.size()); for (size_t i = 0; i < value.size(); ++i) { const auto &v = value[i]; - t->values.push_back(quaternionf(v.w, v.x, v.y, v.z)); + t->values.push_back(normalize(quaternionf(v.w, v.x, v.y, v.z))); } } else if (c.target_path == "weights") { WARN << "animating weights of morph targets not implemented yet" @@ -822,7 +822,8 @@ void GLTFData::applyNodeTransform(NodePtr xfmNode, const tinygltf::Node &n) } if (!n.rotation.empty()) { const auto &r = n.rotation; - xfmNode->child("rotation") = quaternionf(r[3], r[0], r[1], r[2]); + xfmNode->child("rotation") = + normalize(quaternionf(r[3], r[0], r[1], r[2])); } if (!n.translation.empty()) { const auto &t = n.translation; From 4e33827bdd56641bcb8dd7d6597797f329edd1b4 Mon Sep 17 00:00:00 2001 From: Tanner Hobson Date: Sat, 12 Mar 2022 19:42:13 +0000 Subject: [PATCH 11/78] Fix "gridOrigin" typo in CLI --- app/ospStudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ospStudio.cpp b/app/ospStudio.cpp index 2a8f7de9..94d3e212 100644 --- a/app/ospStudio.cpp +++ b/app/ospStudio.cpp @@ -134,7 +134,7 @@ void StudioContext::addToCommandLine(std::shared_ptr app) { "--gridOrigin", [&](const std::vector val) { auto gridOrigin = vec3f(std::stof(val[0]), std::stof(val[1]), std::stof(val[2])); - volumeParams->createChild("gridSpacing", "vec3f", gridOrigin); + volumeParams->createChild("gridOrigin", "vec3f", gridOrigin); return true; }, "Set the grid origin for imported volumes" From e295e7a82917e2a16ec67ff9a768a090e6760948 Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Mon, 14 Mar 2022 11:47:29 -0400 Subject: [PATCH 12/78] Fix CMake support for draco --- cmake/draco.cmake | 14 +++++++++++++- sg/CMakeLists.txt | 6 ++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cmake/draco.cmake b/cmake/draco.cmake index 93433552..c5028cba 100644 --- a/cmake/draco.cmake +++ b/cmake/draco.cmake @@ -53,7 +53,19 @@ if(NOT "${draco_FOUND}") draco URL "https://github.com/google/draco/archive/refs/tags/${DRACO_VERSION}.${_ARCHIVE_EXT}" ) - FetchContent_MakeAvailable(draco) + ## Bypass FetchContent_MakeAvailable() shortcut to disable install + FetchContent_GetProperties(draco) + if(NOT draco_POPULATED) + FetchContent_Populate(draco) + ## the subdir will still be built since targets depend on it, but it won't be installed + add_subdirectory(${draco_SOURCE_DIR} ${draco_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + ## On Windows, the target is always 'draco' regardless of whether it builds a static or a shared library. + ## On non-Windows, both static AND shared libraries can be built, so we explicitly choose the static target. + if(NOT MSVC) + add_library(draco ALIAS draco_static) + endif() unset(${_ARCHIVE_EXT}) diff --git a/sg/CMakeLists.txt b/sg/CMakeLists.txt index 6e0d38d2..283a619a 100644 --- a/sg/CMakeLists.txt +++ b/sg/CMakeLists.txt @@ -224,10 +224,8 @@ endif() if (ENABLE_GLTF_DRACO) target_compile_definitions(ospray_sg PRIVATE -DTINYGLTF_ENABLE_DRACO) target_include_directories(ospray_sg PRIVATE ${draco_SOURCE_DIR}/src) - # draco_features.h get placed just under the main build dir ??? - target_include_directories(ospray_sg PRIVATE $) - # libdraco.a isn't found otherwise ??? - target_link_libraries(ospray_sg PRIVATE ${CMAKE_BINARY_DIR}/libdraco.a) + target_include_directories(ospray_sg PRIVATE $) + target_link_libraries(ospray_sg PRIVATE draco) endif() ## Version header ## From f8196933f9b41d6a074038e54f18f6afafdf1701 Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Mon, 14 Mar 2022 13:08:26 -0400 Subject: [PATCH 13/78] Fix CMake support for benchmark --- cmake/benchmark.cmake | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake index a61fbd0e..73e7a620 100644 --- a/cmake/benchmark.cmake +++ b/cmake/benchmark.cmake @@ -31,12 +31,16 @@ if(NOT "${benchmark_FOUND}") benchmark URL "https://github.com/google/benchmark/archive/refs/tags/v${BENCHMARK_VERSION}.${_BENCHMARK_ARCHIVE_EXT}" ) - ## Bypass FetchContent_MakeAvailable() shortcut to disable install - FetchContent_GetProperties(benchmark) - if(NOT benchmark_POPULATED) - FetchContent_Populate(benchmark) - ## the subdir will still be built since targets depend on it, but it won't be installed - add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR} EXCLUDE_FROM_ALL) + + if("${USE_BENCHMARK}") + FetchContent_MakeAvailable(benchmark) + else() + ## Bypass FetchContent_MakeAvailable() shortcut to disable install + if(NOT benchmark_POPULATED) + FetchContent_Populate(benchmark) + ## the subdir will still be built since targets depend on it, but it won't be installed + add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() endif() endif() From eb0eccfadb1ef2e1a3b82183af59369d3f41e34d Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Mon, 14 Mar 2022 17:29:26 -0500 Subject: [PATCH 14/78] Implement KHR_materials_emissive_strength --- sg/importer/glTF.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 15395eae..01989a6e 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -1568,9 +1568,26 @@ NodePtr GLTFData::createOSPMaterial(const tinygltf::Material &mat) auto ospMat = createNode(matName, "luminous"); if (emissiveColor != rgb(0.f)) { + // Material Extensions + const auto &exts = mat.extensions; + + float intensity = 1.f; + + // KHR_materials_emissive_strength + if (exts.find("KHR_materials_emissive_strength") != exts.end()) { + // (Not yet ratified) + // https://github.com/KhronosGroup/glTF/tree/KHR_materials_emissive_strength/extensions/2.0/Khronos/KHR_materials_emissive_strength + auto params = exts.find("KHR_materials_emissive_strength")->second; + + // default: 1.0 + intensity = 1.f; + if (params.Has("emissiveStrength")) { + intensity = (float)params.Get("emissiveStrength").Get(); + } + } + ospMat->createChild("color", "rgb") = emissiveColor; - ospMat->createChild("intensity", "float") = - 20.f; // XXX what's good default intensity? + ospMat->createChild("intensity", "float") = intensity; // Already checked for constant color above. if (mat.emissiveTexture.index != -1) { From 11a986101bbbb215071f0f2990560211d130fe81 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 11 Mar 2022 17:54:20 -0600 Subject: [PATCH 15/78] Remove optional Animation. Always load scenes with animation enabled --- app/Batch.cpp | 3 +- app/MainWindow.cpp | 52 ++++++++++++++++----------------- app/widgets/AnimationWidget.cpp | 8 ++++- app/widgets/AnimationWidget.h | 4 ++- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index c1d287bf..dc378638 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -537,8 +537,7 @@ void BatchContext::importFiles(sg::NodePtr world) { importedModels = createNode("importXfm", "transform"); frame->child("world").add(importedModels); - if (fps) - animationManager = std::shared_ptr(new AnimationManager); + animationManager = std::shared_ptr(new AnimationManager); for (auto file : filesToImport) { try { diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 7ec6f875..abc54c73 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -1128,11 +1128,6 @@ void MainWindow::refreshScene(bool resetCam) } void MainWindow::addToCommandLine(std::shared_ptr app) { - app->add_flag( - "--animate", - optAnimate, - "enable loading glTF animations" - ); } bool MainWindow::parseCommandLine() @@ -1170,8 +1165,7 @@ bool MainWindow::parseCommandLine() void MainWindow::importFiles(sg::NodePtr world) { std::vector cameras; - if (optAnimate) - animationManager = std::shared_ptr(new AnimationManager); + animationManager = std::shared_ptr(new AnimationManager); for (auto file : filesToImport) { try { @@ -1201,8 +1195,7 @@ void MainWindow::importFiles(sg::NodePtr world) importer->setLightsManager(lightsManager); importer->setArguments(studioCommon.argc, (char**)studioCommon.argv); importer->setScheduler(scheduler); - if (animationManager) - importer->setAnimationList(animationManager->getAnimations()); + importer->setAnimationList(animationManager->getAnimations()); if (optInstanceConfig == "dynamic") importer->setInstanceConfiguration( sg::InstanceConfiguration::DYNAMIC); @@ -1239,12 +1232,7 @@ void MainWindow::importFiles(sg::NodePtr world) } filesToImport.clear(); - if (animationManager) { - animationManager->init(); - animationWidget = std::shared_ptr( - new AnimationWidget("Animation Controls", animationManager)); - registerImGuiCallback([&]() { animationWidget->addAnimationUI(); }); - } + animationManager->init(); if (cameras.size() > 0) { auto mainCamera = frame->child("camera").nodeAs(); @@ -1326,13 +1314,9 @@ void MainWindow::buildMainMenuFile() static bool showImportFileBrowser = false; if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Import ...", nullptr)) { - showImportFileBrowser = true; - optAnimate = false; - } else if (ImGui::MenuItem("Import and animate ...", nullptr)) { + if (ImGui::MenuItem("Import ...", nullptr)) + showImportFileBrowser = true; - optAnimate = true; - } if (ImGui::BeginMenu("Demo Scene")) { for (size_t i = 0; i < g_scenes.size(); ++i) { if (ImGui::MenuItem(g_scenes[i].c_str(), nullptr)) { @@ -1470,9 +1454,10 @@ void MainWindow::buildMainMenuEdit() frame->waitOnFrame(); frame->remove("world"); lightsManager->clear(); + animationManager->getAnimations().clear(); + animationManager->getTimeRange() = range1f{empty}; if (animationWidget) { animationWidget.reset(); - registerImGuiCallback(nullptr); } // TODO: lights caching to avoid complete re-importing after clearing @@ -1527,11 +1512,6 @@ void MainWindow::buildMainMenuView() } } - if (ImGui::MenuItem("Keyframes...", "", nullptr)) - showKeyframes = true; - if (ImGui::MenuItem("Snapshots...", "", nullptr)) - showSnapshots = true; - ImGui::Text("Camera Movement Speed:"); ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); ImGui::SliderFloat("Speed##camMov", &maxMoveSpeed, 0.1f, 5.0f); @@ -1541,6 +1521,20 @@ void MainWindow::buildMainMenuView() ImGui::Separator(); + if (ImGui::MenuItem("Animation Controls...", "", nullptr)) { + if (!animationWidget) + animationWidget = std::shared_ptr( + new AnimationWidget("Animation Controls", animationManager)); + animationWidget->showUI = true; + } + + if (ImGui::MenuItem("Keyframes...", "", nullptr)) + showKeyframes = true; + if (ImGui::MenuItem("Snapshots...", "", nullptr)) + showSnapshots = true; + + ImGui::Separator(); + // Renderer stuff ////////////////////////////////////////////////// if (ImGui::MenuItem("Renderer...")) @@ -1727,6 +1721,10 @@ void MainWindow::buildWindows() buildWindowTransformEditor(); if (showRenderingStats) buildWindowRenderingStats(); + + // Show the animation widget + if (animationWidget && animationWidget->showUI) + animationWidget->addAnimationUI(); } void MainWindow::buildWindowRendererEditor() diff --git a/app/widgets/AnimationWidget.cpp b/app/widgets/AnimationWidget.cpp index 6d1ba984..21da390c 100644 --- a/app/widgets/AnimationWidget.cpp +++ b/app/widgets/AnimationWidget.cpp @@ -42,7 +42,13 @@ void AnimationWidget::addAnimationUI() { auto &timeRange = animationManager->getTimeRange(); auto &animations = animationManager->getAnimations(); - ImGui::Begin(name.c_str()); + ImGui::Begin(name.c_str(), &showUI); + + if (animationManager->getAnimations().empty()) { + ImGui::Text("== No animated objects in the scene =="); + ImGui::End(); + return; + } if (ImGui::Button(play ? "Pause" : "Play ")) { play = !play; diff --git a/app/widgets/AnimationWidget.h b/app/widgets/AnimationWidget.h index 69de94f2..9d8088eb 100644 --- a/app/widgets/AnimationWidget.h +++ b/app/widgets/AnimationWidget.h @@ -1,4 +1,4 @@ -// Copyright 2021 Intel Corporation +// Copyright 2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -18,6 +18,8 @@ class AnimationWidget return shutter; } + bool showUI{true}; + private: bool play{false}; bool loop{true}; From e0c1c8cba566f2068ceb5740c960cafe38141ca3 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Tue, 15 Mar 2022 18:26:55 -0500 Subject: [PATCH 16/78] Fix animation start time and allow playback without UI showing - Set animation start time correctly - Add hotkey to toggle animation playback --- app/AnimationManager.cpp | 2 ++ app/Batch.cpp | 23 ++++++++++++----------- app/MainWindow.cpp | 31 +++++++++++++++++-------------- app/widgets/AnimationWidget.cpp | 8 ++++++-- app/widgets/AnimationWidget.h | 29 ++++++++++++++++++++++++++--- 5 files changed, 63 insertions(+), 30 deletions(-) diff --git a/app/AnimationManager.cpp b/app/AnimationManager.cpp index 66b0748d..e1b7f2e2 100644 --- a/app/AnimationManager.cpp +++ b/app/AnimationManager.cpp @@ -8,6 +8,8 @@ void AnimationManager::init() { for (auto &a : animations) timeRange.extend(a.timeRange); + + update(timeRange.lower); } void AnimationManager::update(const float time, const float shutter) diff --git a/app/Batch.cpp b/app/Batch.cpp index dc378638..6415ad2a 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -550,6 +550,13 @@ void BatchContext::importFiles(sg::NodePtr world) auto importer = sg::getImporter(world, file); if (importer) { + if (volumeParams->children().size() > 0) { + auto vp = importer->getVolumeParams(); + for (auto &c : volumeParams->children()) { + vp->remove(c.first); + vp->add(c.second); + } + } // Could be any type of importer. Need to pass the MaterialRegistry, // importer will use what it needs. importer->setFb(frame->childAs("framebuffer")); @@ -558,15 +565,7 @@ void BatchContext::importFiles(sg::NodePtr world) importer->setLightsManager(lightsManager); importer->setArguments(studioCommon.argc, (char**)studioCommon.argv); importer->setScheduler(scheduler); - if (volumeParams->children().size() > 0) { - auto vp = importer->getVolumeParams(); - for (auto &c : volumeParams->children()) { - vp->remove(c.first); - vp->add(c.second); - } - } - if (animationManager) - importer->setAnimationList(animationManager->getAnimations()); + importer->setAnimationList(animationManager->getAnimations()); if (optInstanceConfig == "dynamic") importer->setInstanceConfiguration( sg::InstanceConfiguration::DYNAMIC); @@ -576,6 +575,7 @@ void BatchContext::importFiles(sg::NodePtr world) else if (optInstanceConfig == "robust") importer->setInstanceConfiguration( sg::InstanceConfiguration::ROBUST); + importer->importScene(); } } @@ -625,6 +625,7 @@ void BatchContext::importFiles(sg::NodePtr world) } filesToImport.clear(); - if (animationManager) - animationManager->init(); + + // Initializes time range for newly imported models + animationManager->init(); } diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index abc54c73..04b93bf8 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -184,6 +184,11 @@ MainWindow::MainWindow(StudioCommon &_common) optSPP = 1; // Default SamplesPerPixel in interactive mode is one. + // Always create animationManager and animationWidget + animationManager = std::shared_ptr(new AnimationManager); + animationWidget = std::shared_ptr( + new AnimationWidget("Animation Controls", animationManager)); + activeWindow = this; glfwSetErrorCallback(error_callback); @@ -318,6 +323,7 @@ MainWindow::MainWindow(StudioCommon &_common) activeWindow->cameraStack, g_camPathSpeed * 0.01); } } + activeWindow->animationWidget->togglePlay(); break; case GLFW_KEY_EQUAL: activeWindow->pushLookMark(); @@ -799,6 +805,10 @@ void MainWindow::display() updateCamera(); } + // Update animation controller if playing + if (animationWidget->isPlaying()) + animationWidget->update(); + if (g_animatingPath) { static int framesPaused = 0; CameraState current = g_camPath[g_camCurrentPathIndex]; @@ -1165,7 +1175,6 @@ bool MainWindow::parseCommandLine() void MainWindow::importFiles(sg::NodePtr world) { std::vector cameras; - animationManager = std::shared_ptr(new AnimationManager); for (auto file : filesToImport) { try { @@ -1232,7 +1241,8 @@ void MainWindow::importFiles(sg::NodePtr world) } filesToImport.clear(); - animationManager->init(); + // Initializes time range for newly imported models + animationWidget->init(); if (cameras.size() > 0) { auto mainCamera = frame->child("camera").nodeAs(); @@ -1456,9 +1466,7 @@ void MainWindow::buildMainMenuEdit() lightsManager->clear(); animationManager->getAnimations().clear(); animationManager->getTimeRange() = range1f{empty}; - if (animationWidget) { - animationWidget.reset(); - } + animationWidget.reset(); // TODO: lights caching to avoid complete re-importing after clearing sg::clearAssets(); @@ -1521,12 +1529,8 @@ void MainWindow::buildMainMenuView() ImGui::Separator(); - if (ImGui::MenuItem("Animation Controls...", "", nullptr)) { - if (!animationWidget) - animationWidget = std::shared_ptr( - new AnimationWidget("Animation Controls", animationManager)); - animationWidget->showUI = true; - } + if (ImGui::MenuItem("Animation Controls...", "", nullptr)) + animationWidget->setShowUI(); if (ImGui::MenuItem("Keyframes...", "", nullptr)) showKeyframes = true; @@ -1722,9 +1726,8 @@ void MainWindow::buildWindows() if (showRenderingStats) buildWindowRenderingStats(); - // Show the animation widget - if (animationWidget && animationWidget->showUI) - animationWidget->addAnimationUI(); + // Add the animation widget's UI + animationWidget->addUI(); } void MainWindow::buildWindowRendererEditor() diff --git a/app/widgets/AnimationWidget.cpp b/app/widgets/AnimationWidget.cpp index 21da390c..1b7d9270 100644 --- a/app/widgets/AnimationWidget.cpp +++ b/app/widgets/AnimationWidget.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 Intel Corporation +// Copyright 2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "AnimationWidget.h" @@ -38,10 +38,14 @@ void AnimationWidget::update() } // update UI and process any UI events -void AnimationWidget::addAnimationUI() +void AnimationWidget::addUI() { + if (!showUI) + return; + auto &timeRange = animationManager->getTimeRange(); auto &animations = animationManager->getAnimations(); + ImGui::Begin(name.c_str(), &showUI); if (animationManager->getAnimations().empty()) { diff --git a/app/widgets/AnimationWidget.h b/app/widgets/AnimationWidget.h index 9d8088eb..a15cc6d7 100644 --- a/app/widgets/AnimationWidget.h +++ b/app/widgets/AnimationWidget.h @@ -11,16 +11,40 @@ class AnimationWidget AnimationWidget( std::string name, std::shared_ptr animationManager); ~AnimationWidget(); - void addAnimationUI(); + void addUI(); + void update(); float getShutter() { return shutter; } - bool showUI{true}; + // Initialize widget starting time based on animations it controls + void init() + { + animationManager->init(); + time = animationManager->getTimeRange().lower; + } + + void setShowUI() + { + showUI = true; + } + + void togglePlay() + { + play = !play; + lastUpdated = std::chrono::system_clock::now(); + } + + bool isPlaying() + { + return play; + } + private: + bool showUI{false}; bool play{false}; bool loop{true}; float speedup{1.0f}; @@ -29,5 +53,4 @@ class AnimationWidget std::chrono::time_point lastUpdated; float time{0.0f}; float shutter{0.0f}; - void update(); }; From 2f383c595e8f1c2c5abf46c67f33539fede00d46 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 18 Mar 2022 12:39:11 -0500 Subject: [PATCH 17/78] Add menu option for Quit to make it consistent to exit application - Addresses a customer suggestion --- app/MainWindow.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 04b93bf8..786a8f98 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -292,8 +292,7 @@ MainWindow::MainWindow(StudioCommon &_common) case GLFW_KEY_Q: { auto showMode = rkcommon::utility::getEnvVar("OSPSTUDIO_SHOW_MODE"); - // XXX Invoke the "Jim-Q" key, make it more difficult to exit - // by mistake. + // Enforce "ctrl-Q" to make it more difficult to exit by mistake. if (showMode && mod != GLFW_MOD_CONTROL) std::cout << "Use ctrl-Q to exit\n"; else @@ -1405,6 +1404,20 @@ void MainWindow::buildMainMenuFile() ImGui::EndMenu(); } + + ImGui::Separator(); + // Remove Quit option if in "show mode" to make it more difficult to exit + // by mistake. + auto showMode = + rkcommon::utility::getEnvVar("OSPSTUDIO_SHOW_MODE"); + if (showMode) { + ImGui::TextColored( + ImVec4(.8f, .2f, .2f, 1.f), "ShowMode, use ctrl-Q to exit"); + } else { + if (ImGui::MenuItem("Quit", "Alt+F4")) + g_quitNextFrame = true; + } + ImGui::EndMenu(); } From 0464e821e2b7c355317cd93090c2bd1ddd1166e3 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Mon, 28 Feb 2022 12:16:21 +0100 Subject: [PATCH 18/78] convert euler angles to quaternion --- sg/Math.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sg/Math.h b/sg/Math.h index c0781290..e2cb4e33 100644 --- a/sg/Math.h +++ b/sg/Math.h @@ -8,6 +8,24 @@ namespace ospray { namespace sg { +// convert euler angles(in radians) to quaternion +inline quaternionf toQuaternion(const vec3f &rotation) +{ + float cy = cos(rotation[2] * 0.5); + float sy = sin(rotation[2] * 0.5); + float cp = cos(rotation[1] * 0.5); + float sp = sin(rotation[1] * 0.5); + float cr = cos(rotation[0] * 0.5); + float sr = sin(rotation[0] * 0.5); + + auto w = cr * cp * cy + sr * sp * sy; + auto x = sr * cp * cy - cr * sp * sy; + auto y = cr * sp * cy + sr * cp * sy; + auto z = cr * cp * sy - sr * sp * cy; + + return quaternionf(w, x, y, z); +} + inline quaternionf getRotationQuaternion(const LinearSpace3f &rot) { float t = rot.vx[0] + rot.vy[1] + rot.vz[2]; From 11d4cdf4a0dd8241f1b1f679f588ddba7dae27ec Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Mon, 21 Mar 2022 19:09:31 +0100 Subject: [PATCH 19/78] make importers unique for a basename --- sg/importer/Importer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sg/importer/Importer.h b/sg/importer/Importer.h index 5188430f..082a7559 100644 --- a/sg/importer/Importer.h +++ b/sg/importer/Importer.h @@ -157,7 +157,8 @@ inline std::shared_ptr getImporter( return nullptr; } std::string importer = fnd->second; - std::string nodeName = baseName + "_importer"; + static int num = 0; + std::string nodeName = baseName + "_" + std::to_string(num++) + "_importer"; if (cat.find(fullName) != cat.end()) { // Existing import, instance it! From 380f32236fc27da86176cfe97a75d022686d720f Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Mon, 21 Mar 2022 19:25:47 +0100 Subject: [PATCH 20/78] remove aspect check --- sg/importer/glTF.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 01989a6e..f7fa6637 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -425,9 +425,7 @@ void GLTFData::createCameraTemplates() sgCamera->child("fovy") = fovy; sgCamera->child("nearClip") = (float)m.perspective.znear; - if (m.perspective.aspectRatio > 0) - sgCamera->child( - "aspect") = (float)m.perspective.aspectRatio; + sgCamera->child("aspect") = (float)m.perspective.aspectRatio; } else if (m.type == "panoramic") { // custom extension sgCamera = createNode("camera", "camera_panoramic"); From 6d460cf24d9a3efa8bf12f546ab2a1799a417d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Thu, 24 Mar 2022 13:51:59 +0100 Subject: [PATCH 21/78] Fix skinning - use importer root node for skins without skeleton root - do skinning in post traversal to use most recent bone transforms --- sg/importer/glTF.cpp | 2 ++ sg/visitors/RenderScene.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index f7fa6637..e3b87ba0 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -692,6 +692,8 @@ void GLTFData::buildScene() if (n.skin != -1) { if (model.skins[n.skin].skeleton != -1) targetNode = sceneNodes[model.skins[n.skin].skeleton]; + else + targetNode = rootNode; for (auto &m : ospMesh) { const auto &geom = m->nodeAs(); geom->skin = skins[n.skin]; diff --git a/sg/visitors/RenderScene.h b/sg/visitors/RenderScene.h index 3c258e6c..94ac0cac 100644 --- a/sg/visitors/RenderScene.h +++ b/sg/visitors/RenderScene.h @@ -99,9 +99,6 @@ namespace ospray { setTextureVolume = true; current.textures.push_back(node.valueAs()); break; - case NodeType::GEOMETRY: - createGeometry(node); - break; case NodeType::VOLUME: createVolume(node); traverseChildren = false; @@ -194,6 +191,9 @@ namespace ospray { case NodeType::TRANSFER_FUNCTION: tfns.pop(); break; + case NodeType::GEOMETRY: + createGeometry(node); + break; case NodeType::LIGHT: { // Only lights marked as "inGroup" belong in a group lights list, others // have been put on the world list. From 8562d45381e4bb134ceede8ba2872558fd27ad84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20G=C3=BCnther?= Date: Fri, 25 Mar 2022 10:30:22 +0100 Subject: [PATCH 22/78] Fallback for non-normalized weigths --- sg/importer/glTF.cpp | 4 ++++ sg/scene/geometry/Geometry.cpp | 34 +++++++++++++++++++++++++++++++++- sg/scene/geometry/Geometry.h | 5 +++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index e3b87ba0..0bc41254 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -1065,6 +1065,10 @@ NodePtr GLTFData::createOSPMesh( } else WARN << "invalid WEIGHTS_" << set << std::endl; } + + if (ospGeom->checkAndNormalizeWeights()) + WARN << "non-normalized weights\n"; + } else if (ospGeom->subType() == "geometry_spheres") { // Positions: vec3f Accessor pos_accessor( diff --git a/sg/scene/geometry/Geometry.cpp b/sg/scene/geometry/Geometry.cpp index 1791d8af..b573c6f3 100644 --- a/sg/scene/geometry/Geometry.cpp +++ b/sg/scene/geometry/Geometry.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Geometry.h" @@ -68,5 +68,37 @@ void Geometry::postCommit() group->commit(); } +bool Geometry::checkAndNormalizeWeights() +{ + bool warn = false; + if (!weightsPerVertex) + return warn; + + const float eps = 2e-7f * weightsPerVertex; // glTF validator threshold + for (float *w = weights.data(); w < &weights[weights.size()];) { + float sum = 0.0f; + for (size_t i = 0; i < weightsPerVertex; ++i, ++w) { + if (*w < 0.0f) { // clamp negative weights + warn = true; + *w = 0.0f; + } + sum += *w; + } + if (std::abs(sum - 1.0f) > eps) { // normalize + warn = true; + if (sum == 0.0f) // handle all-zero weigths somehow gracefully + *(w-weightsPerVertex) = 1.0f; + else { + const float scale = 1.0f / sum; + w -= weightsPerVertex; + for (size_t i = 0; i < weightsPerVertex; ++i, ++w) + *w *= scale; + } + } + } + + return warn; +} + } // namespace sg } // namespace ospray diff --git a/sg/scene/geometry/Geometry.h b/sg/scene/geometry/Geometry.h index aedef9bd..67b3e230 100644 --- a/sg/scene/geometry/Geometry.h +++ b/sg/scene/geometry/Geometry.h @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -28,7 +28,8 @@ struct OSPSG_INTERFACE Geometry virtual void postCommit() override; - // skinning info + // skinning + bool checkAndNormalizeWeights(); SkinPtr skin; NodePtr skeletonRoot; size_t weightsPerVertex{0}; From 7e0e4f1584fd8c0b2aa0f39b204b8b9895879c10 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Wed, 30 Mar 2022 12:15:05 +0200 Subject: [PATCH 23/78] instance importer uniquely without static num --- sg/importer/Importer.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sg/importer/Importer.h b/sg/importer/Importer.h index 082a7559..32ec5355 100644 --- a/sg/importer/Importer.h +++ b/sg/importer/Importer.h @@ -157,17 +157,16 @@ inline std::shared_ptr getImporter( return nullptr; } std::string importer = fnd->second; - static int num = 0; - std::string nodeName = baseName + "_" + std::to_string(num++) + "_importer"; + std::string nodeName; if (cat.find(fullName) != cat.end()) { - // Existing import, instance it! - std::cout << "Instancing: " << nodeName << std::endl; - // Importer node and its rootXfm auto origNode = cat[fullName].lock(); std::string rootXfmName = baseName + "_rootXfm"; + // Existing import, instance it! + std::cout << "Instancing: " << origNode->name() << std::endl; + if (!origNode->hasChild(rootXfmName)) { std::cout << "!!! error... importer rootXfm is missing?! Is async tasking enabled? --no-async-tasking to disable" << std::endl; return nullptr; @@ -175,11 +174,9 @@ inline std::shared_ptr getImporter( auto &rootXfmNode = origNode->child(rootXfmName); // Create a unique instanceXfm nodeName - auto count = 1; - do { - nodeName = baseName + "_instanceXfm_" + std::to_string(count++); - } while (root->hasChild(nodeName)); - + int count = ++origNode->child("count").valueAs(); + nodeName = baseName + "_instanceXfm_" + std::to_string(count); + auto instanceXfm = createNode(nodeName, "transform"); // Add all children of the original rootXfm to this instanceXfm @@ -191,7 +188,10 @@ inline std::shared_ptr getImporter( return nullptr; } else { + nodeName = baseName + "_importer"; auto importNode = createNodeAs(nodeName, importer); + importNode->createChild("count", "int", 0); + importNode->child("count").setSGNoUI(); if (importer == "importer_raw") { std::cout << "Loading volumes with default volume parameters ..." << std::endl; From 018da0eb45f26f3fe591f47d4452116e802f055f Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Thu, 31 Mar 2022 14:01:20 +0200 Subject: [PATCH 24/78] add rootXfm directly to instanceXfm --- sg/importer/Importer.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sg/importer/Importer.h b/sg/importer/Importer.h index 32ec5355..965ae3af 100644 --- a/sg/importer/Importer.h +++ b/sg/importer/Importer.h @@ -179,9 +179,7 @@ inline std::shared_ptr getImporter( auto instanceXfm = createNode(nodeName, "transform"); - // Add all children of the original rootXfm to this instanceXfm - for (auto &g : rootXfmNode.children()) - instanceXfm->add(g.second); + instanceXfm->add(rootXfmNode); root->add(instanceXfm); From f04ee3a65e2baf0d5f46c101f9f5b46a95956e63 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Tue, 29 Mar 2022 15:20:56 +0200 Subject: [PATCH 25/78] remove arcball from batch --- app/ArcballCamera.cpp | 4 ++-- app/ArcballCamera.h | 17 ++++++++------ app/Batch.cpp | 51 ++++++++++++++++++---------------------- app/Batch.h | 5 +--- app/ospStudio.h | 6 ++++- sg/JSONDefs.h | 4 +++- sg/importer/Importer.cpp | 3 ++- 7 files changed, 46 insertions(+), 44 deletions(-) diff --git a/app/ArcballCamera.cpp b/app/ArcballCamera.cpp index 6602df23..02982406 100644 --- a/app/ArcballCamera.cpp +++ b/app/ArcballCamera.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2021 Intel Corporation +// Copyright 2017-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "ArcballCamera.h" @@ -150,7 +150,7 @@ void ArcballCamera::setZoomLevel(float zoomLevel) CameraState ArcballCamera::getState() const { - return CameraState(centerTranslation, translation, rotation); + return CameraState(centerTranslation, translation, rotation, cameraToWorld); } void ArcballCamera::updateWindowSize(const vec2i &windowSize) diff --git a/app/ArcballCamera.h b/app/ArcballCamera.h index 90312f81..3668ec04 100644 --- a/app/ArcballCamera.h +++ b/app/ArcballCamera.h @@ -1,4 +1,4 @@ -// Copyright 2017-2021 Intel Corporation +// Copyright 2017-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -16,11 +16,14 @@ class CameraState public: CameraState() = default; CameraState(const AffineSpace3f ¢erTrans, - const AffineSpace3f &trans, - const quaternionf &rot) - : centerTranslation(centerTrans), translation(trans), rotation(rot) - { - } + const AffineSpace3f &trans, + const quaternionf &rot, + const AffineSpace3f &camToWorld) + : centerTranslation(centerTrans), + translation(trans), + rotation(rot), + cameraToWorld(camToWorld) + {} CameraState slerp(const CameraState &to, float frac) const { @@ -55,7 +58,7 @@ class CameraState template void serialize(Archive & archive) { - archive( centerTranslation, translation, rotation ); + archive(centerTranslation, translation, rotation, cameraToWorld); } AffineSpace3f centerTranslation, translation; diff --git a/app/Batch.cpp b/app/Batch.cpp index 6415ad2a..25cd3bda 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -53,8 +53,9 @@ void BatchContext::start() for (int cameraIdx = cameraRange.lower; cameraIdx <= cameraRange.upper; ++cameraIdx) { resetFileId = true; - bool useCamera = refreshCamera(cameraIdx, true); + bool useCamera = refreshCamera(cameraIdx); if (useCamera) { + frame->child("windowSize") = optResolution; render(); if (fps) { std::cout << "..rendering animation!" << std::endl; @@ -269,18 +270,10 @@ void BatchContext::reshape() frame->child("windowSize") = fSize; frame->currentAccum = 0; - - // update camera - arcballCamera->updateWindowSize(fSize); } -bool BatchContext::refreshCamera(int cameraIdx, bool resetArcball) +bool BatchContext::refreshCamera(int cameraIdx) { - if (resetArcball) { - frame->child("windowSize") = optResolution; - arcballCamera.reset( - new ArcballCamera(getSceneBounds(), optResolution)); - } int hasParents{0}; if (cameraIdx <= cameras.size() && cameraIdx > 0) { @@ -329,10 +322,8 @@ bool BatchContext::refreshCamera(int cameraIdx, bool resetArcball) reshape(); // resets aspect - // if imported cameras don't have parent transform then use Arcball properties - if (!hasParents) - useArcball = true; - updateCamera(); + if (!sgScene) + updateCamera(); return true; } @@ -496,10 +487,6 @@ void BatchContext::refreshScene(bool resetCam) frame->add(world); - if (resetCam && !sgScene) - arcballCamera.reset( - new ArcballCamera(getSceneBounds(), optResolution)); - auto &fb = frame->childAs("framebuffer"); fb.resetAccumulation(); } @@ -508,11 +495,24 @@ void BatchContext::updateCamera() { frame->currentAccum = 0; auto &camera = frame->child("camera"); - - if (useArcball) { - camera["position"] = arcballCamera->eyePos(); - camera["direction"] = arcballCamera->lookDir(); - camera["up"] = arcballCamera->upDir(); + // if a finalCameraView is present, use that + if (finalCameraView) { + affine3f cameraToWorld = *finalCameraView; + camera["position"] = xfmPoint(cameraToWorld, vec3f(0, 0, 0)); + camera["direction"] = xfmVector(cameraToWorld, vec3f(0, 0, -1)); + camera["up"] = xfmVector(cameraToWorld, vec3f(0, 1, 0)); + } + // if no camera view or scene camera is selected calculate a default view + else if (cameraRange.lower == 0) { + auto worldBounds = getSceneBounds(); + auto worldDiag = length(worldBounds.size()); + auto centerTranslation = AffineSpace3f::translate(-worldBounds.center()); + auto translation = AffineSpace3f::translate(vec3f(0, 0, -worldDiag)); + auto cameraToWorld = rcp(translation * centerTranslation); + + camera["position"] = xfmPoint(cameraToWorld, vec3f(0, 0, 0)); + camera["direction"] = xfmVector(cameraToWorld, vec3f(0, 0, -1)); + camera["up"] = xfmVector(cameraToWorld, vec3f(0, 1, 0)); } if (cmdlCam) { @@ -528,11 +528,6 @@ void BatchContext::updateCamera() camera["interpupillaryDistance"] = optInterpupillaryDistance; } -void BatchContext::setCameraState(CameraState &cs) -{ - arcballCamera->setState(cs); -} - void BatchContext::importFiles(sg::NodePtr world) { importedModels = createNode("importXfm", "transform"); diff --git a/app/Batch.h b/app/Batch.h index 7503a3b8..d281cf20 100644 --- a/app/Batch.h +++ b/app/Batch.h @@ -5,7 +5,6 @@ #include "ospStudio.h" -#include "ArcballCamera.h" // ospray sg #include "sg/Frame.h" #include "sg/Node.h" @@ -32,11 +31,10 @@ class BatchContext : public StudioContext void refreshRenderer(); void refreshScene(bool resetCam) override; void updateCamera() override; - void setCameraState(CameraState &cs) override; void render(); virtual void renderFrame(); void renderAnimation(); - bool refreshCamera(int cameraIdx, bool resetArcball = false); + bool refreshCamera(int cameraIdx); void reshape(); protected: @@ -67,7 +65,6 @@ class BatchContext : public StudioContext sg::NodePtr selectedSceneCamera; float lockAspectRatio = 0.0; - bool useArcball{false}; // CLI std::string optImageName = "ospBatch"; diff --git a/app/ospStudio.h b/app/ospStudio.h index fb67c20b..dc4a3202 100644 --- a/app/ospStudio.h +++ b/app/ospStudio.h @@ -122,7 +122,7 @@ class StudioContext : public std::enable_shared_from_this // this method is so that importScene (in sg) does not need // to compile/link with ArcballCamera/UI - virtual void setCameraState(CameraState &cs) = 0; + virtual void setCameraState(CameraState &cs){}; std::shared_ptr frame; std::shared_ptr baseMaterialRegistry; @@ -134,6 +134,10 @@ class StudioContext : public std::enable_shared_from_this std::vector filesToImport; std::unique_ptr arcballCamera; + // for loading camera view from cameraToWorld matrix directly, used in modes + // with no dependency on CameraState or Arcball + std::shared_ptr finalCameraView{nullptr}; + int defaultMaterialIdx = 0; std::string outputFilename{""}; diff --git a/sg/JSONDefs.h b/sg/JSONDefs.h index 6b4226f1..3861fa26 100644 --- a/sg/JSONDefs.h +++ b/sg/JSONDefs.h @@ -450,7 +450,8 @@ inline void to_json(JSON &j, const CameraState &cs) { j = JSON{{"centerTranslation", cs.centerTranslation}, {"translation", cs.translation}, - {"rotation", cs.rotation}}; + {"rotation", cs.rotation}, + {"cameraToWorld", cs.cameraToWorld}}; } inline void from_json(const JSON &j, CameraState &cs) @@ -458,4 +459,5 @@ inline void from_json(const JSON &j, CameraState &cs) j.at("centerTranslation").get_to(cs.centerTranslation); j.at("translation").get_to(cs.translation); j.at("rotation").get_to(cs.rotation); + j.at("cameraToWorld").get_to(cs.cameraToWorld); } diff --git a/sg/importer/Importer.cpp b/sg/importer/Importer.cpp index 7e41f73c..e228b497 100644 --- a/sg/importer/Importer.cpp +++ b/sg/importer/Importer.cpp @@ -1,4 +1,4 @@ -// Copyright 2009-2021 Intel Corporation +// Copyright 2009-2022 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "Importer.h" @@ -204,6 +204,7 @@ OSPSG_INTERFACE void importScene( if (j.contains("camera")) { CameraState cs = j["camera"]; context->setCameraState(cs); + context->finalCameraView = std::make_shared(cs.cameraToWorld); context->updateCamera(); } From b3e3265da3dd5e3dfb5ce6b9c7c88ff95cfc4621 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Thu, 31 Mar 2022 18:51:02 +0200 Subject: [PATCH 26/78] support cams.json with affine3fs --- app/Batch.cpp | 72 ++++++++++++++++++++++++++++++++------------------- app/Batch.h | 4 +-- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 25cd3bda..8f398dad 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -38,10 +38,20 @@ void BatchContext::start() for (auto &p : studioCommon.pluginsToLoad) pluginManager->loadPlugin(p); + // read from cams.json + std::ifstream cams("cams.json"); + if (cams) { + JSON j; + cams >> j; + for (auto &cs : j) + cameraStack.push_back(cs.at("cameraToWorld")); + } + if (parseCommandLine()) { std::cout << "...importing files!" << std::endl; refreshRenderer(); refreshScene(true); + if (cameras.size()) std::cout << "List of imported scene cameras:\n"; for (int c = 1; c <= cameras.size(); ++c) { @@ -50,12 +60,29 @@ void BatchContext::start() << cameras[c - 1]->child("uniqueCameraName").valueAs() << std::endl; } + for (int cameraIdx = cameraRange.lower; cameraIdx <= cameraRange.upper; ++cameraIdx) { resetFileId = true; - bool useCamera = refreshCamera(cameraIdx); - if (useCamera) { - frame->child("windowSize") = optResolution; + refreshCamera(cameraIdx); + + // if a camera stack is present loop over every entry of camera stack for + // every camera + if (cameraStack.size()) + for (auto &c : cameraStack) { + finalCameraView = std::make_shared(c); + if (!sgScene) + updateCamera(); + render(); + if (fps) { + std::cout << "..rendering animation!" << std::endl; + renderAnimation(); + } else + renderFrame(); + } + else { + if (!sgScene) + updateCamera(); render(); if (fps) { std::cout << "..rendering animation!" << std::endl; @@ -64,6 +91,7 @@ void BatchContext::start() renderFrame(); } } + std::cout << "...finished!" << std::endl; sg::clearAssets(); } @@ -272,7 +300,7 @@ void BatchContext::reshape() frame->currentAccum = 0; } -bool BatchContext::refreshCamera(int cameraIdx) +void BatchContext::refreshCamera(int cameraIdx) { int hasParents{0}; @@ -305,27 +333,22 @@ bool BatchContext::refreshCamera(int cameraIdx) cameraId = cameraXfm->child("geomId").valueAs(); else cameraId = ".Camera_" + std::to_string(cameraIdx); + return; } else { - std::cout << "camera not used in GLTF scene" << std::endl; - return false; + std::cout << "camera not used in GLTF scene, using default camera.." + << std::endl; } - - } else { - std::cout << "No scene camera is selected." << std::endl; - if (optCameraTypeStr != "perspective") { - auto optCamera = createNode("camera", "camera_" + optCameraTypeStr); - frame->remove("camera"); - frame->add(optCamera); - } else - std::cout << "using default camera..." << std::endl; } - reshape(); // resets aspect - - if (!sgScene) - updateCamera(); + std::cout << "No scene camera is selected." << std::endl; + if (optCameraTypeStr != "perspective") { + auto optCamera = createNode("camera", "camera_" + optCameraTypeStr); + frame->remove("camera"); + frame->add(optCamera); + } else + std::cout << "using default camera..." << std::endl; - return true; + reshape(); // resets aspect } void BatchContext::render() @@ -358,13 +381,6 @@ void BatchContext::render() } frame->child("navMode") = false; - - std::ifstream cams("cams.json"); - if (cams) { - JSON j; - cams >> j; - cameraStack = j.get>(); - } } void BatchContext::renderFrame() @@ -489,6 +505,8 @@ void BatchContext::refreshScene(bool resetCam) auto &fb = frame->childAs("framebuffer"); fb.resetAccumulation(); + + frame->child("windowSize") = optResolution; } void BatchContext::updateCamera() diff --git a/app/Batch.h b/app/Batch.h index d281cf20..faa90d9a 100644 --- a/app/Batch.h +++ b/app/Batch.h @@ -34,7 +34,7 @@ class BatchContext : public StudioContext void render(); virtual void renderFrame(); void renderAnimation(); - bool refreshCamera(int cameraIdx); + void refreshCamera(int cameraIdx); void reshape(); protected: @@ -59,7 +59,7 @@ class BatchContext : public StudioContext std::vector cameras; std::string cameraId{""}; - std::vector cameraStack; + std::vector cameraStack; //camera animation sg::NodePtr selectedSceneCamera; From be367b526876c7206645589fbd3791c518797ceb Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Thu, 31 Mar 2022 19:10:33 +0200 Subject: [PATCH 27/78] cleanup refreshCamera --- app/Batch.cpp | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 8f398dad..212d0fc2 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -302,38 +302,33 @@ void BatchContext::reshape() void BatchContext::refreshCamera(int cameraIdx) { - int hasParents{0}; - if (cameraIdx <= cameras.size() && cameraIdx > 0) { std::cout << "Loading camera from index: " << std::to_string(cameraIdx) << std::endl; selectedSceneCamera = cameras[cameraIdx - 1]; - hasParents = selectedSceneCamera->parents().size(); - frame->remove("camera"); - frame->add(selectedSceneCamera); + bool hasParent = selectedSceneCamera->parents().size() > 0; // TODO: remove this Hack : for some reason the accumulated transform in // transform node does not get updated for the BIT animation scene. // Attempting to make transform modified so it picks up accumulated // transform values made by renderScene - if (hasParents) { + if (hasParent) { auto cameraXfm = selectedSceneCamera->parents().front(); if (cameraXfm->valueAs() == affine3f(one)) cameraXfm->createChild("refresh", "bool"); - } - if (selectedSceneCamera->hasChild("aspect")) - lockAspectRatio = selectedSceneCamera->child("aspect").valueAs(); - - // create unique cameraId for every camera - auto &cameraParents = selectedSceneCamera->parents(); - if (cameraParents.size()) { - auto &cameraXfm = cameraParents.front(); - if (cameraXfm->hasChild("geomId")) - cameraId = cameraXfm->child("geomId").valueAs(); - else - cameraId = ".Camera_" + std::to_string(cameraIdx); - return; + if (selectedSceneCamera->hasChild("aspect")) + lockAspectRatio = selectedSceneCamera->child("aspect").valueAs(); + + // create unique cameraId for every camera + if (cameraXfm->hasChild("geomId")) + cameraId = cameraXfm->child("geomId").valueAs(); + else + cameraId = ".Camera_" + std::to_string(cameraIdx); + frame->remove("camera"); + frame->add(selectedSceneCamera); + reshape(); // resets aspect + return; } else { std::cout << "camera not used in GLTF scene, using default camera.." << std::endl; @@ -513,7 +508,7 @@ void BatchContext::updateCamera() { frame->currentAccum = 0; auto &camera = frame->child("camera"); - // if a finalCameraView is present, use that + // use given camera view if present if (finalCameraView) { affine3f cameraToWorld = *finalCameraView; camera["position"] = xfmPoint(cameraToWorld, vec3f(0, 0, 0)); From e47a98e349c3dc4e87dcc1718bd4cc005c83cd27 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Fri, 1 Apr 2022 14:45:12 +0200 Subject: [PATCH 28/78] use json cameraToWorld only if present --- app/Batch.cpp | 6 ++++-- sg/JSONDefs.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 212d0fc2..3212d74a 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -43,8 +43,10 @@ void BatchContext::start() if (cams) { JSON j; cams >> j; - for (auto &cs : j) - cameraStack.push_back(cs.at("cameraToWorld")); + for (auto &cs : j) { + if (cs.find("cameraToWorld") != cs.end()) + cameraStack.push_back(cs.at("cameraToWorld")); + } } if (parseCommandLine()) { diff --git a/sg/JSONDefs.h b/sg/JSONDefs.h index 3861fa26..e6559823 100644 --- a/sg/JSONDefs.h +++ b/sg/JSONDefs.h @@ -459,5 +459,6 @@ inline void from_json(const JSON &j, CameraState &cs) j.at("centerTranslation").get_to(cs.centerTranslation); j.at("translation").get_to(cs.translation); j.at("rotation").get_to(cs.rotation); - j.at("cameraToWorld").get_to(cs.cameraToWorld); + if (j.find("cameraToWorld") != j.end()) + j.at("cameraToWorld").get_to(cs.cameraToWorld); } From a775ed1b689f09696785b72e958bb1663b7e8cf4 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Tue, 5 Apr 2022 10:44:29 -0500 Subject: [PATCH 29/78] [ci] Store klocwork report using store-files script --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 742548f5..364a9968 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -247,6 +247,7 @@ kw-gen-report: needs: [build-kw-dev] script: - gitlab/kw-gen-report.sh + - gitlab/store-files.sh $CI_PROJECT_NAME $CI_PIPELINE_ID klocwork "klocwork/report.log" artifacts: paths: - klocwork/report.log From 8cd23402a3b813ece4336e5d9f5633682b411fa8 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Fri, 25 Mar 2022 17:50:02 +0100 Subject: [PATCH 30/78] helper function to derive pers matrix --- sg/Math.h | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/sg/Math.h b/sg/Math.h index e2cb4e33..bf9c6f63 100644 --- a/sg/Math.h +++ b/sg/Math.h @@ -8,8 +8,87 @@ namespace ospray { namespace sg { +// input fov: in degrees +// input resolution: final resolution of the image +// output 4x4 projection matrix +// (can also be called the K-matrix) +inline std::vector> perspectiveMatrix( + float &fovy, vec2i &resolution) +{ + // scale by inverse FOV resolution + // flip Y axis because OSPRay has image start as lower left corner + // change w and z to -1 (see: + // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#projection-matrices) + auto x = 1.f / (2.f * tanf(deg2rad(0.5f * fovy)) * 1.5); + auto y = 1.f / (2.f * tanf(deg2rad(0.5f * fovy))); + float m[4][4]; + m[0][0] = x; + m[0][1] = 0; + m[0][2] = 0; + m[0][3] = 0; + m[1][0] = 0; + m[1][1] = -y; + m[1][2] = 0; + m[1][3] = 0; + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = -1; + m[2][3] = 0; + m[3][0] = 0; + m[3][1] = 0; + m[3][2] = -1; + m[3][3] = 0; + + // scale by final resolution and translate origin to center of image + auto xscale = resolution[0]; + auto yscale = resolution[1]; + auto xorigin = xscale / 2.f; + auto yorigin = yscale / 2.f; + float mRes[4][4]; + mRes[0][0] = xscale; + mRes[0][1] = 0; + mRes[0][2] = xorigin; + mRes[0][3] = 0; + mRes[1][0] = 0; + mRes[1][1] = yscale; + mRes[1][2] = yorigin; + mRes[1][3] = 0; + mRes[2][0] = 0; + mRes[2][1] = 0; + mRes[2][2] = 1; + mRes[2][3] = 0; + mRes[3][0] = 0; + mRes[3][1] = 0; + mRes[3][2] = 0; + mRes[3][3] = 1; + + std::vector> projMat; + float sum; + for (int i = 0; i < 4; i++) { + std::vector row(4); + for (int j = 0; j < 4; j++) { + sum = 0; + for (int k = 0; k < 4; k++) { + sum = sum + (mRes[i][k] * m[k][j]); + } + row[j] = sum; + } + projMat.push_back(row); + } +#if 0 + std::cout << "\nFinal projection matrix:\n"; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) + std::cout << projMat[i][j] << "\t"; + std::cout << std::endl; + } + std::cout << std::endl; +#endif + return projMat; +} + // convert euler angles(in radians) to quaternion -inline quaternionf toQuaternion(const vec3f &rotation) +inline quaternionf eulerToQuaternion(const vec3f &rotation) { float cy = cos(rotation[2] * 0.5); float sy = sin(rotation[2] * 0.5); From 584ba9f68e4a6285b44a91548017e27ac926cba5 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Fri, 1 Apr 2022 15:50:05 +0200 Subject: [PATCH 31/78] fix arcball navigation with scene cameras --- app/ArcballCamera.cpp | 14 +++++++++++- app/ArcballCamera.h | 2 ++ app/MainWindow.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/app/ArcballCamera.cpp b/app/ArcballCamera.cpp index 02982406..75467607 100644 --- a/app/ArcballCamera.cpp +++ b/app/ArcballCamera.cpp @@ -18,6 +18,18 @@ ArcballCamera::ArcballCamera(const box3f &worldBounds, const vec2i &windowSize) updateCamera(); } +void ArcballCamera::updateCameraToWorld( + const affine3f &_cameraToWorld, const quaternionf &rot) +{ + cameraToWorld = _cameraToWorld; + auto worldToCamera = rcp(cameraToWorld); + // update Translation and Rotation matrices + affine3f newTrans{one}; + newTrans.p = worldToCamera.p; + translation = newTrans; + rotation = rot; +} + void ArcballCamera::setNewWorldBounds(const box3f &worldBounds) { centerTranslation = AffineSpace3f::translate(-worldBounds.center()); updateCamera(); @@ -116,7 +128,7 @@ void ArcballCamera::updateCamera() { const AffineSpace3f rot = LinearSpace3f(rotation); const AffineSpace3f worldToCamera = translation * rot * centerTranslation; - cameraToWorld = rcp(worldToCamera); + cameraToWorld = rcp(worldToCamera); } void ArcballCamera::setRotation(quaternionf q) diff --git a/app/ArcballCamera.h b/app/ArcballCamera.h index 3668ec04..569840d7 100644 --- a/app/ArcballCamera.h +++ b/app/ArcballCamera.h @@ -104,6 +104,8 @@ class ArcballCamera public: ArcballCamera(const box3f &worldBounds, const vec2i &windowSize); + void updateCameraToWorld(const affine3f &cameraToWorld, const quaternionf &rot); + // All mouse positions passed should be in [-1, 1] normalized screen coords void rotate(const vec2f &from, const vec2f &to); void constrainedRotate(const vec2f &from, const vec2f &to, int axis /* 0 = x, 1 = y, 2 = z, otherwise none*/); diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 786a8f98..5cc499a0 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -25,6 +25,7 @@ #include "sg/visitors/SetParamByNode.h" #include "sg/visitors/CollectTransferFunctions.h" #include "sg/scene/volume/Volume.h" +#include "sg/Math.h" // rkcommon #include "rkcommon/math/rkmath.h" #include "rkcommon/os/FileName.h" @@ -725,6 +726,42 @@ void MainWindow::motion(const vec2f &position) bool cameraChanged = leftDown || rightDown || middleDown; + // if cameraChanged then switch back to default-camera and use current scene SG + // camera state + if (cameraChanged + && frame->child("camera") + .child("uniqueCameraName") + .valueAs() + != "default") { + auto defaultCamera = g_sceneCameras["default"]; + auto sgSceneCamera = frame->child("camera").nodeAs(); + + for (auto &c : sgSceneCamera->children()) { + if (c.first != "uniqueCameraName") { + if (defaultCamera->hasChild(c.first)) + defaultCamera->child(c.first) = c.second->value(); + else { + defaultCamera->createChild( + c.first, c.second->subType(), c.second->value()); + if (sgSceneCamera->child(c.first).sgOnly()) + defaultCamera->child(c.first).setSGOnly(); + } + } + } + + frame->remove("camera"); + frame->add(defaultCamera); + + auto worldToCamera = rcp(sgSceneCamera->cameraToWorld); + LinearSpace3f R, S; + ospray::sg::getRSComponent(worldToCamera, R, S); + auto rotation = ospray::sg::getRotationQuaternion(R); + + arcballCamera->updateCameraToWorld(sgSceneCamera->cameraToWorld, rotation); + // update centerTranslation of ArcBall + arcballCamera->setCenter(vec3f(0.f)); + } + auto sensitivity = maxMoveSpeed; if (glfwGetKey(glfwWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) sensitivity *= fineControl; @@ -805,8 +842,15 @@ void MainWindow::display() } // Update animation controller if playing - if (animationWidget->isPlaying()) + if (animationWidget->isPlaying()) { animationWidget->update(); + // use scene camera while playing animation + if (frame->child("camera").child("uniqueCameraName").valueAs() + == "default" && g_selectedSceneCamera) { + frame->remove("camera"); + frame->add(g_selectedSceneCamera); + } + } if (g_animatingPath) { static int framesPaused = 0; @@ -2261,6 +2305,10 @@ void MainWindow::buildWindowCameraEditor() return; } + if (frame->child("camera").child("uniqueCameraName").valueAs() + == "default") + whichCamera = 0; + // Only present selector UI if more than one camera if (!g_sceneCameras.empty() && ImGui::Combo("sceneCameras##whichCamera", From 6c47eee2577800b748237f7c7428277c8585ba24 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 8 Apr 2022 18:03:50 -0500 Subject: [PATCH 32/78] Validate and set cameraRange to existing cameras --- app/Batch.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 3212d74a..89868c98 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -63,6 +63,7 @@ void BatchContext::start() << std::endl; } + cameraRange.upper = std::min(cameraRange.upper, (int)cameras.size()); for (int cameraIdx = cameraRange.lower; cameraIdx <= cameraRange.upper; ++cameraIdx) { resetFileId = true; @@ -208,7 +209,7 @@ void BatchContext::addToCommandLine(std::shared_ptr app) { app->add_option( "--cameras", [&](const std::vector val) { - cameraRange.lower = std::stoi(val[0]); + cameraRange.lower = std::max(1, std::stoi(val[0])); cameraRange.upper = std::stoi(val[1]); return true; }, From 1fa744d01ab11f8d6cd19d2860f505232669bda3 Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Mon, 11 Apr 2022 11:21:52 +0200 Subject: [PATCH 33/78] exporting joints json --- sg/Node.cpp | 11 +++++++++++ sg/Node.h | 5 +++++ sg/importer/glTF.cpp | 2 ++ 3 files changed, 18 insertions(+) diff --git a/sg/Node.cpp b/sg/Node.cpp index 8efeda62..f33f9a8e 100644 --- a/sg/Node.cpp +++ b/sg/Node.cpp @@ -53,6 +53,17 @@ namespace ospray { return properties.whenCreated; } + // original node name(if present) as specified in a scene format file + void Node::setOrigName(const std::string &origName) + { + properties.origName = origName; + } + + std::string Node::getOrigName() + { + return properties.origName; + } + ///////////////////////////////////////////////////////////////////////////// // Node stored value (data) interface /////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// diff --git a/sg/Node.h b/sg/Node.h index dea681f9..80342943 100644 --- a/sg/Node.h +++ b/sg/Node.h @@ -93,6 +93,10 @@ namespace sg { std::string subType() const; std::string description() const; + // original node name(if present) as specified in a scene format file + void setOrigName(const std::string &origName); + std::string getOrigName(); + size_t uniqueID() const; // Node stored value (data) interface ///////////////////////////////////// @@ -261,6 +265,7 @@ namespace sg { NodeType type; std::string subType; std::string description; + std::string origName; Any value; // vectors allows using length to determine if min/max is set diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 0bc41254..ec5603fb 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -722,6 +722,8 @@ void GLTFData::visitNode(NodePtr sgNode, auto newXfm = createNode(nodeName + "_xfm_" + std::to_string(level), "transform"); + if (n.name != "") + newXfm->setOrigName(n.name); if (ic == 1) newXfm->child("dynamicScene").setValue(true); else if (ic == 2) From 93e2238913c4c3492e71d20472b33fd170d0dd30 Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Wed, 20 Apr 2022 12:58:38 -0400 Subject: [PATCH 34/78] Test centos:7 image containing packages for klocwork --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 364a9968..f0803d24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ stages: .centos7: tags: [ build, docker, modules ] - image: $DOCKER_REGISTRY/centos:7 + image: $DOCKER_REGISTRY/centos:7-tgt-centos7-kw-support before_script: - module load python/$PYTHON_VERSION - echo $Python3_ROOT @@ -144,7 +144,7 @@ build-windows-msvc15: build-kw: stage: build - extends: .ubuntu18.04 + extends: .centos7 script: - gitlab/build-kw.sh artifacts: @@ -226,7 +226,7 @@ build-windows-icl: build-kw-dev: stage: build - extends: .ubuntu18.04 + extends: .centos7 script: - gitlab/build-kw-dev.sh artifacts: From c60ca5403af13a23d5d5df14315e214b32991e2d Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Wed, 20 Apr 2022 12:58:38 -0400 Subject: [PATCH 35/78] Testing devel version of centos:7 image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f0803d24..418dc982 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ stages: .centos7: tags: [ build, docker, modules ] - image: $DOCKER_REGISTRY/centos:7-tgt-centos7-kw-support + image: $DOCKER_REGISTRY/centos:7-devel before_script: - module load python/$PYTHON_VERSION - echo $Python3_ROOT From 22452d0165046a2675c169631d5d70a064ac7ddb Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Wed, 20 Apr 2022 13:00:18 -0400 Subject: [PATCH 36/78] Devel branch of centos:7 merged to master --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 418dc982..d1b84bfb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ stages: .centos7: tags: [ build, docker, modules ] - image: $DOCKER_REGISTRY/centos:7-devel + image: $DOCKER_REGISTRY/centos:7 before_script: - module load python/$PYTHON_VERSION - echo $Python3_ROOT From d921f53efdf2ff8644af88eeda2955cf1a01b21d Mon Sep 17 00:00:00 2001 From: Isha Sharma Date: Wed, 13 Apr 2022 16:58:37 +0200 Subject: [PATCH 37/78] eyepos for arcball center, fix cameraeditor name display --- app/ArcballCamera.cpp | 7 +--- app/MainWindow.cpp | 85 ++++++++++++++++++++++--------------------- app/MainWindow.h | 1 + sg/camera/Camera.cpp | 9 +++++ sg/importer/glTF.cpp | 3 +- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/app/ArcballCamera.cpp b/app/ArcballCamera.cpp index 75467607..75d46e63 100644 --- a/app/ArcballCamera.cpp +++ b/app/ArcballCamera.cpp @@ -22,11 +22,6 @@ void ArcballCamera::updateCameraToWorld( const affine3f &_cameraToWorld, const quaternionf &rot) { cameraToWorld = _cameraToWorld; - auto worldToCamera = rcp(cameraToWorld); - // update Translation and Rotation matrices - affine3f newTrans{one}; - newTrans.p = worldToCamera.p; - translation = newTrans; rotation = rot; } @@ -90,7 +85,7 @@ void ArcballCamera::pan(const vec2f &delta) vec3f ArcballCamera::eyePos() const { - return xfmPoint(cameraToWorld, vec3f(0, 0, 0)); + return cameraToWorld.p; } void ArcballCamera::setCenter(const vec3f &newCenter) diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 5cc499a0..5817ae55 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -709,6 +709,37 @@ void MainWindow::keyboardMotion() updateCamera(); } +void MainWindow::changeToDefaultCamera() +{ + auto defaultCamera = g_sceneCameras["default"]; + auto sgSceneCamera = frame->child("camera").nodeAs(); + + for (auto &c : sgSceneCamera->children()) { + if (c.first != "uniqueCameraName" && c.first != "cameraId") { + if (defaultCamera->hasChild(c.first)) + defaultCamera->child(c.first) = c.second->value(); + else { + defaultCamera->createChild( + c.first, c.second->subType(), c.second->value()); + if (sgSceneCamera->child(c.first).sgOnly()) + defaultCamera->child(c.first).setSGOnly(); + } + } + } + + frame->remove("camera"); + frame->add(defaultCamera); + + auto worldToCamera = rcp(sgSceneCamera->cameraToWorld); + LinearSpace3f R, S; + ospray::sg::getRSComponent(worldToCamera, R, S); + auto rotation = ospray::sg::getRotationQuaternion(R); + + arcballCamera->updateCameraToWorld(sgSceneCamera->cameraToWorld, rotation); + activeWindow->centerOnEyePos(); + updateCamera(); // to reflect new default camera properties in GUI +} + void MainWindow::motion(const vec2f &position) { if (frame->pauseRendering) @@ -726,41 +757,11 @@ void MainWindow::motion(const vec2f &position) bool cameraChanged = leftDown || rightDown || middleDown; - // if cameraChanged then switch back to default-camera and use current scene SG - // camera state - if (cameraChanged - && frame->child("camera") - .child("uniqueCameraName") - .valueAs() - != "default") { - auto defaultCamera = g_sceneCameras["default"]; - auto sgSceneCamera = frame->child("camera").nodeAs(); - - for (auto &c : sgSceneCamera->children()) { - if (c.first != "uniqueCameraName") { - if (defaultCamera->hasChild(c.first)) - defaultCamera->child(c.first) = c.second->value(); - else { - defaultCamera->createChild( - c.first, c.second->subType(), c.second->value()); - if (sgSceneCamera->child(c.first).sgOnly()) - defaultCamera->child(c.first).setSGOnly(); - } - } - } - - frame->remove("camera"); - frame->add(defaultCamera); - - auto worldToCamera = rcp(sgSceneCamera->cameraToWorld); - LinearSpace3f R, S; - ospray::sg::getRSComponent(worldToCamera, R, S); - auto rotation = ospray::sg::getRotationQuaternion(R); - - arcballCamera->updateCameraToWorld(sgSceneCamera->cameraToWorld, rotation); - // update centerTranslation of ArcBall - arcballCamera->setCenter(vec3f(0.f)); - } + // if cameraChanged then switch back to default-camera and use current scene + // SG camera state + if (cameraChanged && frame->child("camera").child("uniqueCameraName").valueAs() + != "default") + changeToDefaultCamera(); auto sensitivity = maxMoveSpeed; if (glfwGetKey(glfwWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) @@ -797,9 +798,14 @@ void MainWindow::mouseButton(const vec2f &position) { if (frame->pauseRendering) return; - + if (glfwGetKey(glfwWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS && glfwGetMouseButton(glfwWindow, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { + // when picking new center of rotation change to default camera first + if (frame->child("camera").child("uniqueCameraName").valueAs() + != "default") + changeToDefaultCamera(); + vec2f scaledPosition = position * contentScale; pickCenterOfRotation(scaledPosition.x, scaledPosition.y); } @@ -1291,9 +1297,8 @@ void MainWindow::importFiles(sg::NodePtr world) auto mainCamera = frame->child("camera").nodeAs(); g_sceneCameras[mainCamera->child("uniqueCameraName") .valueAs()] = mainCamera; - // populate cameras in camera editor in View menu - for (auto &c : cameras) + for (auto c : cameras) g_sceneCameras[c->child("uniqueCameraName").valueAs()] = c; } } @@ -2305,9 +2310,7 @@ void MainWindow::buildWindowCameraEditor() return; } - if (frame->child("camera").child("uniqueCameraName").valueAs() - == "default") - whichCamera = 0; + whichCamera = frame->child("camera").child("cameraId").valueAs(); // Only present selector UI if more than one camera if (!g_sceneCameras.empty() diff --git a/app/MainWindow.h b/app/MainWindow.h index d6071190..eafa632e 100644 --- a/app/MainWindow.h +++ b/app/MainWindow.h @@ -89,6 +89,7 @@ class MainWindow : public StudioContext void updateTitleBar(); void refreshRenderer(); + void changeToDefaultCamera(); void updateCamera() override; void setCameraState(CameraState &cs) override; diff --git a/sg/camera/Camera.cpp b/sg/camera/Camera.cpp index a19ea849..bd998d51 100644 --- a/sg/camera/Camera.cpp +++ b/sg/camera/Camera.cpp @@ -54,6 +54,15 @@ Camera::Camera(const std::string &type) createChild("uniqueCameraName", "string", "default", std::string("default")); child("uniqueCameraName").setSGOnly(); child("uniqueCameraName").setSGNoUI(); + + // optional camera Index for a camera in a multi-camera scene + // to be set by the application or importer + createChild("cameraId", + "int", + "sets index of the camera in a scene with multiple cameras", + 0); + child("cameraId").setSGOnly(); + child("cameraId").setSGNoUI(); } NodeType Camera::type() const diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index ec5603fb..752dc63b 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -759,9 +759,8 @@ void GLTFData::visitNode(NodePtr sgNode, auto uniqueCamName = n.name != "" ? n.name : "camera_" + std::to_string(nCamera); camera->child("uniqueCameraName") = uniqueCamName; - camera->createChild("cameraId", "int", ++nCamera); + camera->child("cameraId").setValue(++nCamera); - camera->child("cameraId").setSGOnly(); cameras->push_back(camera); newXfm->add(camera); } From 1ebd6aac2d34ce3bd770119d4827bcc7b5f11d3e Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Wed, 20 Apr 2022 13:02:05 -0500 Subject: [PATCH 38/78] When switching cameras, commit frame after replacing camera node --- app/MainWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 5817ae55..1303beb2 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -729,6 +729,7 @@ void MainWindow::changeToDefaultCamera() frame->remove("camera"); frame->add(defaultCamera); + frame->commit(); auto worldToCamera = rcp(sgSceneCamera->cameraToWorld); LinearSpace3f R, S; From 97d24ba410c8fa468ed2c344c33c951f282d62cf Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 8 Apr 2022 14:09:53 -0500 Subject: [PATCH 39/78] Move several cmd options from batchmode-exclusive to studio common - imageFormat, imageName, layers, and bgColor command line options are now available to both batch and GUI. --- app/Batch.cpp | 51 +++++-------------------------- app/Batch.h | 13 -------- app/MainWindow.cpp | 35 +++++++++++++--------- app/MainWindow.h | 7 ----- app/ospStudio.cpp | 40 +++++++++++++++++++++++++ app/ospStudio.h | 75 ++++++++++++++++++++++++---------------------- 6 files changed, 107 insertions(+), 114 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 89868c98..7c628786 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -28,6 +28,9 @@ BatchContext::BatchContext(StudioCommon &_common) { frame->child("scaleNav").setValue(1.f); pluginManager = std::make_shared(); + + // Default saved image baseName (cmdline --image to override) + optImageName = "ospBatch"; } void BatchContext::start() @@ -133,16 +136,6 @@ void BatchContext::addToCommandLine(std::shared_ptr app) { }, "Set the camera up vector" )->expected(3); - app->add_option( - "--format", - optImageFormat, - "Sets the image format for color components(RGBA)" - )->check(CLI::IsMember({"png", "jpg", "ppm", "pfm", "exr", "hdr"})); - app->add_option( - "--image", - optImageName, - "Sets the image name (inclusive of path and filename)" - ); app->add_option( "--interpupillaryDistance", optInterpupillaryDistance, @@ -167,25 +160,6 @@ void BatchContext::addToCommandLine(std::shared_ptr app) { }, "Set the camera position" )->expected(3); - app->add_flag( - "--saveAlbedo", - saveAlbedo, - "Save albedo values" - ); - app->add_flag( - "--saveDepth", - saveDepth, - "Save depth values" - ); - app->add_flag( - "--saveNormal", - saveNormal, - "Save normal values" - ); - app->add_flag( - "--saveLayers", - saveLayersSeparatly, - "Save layers in separate files"); app->add_flag( "--saveMetadata", saveMetaData, @@ -229,17 +203,6 @@ void BatchContext::addToCommandLine(std::shared_ptr app) { frameStep, "Set the frames step when (frameRange is used)" )->check(CLI::PositiveNumber); - app->add_option( - "--bgColor", - [&](const std::vector val) { - bgColor = rgba(std::stof(val[0]), - std::stof(val[1]), - std::stof(val[2]), - std::stof(val[3])); - return true; - }, - "Set the renderer background color" - )->expected(4); } bool BatchContext::parseCommandLine() @@ -277,7 +240,7 @@ void BatchContext::refreshRenderer() if (renderer.hasChild("maxContribution") && maxContribution < (float)math::inf) renderer["maxContribution"].setValue(maxContribution); - renderer["backgroundColor"] = bgColor; + renderer["backgroundColor"] = optBackGroundColor; } void BatchContext::reshape() @@ -430,8 +393,8 @@ void BatchContext::renderFrame() } filenum += frameStep; - int screenshotFlags = saveLayersSeparatly << 3 | saveNormal << 2 - | saveDepth << 1 | saveAlbedo; + int screenshotFlags = optSaveLayersSeparately << 3 | optSaveNormal << 2 + | optSaveDepth << 1 | optSaveAlbedo; frame->saveFrame(filename, screenshotFlags); @@ -538,7 +501,7 @@ void BatchContext::updateCamera() } if (camera.hasChild("stereoMode")) - camera["stereoMode"] = optStereoMode; + camera["stereoMode"] = (int)optStereoMode; if (camera.hasChild("interpupillaryDistance")) camera["interpupillaryDistance"] = optInterpupillaryDistance; diff --git a/app/Batch.h b/app/Batch.h index faa90d9a..331f94c5 100644 --- a/app/Batch.h +++ b/app/Batch.h @@ -5,10 +5,6 @@ #include "ospStudio.h" -// ospray sg -#include "sg/Frame.h" -#include "sg/Node.h" -#include "sg/renderer/MaterialRegistry.h" // Plugin #include #include "sg/scene/Animation.h" @@ -41,13 +37,7 @@ class BatchContext : public StudioContext NodePtr importedModels; bool cmdlCam{false}; vec3f pos, up{0.f, 1.f, 0.f}, gaze{0.f, 0.f, 1.f}; - bool saveAlbedo{false}; - bool saveDepth{false}; - bool saveNormal{false}; - bool saveLayersSeparatly{false}; bool saveMetaData{true}; - std::string optImageFormat{"png"}; - rgba bgColor{vec3f(0.1f), 1.f}; float fps{0.0f}; bool forceRewrite{false}; @@ -65,7 +55,4 @@ class BatchContext : public StudioContext sg::NodePtr selectedSceneCamera; float lockAspectRatio = 0.0; - - // CLI - std::string optImageName = "ospBatch"; }; diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 1303beb2..7c77b552 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -183,6 +183,8 @@ MainWindow::MainWindow(StudioCommon &_common) throw std::runtime_error("Cannot create more than one MainWindow!"); } + // Default saved image baseName (cmdline --image to override) + optImageName = "studio"; optSPP = 1; // Default SamplesPerPixel in interactive mode is one. // Always create animationManager and animationWidget @@ -1122,6 +1124,7 @@ void MainWindow::refreshRenderer() if (optPF >= 0) r.createChild("pixelFilter", "int", optPF); + r.child("backgroundColor").setValue(optBackGroundColor); r.child("pixelSamples").setValue(optSPP); r.child("varianceThreshold").setValue(optVariance); if (r.hasChild("maxContribution") && maxContribution < (float)math::inf) @@ -1308,13 +1311,16 @@ void MainWindow::saveCurrentFrame() { int filenum = 0; char filename[64]; - const char *ext = screenshotFiletype.c_str(); + const char *ext = optImageFormat.c_str(); + // Find an unused filename to ensure we don't overwrite and existing file do - std::snprintf(filename, 64, "studio.%04d.%s", filenum++, ext); + std::snprintf( + filename, 64, "%s.%04d.%s", optImageName.c_str(), filenum++, ext); while (std::ifstream(filename).good()); - int screenshotFlags = screenshotLayersSeparatly << 3 - | screenshotNormal << 2 | screenshotDepth << 1 | screenshotAlbedo; + + int screenshotFlags = optSaveLayersSeparately << 3 | optSaveNormal << 2 + | optSaveDepth << 1 | optSaveAlbedo; auto &fb = frame->childAs("framebuffer"); auto fbFloatFormat = fb["floatFormat"].valueAs(); @@ -1421,10 +1427,11 @@ void MainWindow::buildMainMenuFile() static const std::vector screenshotFiletypes = sg::getExporterTypes(); - static int screenshotFiletypeChoice = std::distance( - screenshotFiletypes.begin(), - std::find( - screenshotFiletypes.begin(), screenshotFiletypes.end(), "png")); + static int screenshotFiletypeChoice = + std::distance(screenshotFiletypes.begin(), + std::find(screenshotFiletypes.begin(), + screenshotFiletypes.end(), + optImageFormat)); ImGui::SetNextItemWidth(5.f * ImGui::GetFontSize()); if (ImGui::Combo("##screenshot_filetype", @@ -1432,11 +1439,11 @@ void MainWindow::buildMainMenuFile() stringVec_callback, (void *)screenshotFiletypes.data(), screenshotFiletypes.size())) { - screenshotFiletype = screenshotFiletypes[screenshotFiletypeChoice]; + optImageFormat = screenshotFiletypes[screenshotFiletypeChoice]; } sg::showTooltip("Image filetype for saving screenshots"); - if (screenshotFiletype == "exr") { + if (optImageFormat == "exr") { // the following options should be available only when FB format is // float. auto &fb = frame->childAs("framebuffer"); @@ -1444,11 +1451,11 @@ void MainWindow::buildMainMenuFile() if (ImGui::Checkbox("FB float format ", &fbFloatFormat)) fb["floatFormat"] = fbFloatFormat; if (fbFloatFormat) { - ImGui::Checkbox("albedo##screenshotAlbedo", &screenshotAlbedo); + ImGui::Checkbox("albedo##saveAlbedo", &optSaveAlbedo); ImGui::SameLine(); - ImGui::Checkbox("layers as separate files", &screenshotLayersSeparatly); - ImGui::Checkbox("depth##screenshotDepth", &screenshotDepth); - ImGui::Checkbox("normal##screenshotNormal", &screenshotNormal); + ImGui::Checkbox("layers as separate files", &optSaveLayersSeparately); + ImGui::Checkbox("depth##saveDepth", &optSaveDepth); + ImGui::Checkbox("normal##saveNormal", &optSaveNormal); } } diff --git a/app/MainWindow.h b/app/MainWindow.h index eafa632e..f893d876 100644 --- a/app/MainWindow.h +++ b/app/MainWindow.h @@ -162,13 +162,6 @@ class MainWindow : public StudioContext bool showTransformEditor{false}; bool showRenderingStats{false}; - // imgui-controlled options - std::string screenshotFiletype{"png"}; - bool screenshotAlbedo{false}; - bool screenshotDepth{false}; - bool screenshotNormal{false}; - bool screenshotLayersSeparatly{false}; - // Option to always show a gamma corrected display to user. Native sRGB // buffer is untouched, linear buffers are displayed as sRGB. bool uiDisplays_sRGB{true}; diff --git a/app/ospStudio.cpp b/app/ospStudio.cpp index 94d3e212..b43dd6a9 100644 --- a/app/ospStudio.cpp +++ b/app/ospStudio.cpp @@ -76,11 +76,51 @@ void StudioContext::addToCommandLine(std::shared_ptr app) { optVariance, "set the threshold for adaptive accumluation when rendering" )->check(CLI::PositiveNumber); + app->add_option( + "--bgColor", + [&](const std::vector val) { + optBackGroundColor = rgba(std::stof(val[0]), + std::stof(val[1]), + std::stof(val[2]), + std::stof(val[3])); + return true; + }, + "Set the renderer background color" + )->expected(4); app->add_option( "--pixelfilter", optPF, "set default pixel filter (0=point, 1=box, 2=Gaussian, 3=Mitchell-Netravali, 4=Blackman-Harris)" ); + app->add_option( + "--format", + optImageFormat, + "Sets the default format for saved image files" + )->check(CLI::IsMember({"png", "jpg", "ppm", "pfm", "exr", "hdr"})); + app->add_option( + "--image", + optImageName, + "Sets the image name (inclusive of path and filename)" + ); + app->add_flag( + "--saveAlbedo", + optSaveAlbedo, + "Save albedo values" + ); + app->add_flag( + "--saveDepth", + optSaveDepth, + "Save depth values" + ); + app->add_flag( + "--saveNormal", + optSaveNormal, + "Save normal values" + ); + app->add_flag( + "--saveLayers", + optSaveLayersSeparately, + "Save layers in separate files"); app->add_option( "--resolution", [&](const std::vector val) { diff --git a/app/ospStudio.h b/app/ospStudio.h index dc4a3202..85696957 100644 --- a/app/ospStudio.h +++ b/app/ospStudio.h @@ -32,7 +32,7 @@ class App; using namespace ospray; using namespace rkcommon::math; -class PluginManager; +class PluginManager; enum class StudioMode { GUI, @@ -52,21 +52,20 @@ const static std::map StudioModeMap = { #ifdef USE_BENCHMARK {"benchmark", StudioMode::BENCHMARK}, #endif - }; +}; const static std::map standardResolutionSizeMap = { - {"144p", {256, 144}}, - {"240p", {426, 240}}, - {"360p", {640, 360}}, - {"480p", {640, 480}}, - {"720p", {1280, 720}}, - {"1080p", {1920, 1080}}, - {"1440p", {2560, 1440}}, - {"2160p", {3840, 2160}}, - {"4k", {3840, 2160}}, - {"4320p", {7680, 4320}}, - {"8k", {7680, 4320}}}; - + {"144p", {256, 144}}, + {"240p", {426, 240}}, + {"360p", {640, 360}}, + {"480p", {640, 480}}, + {"720p", {1280, 720}}, + {"1080p", {1920, 1080}}, + {"1440p", {2560, 1440}}, + {"2160p", {3840, 2160}}, + {"4k", {3840, 2160}}, + {"4320p", {7680, 4320}}, + {"8k", {7680, 4320}}}; // Common across all modes @@ -80,7 +79,8 @@ class StudioCommon : pluginsToLoad(_pluginsToLoad), denoiserAvailable(denoiser), argc(_argc), - argv(_argv) {} + argv(_argv) + {} void splitPluginArguments(); @@ -144,17 +144,17 @@ class StudioContext : public std::enable_shared_from_this StudioMode mode; - std::string optRendererTypeStr = "pathtracer"; - std::string optCameraTypeStr = "perspective"; - int optSPP = 32; - float optVariance = 0.f; // varianceThreshold - int optPF = -1; // use default + std::string optRendererTypeStr{"pathtracer"}; + std::string optCameraTypeStr{"perspective"}; + int optSPP{32}; + float optVariance{0.f}; // varianceThreshold + sg::rgba optBackGroundColor{vec3f(0.1f), 1.f}; + int optPF{-1}; // use default bool optDenoiser{false}; - bool optGridEnable = false; - vec3i optGridSize = {1, 1, 1}; - // XXX should be OSPStereoMode, but for that we need 'uchar' Nodes - int optStereoMode = 0; - float optInterpupillaryDistance = 0.0635f; + bool optGridEnable{false}; + vec3i optGridSize{1, 1, 1}; + OSPStereoMode optStereoMode{OSP_STEREO_NONE}; + float optInterpupillaryDistance{0.0635f}; sg::NodePtr volumeParams{}; float pointSize{0.05f}; vec2i optResolution{0, 0}; @@ -163,6 +163,12 @@ class StudioContext : public std::enable_shared_from_this bool optDoAsyncTasking{false}; float maxContribution{math::inf}; int frameAccumLimit{0}; + std::string optImageName{"studio"}; // (each mode sets this default) + std::string optImageFormat{"png"}; + bool optSaveAlbedo{false}; + bool optSaveDepth{false}; + bool optSaveNormal{false}; + bool optSaveLayersSeparately{false}; StudioCommon &studioCommon; @@ -174,17 +180,15 @@ class StudioContext : public std::enable_shared_from_this virtual box3f getSceneBounds(); bool sgScene{false}; // whether we are loading a scene file - }; inline OSPError initializeOSPRay(int &argc, const char **argv, bool use_mpi) { OSPDevice device; - if (use_mpi) - { - //TODO: calling ospInit seems to be required, - //even though the OSPRay MPI warns us not to do this... + if (use_mpi) { + // TODO: calling ospInit seems to be required, + // even though the OSPRay MPI warns us not to do this... OSPError initError = ospInit(&argc, argv); if (initError != OSP_NO_ERROR) { @@ -198,12 +202,11 @@ inline OSPError initializeOSPRay(int &argc, const char **argv, bool use_mpi) return OSP_UNKNOWN_ERROR; } - //TODO: setErrorCallback and ospDeviceSetParam calls seem to break mpi. Why? - } - else - { - // initialize OSPRay; OSPRay parses (and removes) its commandline parameters, - // e.g. "--osp:debug" + // TODO: setErrorCallback and ospDeviceSetParam calls seem to break mpi. + // Why? + } else { + // initialize OSPRay; OSPRay parses (and removes) its commandline + // parameters, e.g. "--osp:debug" OSPError initError = ospInit(&argc, argv); From 6c026136d3f58f8695288b72eb3effb002810382 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 8 Apr 2022 20:27:28 -0500 Subject: [PATCH 40/78] Extend BIT_scene_background with additional parameters - Optionally accept and parse HDRI intensity and visible-flag. - Note! The background color now defaults to black. It is left to the user to set a background color through either the UI or command-line arg --bgColor --- app/Batch.cpp | 20 +++--- app/MainWindow.cpp | 111 ++++++++++++++++++------------ app/MainWindow.h | 3 +- app/ospStudio.cpp | 2 +- app/ospStudio.h | 4 +- sg/importer/glTF.cpp | 7 ++ sg/renderer/Renderer.cpp | 2 +- sg/scene/lights/LightsManager.cpp | 15 +--- sg/scene/lights/LightsManager.h | 1 - 9 files changed, 90 insertions(+), 75 deletions(-) diff --git a/app/Batch.cpp b/app/Batch.cpp index 7c628786..c3cbe3a7 100644 --- a/app/Batch.cpp +++ b/app/Batch.cpp @@ -229,18 +229,14 @@ bool BatchContext::parseCommandLine() void BatchContext::refreshRenderer() { frame->createChild("renderer", "renderer_" + optRendererTypeStr); - auto &renderer = frame->childAs("renderer"); - - if (optPF >= 0) - renderer.createChild("pixelFilter", "int", optPF); - - renderer.child("pixelSamples").setValue(optSPP); - renderer.child("varianceThreshold").setValue(optVariance); - - if (renderer.hasChild("maxContribution") && maxContribution < (float)math::inf) - renderer["maxContribution"].setValue(maxContribution); - - renderer["backgroundColor"] = optBackGroundColor; + auto &r = frame->childAs("renderer"); + + r["pixelFilter"] = (int)optPF; + r["backgroundColor"] = optBackGroundColor; + r["pixelSamples"] = optSPP; + r["varianceThreshold"] = optVariance; + if (r.hasChild("maxContribution") && maxContribution < (float)math::inf) + r["maxContribution"].setValue(maxContribution); } void BatchContext::reshape() diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index 7c77b552..f2d93b34 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -485,6 +485,7 @@ void MainWindow::start() if (parseCommandLine()) { refreshRenderer(); + refreshScene(true); mainLoop(); } } @@ -1120,15 +1121,19 @@ void MainWindow::buildUI() void MainWindow::refreshRenderer() { - auto &r = frame->childAs("renderer"); - if (optPF >= 0) - r.createChild("pixelFilter", "int", optPF); + // Change renderer if current type doesn't match requested + auto currentType = frame->childAs("renderer").child("type") + .valueAs(); + if (currentType != optRendererTypeStr) + frame->createChild("renderer", "renderer_" + optRendererTypeStr); - r.child("backgroundColor").setValue(optBackGroundColor); - r.child("pixelSamples").setValue(optSPP); - r.child("varianceThreshold").setValue(optVariance); + auto &r = frame->childAs("renderer"); + r["pixelFilter"] = (int)optPF; + r["backgroundColor"] = optBackGroundColor; + r["pixelSamples"] = optSPP; + r["varianceThreshold"] = optVariance; if (r.hasChild("maxContribution") && maxContribution < (float)math::inf) - r["maxContribution"].setValue(maxContribution); + r["maxContribution"] = maxContribution; // Re-add the backplate on renderer change if (backPlateTexture != "") { @@ -1140,9 +1145,28 @@ void MainWindow::refreshRenderer() backplateTex = nullptr; backPlateTexture = ""; } + } else { + // Node removal requires waiting on previous frame completion + frame->cancelFrame(); + frame->waitOnFrame(); + r.remove("map_backplate"); + r.handle().removeParam("map_backplate"); } } +void MainWindow::saveRendererParams() +{ + auto &r = frame->childAs("renderer"); + + optRendererTypeStr = r["type"].valueAs(); + optPF = (OSPPixelFilterTypes) r["pixelFilter"].valueAs(); + optBackGroundColor = r["backgroundColor"].valueAs(); + optSPP = r["pixelSamples"].valueAs(); + optVariance = r["varianceThreshold"].valueAs(); + if (r.hasChild("maxContribution")) + maxContribution = r["maxContribution"].valueAs(); +} + void MainWindow::refreshScene(bool resetCam) { if (frameAccumLimit) @@ -1216,11 +1240,6 @@ bool MainWindow::parseCommandLine() } rendererTypeStr = optRendererTypeStr; - if (!filesToImport.empty()) { - std::cout << "Import files from cmd line" << std::endl; - refreshScene(true); - } - return true; } @@ -1607,12 +1626,21 @@ void MainWindow::buildMainMenuView() if (ImGui::MenuItem("Snapshots...", "", nullptr)) showSnapshots = true; + ImGui::Checkbox("Auto rotate", &optAutorotate); + if (optAutorotate) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); + ImGui::SliderInt(" speed", &autorotateSpeed, 1, 100); + } ImGui::Separator(); // Renderer stuff ////////////////////////////////////////////////// if (ImGui::MenuItem("Renderer...")) showRendererEditor = true; + + auto &renderer = frame->childAs("renderer"); + ImGui::Checkbox("Rendering stats", &showRenderingStats); ImGui::Checkbox("Pause rendering", &frame->pauseRendering); ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); @@ -1621,20 +1649,17 @@ void MainWindow::buildMainMenuView() // Although varianceThreshold is found under the renderer, add it here to // make it easier to find, alongside accumulation limit ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); - frame->childAs("renderer")["varianceThreshold"]. - traverse(sg::TreeState::ROOTOPEN); - ImGui::Checkbox("Auto rotate", &optAutorotate); - if (optAutorotate) { - ImGui::SameLine(); - ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); - ImGui::SliderInt(" speed", &autorotateSpeed, 1, 100); - } - ImGui::Separator(); + renderer["varianceThreshold"].traverse( + sg::TreeState::ROOTOPEN); - // Framebuffer and window stuff //////////////////////////////////// + // backgroundColor is also found under the renderer UI, but add it here to + // make it easier to find. + bool updated = false; + renderer["backgroundColor"].traverse( + sg::TreeState::ROOTOPEN, updated); + if (updated) + saveRendererParams(); - if (ImGui::MenuItem("Framebuffer...")) - showFrameBufferEditor = true; if (ImGui::MenuItem("Set background texture...")) showFileBrowser = true; if (!backPlateTexture.str().empty()) { @@ -1642,15 +1667,15 @@ void MainWindow::buildMainMenuView() "current: %s", backPlateTexture.base().c_str()); if (ImGui::MenuItem("Clear background texture")) { - frame->cancelFrame(); - frame->waitOnFrame(); backPlateTexture = ""; - // Needs to be removed from the renderer node and its OSPRay params - auto &renderer = frame->childAs("renderer"); - renderer.remove("map_backplate"); - renderer.handle().removeParam("map_backplate"); + refreshRenderer(); } } + + // Framebuffer and window stuff //////////////////////////////////// + ImGui::Separator(); + if (ImGui::MenuItem("Framebuffer...")) + showFrameBufferEditor = true; if (ImGui::BeginMenu("Quick window size")) { const std::vector options = {{480, 270}, {960, 540}, @@ -1744,15 +1769,7 @@ void MainWindow::buildMainMenuView() if (!fileList.empty()) { backPlateTexture = fileList[0]; - - auto backplateTex = - sg::createNodeAs("map_backplate", "texture_2d"); - if (backplateTex->load(backPlateTexture, false, false)) - frame->child("renderer").add(backplateTex); - else { - backplateTex = nullptr; - backPlateTexture = ""; - } + refreshRenderer(); } } } @@ -1841,11 +1858,13 @@ void MainWindow::buildWindowRendererEditor() rendererType = OSPRayRendererType::OTHER; // Change the renderer type, if the new renderer is different. - auto currentType = frame->childAs("renderer").subType(); + auto &renderer = frame->childAs("renderer"); auto newType = "renderer_" + rendererTypeStr; - if (currentType != newType) { - frame->createChildAs("renderer", newType); + if (renderer["type"].valueAs() != newType) { + // Save properties of current renderer then create new renderer + saveRendererParams(); + optRendererTypeStr = rendererTypeStr; refreshRenderer(); } } @@ -1862,7 +1881,10 @@ void MainWindow::buildWindowRendererEditor() } } - renderer.traverse(sg::TreeState::ROOTOPEN); + bool updated = false; + renderer.traverse(sg::TreeState::ROOTOPEN, updated); + if (updated) + saveRendererParams(); ImGui::End(); } @@ -2230,6 +2252,9 @@ void MainWindow::buildWindowLightEditor() if (lights.size() > 1) { if (ImGui::Button("remove")) { if (whichLight != -1) { + // Node removal requires waiting on previous frame completion + frame->cancelFrame(); + frame->waitOnFrame(); lightsManager->removeLight(lights.at_index(whichLight).first); whichLight = std::max(0, whichLight - 1); } diff --git a/app/MainWindow.h b/app/MainWindow.h index f893d876..c0ec2a14 100644 --- a/app/MainWindow.h +++ b/app/MainWindow.h @@ -89,8 +89,9 @@ class MainWindow : public StudioContext void updateTitleBar(); void refreshRenderer(); - void changeToDefaultCamera(); + void saveRendererParams(); + void changeToDefaultCamera(); void updateCamera() override; void setCameraState(CameraState &cs) override; void refreshScene(bool resetCamera) override; diff --git a/app/ospStudio.cpp b/app/ospStudio.cpp index b43dd6a9..d0bd8930 100644 --- a/app/ospStudio.cpp +++ b/app/ospStudio.cpp @@ -91,7 +91,7 @@ void StudioContext::addToCommandLine(std::shared_ptr app) { "--pixelfilter", optPF, "set default pixel filter (0=point, 1=box, 2=Gaussian, 3=Mitchell-Netravali, 4=Blackman-Harris)" - ); + )->check(CLI::Range(OSP_PIXELFILTER_POINT, OSP_PIXELFILTER_BLACKMAN_HARRIS)); app->add_option( "--format", optImageFormat, diff --git a/app/ospStudio.h b/app/ospStudio.h index 85696957..e1ed17db 100644 --- a/app/ospStudio.h +++ b/app/ospStudio.h @@ -148,8 +148,8 @@ class StudioContext : public std::enable_shared_from_this std::string optCameraTypeStr{"perspective"}; int optSPP{32}; float optVariance{0.f}; // varianceThreshold - sg::rgba optBackGroundColor{vec3f(0.1f), 1.f}; - int optPF{-1}; // use default + sg::rgba optBackGroundColor{vec3f(0.0f), 1.f}; // default to black + OSPPixelFilterTypes optPF{OSP_PIXELFILTER_GAUSS}; bool optDenoiser{false}; bool optGridEnable{false}; vec3i optGridSize{1, 1, 1}; diff --git a/sg/importer/glTF.cpp b/sg/importer/glTF.cpp index 752dc63b..eac49235 100644 --- a/sg/importer/glTF.cpp +++ b/sg/importer/glTF.cpp @@ -246,6 +246,13 @@ void GLTFData::applySceneBackground(NodePtr bgXfm) r[2].Get())); } + if (background.Has("visible")) + bgNode->child("visible") = background.Get("visible").Get(); + + if (background.Has("intensity")) + bgNode->child("intensity") = + (float)background.Get("intensity").Get(); + lights.push_back(bgNode); bgXfm->add(bgNode); } diff --git a/sg/renderer/Renderer.cpp b/sg/renderer/Renderer.cpp index 9bb24a93..ba0d91d2 100644 --- a/sg/renderer/Renderer.cpp +++ b/sg/renderer/Renderer.cpp @@ -22,7 +22,7 @@ Renderer::Renderer(std::string type) createChild("backgroundColor", "rgba", "transparent background color and alpha (RGBA), if no map_backplate set", - rgba(vec3f(0.1f), 1.f)); // Near black, with opaque alpha + rgba(vec3f(0.f), 1.f)); // black, with opaque alpha createChild("pixelFilter", "int", "pixel filter used by the renderer for antialiasing\n" diff --git a/sg/scene/lights/LightsManager.cpp b/sg/scene/lights/LightsManager.cpp index a1a4ad22..7581f922 100644 --- a/sg/scene/lights/LightsManager.cpp +++ b/sg/scene/lights/LightsManager.cpp @@ -35,16 +35,6 @@ bool LightsManager::addLight(NodePtr light) if (hasChild("default-ambient") && rmDefaultLight) removeLight("default-ambient"); - // When adding HDRI or sunSky, set background color to black. - // It's otherwise confusing. The user can still adjust it afterward. - if (light->subType() == "hdri" || light->subType() == "sunSky") { - if (!parents().empty()) { - auto &frame = parents().front(); - auto &renderer = frame->childAs("renderer"); - renderer["backgroundColor"] = rgba(vec3f(0.f), 1.f); // black, opaque - } - } - lightNames.push_back(light->name()); add(light); return true; @@ -101,11 +91,8 @@ void LightsManager::clear() removeLight(name); } - // Re-add the default-ambient light and gray background, when clearing lights + // Re-add the default-ambient light when clearing lights addLight("default-ambient", "ambient"); - auto &frame = parents().front(); - auto &renderer = frame->childAs("renderer"); - renderer["backgroundColor"] = rgba(vec3f(0.1f), 1.f); // Near black } void LightsManager::preCommit() diff --git a/sg/scene/lights/LightsManager.h b/sg/scene/lights/LightsManager.h index 0e106e22..0476c1ff 100644 --- a/sg/scene/lights/LightsManager.h +++ b/sg/scene/lights/LightsManager.h @@ -5,7 +5,6 @@ #include "sg/Node.h" #include "sg/scene/World.h" -#include "sg/renderer/Renderer.h" namespace ospray { namespace sg { From ea8a96194856be6ca3e17848b626a31c99970cb1 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Tue, 19 Apr 2022 18:10:03 -0500 Subject: [PATCH 41/78] Add GenerateWidget() helper - Now call GenerateWidget() rather than .traverse(sg::TreeState::ROOTOPEN); --- app/MainWindow.cpp | 39 ++++++++++-------------------- app/widgets/GenerateImGuiWidgets.h | 16 ++++++++++++ 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index f2d93b34..b77de118 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -1646,19 +1646,12 @@ void MainWindow::buildMainMenuView() ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); ImGui::DragInt( "Limit accumulation", &frame->accumLimit, 1, 0, INT_MAX, "%d frames"); - // Although varianceThreshold is found under the renderer, add it here to - // make it easier to find, alongside accumulation limit + + // Although varianceThreshold and backgroundColor are found in the + // renderer UI, also add them here to make them easier to find. ImGui::SetNextItemWidth(5 * ImGui::GetFontSize()); - renderer["varianceThreshold"].traverse( - sg::TreeState::ROOTOPEN); - - // backgroundColor is also found under the renderer UI, but add it here to - // make it easier to find. - bool updated = false; - renderer["backgroundColor"].traverse( - sg::TreeState::ROOTOPEN, updated); - if (updated) - saveRendererParams(); + GenerateWidget(renderer["varianceThreshold"]); + GenerateWidget(renderer["backgroundColor"]); if (ImGui::MenuItem("Set background texture...")) showFileBrowser = true; @@ -1881,9 +1874,7 @@ void MainWindow::buildWindowRendererEditor() } } - bool updated = false; - renderer.traverse(sg::TreeState::ROOTOPEN, updated); - if (updated) + if (GenerateWidget(renderer)) saveRendererParams(); ImGui::End(); @@ -1898,7 +1889,7 @@ void MainWindow::buildWindowFrameBufferEditor() } auto &fb = frame->childAs("framebuffer"); - fb.traverse(sg::TreeState::ALLOPEN); + GenerateWidget(fb, sg::TreeState::ALLOPEN); ImGui::Separator(); @@ -2244,8 +2235,7 @@ void MainWindow::buildWindowLightEditor() if (whichLight != -1) { ImGui::Text("edit"); - lightsManager->child(lights.at_index(whichLight).first) - .traverse(sg::TreeState::ROOTOPEN); + GenerateWidget(lightsManager->child(lights.at_index(whichLight).first)); } } @@ -2386,9 +2376,7 @@ void MainWindow::buildWindowCameraEditor() } // UI Widget - auto &camera = frame->childAs("camera"); - bool updated = false; - camera.traverse(sg::TreeState::ROOTOPEN, updated); + GenerateWidget(frame->childAs("camera")); ImGui::End(); } @@ -2408,7 +2396,7 @@ void MainWindow::buildWindowMaterialEditor() searchWidget.addSearchResultsUI(*baseMaterialRegistry); auto selected = searchWidget.getSelected(); if (selected) { - selected->traverse(sg::TreeState::ROOTOPEN); + GenerateWidget(*selected); ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(245, 200, 66, 255)); if (ImGui::TreeNodeEx( "Advanced options##materials", ImGuiTreeNodeFlags_None)) { @@ -2637,7 +2625,7 @@ void MainWindow::buildWindowIsosurfaceEditor() ImGui::Text("== empty == "); } else { for (auto &surface : surfaces) { - surface->traverse(); + GenerateWidget(*surface); if (surface->isModified()) break; } @@ -2680,9 +2668,8 @@ void MainWindow::buildWindowTransformEditor() searchWidget.addSearchResultsUI(warudo); auto selected = searchWidget.getSelected(); - if (selected) { - selected->traverse(sg::TreeState::ROOTOPEN); - } + if (selected) + GenerateWidget(*selected); ImGui::End(); } diff --git a/app/widgets/GenerateImGuiWidgets.h b/app/widgets/GenerateImGuiWidgets.h index 346ffd79..9a69d46e 100644 --- a/app/widgets/GenerateImGuiWidgets.h +++ b/app/widgets/GenerateImGuiWidgets.h @@ -671,5 +671,21 @@ inline void GenerateImGuiWidgets::postChildren(Node &, TraversalContext &ctx) } } + +// +// Helpers to generate widget and return whether it was modified +// +inline bool GenerateWidget(Node *root, TreeState state = TreeState::ROOTOPEN) +{ + bool updated = false; + root->traverse(state, updated); + return updated; +} + +inline bool GenerateWidget(Node &root, TreeState state = TreeState::ROOTOPEN) +{ + return GenerateWidget(&root, state); +} + } // namespace sg } // namespace ospray From c8d710623a5046abf56c9ec1ff4bd94a9d59dd06 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Thu, 21 Apr 2022 16:39:46 -0500 Subject: [PATCH 42/78] Fix FileDialog default directory. - Now opens up in correct path, not one above --- app/widgets/FileBrowserWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/widgets/FileBrowserWidget.cpp b/app/widgets/FileBrowserWidget.cpp index a57f554d..348ef7a4 100644 --- a/app/widgets/FileBrowserWidget.cpp +++ b/app/widgets/FileBrowserWidget.cpp @@ -33,6 +33,7 @@ bool fileBrowser(FileList &fileList, prompt.c_str(), filters.c_str(), defaultPath, + "", allowMultipleSelection ? 0 : 1); if (ImGuiFileDialog::Instance()->Display(prompt.c_str(), From 86d7bb375171e900f6ed759e25a6d70a23696a1e Mon Sep 17 00:00:00 2001 From: Trevor Thomson Date: Tue, 26 Apr 2022 16:57:31 +0000 Subject: [PATCH 43/78] Remove --static-libstdc++ flag --- .gitlab-ci.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d1b84bfb..24a6c2a3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,6 +23,14 @@ stages: - echo $Python3_ROOT - ls $Python3_ROOT +.centos7-test: + tags: [ build, docker, modules ] + image: $DOCKER_REGISTRY/ospray/docker-images:centos7-mod + before_script: + - module load python/$PYTHON_VERSION + - echo $Python3_ROOT + - ls $Python3_ROOT + .ubuntu18.04: tags: [ build, docker, modules ] image: $DOCKER_REGISTRY/ubuntu:18.04 @@ -106,7 +114,7 @@ build-centos7: - module load intel/2020.1 - export CC=icc - export CXX=icpc - - export CXXFLAGS="-static-intel -static-libstdc++ -fPIC -D_GLIBCXX_USE_CXX11_ABI=0" + - export CXXFLAGS="-static-intel -fPIC -D_GLIBCXX_USE_CXX11_ABI=0" - gitlab/build.sh build-ubuntu18.04: @@ -156,7 +164,7 @@ build-kw: test-run-centos7: stage: test - extends: .centos7 + extends: .centos7-test needs: [ build-centos7 ] script: - build/install/bin/ospStudio --verify_install @@ -278,7 +286,7 @@ release-centos7: - module load intel/2020.1 - export CC=icc - export CXX=icpc - - export CXXFLAGS="-static-intel -static-libstdc++ -fPIC -D_GLIBCXX_USE_CXX11_ABI=0" + - export CXXFLAGS="-static-intel -fPIC -D_GLIBCXX_USE_CXX11_ABI=0" - gitlab/build.sh package release-windows: From 2be63a259cb5cd3e0f964f40ca64c0c06916d678 Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Tue, 26 Apr 2022 18:11:42 -0500 Subject: [PATCH 44/78] Fix crash when using the "clear scene" menu --- app/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/MainWindow.cpp b/app/MainWindow.cpp index b77de118..0ce79d79 100644 --- a/app/MainWindow.cpp +++ b/app/MainWindow.cpp @@ -1555,7 +1555,7 @@ void MainWindow::buildMainMenuEdit() lightsManager->clear(); animationManager->getAnimations().clear(); animationManager->getTimeRange() = range1f{empty}; - animationWidget.reset(); + animationWidget->update(); // TODO: lights caching to avoid complete re-importing after clearing sg::clearAssets(); From 84c5bc29342f3c7ccf127af9dbed994f94101f8d Mon Sep 17 00:00:00 2001 From: Bruce Cherniak Date: Fri, 13 May 2022 15:37:09 -0500 Subject: [PATCH 45/78] Update version of 3rd party Catch2 catch.hpp --- sg/tests/catch/catch.hpp | 15652 +++++++++++++++++++++++++------------ sg/tests/test_Node.cpp | 2 - 2 files changed, 10803 insertions(+), 4851 deletions(-) diff --git a/sg/tests/catch/catch.hpp b/sg/tests/catch/catch.hpp index 03f5add5..d2a12427 100644 --- a/sg/tests/catch/catch.hpp +++ b/sg/tests/catch/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.0.1 - * Generated: 2017-11-03 11:53:39.642003 + * Catch v2.13.9 + * Generated: 2022-04-12 22:37:23.260201 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +13,10 @@ // start catch.hpp +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 9 + #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ @@ -26,50 +30,62 @@ # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wparentheses" + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_MAC == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) # define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -77,6 +93,13 @@ # endif #endif +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h // start catch_tag_alias_autoregistrar.h // start catch_common.h @@ -89,6 +112,7 @@ // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -101,37 +125,74 @@ #ifdef __cplusplus -# if __cplusplus >= 201402L +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) # define CATCH_CPP14_OR_GREATER # endif +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + #endif -#ifdef __clang__ +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# endif +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ @@ -139,6 +200,25 @@ # define CATCH_CONFIG_COLOUR_NONE #endif +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ @@ -146,12 +226,19 @@ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#ifdef _MSC_VER +#if defined(_MSC_VER) // Universal Windows platform does not support SEH // Or console colours (or console at all...) @@ -161,8 +248,45 @@ # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif +# if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + //////////////////////////////////////////////////////////////////////////////// // Use of __COUNTER__ is suppressed during code analysis in @@ -174,24 +298,170 @@ #define CATCH_INTERNAL_CONFIG_COUNTER #endif +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h @@ -207,6 +477,10 @@ #include #include +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + namespace Catch { struct CaseSensitive { enum Choice { @@ -228,14 +502,17 @@ namespace Catch { struct SourceLineInfo { SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - bool empty() const noexcept; + bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; @@ -245,10 +522,10 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - bool isTrue( bool value ); - bool alwaysTrue(); - bool alwaysFalse(); + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop @@ -275,7 +552,11 @@ namespace Catch { } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h @@ -283,7 +564,6 @@ namespace Catch { // start catch_interfaces_testcase.h #include -#include namespace Catch { @@ -294,8 +574,6 @@ namespace Catch { virtual ~ITestInvoker(); }; - using ITestCasePtr = std::shared_ptr; - class TestCase; struct IConfig; @@ -305,6 +583,7 @@ namespace Catch { virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); @@ -317,74 +596,366 @@ namespace Catch { #include #include #include +#include namespace Catch { - class StringData; - /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. + /// it may not be null terminated. class StringRef { - friend struct StringRefTestAccess; - + public: using size_type = std::size_t; + using const_iterator = const char*; - char const* m_start; - size_type m_size; + private: + static constexpr char const* const s_empty = ""; - char* m_data = nullptr; + char const* m_start = s_empty; + size_type m_size = 0; - void takeOwnership(); + public: // construction + constexpr StringRef() noexcept = default; - public: // construction/ assignment - StringRef() noexcept; - StringRef( StringRef const& other ) noexcept; - StringRef( StringRef&& other ) noexcept; StringRef( char const* rawChars ) noexcept; - StringRef( char const* rawChars, size_type size ) noexcept; - StringRef( std::string const& stdString ) noexcept; - ~StringRef() noexcept; - auto operator = ( StringRef other ) noexcept -> StringRef&; - operator std::string() const; + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} - void swap( StringRef& other ) noexcept; + explicit operator std::string() const { + return std::string(m_start, m_size); + } public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } - auto operator[] ( size_type index ) const noexcept -> char; + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } public: // named queries - auto empty() const noexcept -> bool; - auto size() const noexcept -> size_type; - auto numberOfCharacters() const noexcept -> size_type; + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; + // Returns the current start pointer. May not be null-terminated. auto data() const noexcept -> char const*; - }; - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } } // namespace Catch +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + // end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template